PySide2 or Pyqt5 - Lost selection highlighting on custom widget when adding to a QTreeViewItem in “IconMode”
-
I am trying to create a list of custom widgets where a user clicks them to open pictures/movies. I have everything working but I have lost the selection highlighting that usually comes with an item.
I know it's not there because I'm using a custom widget. How do I get selection highlight on the widget? I would like the item to show a transparent layer of blue.
Curious, when I change the view mode to ListMode you can see the blue selection. I have commented it out in the example code.
He is a striped back example, well as striped back as I can get it anyway. Switch the list views and select an item to see the different behaviour.
import sys from datetime import datetime from PySide2 import QtWidgets from PySide2 import QtCore from PySide2 import QtGui class EntryWidget(QtWidgets.QWidget): def __init__(self): super(EntryWidget, self).__init__() self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) # controls self.thumbnail = QtWidgets.QLabel() self.version = QtWidgets.QLabel() self.date = QtWidgets.QLabel() self.name = QtWidgets.QLabel() self.name.setAlignment(QtCore.Qt.AlignCenter) self.author = QtWidgets.QLabel() self.author.setAlignment(QtCore.Qt.AlignRight) self.dummy = QtWidgets.QLabel(" ") # layout main_layout = QtWidgets.QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) main_layout.addWidget(self.name) main_layout.addWidget(self.thumbnail) main_layout.addWidget(self.version) main_layout.addWidget(self.date) main_layout.addWidget(self.author) main_layout.addWidget(self.dummy) main_layout.addStretch() self.setLayout(main_layout) def set_size(self, w, h): self.thumbnail.setFixedSize(w, h) def set_version(self, name): self.version.setText(" Version:" + str(name)) def set_date(self, name): date_string = " Date: {0}/{1}/{2}\n Time: {3}:{4}:{5}".format( str(name.day).zfill(2), str(name.month).zfill(2), name.year, name.hour, name.minute, name.second) self.date.setText(date_string) def set_name(self, name): self.name.setText(name) def set_author(self, name): self.author.setText(name + " ") class QuickExample(QtWidgets.QDialog): def __init__(self, parent=None): super(QuickExample, self).__init__(parent) self.resize(500, 500) layout = QtWidgets.QVBoxLayout() media_list = QtWidgets.QListWidget(self) # switch the views and select an item media_list.setViewMode(QtWidgets.QListWidget.IconMode) # media_list.setViewMode(QtWidgets.QListWidget.ListMode) media_list.setResizeMode(QtWidgets.QListWidget.Adjust) media_list.setMovement(QtWidgets.QListWidget.Static) media_list.setSpacing(5) # dummy media, usually sourced from database media = [ {"version": 1, "date": datetime.now(), "name": "Entry 01", "author": "Bob"}, {"version": 2, "date": datetime.now(), "name": "Entry 02", "author": "John"} ] for i in media: # Create media Entry entry = EntryWidget() entry.set_version(i["version"]) entry.set_date(i["date"]) entry.set_size(128, 72) entry.set_name(i["name"]) entry.set_author(i["author"]) # Create QListWidgetItem media_item = QtWidgets.QListWidgetItem(media_list) # Set size hint media_item.setSizeHint(entry.sizeHint()) # Add QListWidgetItem into QListWidget media_list.addItem(media_item) media_list.setItemWidget(media_item, entry) layout.addWidget(media_list) self.setLayout(layout) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) example = QuickExample() example.show() sys.exit(app.exec_())
-
Well, I have found a hacky way of doing this which works fo me. I added some blank text to the QListWidgetItem and made the font really big. This brought back the highlighting for the item.
media_item.setText(" ") # set the item with a dummy string media_item.setFont(QFont('Verdana', 180)) # make the font big so it covers the whole widget
After some research I found that using QListView and QItemDelegate is way to do this. I couldn't find a good example/tutorial using PyQt5 so I will just use this for now.
Here is the hacked code
from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * from sys import exit as sysExit from datetime import datetime as dtDateTime class EntryWidget(QWidget): def __init__(self): QWidget.__init__(self) self.setCursor(QCursor(Qt.PointingHandCursor)) self.setFocusPolicy(Qt.StrongFocus) # Sets the Highlight when it has focus # Controls self.thumbnail = QLabel() self.version = QLabel() self.date = QLabel() self.name = QLabel() self.name.setAlignment(Qt.AlignCenter) self.author = QLabel() self.author.setAlignment(Qt.AlignRight) self.dummy = QLabel(" ") # Layout Container VBox = QVBoxLayout() VBox.setContentsMargins(0, 0, 0, 0) VBox.setSpacing(0) VBox.addWidget(self.name) VBox.addWidget(self.thumbnail) VBox.addWidget(self.version) VBox.addWidget(self.date) VBox.addWidget(self.author) VBox.addWidget(self.dummy) VBox.addStretch() self.setLayout(VBox) def set_size(self, w, h): self.thumbnail.setFixedSize(w, h) def set_version(self, name): self.version.setText(" Version:" + str(name)) def set_date(self, name): date_string = " Date: {0}/{1}/{2}\n Time: {3}:{4}:{5}".format( str(name.day).zfill(2), str(name.month).zfill(2), name.year, name.hour, name.minute, name.second) self.date.setText(date_string) def set_name(self, name): self.name.setText(name) def set_author(self, name): self.author.setText(name + " ") class QuickExample(QDialog): def __init__(self): QDialog.__init__(self) self.resize(500, 500) media_list = QListWidget(self) # switch the views and select an item media_list.setViewMode(QListWidget.IconMode) media_list.setResizeMode(QListWidget.Adjust) media_list.setMovement(QListWidget.Static) media_list.setSpacing(5) # dummy media, usually sourced from database media = [ {"version": 1, "date": dtDateTime.now(), "name": "Entry 01", "author": "Bob"}, {"version": 2, "date": dtDateTime.now(), "name": "Entry 02", "author": "John"} ] for i in media: # Create media Entry entry = EntryWidget() entry.set_version(i["version"]) entry.set_date(i["date"]) entry.set_size(128, 72) entry.set_name(i["name"]) entry.set_author(i["author"]) # Create QListWidgetItem media_item = QListWidgetItem(media_list) ########### # the fix # ########### media_item.setText(" ") # set the item with a dummy string media_item.setFont(QFont('Verdana', 180)) # make the font big so it covers the whole widget # Set size hint media_item.setSizeHint(entry.sizeHint()) # Add QListWidgetItem into QListWidget media_list.addItem(media_item) media_list.setItemWidget(media_item, entry) VBox = QVBoxLayout() VBox.addWidget(media_list) self.setLayout(VBox) if __name__ == "__main__": MainEventHandler = QApplication([]) MainApplication = QuickExample() MainApplication.show() sysExit(MainEventHandler.exec_())
-
Thanks for replying. I am trying to create a gallery where a user can select an item and it will display a picture. I have this working but there is no way of telling which item is selected.
If you switch the above code to "IconMode" (uncomment this line)
media_list.setViewMode(QtWidgets.QListWidget.ListMode)
Then select an item you will see a blue box around the selected item. This is not the case for IconMode. If you select an item there is no indication that it has been selected.
Thats pretty much it. I'm not sure why it looses the selection colour when you switch modes, I'd expect the behaviour to be the same.
-
Thanks for taking the time to adjust my code, but I'm still not seeing an item highlight on IconMode. I have tested it in PySide2 and PyQt5. Did you see highlighting in IconMode?
Thanks for the comments regarding super(), I have done it ever since I start learning PySide/PyQt, just a habit.
-
Sorry for the confusion but it is IconView I would like to use. I was using the list view as an example on how it works there and not on IconView.
If you add a QListWidgetItem without a custom widget, the selection highlighting works, example below. For some reason my custom widget is blowing away the highlighting.
Now if I can get my custom widget in IconMode to have the highlighting, I will be very happy.
Example code (you will need an image to see icon, but you can still see it works without it)
from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from sys import exit as sysExit from datetime import datetime as dtDateTime class QuickExample(QDialog): def __init__(self): QDialog.__init__(self) self.resize(500, 500) media_list = QListWidget(self) # switch the views and select an item media_list.setViewMode(QListWidget.IconMode) # media_list.setViewMode(QListWidget.ListMode) media_list.setResizeMode(QListWidget.Adjust) media_list.setMovement(QListWidget.Static) media_list.setSpacing(5) media_list.setIconSize(QSize(72, 128)) # dummy media, usually sourced from database media = [ {"version": 1, "date": dtDateTime.now(), "name": "Entry 01", "author": "Bob"}, {"version": 2, "date": dtDateTime.now(), "name": "Entry 02", "author": "John"} ] for i in media: # Create QListWidgetItem media_item = QListWidgetItem(media_list) media_item.setText(i["name"]) media_item.setIcon(QIcon(r"/Users/jeremyball/Desktop/seq/soul.jpg")) # Add QListWidgetItem into QListWidget media_list.addItem(media_item) VBox = QVBoxLayout() VBox.addWidget(media_list) self.setLayout(VBox) if __name__ == "__main__": MainEventHandler = QApplication([]) MainApplication = QuickExample() MainApplication.show() sysExit(MainEventHandler.exec_())
-
I think I'm confusing things more with that last example. I was trying to show that it is working on a listWidgetItem without a custom widget.
What I would like is, selection highlighting on my custom Qwidget "EntryWidget".
Here is the code you previously supplied where the problem lies. There is no selection highlighting on the icons.
from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * from sys import exit as sysExit from datetime import datetime as dtDateTime class EntryWidget(QWidget): def __init__(self): # First you should not use super( ) in Python as it introduces 4 known issues # that must be handled properly. Further there were still actual bugs within # the usage of super( ) when used in Python. Yes while super( ) works fine # within C++ it does not work as seamlessly within Python due to the major # differences between these 2 languages. Next the reason it was created was # to handle a rather rare issue and unless you are doing some complicated # inheritance you will most likely never run into this extremely rare issue # However the 4 major issues that get included by using super( ) you are much # more likely to occur than that rare issue its meant for to solve. Of course # using the basic explicit method, as follows, does not cause these issues and # is as just as simple as using `super( )` further you do not actually gain # anything useful by using `super( )` in Python that could not be done in a # much safer manner. # super(EntryWidget, self).__init__() QWidget.__init__(self) self.setCursor(QCursor(Qt.PointingHandCursor)) self.setFocusPolicy(Qt.StrongFocus) # Sets the Highlight when it has focus # Controls self.thumbnail = QLabel() self.version = QLabel() self.date = QLabel() self.name = QLabel() self.name.setAlignment(Qt.AlignCenter) self.author = QLabel() self.author.setAlignment(Qt.AlignRight) self.dummy = QLabel(" ") # Layout Container VBox = QVBoxLayout() VBox.setContentsMargins(0, 0, 0, 0) VBox.setSpacing(0) VBox.addWidget(self.name) VBox.addWidget(self.thumbnail) VBox.addWidget(self.version) VBox.addWidget(self.date) VBox.addWidget(self.author) VBox.addWidget(self.dummy) VBox.addStretch() self.setLayout(VBox) def set_size(self, w, h): self.thumbnail.setFixedSize(w, h) def set_version(self, name): self.version.setText(" Version:" + str(name)) def set_date(self, name): date_string = " Date: {0}/{1}/{2}\n Time: {3}:{4}:{5}".format( str(name.day).zfill(2), str(name.month).zfill(2), name.year, name.hour, name.minute, name.second) self.date.setText(date_string) def set_name(self, name): self.name.setText(name) def set_author(self, name): self.author.setText(name + " ") class QuickExample(QDialog): def __init__(self): # Per as above # super(QuickExample, self).__init__(parent) QDialog.__init__(self) self.resize(500, 500) media_list = QListWidget(self) # switch the views and select an item media_list.setViewMode(QListWidget.IconMode) media_list.setResizeMode(QListWidget.Adjust) media_list.setMovement(QListWidget.Static) media_list.setSpacing(5) # dummy media, usually sourced from database media = [ {"version": 1, "date": dtDateTime.now(), "name": "Entry 01", "author": "Bob"}, {"version": 2, "date": dtDateTime.now(), "name": "Entry 02", "author": "John"} ] for i in media: # Create media Entry entry = EntryWidget() entry.set_version(i["version"]) entry.set_date(i["date"]) entry.set_size(128, 72) entry.set_name(i["name"]) entry.set_author(i["author"]) # Create QListWidgetItem media_item = QListWidgetItem(media_list) # Set size hint media_item.setSizeHint(entry.sizeHint()) # Add QListWidgetItem into QListWidget media_list.addItem(media_item) media_list.setItemWidget(media_item, entry) # Not needed until here and this keeps like things grouped VBox = QVBoxLayout() VBox.addWidget(media_list) self.setLayout(VBox) if __name__ == "__main__": # If you are not using Command Line arguements then do not include # them. However if you do plan to use Command Line arguements then # I strongly suggest you look into using the argparser library as # it handles them more cleanly, intuitively, and efficiently # app = QtWidgets.QApplication(sys.argv) MainEventHandler = QApplication([]) MainApplication = QuickExample() MainApplication.show() # Just so you know this is actually still PyQt4 style as this # has changed in Qt5 but PySide2 is still behind the ball on # this -- MainEventHandler.exec() < this is all that is needed # now in Qt5 as opposed to Qt4 and PySide2 hopefully the will # fix this minor issue soon sysExit(MainEventHandler.exec_())
-
Well, I have found a hacky way of doing this which works fo me. I added some blank text to the QListWidgetItem and made the font really big. This brought back the highlighting for the item.
media_item.setText(" ") # set the item with a dummy string media_item.setFont(QFont('Verdana', 180)) # make the font big so it covers the whole widget
After some research I found that using QListView and QItemDelegate is way to do this. I couldn't find a good example/tutorial using PyQt5 so I will just use this for now.
Here is the hacked code
from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * from sys import exit as sysExit from datetime import datetime as dtDateTime class EntryWidget(QWidget): def __init__(self): QWidget.__init__(self) self.setCursor(QCursor(Qt.PointingHandCursor)) self.setFocusPolicy(Qt.StrongFocus) # Sets the Highlight when it has focus # Controls self.thumbnail = QLabel() self.version = QLabel() self.date = QLabel() self.name = QLabel() self.name.setAlignment(Qt.AlignCenter) self.author = QLabel() self.author.setAlignment(Qt.AlignRight) self.dummy = QLabel(" ") # Layout Container VBox = QVBoxLayout() VBox.setContentsMargins(0, 0, 0, 0) VBox.setSpacing(0) VBox.addWidget(self.name) VBox.addWidget(self.thumbnail) VBox.addWidget(self.version) VBox.addWidget(self.date) VBox.addWidget(self.author) VBox.addWidget(self.dummy) VBox.addStretch() self.setLayout(VBox) def set_size(self, w, h): self.thumbnail.setFixedSize(w, h) def set_version(self, name): self.version.setText(" Version:" + str(name)) def set_date(self, name): date_string = " Date: {0}/{1}/{2}\n Time: {3}:{4}:{5}".format( str(name.day).zfill(2), str(name.month).zfill(2), name.year, name.hour, name.minute, name.second) self.date.setText(date_string) def set_name(self, name): self.name.setText(name) def set_author(self, name): self.author.setText(name + " ") class QuickExample(QDialog): def __init__(self): QDialog.__init__(self) self.resize(500, 500) media_list = QListWidget(self) # switch the views and select an item media_list.setViewMode(QListWidget.IconMode) media_list.setResizeMode(QListWidget.Adjust) media_list.setMovement(QListWidget.Static) media_list.setSpacing(5) # dummy media, usually sourced from database media = [ {"version": 1, "date": dtDateTime.now(), "name": "Entry 01", "author": "Bob"}, {"version": 2, "date": dtDateTime.now(), "name": "Entry 02", "author": "John"} ] for i in media: # Create media Entry entry = EntryWidget() entry.set_version(i["version"]) entry.set_date(i["date"]) entry.set_size(128, 72) entry.set_name(i["name"]) entry.set_author(i["author"]) # Create QListWidgetItem media_item = QListWidgetItem(media_list) ########### # the fix # ########### media_item.setText(" ") # set the item with a dummy string media_item.setFont(QFont('Verdana', 180)) # make the font big so it covers the whole widget # Set size hint media_item.setSizeHint(entry.sizeHint()) # Add QListWidgetItem into QListWidget media_list.addItem(media_item) media_list.setItemWidget(media_item, entry) VBox = QVBoxLayout() VBox.addWidget(media_list) self.setLayout(VBox) if __name__ == "__main__": MainEventHandler = QApplication([]) MainApplication = QuickExample() MainApplication.show() sysExit(MainEventHandler.exec_())