How to toggle enabled state of widgets based on QcomboBox index - python

I am using pyQT 4.8.3 in order to create a proper GUI for a QGIS plugin
There are three widgets in the form
my_comboBox , my_lineEdit , my_spinBox
Assume that comboBox has three entries
'combo_first_item' , 'combo_second_item' , 'combo_third_item'
What exactly I want is;
if 'combo_second_item' is selected, then my_lineEdit toggles state to disabled
if 'combo_third_item' selected, then my_spinBox toggles state to disabled
So, how can I toggle enabled state of widgets in the form based on selected string (or index value) from the combobox?
What should be the proper signal -> slot assignment?
Unlike QbuttonBox, QcomboBox does not fire SetDisabled slot
Thanks.

Make a dictionary that maps the string to widget:
widgets = {'combo_first_item': my_comboBox,
'combo_second_item': my_lineEdit,
'combo_third_item': my_spinBox}
And a slot:
def disableWidget(currentIndex):
widget = widgets[currentIndex]
widget.setEnabled(False)
# or anything else you want to do on the widget
Then you can connect the currentIndexChanged[QString] signal to this:
comboBox.currentIndexChanged['QString'].connect(disableWidget)
Alternatively you can use currentIndexChanged[int] and a list instead of a dictionary.
PS: If this is inside a class instance, put self accordingly.

Related

Entry widget and state 'normal'

I had to use an Entry widget in the 'readonly' state, then I wanted to change the text with the insert method : to do so, I understand that it is needed to put the widget in the state 'normal', but I get an error : is that "normal"?
For instance,
entry = ttk.Entry(root,width=30)
entry.insert(0,"Please enter your name")
entry.state(['readonly'])
entry.state(['normal'])
Gives me as a result :
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/ttk.py", line 595, in state
return self.tk.splitlist(str(self.tk.call(self._w, "state", statespec)))
_tkinter.TclError: Invalid state name normal
Confusingly, ttk widgets have two different concepts of "state". One is a configuration option named state, and one is a method named state. The configuration option is a shorthand to change the editability of the widget. The method is used to get and set several internal states used for styling.
These two ways of specifying state accept different values. The "readonly" state is one state that is valid on both, but "normal" is only valid for the configuration option.
To use the state method, you pass in a stateSpec which is a list of one or more states or negated states. For example, to switch between readonly and not readonly you would do it this way:
entry.state(["readonly"])
entry.state(["!readonly"])
This manner of specifying a widget state is much more flexible and fine-grained, and is designed to allow complex styling. For example, a widget can at one time be in the states readonly, disabled, invalid, and hover.
The simpler way to configure whether the widget is editable or not is with the state configuration option. It must always be one of the string values "normal", "disabled", or "readonly".
entry.configure(state="readonly")
entry.configure(state="normal")
The allowable values for the configuration option are fairly self-explanatory. The values for building up a stateSpec via the state method are the following:
active - The mouse cursor is over the widget and pressing a mouse button will cause some action to occur. (aka “prelight” (Gnome), “hot” (Windows), “hover”).
disabled - Widget is disabled under program control (aka “unavailable”, “inactive”).
focus - Widget has keyboard focus.
pressed - Widget is being pressed (aka “armed” in Motif).
selected - “On”, “true”, or “current” for things like checkbuttons and radiobuttons.
background - Windows and the Mac have a notion of an “active” or foreground window. The background state is set for widgets in a background window, and cleared for those in the foreground window.
readonly - Widget should not allow user modification.
alternate - A widget-specific alternate display format. For example, used for checkbuttons and radiobuttons in the “tristate” or “mixed” state, and for buttons with -default active.
invalid - The widget's value is invalid. (Potential uses: scale widget value out of bounds, entry widget value failed validation.)
hover - The mouse cursor is within the widget. This is similar to the active state; it is used in some themes for widgets that provide distinct visual feedback for the active widget in addition to the active element within the widget.

When QComboBox is set editable

The code below creates QComboBox and QPushButton both assigned to the same layout. Combobox is set to be editable so the user is able to type a new combobox item's value.
If the user hits Tab keyboard key (instead of Enter) the New Value will not be added to the ComboBox.
Question: How to make sure the ComboBox's items are updated with the New Value even if the user leaves the ComboBox with Tab key?
from PyQt4 import QtGui
def comboActivated(arg=None):
print '\n ...comboActivated: %s'%arg
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
widget.setLayout(layout)
combo = QtGui.QComboBox()
combo.setEditable(True)
combo.addItems(['One','Two','Three'])
combo.activated.connect(comboActivated)
layout.addWidget(combo)
layout.addWidget(QtGui.QPushButton('Push'))
widget.show()
When a user edits the text in the box, the editTextChanged() signal is emitted with the edited text as its argument. In addition, when the widget itself loses focus, as when the user types Tab to move to the button, the widget emits the focusOutEvent() signal. The argument for this signal is a QFocusEvent, which you can query for the reason focus was lost. The reason() method of the event would return Qt.TabFocusReason, for example, if the user hit the Tab button to leave the widget.
You can connect a slot to either (or both) of these signals, so that when the user leaves the widget after editing text, you process it and add it to the box's list of values.
You may also want to look into the QValidator class and its subclasses, which you attach to widgets with editable text, and define the types of valid input for the widget (e.g., integers, text, etc.). This is the best and easiest way to verify a user's input for editable widgets.

Python PyQt QtoolbuttonPopup mode

