Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Making a list of widgets should be simple, yet it is dauntingly complex.
Forum Updated to NodeBB v4.3 + New Features

Making a list of widgets should be simple, yet it is dauntingly complex.

Scheduled Pinned Locked Moved Unsolved General and Desktop
2 Posts 2 Posters 391 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    Paddle
    wrote on last edited by Paddle
    #1

    What one would expect is the QListWidget approach. You make a widget and add it to the list. But this is not how one is supposed to do as this support only static widgets. So QListView and custom delegate.

    I'm trying to do something relatively simple :
    7fdfe8f7-19d3-45b1-8516-3a09f9eff549-image.png

    Just a list with a thumbnail, a title, a combobox and a button. Sounds easy right? Well 2 days in I'm still banging my head on the wall.

    Why this complexity is not abstracted for the user? I understand QListWidget is not efficient because it creates many widget so for big lists it's inefficient. But it shouldn't need to. Qt could just generate the paint function from the widget you supply as template + the model data.

    My second question is just how can I make it? Do I need to :

    • Paint manually the thumbnail, text (those 2 are not too hard) but the combobox? Do I need to draw a rectangle the text and the arrow myself? The button?
    • Make a widget class for my items anyway because it's needed for the createEditor for when the user click the item such that the thing can actually be modified and not just painted?

    How am I ever going to get the editor and the painting to be the exactly the same ? Especially with the different stylesheets and so on.

    I remember there's a star delegate example, but I also remember it didn't help much last time. Is there not a simple example with regular widgets like combobox checkboxes and buttons?

    Thanks very much.
    Below my 2 attemps :

    
    class FileListDelegate(QStyledItemDelegate):
        thumbnail_size = QtCore.QSize(150, 100)
    
        def __init__(self, parent=None):
            super(FileListDelegate, self).__init__(parent)
            
            self.combobox, self.button = self.createEditor(option.widget, option, index)
    
            
        def createEditor(self, parent, option, index):
            # Create combobox and button editors
            combobox = QComboBox(parent)
            button = QPushButton("Details", parent)
    
            return combobox, button
    
        def setEditorData(self, editor, index):
            # Retrieve data for the current index and update editor accordingly
            fileData = index.data(QtCore.Qt.DisplayRole)
            if isinstance(editor, QComboBox):
                editor.clear()
    
                versions = fileData["versions"]
                for version in versions:
                    editor.addItem(version)
    
                index = editor.findText(fileData["CurrentVersion"])
                if index >= 0:
                    editor.setCurrentIndex(index)
    
        def setModelData(self, editor, model, index):
            # Update the model with the selected version from the combobox
            if isinstance(editor, QComboBox):
                fileData = index.data(QtCore.Qt.DisplayRole)
                fileData["currentVersion"] = editor.currentText()
                model.setData(index, fileData, QtCore.Qt.EditRole)
    
        def paint(self, painter, option, index):
    
            # Get the data for the current index
            fileData = index.data(QtCore.Qt.DisplayRole)
    
            # Draw the thumbnail
            thumbnail_rect = QtCore.QRect(option.rect.topLeft(), self.thumbnail_size)
            thumbnail = QtGui.QPixmap(f"{modPath}/thumbnail.png") #QtGui.QPixmap(fileData["localUrl"])
            painter.drawPixmap(thumbnail_rect, thumbnail)
    
            # Draw the name label
            name_rect = QtCore.QRect(option.rect.left(), thumbnail_rect.bottom() + 10, option.rect.width(), 20)
            painter.drawText(name_rect, QtCore.Qt.AlignCenter, fileData["custFileName"])
    
            # Set the geometries of the combobox and button
            self.combobox.setGeometry(option.rect.left(), name_rect.bottom() + 10, option.rect.width() // 2, 20)
            self.button.setGeometry(self.combobox.geometry().translated(self.combobox.geometry().width() + 10, 0))
    
            # Draw the combobox and button
            self.combobox.show()
            self.button.show()
    
        def sizeHint(self, option, index):
            item_height = self.thumbnail_size.height() + 10 + 20 + 10  # Thumbnail height + spacing + name label height + spacing
            item_width = option.rect.width()
    
            return QtCore.QSize(item_width, item_height)
    
        def editorEvent(self, event, model, option, index):
            # Handle any custom editor event handling, such as button clicks
            pass
    

    Attemp 2 :

    
    class FileItemWidget(QWidget):
        def __init__(self, fileData, parent=None):
            super(FileItemWidget, self).__init__(parent)
    
            self.thumbnail_label = QLabel()
            self.name_label = QLabel()
            self.combobox = QComboBox()
            self.button = QPushButton("Details")
            h_layout = QHBoxLayout()
            h_layout.addWidget(self.combobox)
            h_layout.addWidget(self.button)
    
            layout = QVBoxLayout()
            layout.addWidget(self.thumbnail_label)
            layout.addWidget(self.name_label)
            layout.addLayout(h_layout)
            self.setLayout(layout)
    
            self.setFileData(fileData)
    
        def setFileData(self, fileData):
            # Update the widget with the file data
            self.thumbnail_label.setPixmap(QPixmap(f"{modPath}/thumbnail.png"))  # Set thumbnail pixmap
            self.name_label.setText(fileData["custFileName"])  # Set file name
    
            self.combobox.clear()
    
            versions = fileData["versions"]
            for version in versions:
                self.combobox.addItem(version)
    
            index = self.combobox.findText(fileData["CurrentVersion"])
            if index >= 0:
                self.combobox.setCurrentIndex(index)
    
        def getVersionName(self):
            return self.combobox.currentText()
    
    class FileListDelegate(QStyledItemDelegate):
    
        def __init__(self, parent=None):
            super(FileListDelegate, self).__init__(parent)
                    
        def createEditor(self, parent, option, index):
            fileData = index.data(QtCore.Qt.DisplayRole)
    
            # Create the custom FileItemWidget as the editor
            editor = FileItemWidget(fileData, parent)
            return editor
    
        def setEditorData(self, editor, index):
            # Retrieve data for the current index and update editor accordingly
            fileData = index.data(QtCore.Qt.DisplayRole)
            editor.show()
            editor.setText("fsdfsdf")
            if isinstance(editor, FileItemWidget):
                editor.setFileData(fileData)
    
    
        def setModelData(self, editor, model, index):
            # Update the model with the selected version from the combobox
            if isinstance(editor, FileItemWidget):
                fileData = index.data(QtCore.Qt.DisplayRole)
                fileData["currentVersion"] = FileItemWidget.getVersionName()
                model.setData(index, fileData, QtCore.Qt.EditRole)
        
        def updateEditorGeometry(self, editor, option, index):
            editor.setGeometry(option.rect)
    
        def sizeHint(self, option, index):
            return QtCore.QSize(200, 150)
    
    Pl45m4P 1 Reply Last reply
    0
    • P Paddle

      What one would expect is the QListWidget approach. You make a widget and add it to the list. But this is not how one is supposed to do as this support only static widgets. So QListView and custom delegate.

      I'm trying to do something relatively simple :
      7fdfe8f7-19d3-45b1-8516-3a09f9eff549-image.png

      Just a list with a thumbnail, a title, a combobox and a button. Sounds easy right? Well 2 days in I'm still banging my head on the wall.

      Why this complexity is not abstracted for the user? I understand QListWidget is not efficient because it creates many widget so for big lists it's inefficient. But it shouldn't need to. Qt could just generate the paint function from the widget you supply as template + the model data.

      My second question is just how can I make it? Do I need to :

      • Paint manually the thumbnail, text (those 2 are not too hard) but the combobox? Do I need to draw a rectangle the text and the arrow myself? The button?
      • Make a widget class for my items anyway because it's needed for the createEditor for when the user click the item such that the thing can actually be modified and not just painted?

      How am I ever going to get the editor and the painting to be the exactly the same ? Especially with the different stylesheets and so on.

      I remember there's a star delegate example, but I also remember it didn't help much last time. Is there not a simple example with regular widgets like combobox checkboxes and buttons?

      Thanks very much.
      Below my 2 attemps :

      
      class FileListDelegate(QStyledItemDelegate):
          thumbnail_size = QtCore.QSize(150, 100)
      
          def __init__(self, parent=None):
              super(FileListDelegate, self).__init__(parent)
              
              self.combobox, self.button = self.createEditor(option.widget, option, index)
      
              
          def createEditor(self, parent, option, index):
              # Create combobox and button editors
              combobox = QComboBox(parent)
              button = QPushButton("Details", parent)
      
              return combobox, button
      
          def setEditorData(self, editor, index):
              # Retrieve data for the current index and update editor accordingly
              fileData = index.data(QtCore.Qt.DisplayRole)
              if isinstance(editor, QComboBox):
                  editor.clear()
      
                  versions = fileData["versions"]
                  for version in versions:
                      editor.addItem(version)
      
                  index = editor.findText(fileData["CurrentVersion"])
                  if index >= 0:
                      editor.setCurrentIndex(index)
      
          def setModelData(self, editor, model, index):
              # Update the model with the selected version from the combobox
              if isinstance(editor, QComboBox):
                  fileData = index.data(QtCore.Qt.DisplayRole)
                  fileData["currentVersion"] = editor.currentText()
                  model.setData(index, fileData, QtCore.Qt.EditRole)
      
          def paint(self, painter, option, index):
      
              # Get the data for the current index
              fileData = index.data(QtCore.Qt.DisplayRole)
      
              # Draw the thumbnail
              thumbnail_rect = QtCore.QRect(option.rect.topLeft(), self.thumbnail_size)
              thumbnail = QtGui.QPixmap(f"{modPath}/thumbnail.png") #QtGui.QPixmap(fileData["localUrl"])
              painter.drawPixmap(thumbnail_rect, thumbnail)
      
              # Draw the name label
              name_rect = QtCore.QRect(option.rect.left(), thumbnail_rect.bottom() + 10, option.rect.width(), 20)
              painter.drawText(name_rect, QtCore.Qt.AlignCenter, fileData["custFileName"])
      
              # Set the geometries of the combobox and button
              self.combobox.setGeometry(option.rect.left(), name_rect.bottom() + 10, option.rect.width() // 2, 20)
              self.button.setGeometry(self.combobox.geometry().translated(self.combobox.geometry().width() + 10, 0))
      
              # Draw the combobox and button
              self.combobox.show()
              self.button.show()
      
          def sizeHint(self, option, index):
              item_height = self.thumbnail_size.height() + 10 + 20 + 10  # Thumbnail height + spacing + name label height + spacing
              item_width = option.rect.width()
      
              return QtCore.QSize(item_width, item_height)
      
          def editorEvent(self, event, model, option, index):
              # Handle any custom editor event handling, such as button clicks
              pass
      

      Attemp 2 :

      
      class FileItemWidget(QWidget):
          def __init__(self, fileData, parent=None):
              super(FileItemWidget, self).__init__(parent)
      
              self.thumbnail_label = QLabel()
              self.name_label = QLabel()
              self.combobox = QComboBox()
              self.button = QPushButton("Details")
              h_layout = QHBoxLayout()
              h_layout.addWidget(self.combobox)
              h_layout.addWidget(self.button)
      
              layout = QVBoxLayout()
              layout.addWidget(self.thumbnail_label)
              layout.addWidget(self.name_label)
              layout.addLayout(h_layout)
              self.setLayout(layout)
      
              self.setFileData(fileData)
      
          def setFileData(self, fileData):
              # Update the widget with the file data
              self.thumbnail_label.setPixmap(QPixmap(f"{modPath}/thumbnail.png"))  # Set thumbnail pixmap
              self.name_label.setText(fileData["custFileName"])  # Set file name
      
              self.combobox.clear()
      
              versions = fileData["versions"]
              for version in versions:
                  self.combobox.addItem(version)
      
              index = self.combobox.findText(fileData["CurrentVersion"])
              if index >= 0:
                  self.combobox.setCurrentIndex(index)
      
          def getVersionName(self):
              return self.combobox.currentText()
      
      class FileListDelegate(QStyledItemDelegate):
      
          def __init__(self, parent=None):
              super(FileListDelegate, self).__init__(parent)
                      
          def createEditor(self, parent, option, index):
              fileData = index.data(QtCore.Qt.DisplayRole)
      
              # Create the custom FileItemWidget as the editor
              editor = FileItemWidget(fileData, parent)
              return editor
      
          def setEditorData(self, editor, index):
              # Retrieve data for the current index and update editor accordingly
              fileData = index.data(QtCore.Qt.DisplayRole)
              editor.show()
              editor.setText("fsdfsdf")
              if isinstance(editor, FileItemWidget):
                  editor.setFileData(fileData)
      
      
          def setModelData(self, editor, model, index):
              # Update the model with the selected version from the combobox
              if isinstance(editor, FileItemWidget):
                  fileData = index.data(QtCore.Qt.DisplayRole)
                  fileData["currentVersion"] = FileItemWidget.getVersionName()
                  model.setData(index, fileData, QtCore.Qt.EditRole)
          
          def updateEditorGeometry(self, editor, option, index):
              editor.setGeometry(option.rect)
      
          def sizeHint(self, option, index):
              return QtCore.QSize(200, 150)
      
      Pl45m4P Offline
      Pl45m4P Offline
      Pl45m4
      wrote on last edited by
      #2

      @Paddle said in Making a list of widgets should be simple, yet it is dauntingly complex.:

      Paint manually the thumbnail, text (those 2 are not too hard) but the combobox? Do I need to draw a rectangle the text and the arrow myself? The button?

      No, there is this, which provides everything to style/paint a comboBox.

      • https://doc.qt.io/qt-6/qstyleoptioncombobox.html

      Make a widget class for my items anyway because it's needed for the createEditor for when the user click the item such that the thing can actually be modified and not just painted?

      The delegate only uses the real widgets, when you "activate" it (in editing mode), otherwise it's just "flat", so it doesn't need too much resources.

      Here's another delegate example:

      • https://doc.qt.io/qt-6/qtwidgets-itemviews-stardelegate-example.html

      If debugging is the process of removing software bugs, then programming must be the process of putting them in.

      ~E. W. Dijkstra

      1 Reply Last reply
      2

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved