How PyQt5 cursor above QListWidget item pops WhatsThis documentation? - python

How to give a definition for each item in QListWidget, so that the cursor changes to WhatsThis Cursor and pops an doc of explication
when the cursor is over it?
Something like this but for an item of QListWidget
I tried simply:
for i, def in zip(range(self.listWidget.count()), some_doc):
self.listWidget.item(i).setWhatsThis(def)
But the doc didn't show up

That appears to be the correct way to set the WhatsThis. However, you state that you want this to appear on hovering, which is instead the tooltip property.
solution:
for i, def in zip(range(self.listWidget.count()), some_doc):
self.listWidget.item(i).setToolTip(def)
If you want this behavior only in WhatsThis mode, you will have to subclass the QListWidgetItem and set the tooltip to only return when QtWidgets.QWhatsThis.inWhatsThisMode() is true.
If you really are just having problems with the WhatsThis property working on click, we will need to see more of your code and possibly a screenshot of your window. Because like I said, the loop at the beginning should work at just setting the normal property.

Related

Tkinter - Selecting an item from a Treeview using single click instead of double click (Callback on Treeview item selection)

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!

How to hide QComboBox items instead of clearing them out

I can't find a way to hide QComboBox items. So far the only way to filter its items out is to delete the existing ones (with .clear() method). And then to rebuild the entire QComboBox again using its .addItem() method.
I would rather temporary hide the items. And when they are needed to unhide them back.
Is hide/unhide on QCombobox items could be accomplished?
In case someone still looking for an answer:
By default, QComboBox uses QListView to display the popup list and QListView has the setRowHidden() method:
qobject_cast<QListView *>(comboBox->view())->setRowHidden(0, true);
Edit: fix code according to #Tobias Leupold's comment.
Edit: Python version:
# hide row
view = comboBox.view()
view.setRowHidden(row, True)
# disable item
model = comboBox.model()
item = model.item(row)
item.setFlags(item.flags() & ~Qt.ItemIsEnabled)
# enable item
view.setRowHidden(row, false)
item.setFlags(item.flags() | Qt.ItemIsEnabled)
To build on what #kef answered:
(excuse the C++ on the python question)
By default the QComboBox will use a QListView for the view, thus you can do the following:
QListView* view = qobject_cast<QListView *>(combo->view());
Q_ASSERT(view != nullptr);
view->setRowHidden(row, true);
The one drawback with the above is, that even though the item will be hidden from the popup, the user can still select it using the mouse wheel. To overcome this add the following for the hidden row:
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(combo->model());
Q_ASSERT(model != nullptr);
QStandardItem* item = model->item(row);
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
With the above the row will be hidden and the user will not be able to scroll to it with the mouse wheel.
To unhide it, just do the reverse:
view->setRowHidden(row, false);
item->setFlags(item->flags() | Qt::ItemIsEnabled);
You can use the removeItem() method to remove an item from the QComboBox.
void QComboBox::removeItem ( int index )
Removes the item at the given index from the combobox. This will update the current index if the index is removed.
This function does nothing if index is out of range.
If you don't know the index, use the findText() method.
There are no hide/unhide methods for QComboBox items.
Althought there is no direct way to hide the item of the QComboBox, but you can use QComboBox::setItemData and set the size to (0,0) to hide the item of QComboBox:
comboBox->setItemData(row, QSize(0,0), Qt::SizeHintRole);
Use the setVisible() to alter the visibility of your object:
.setVisible(False) # Not Visible
.setVisible(True) # Visible
To show the item again:
comboBox->setItemData(row, QVariant(), Qt::SizeHintRole);
Note: changing the SizeHintRole doesn't work on OS X.
I came across this thread after getting frustrated with a lack of hide functionality that would keep the item indexing etc.
I got it to work based on #Kef and #CJCombrink answers. This is basically just a python translation.
I had a problem with qobject_cast.
Solved it by setting .setView(QListView()) to the QComboBox.
combo=QComboBox()
combo.setView(QListView())
hide:
combo.view().setRowHidden(rowindex,True)
tmp_item=combo.model().item(rowindex)
tmp_item.setFlags(tmp_item.flags() & ~Qt.ItemIsEnabled)
unhide:
combo.view().setRowHidden(rowindex,False)
tmp_item=combo.model().item(rowindex)
tmp_item.setFlags(tmp_item.flags() | Qt.ItemIsEnabled)
I decided to subclass the QComboBox and add the hide functionality. Bellow is an use example for testing.
You are welcome to use it. I make no assurances.
import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
#subclassed QComboBox with added hide row functionality
class ComboBox_whide(QComboBox):
def __init__(self):
super().__init__()
self.setView(QListView())#default self.view() is a QAbstractItemView object which is missing setRowHidden, therefore a QListView needs to be set
def hide_row_set(self,row,value=True):
"""sets the row accesibility
value=True hides the row"""
self.view().setRowHidden(row,value)#hides the item from dropdown, however the item is stil accesible by moving down with arrow keys or mouse wheel. The following disables solves that
tmp_item=self.model().item(row)
if value:#hide -> disable
tmp_item.setFlags(tmp_item.flags() & ~Qt.ItemIsEnabled)
else:#enable
tmp_item.setFlags(tmp_item.flags() | Qt.ItemIsEnabled)
def hide_row_toggle(self,row):
"""toggles the row accesibility"""
if self.view().isRowHidden(row):#is hidden, therefore make available
self.hide_row_set(row,False)
else:#is not hidden, therefore hide
self.hide_row_set(row,True)
class Main(QMainWindow):
def __init__(self):
super().__init__()
cwidg=QWidget()
clayer=QVBoxLayout()
cwidg.setLayout(clayer)
self.setCentralWidget(cwidg)
#button for testing
self.btn=QPushButton('Button')
self.btn.setCheckable(True)
clayer.addWidget(self.btn)
#subclassed QComboBox
self.combo=ComboBox_whide()
for n in range(3):#add 3 items with tooltips
self.combo.addItem('item%i'%n)
self.combo.setItemData(n,'tip%i'%n,Qt.ToolTipRole)
clayer.addWidget(self.combo)
#button test function - choose either or for testing
self.btn.clicked.connect(self.btn_clicked)
#uncomment for add/remove example self.btn.clicked.connect(self.remove_add_item)
def btn_clicked(self):
self.combo.hide_row_toggle(1)
def remove_add_item(self):# here for naive comparison and to show why removing and adding is not ok
if self.combo.count()==3:
self.combo.removeItem(1)
else:
self.combo.addItem('new')#new "item1" withouth the ToolTip
if __name__ == '__main__':
app = QApplication.instance()
if app is None:#Pyside2 ipython notebook check
app = QApplication(sys.argv)
main = Main()
main.show()
app.exec_()

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.

