Put Qt objects from QtCreator into a Python list - python

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?

Related

wxPython - How to set an editor for a column of a grid

The docs for wx.grid.GridCellEditor say
Instances of wx.grid.GridCellEditor ... can be associated with the cell attributes for individual cells, rows, columns, or even for the entire grid.
Now, I know how to associate an editor with a cell:
self.mygrid.SetCellEditor(row, 1, wx.grid.GridCellEditorSubclass())
And I know how to associate an editor with an entire grid:
self.mygrid.SetDefaultEditor(wx.grid.GridCellEditorSubclass())
But I don't know how to set an editor for a single column. This obvious workaround is not a good solution:
for row in range(nrows):
self.mygrid.SetCellEditor(row, 1, wx.grid.GridCellEditorSubclass())
because if I add rows to the grid (which is a common operation), the new rows don't have the editor until I specifically set it again.
The other obvious workaround is to associate a generic editor class with the entire grid and instantiate a column-specific editor at runtime based on the column number.
The documentation clearly implies that I shouldn't have to resort to either workaround. But it also doesn't offer (at least anywhere I can find) another way to do it.
So, the question is, how do I set an editor for an entire grid column?
The most obvious solution for this, a method name something like "SetDefaultEditorForColumn", does not seem to exist.
But I think that the idea with Grid is to specify an editor for a data type rather than for a single column. This way you can have more than one column with the same data type in your grid and use the same editor.
That's where Grid.RegisterDataType(self, typeName, renderer, editor) makes its entry.
If you only want to modify only one of the two, renderer or editor, you may also want to call Grid.GetDefaultRendererForType() and Grid.GetDefaultEditorForType().
Isn't that function performed by the following? :
SetColFormatBool(col)
SetColFormatFloat(col)
SetColFormatNumber(col)
SetColFormatCustom(col, typeName)
These functions set both the editor and renderer for the column in question.
The approach in the other two answers is to create a datatype, associate an editor with the datatype, and and then associate the datatype with a column. Decoupling the editor from the column number is a good idea if you have multiple columns with the same datatype, and so these solutions are probably the best generic approach.
But I wanted a simpler approach that associates an editor with a specific column because, in my application, every column is a different datatype. Sharing the datatype across columns makes no sense. So this answer is a closer fit to the original question.
It all revolves around the confusingly named class wx.grid.GridCellAttr. You could be forgiven for assuming that the name element Attr essentially means alignment and colour, because that is what it means in, for example, wx.TextAttr. And the most clearly documented constructor of this class is GridCellAttr(colText, colBack, font, hAlign, vAlign) which again suggests that Attr is about alignment and colour.
But the class wx.grid.GridCellAttr is actually a very generic cell properties descriptor class with over 30 methods for setting and getting alignment and colour, as expected, but also (among other things) size, read/write mode, renderer, and editor.
So the way to set an editor for the column of a grid is
my_col_property_settings = wx.grid.GridCellAttr()
my_col_property_settings.SetEditor(MyColumnSpecificEditor())
my_col_property_settings.SetAlignment(hAlign=wx.ALIGN_CENTRE, vAlign=wx.ALIGN_CENTRE)
# ... etc ...
self.mygrid.SetColAttr(1, my_col_property_settings)
But if you want to set the same editor for two columns, do not call SetColAttr() with the same instance of GridCellAttr, because you will get reference count errors at program shutdown if you do. Instead, do
self.mygrid.SetColAttr(1, my_col_property_settings.Clone())
self.mygrid.SetColAttr(2, my_col_property_settings.Clone())

Clearing all Labels from a tkinter window

So I'm new to tkinter, but I've got what I want working, up to a certain point.
I'm not sure I've set it up correctly, but I've got a world map with buttons on the right, and an events log on the left, which fills up with labels as stuff happens.
Issue is that after a little while, the whole log fills up.
What is the best way to delete all the labels, or maybe delete the oldest (top) label each time?
Here's what I mean:
Defined here:
root=Tk()
Map=PhotoImage(file="C:/Users/Willam/Desktop/CWProgram/map2.gif")
background=Label(root,image=Map).place(x=100,y=0,relwidth=1,relheight=1)
Title=Label(root,text=' LOG').pack(anchor=NW)
And I create my labels like this:
info=Label(root,text='Select a sector to move units from',wraplength=170)
info.pack(anchor=NW)
I tried the usual info.destoy() and info.forget(), but these only work on the last label used in that function.
Should I have grouped all labels or something?
As PM 2Ring suggested it is usually useful to append labels to a list for future ref:
tmp = Label(...)
labels.append(tmp)
then just:
foreach label in labels: label.destroy()
If you do not want a list, and you're sure you want to clear everything in root:
foreach label in root.children.values(): label.destroy()
The children dict always holds the objects contained within. If you want to keep the map label, you will have to make your own list as I showed, without appending info into it.
I would recommend using:
info.pack_forget()
For each pack you created you must do it in the format:
packname.pack_forget()
Which if you have a lot of packs is impractical, but otherwise it works very well.
This also makes it very easy to selectively remove some labels and leave others as it will not purge all packs that you placed.
Just use:
root.children.clear
After clearing screen just input map and functions again...

Python : sort a GTK treeview

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.

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.

PyQt Sort List of Radio Buttons

I am trying to dynamically create a list of radio buttons that represents the open COM ports on my computer. Creating and displaying the list the first time is easy enough since I can just sort the ports to be in numerical order and then add their corresponding radio button to my vertical layout.
However, if the user inserts a new device which creates a new COM port, I have to find some way to add the new button in the correct place since it might not be in the right numerical order. So far, the only way I have been able to do this is to just get rid of all the buttons and then re-add them after sorting the list since addWidget doesn't let me specify where to add the widget. This method seems really inefficient, and I am assuming there is a simpler way, but I just have not found it yet.
Instead of using addWidget(), determine the index in the list of buttons to place the new one, and use QBoxLayout.insertWidget(index, widget) to insert it there:
newButton = QRadioButton(...)
newText = newButton.text()
index = 0
for button in get_buttons():
if button.text() >= newText:
break
index += 1
layout.insertWidget(index, newButton)

Categories

Resources