Python : sort a GTK treeview - python

I don't understand how the gtk.Treeview() list model works:
From what I understand I have to wrap the lismodel in a gtk.TreeModelSort() but when I do that, I can't access the listmodel methods anymore..
lm = gtk.ListStore(object)
listmodel = gtk.TreeModelSort(lm)
for f in files:
listmodel.append([f])
return listmodel
AttributeError: 'gtk.TreeModelSort' object has no attribute 'append'
I'm working with this example (source): how would one make the table sortable on every column?

The gtk.TreeModelSort is just a model (interface for access), it does not actually store anything. In fact, you don't need to invoke it yourself to make your table sortable. The simplest way is to let GTK do the sorting for you, which means that you have to store C data and not Python objects in your table. So, change your make_list() method to
listmodel = gtk.ListStore(str)
instead of object. Then you just have to tell the gtk.TreeView that clicking on the first column header should sort according to the first column in the gtk.ListStore by putting
self.tvcolumn[0].set_sort_column_id(0)
into your constructor. To sort by file size you'd add an extra colum consisting of int in the list store and use that as sort column id etc.
Edit: Ok let me spell the last thing out:
listmodel = gtk.ListStore(str, int)
for f in files:
file_size = os.stat(os.path.join(self.dirname, f)).st_size
listmodel.append([f, file_size])
puts the file size in the second column of the store. Then, to sort by file size you can just add
self.tvcolumn[1].set_sort_column_id(1)
In general, I would recommend to restructure your code that everything is stored in the ListStore once at the beginning. That way, you can code the cell rendering much easier by just tying each view column to a store column.

Related

Django-Import-Export assign Field to specific column

I have a working form which exports all data to xls using Resource and Fields from the django-import-export package. But now I need to go a bit further and make it a bit more advanced. I need to export all data to an existing Excel template, assigning each field to a specific column, as in, for instance: Field 1 needs to be filled in B3, Field 2 in C4 etc. etc.
I'm thinking after_export() can be used here but the docs dont go into detail how to use this method. Does anyone have some experience with this package?
I'm not using the admin integration.
after_export() is a method you can override to modify data before it is returned.
It does nothing by default. However, it supplies the data object, which is a Dataset instance. If you access the dict property, then you will have a list of OrderedDict instances corresponding to each row which is returned by your queryset. Therefore, you can construct an entirely new Dataset (list of dicts) which match up with the format you need.
For example, if you wanted the first column of the first row to appear in cell B3, you could do it like this:
def after_export(self, queryset, data, *args, **kwargs):
newdata = tablib.Dataset()
cell1_val = data.dict[0].get("col1", "")
for i, row in enumerate(data):
if i == 1:
newrow = list(row)
# cell b3
newrow[2] = cell1_val
row = newrow
newdata.append(row)
data.dict = newdata.dict
However, you'll note that this approach is not easy to read and is quite unwieldy. Therefore if you don't need any other features of django-import-export then using a library like openpyxl is probably a better approach.

Put Qt objects from QtCreator into a Python list

I have a QtWidget, built in QtCreator. Let's say there are 10 QLineEdits in the widget named Edit0 to Edit9. The data that is supposed to go in them is stored in a Python list data. Is there a way to put those 10 QLineEdits in a Python list so I can basically assign their values like:
for index in range(len(data)):
Edit[index].setText('{:.2}'.format(data[index]))
Since you're using python, you can access fields via string:
for index in range(len(data)):
getattr(ui, f"Edit{index}").setText('{:.2}'.format(data[index]))
But relying on the name is an ugly style.
You can also iterate over the layout which contains the edits.
Here is how to do it in C++, the method names are the same in python.
But that becomes ugly if you want to add something else to the layout, e.g., a Button.
Neither of these methods do scale.
If you have many edits in a fixed pattern, you should consider creating them yourself with your code and put them in a list in the first place.
edits = []
for index in range(42):
edit = QLineEdit()
edits.append(edit)
ui.some_layout.addWidget(edit) # add edit to the layout
# later:
for edit, data in zip(edits, data):
edit.setText('{:.2}'.format(data[index]))
However, it seems to me like you're building a table.
Do you know QListWidget, QTableWidget, QListView and QTableView?

How to write many objects in binary file and read it an especific object?