How to get (childless) “tabs” in a pygtk application

I am facing the problem to need tabs in a pygtk app. Pretty much just like gedit has, but without any per-child widget content.
I’ve come across gtk.Notebook, but that requires me to put a widget for each tab, which I don't want.
The reason is, that I have one widget, but would only like to updates its content based on which tab is selected.
Any hints on how to do that?
My idea so far would be to just add some invisible widget for each tab and then connect to the select-page signal. Which widget could I use as invisible widget, or is there a better/alternative way of achieving my goal?
The invisble widget idea works. But not with gtk.Invisible (this just crashes), but with gtk.HBox() or any other thing that seems empty.
self.notebook.append_page(gtk.HBox(), gtk.Label("title"))
Now if I want to display stuff inside the tab actually, I can use reparent to move the widget to the current tab like this.
class Tab(gtk.HBox):
def __init__(self, child):
self.child = child
self.notebook.append_page(Tab(myWidget), gtk.Label("title"))
def pageSelected(self, notebook, page, pagenum):
box = notebook.get_nth_page(pagenum)
box.child.reparent(box)
You can have global widgets, one per tab as you want, in order to access them easily when the tab is selected.
self.notebook.append_page(self.rightBox, gtk.Label("Orders"))
Then connect to the "switch page" signal
self.notebook.connect("switch-page", self.pageSelected)
and :
def pageSelected(self, notebook, page, pagenum):
name = notebook.get_tab_label(notebook.get_nth_page(pagenum))
Now you have "name" with the label of the currently selected page. Just test it (if name == "Orders" ...) to interact.
Hope this was of some help !

Get and set the active row in QTreeview programmatically (PyQt)

Is there a way to get and change the active row in a QTreeView (not QTreeWidget)? By active, I mean the row with the focus highlight, not the selected row. In the paint event, I can use QStyle.State_HasFocus to get the active row, but this doesn't seem to work elsewhere.
You can get/set the active row with the currentIndex() and setCurrentIndex() functions that you can find in both QTreeView and QItemSelectionModel (the latter is returned by QTreeView.selectionModel()).
And despite its name, the QItemSelectionModel handles the view current item, and the view selection independently.
Current item is the one which is indicated by the focus rectangle. You can change it using selectionModel function of the tree view. If you don't want to change currently selected items, pass QtGui.QItemSelectionModel.NoUpdate as a second parameter to setCurrentIndex method. Below is an example:
index = model.index(3, 0);
view.selectionModel().setCurrentIndex(index, QtGui.QItemSelectionModel.NoUpdate)
this should move current item to the item with index 3
hope this helps, regards
For me it got nothing here new to ask such question, why because simple; you can use Qt-Designer and create QTreeView and then create line edit, then link them using the action editor, then transform the UI file to Py file, then you will see how things work behind the scene.
You will find this if you try:
QtCore.QObject.connect(self.treeView, QtCore.SIGNAL(_fromUtf8("clicked(QModelIndex)")), self.test)

Categories