From what i understood from the internet resources, I could create a Popup menu of QActions on the Qtoolbar by using Qtoolbuttonpopup mode.
So, I created a QMenu and added a few QActions to it by using QMenu.addAction.
After that i have created a QToolButton and set the ToolButtonPopupMode to 2. Followed by setting the QMenu i have created above as the menu for it by using .setMenu(QMenu)
SettingMenu = QtGui.QMenu()
SettingMenu.addAction(Action1)
SettingMenu.addAction(Action2)
SettingButton = QtGui.QToolButton()
SettingButton.setIcon(QtGui.QIcon(QtGui.QPixmap(':/setting.png')))
SettingButton.ToolButtonPopupMode(2)
SettingButton.setMenu(SettingMenu)
from the above code, i am expecting to have a Qtoolbutton on my toolbar and when i click on it, it should pop up a menu with 2 Actions. But when i run the code, all i see is a Qtoolbutton on my toolbar but when i click the Qtoolbutton it doesn't create any pop up menu.
Am i doing this wrong? How do i create a toolbutton that create a pop up menu of actions upon user click?
ToolButtonPopupMode is an enumerating type. All the values in that enum are instances of that type. Because it inherits from int, calling it with an integer returns the same integer. However, you want to set the popupMode property, so use setPopupMode(2).

PyGtk CellRendererCombo using Pixbuf

I would like to display a ComboBox in my TreeView that contains some Icons. So I created a ListStore to hold the data.
# Initialize a list store for the combobox.
priorityModel = Gtk.ListStore(GObject.TYPE_INT, GdkPixbuf.Pixbuf)
priorityModel.append([0, self.loadPixbuf('./data/media/flag_blue.png')])
priorityModel.append([1, self.loadPixbuf('./data/media/flag_green.png')])
priorityModel.append([2, self.loadPixbuf('./data/media/flag_yellow.png')])
priorityModel.append([3, self.loadPixbuf('./data/media/flag_red.png')])
Then I created the Gtk.CellRendererCombo object and assigned the above Gtk.ListStore as model.
# Setup the priority cell renderer, ...
self.priorityRenderer = Gtk.CellRendererCombo()
self.priorityRenderer.set_property( 'editable', True )
self.priorityRenderer.set_property("model", priorityModel)
self.priorityRenderer.connect("edited", self.on_priority_changed, self.listStore, 3)
Finally I created a new Gtk.TreeViewColumn and assigned the CellRenderer and added it to the TreeView.
# ... setup the priority column ...
self.colPriority = Gtk.TreeViewColumn("Priority", self.priorityRenderer, text=3 )
# ... and add it to the treeview.
self.append_column( self.colPriority )
All nice but how could I display the Pixbuf icon instead of the integer and how do I initialize the ComboBox using the integer? And, second question, is it possible to show only the icon when the combobox is not active and show the icon and some text when the value is changed (ComboBox active)?
You need a Gtk.CellRendererPixbuf to display the icon. See for example the excellent Python 3 Gtk+ Tutorial.
Answering your second question: Yes. Just connect to the "changed" signal of the Gtk.CellRendererCombo and add/remove the cell renderer dynamically.

Big number of pushbuttons and smart checking which is checked

I have dialog:
It contains many flat QPushButtons, QTextEdit and another QPushButton. After click on 'Get list' we can see list of checked buttons in QTextEdit.
My question is how to get this functionality in some smart way. Right now I'm checking every button:
if self.ui.bq6o.isChecked():
cards.append("Q6o")
if self.ui.bk2o.isChecked():
cards.append("K2o")
if self.ui.bq3o.isChecked():
cards.append("Q3o")
if self.ui.bt7s.isChecked():
cards.append("T7s")
if self.ui.bq4o.isChecked():
cards.append("Q4o")
if self.ui.bt4s.isChecked():
cards.append("T4s")
if self.ui.b98o.isChecked():
cards.append("98o")
if self.ui.bjto.isChecked():
cards.append("JTo")
if self.ui.btt.isChecked():
cards.append("TT")
if self.ui.bq7o.isChecked():
cards.append("Q7o")
[...]
Obviously I can't like code like that. I was looking for some widget "button matrix" like, but without luck. I will be grateful for advises.
All the buttons should be children of the same widget, probably the dialog itself. Just get a handle to that widget to get all the child buttons, then loop through them and if they're checked, included their text.
parent = dialog # or whatever
cards = [widget.text() for widget in parent.children() if isinstance(widget, QPushButton) and widget.isChecked()]
You may need to include some code in the if statement to exclude the "Get List" button, or any other pushbuttons in your dialog that could be set to "checked" but shouldn't be included in cards list.
As #Brendan suggested in the other question, you could loop through them in a single list comprehension. But one other approach is to connect each buttons toggled signal to a slot that allows them to register when they are checked.
# somewhere in your class
self.checkedList = set()
def buttonChecked(self, checked):
button = self.sender()
if checked:
self.checkedList.add(button)
else:
if button in self.checkedList:
self.checkedList.remove(button)
# when you create a new button
button.toggled.connect(self.buttonChecked)
This would let you always have a set of just the checked buttons, which are self reporting. Otherwise, you would have to track them under their parent and loop to find out which are checked each time.
Update
Here is a another version that combines #Brendans loop and my signal suggestion. This might help in a situation where your buttons are a bit more spread out across your UI as opposed to be all under a single parent... but first assuming them all under a parent...
parent = dialog
for widget in parent.children():
if isinstance(widget, QPushButton):
widget.toggled.connect(self.buttonChecked)
You could repeat this in your __init__() for all the locations of your buttons and get them all registered to the slot.

Categories