In Python, I have to develop a simple program to write and read objects in a binary file. There will be a maximum of 11 records which will be inserted, loaded or removed one by one. It's something like the file has 11 slots and when the data is inserted, the slot index will be passed. So when the user tries to insert in an index which is already populated, it fails and the user gets the message.
So, my idea was to save the data based depending on the size of the object. For example: if the user inserts a data in the index 3, i would not insert in the beggining of the file, would start inserting in "3 * (Object size)"
But the problem is that the objects sizes aren't always the same, so it would loose the reference to the data requested.
import pickle
class Reg:
def __init__(self, id, name):
self.id = id
self.name = name
def display(self):
print("{}: {}".format(self.id, self.name))
user = Reg(642213, "{}{}".format("José da Silva", 642213))
print(len(pickle.dumps(user)))
user = Reg(462903, "{}{}".format("Carlos Ribeiro", 462903))
print(len(pickle.dumps(user)))
user = Reg(502453, "{}{}".format("Alberto", 502453))
print(len(pickle.dumps(user)))
user = Reg(189567, "{}{}".format("Rodrigo Peixoto Gama", 189567))
print(len(pickle.dumps(user)))
The console log is: 81 81 74 87
So, how can i do it? Is there a way to set a default size for all the objects?
Two ways.
One, if you know the maximum size of the objects, you can use that as the fixed size for all of them. The object protocol shouldn't have a problem detecting the end of the object when you read too much.
The second way, is to create an "index".
The index is a collection of 11 fixed length offsets, stored at the beginning of the file.
Then when you add in the new object, you can just update the index with it's offset. This way you can also put things in "out of order" (i.e. the "3rd" item is the 3rd slot in the index, but the first object in the data).
Obviously in more complicated scenarios these both get much more nuanced.
But if your example is limited, either can do the trick.
Addenda:
On the one hand, you can pad the name out to 20 characters with spaces, and simply trim them off later. That makes them all the same size.
On the other, it doesn't really matter. You need to do random dis I/O to locate the objects. You can simply seek to the proper offset in the file and write/read from there. The pickle process will do the rest.
So, in that case, you don't need to know how big the objects are, just as long as they aren't too long.

How to set multiple items into a GtkSelection for Treeview drag and drop

My current project uses a Gtk.TreeView to display the contents of a ListView with four fields per row, two strings, an int and a boolean. I'm trying to implement drag and drop rearrangement of rows in the TreeView. I don't want simply to use TreeView.set_reorderable(True) for the built-in drag and drop because I want to have some control over the insertion and deletion of data from the model as well as to be able to implement undo/redo of drag and drop operations. I'm using Python 3.2 and PyGObject 3.
The problem I'm now having is figuring out how in my drag_data_get method to set the selection data object with the two strings, one int and one bool that make up the row to be dragged and dropped. All the example code I've been able to find involves treeviews with a single column with string values that get set into the selection with something like this:
def drag_data_get_data(self, treeview, context, selection, target_id, etime):
treeselection = treeview.get_selection()
model, iter = treeselection.get_selected()
data = bytes(model.get_value(iter, 0), "utf-8")
selection.set(selection.get_target(), 8, data)
All my efforts to set the selection object with the data from one of my TreeView rows have failed. The int and bool values in my model can't be encoded like string values and I can't find any examples of how to set all the values for a multi-column TreeView row into a single selection object. Can anyone point me to some relevant examples or docs?
You could encode your tuple of 4 values into a single string. An easy way is to use json for that:
import json
data = ["string", "string2", True, 20]
string_variable = json.dumps(data)
#
# now pass string_variable through drag and drop
#
returned = json.loads(string_variable)
You could also use your own encoding scheme if importing json is not an option for you.
Please do a careful sanity check on the data you get this way. If you don't, some specially crafted string (passed from another program, say) might crash you program or worse.

Custom objects in ListStore/TreeStore

I have a list L of objects of my class A. This class implements __str__/__repr__, so each object has it's own string representation (not necessary unique). I have a GUI in pygtk, where I have a TreeView widget with only one column. I want to populate it with string representations of the objects in L, but then I want to get selected items as objects, not as string. Is there a way to make TreeView to store list of objects, but display them as stings? If not, then what is the best way to know what objects are selected in the TreeView?
The problem also is that depending on some conditions I can populate TreeView not with the whole L, but with some sublist of it, and so indexes of items in TreeView won't correspond to ones in L.
You could store the object in one column (gobject.TYPE_PYOBJECT) and the string representation in a second column, and then only display the second column in your treeview. Similar to what's done here: http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/
If your Glade is 3.7.0 or newer you can type "PyObject" (without the quotes) as the column type for you ListStore. Then use set_cell_data_func to retrieve an object from the model and pass its representation to the CellRenderer as text. No string columns to synchronize and no indexes to worry about.
If the strings are unique you can use a dictionary to link the strings with the objects by using the strings as keys. In this case you can find the objects by its string.

Categories

Resources