I have two combo boxes, and this is my attempt to enable a button only when options in both boxes are selected.
However, when I select only one comboBox, the button will enable itself.
if self.page2.comboBox2.activated and self.page2.comboBox.activated:
self.page2.viewbutton.setEnabled(True)
else:
self.page2.viewbutton.setEnabled(False)
Your code won't work, because the activated attribute is a signal object, which will always evaluate to True. If you are using comboboxes like the ones in your other question, then you need to check the current index to see whether the user has selected a valid option:
if (self.page2.comboBox2.currentIndex() > 0 and
self.page2.comboBox.currentIndex() > 0):
self.page2.viewbutton.setEnabled(True)
else:
self.page2.viewbutton.setEnabled(False)
That is, if the current index is zero, the "Select product" message is still shown.
Related
When you want to select an item in a Treeview, you usually use the double-click:
def print_element(event):
print(my_treeview.selection()[0])
my_treeview.bind("<Double-1>", print_element)
Today I tried to do the same but using a single click instead:
my_treeview.bind("<Button-1>", print_element)
But it wouldn't work. The output was just an empty tuple.
I started to search online for an explanation... why is it not working?
EDIT:
My goal was actually to do something every time a treeview item was selected.
I proposed a solution myself using the identify() function of Tkinter
Another user proposed to use the Tkinter callback <ButtonRelease-1> which is much more appropriate
Finally, a third user focused his answer on using the Tkinter callback <<TreeviewSelect>>, which is for sure the best option
The reason it doesn't work the way you expect is because your custom single-click binding happens before the default behavior. So, when your single-click is processed, that happens before an item is selected. The second time you click, your function will print the previously selected item.
If you want to have a function called when an item is selected, you should bind to <<TreeviewSelect>>, which will fire immediately after the user selects an item with a single click or via the keyboard.
The default behavior of a treeview supports selecting multiple items at once, so the following code will print out the text of all of the selected items as a list, even if only a single item is selected. You can, of course, modify this to only print out the first selected item if you so desire.
def print_element(event):
tree = event.widget
selection = [tree.item(item)["text"] for item in tree.selection()]
print("selected items:", selection)
tree.bind("<<TreeviewSelect>>", print_element)
It is because the selection is not set yet when the <Button-1> (it is the same as <ButtonPress-1>, i.e. when the mouse button 1 is pressed and not released) event callback is called.
You should bind on <ButtonRelease-1> or <<TreeviewSelect>> instead as the selection is set when the event callback is being executed.
Why it doesn't work
When you click an item in a treeview, that item is still not in a SELECTED status in the moment the callback is activated. You are changing the status in that very moment.
Using a double-click, the first click change the status, and at the second click you are activating your callback, so the status has already been changed.
How can it work
Kudos to this website
In short,
def print_element(event):
print(my_treeview.identify('item', e.x, e.y))
my_treeview.bind("<Button-1>", print_element)
This time, print_element() will check the coordinates of the mouse, and will discover the selected item check what is under the mouse. Nice and clean!
I have an object of type QListWidget where the user can select 0 or more items.
If nothing is selected, how can i trigger an event that would call a function?
I know that you can detect clicking on a specific item by using:
QListWidget.itemClicked.connect(self.item_click)
Is there something similar for when nothing is selected at all? (or in other words, the QListWidget is clear)
Thanks!
Generally, you would connect to the itemSelectionChanged signal and then check whether anything is selected.
self.listwidget.itemSelectionChanged.connect(self.on_selection_changed)
def on_selection_changed(self):
if not self.listwidget.selectedItems():
# Do Stuff Here
self.nothing_selected_function()
But that will only catch events where something was selected and then the user deselected everything. If nothing was ever selected, it's not going to trigger this signal (like the first time you build the list, and nothing is selected). You'd have to call the slot manually in that case.
self.listwidget = ... # Code that builds and populates list widget
# Call this manually the first time.
self.on_selection_changed()
But part of your question is ambiguous. Why do you want to know when something is "not selected"? What about when a new item is added to the list? Should it trigger your "not selected" function since the list has changed, but there still isn't anything selected?
I have been looking for a treeView widget option where I can select only a single row at a time by clicking it and then de-select it by clicking on it again. I found how to make it so that you can select only one row at a time, but I was unable to find an option that allowed u to click the selected row to de-select it.
Does anyone know how this can be done? Any help would be appreciated.
You can set a custom binding to deselect the item if it's currently selected. If your binding returns the string break, it will stop event propagation and thus prevent the default behavior for double-click.
...
self.tree = ttk.Treeview(...)
self.tree.bind("<1>", self.on_click)
...
def on_click(self, event):
selection = self.tree.selection()
item = self.tree.select_row(event.y)
if item in selection:
self.tree.selection_remove(item)
return "break"
Unfortunately, the only built-in pattern to toggle the selection state of a treeview item is by selecting a different item but you can do a hack of your own.
In my program, I've got a menu with a group of RadioMenuItem entries. Choosing one of them should trigger a function which can either succeed or fail. If it fails, this RadioMenuItem shouldn't be marked chosen (the previous one should persist). Besides, sometimes I want to set marked item without running the choice processing function.
Here is my current code:
# Update seat menu list
def update_seat_menu(self, seats, selected_seat=None):
seat_menu = self.builder.get_object('seat_menu')
# Delete seat menu items
for menu_item in seat_menu:
# TODO: is it a good way? does remove() delete obsolete menu_item from memory?
if menu_item.__class__.__name__ == 'RadioMenuItem': seat_menu.remove(menu_item)
# Fill menu with new items
group = []
for seat in seats:
menu_item = Gtk.RadioMenuItem.new_with_label(group, str(seat[0]))
group = menu_item.get_group()
seat_menu.append(menu_item)
if str(seat[0]) == selected_seat: menu_item.activate()
menu_item.connect("activate", self.choose_seat, str(seat[0]))
menu_item.show()
# Process item choice
def choose_seat(self, entry, seat_name):
# Looks like this is called when item is deselected, too; must check if active
if entry.get_active():
# This can either succeed or fail
self.logind.AttachDevice(seat_name, '/sys'+self.device_syspath, True)
Chosen RadioMenuItem gets marked irrespective of the choose_seat() execution result; and the only way to set marked item without triggering choose_seat() is to re-run update_seat_menu() with selected_seat argument, which is an overkill.
I tried to connect choose_seat() with 'button-release-event' instead of 'activate' and call entry.activate() in choose_seat() if AttachDevice() succeeds, but this resulted in whole X desktop lockup until AttachDevice() timed out, and chosen item still got marked.
In my opinion a different approach would be better. When the user clicks an item, display a spinner or something to let the user know an operation is going on, then do your long-running choose_seat() in the background. If it fails, then change the radio button to non-active and set it to be insensitive (since you now know it doesn't work.)
Thanks to help from people on irc.gnome.org/gtk+, I've got this with handler_block_by_func()/handler_unblock_by_func():
# Update chosen seat menu item (without triggering choose_seat)
def update_chosen_seat(self, seat):
self.seat_menu_items[seat].handler_block_by_func(self.choose_seat)
self.seat_menu_items[seat].activate()
self.seat_menu_items[seat].handler_unblock_by_func(self.choose_seat)
And in choose_seat() I use the same approach to activate previously chosen item, then run actual handler code and activate chosen item if it succeeds.
I want to display the result of an SQL query with radio buttons. For this purpose, I use an array of radiobuttons. This works fine, it displays what I want. The problem I have is that the buttons are not "checkable". If I click on them, nothing happens. I'm sure it is a stupid mistake, but I can't see what's wrong.
Here is my code :
groups = select.getGroups(self.parent.db) #This returns a list of strings
self.groupList = {}
self.groupBtn = []
i = 0
radioLayout = QtGui.QVBoxLayout()
radioGroup = QtGui.QGroupBox()
for g in groups:
self.groupList[g[0]] = i
name = g[0]
self.groupBtn.append(QtGui.QRadioButton(name))
radioLayout.addWidget(self.groupBtn[i])
i = i+1
radioGroup.setLayout(radioLayout)
self.groupBtn[0].setChecked(True)
self.groupLayout.addWidget(radioGroup)
Is there something obvious I'm missing ?
"Radio buttons typically present the user with a "one of many" choice. In a group of radio buttons only one radio button at a time can be checked; if the user selects another button, the previously selected button is switched off."
If that is not your desired behaviour, you may consider using check buttons (QCheckButton). If it is:
You need to be sure that you have not incidentally set setAutoExclusive(false) somewhere in code, for any of those radio buttons (by default it is set on true).
Last thing I can suggest (based on the code that you have shown): Try to put radio buttons in QButtonGroup() instead of QGroupBox().