Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Language Bindings
  4. Pysyde/QTableView: openPersistentEditor works only once?

Pysyde/QTableView: openPersistentEditor works only once?

Scheduled Pinned Locked Moved Unsolved Language Bindings
11 Posts 4 Posters 4.1k Views
  • 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.
  • D Offline
    D Offline
    dammilo
    wrote on last edited by dammilo
    #1

    Hi guys,
    I have a table view that has to display some comboboxes on its 5th column.
    So, on init, i set a delegate (previewModeDelegate) and draw some comboboxes:

    def initComboBoxes(self):
    
        model = self.view.model()
        self.view.setItemDelegateForColumn(4,  previewModeDelegate(self))
    
        for r in range(model.rowCount()):
            res = self.view.openPersistentEditor(model.index(r, 4))
    

    the delegate:

    class previewModeDelegate(QItemDelegate):
    
        def __init__(self, parent):
            QItemDelegate.__init__(self, parent)
            self.items = []
            self.items.append("No FX")
            self.items.append("Motion Blur")
            self.items.append("DOF")
            self.parent = parent
    
        def createEditor(self, parent, option, index):
    
            self.editor = QComboBox(parent)
    
            for i in range(len(self.items)):
                self.editor.addItem(self.items[i])
    
            # getting node, getting the data 
            cNode = index.model().cameraData[index.row()]
            value = cNode.GetProperty("previewMode")
    
            self.editor.connect(self.editor, SIGNAL("currentIndexChanged(int)"), self.cbCallback)
            self.editor.setCurrentIndex(value)
    
            return self.editor
    
        def cbCallback(self, value):
    
            editor = self.sender()
            self.parent.view.commitData(editor)
            self.parent.view.closeEditor(editor, QAbstractItemDelegate.NoHint)
    
        def setModelData(self, editor, model, index):
    
            value = editor.currentIndex()
            
            # getting the node, setting the data
            cNode = model.cameraData[index.row()]
            cNode.SetProperty("previewMode", value)
    
            return True
    

    everything works fine, i get some nice working comboboxes:

    0_1558020728670_6312abcc-746c-47cd-b8a0-6e00f43492c3-image.png

    problem is. during the sw usage, i have to reset the model and recreate the comboboxes.
    So, in the gui init, i connected the InitComboBoxes method to the the resetModel signal

    self.cameraModel.modelReset.connect(self.initComboBoxes)
    

    After i reset the model, the initComboBoxes method is launched but the comboboxes are not reinitialized correctly:

    0_1558020990731_8f1c6856-d5fa-4c50-a99c-5d11e71f0c04-image.png

    I am still very new to Pyside MVC. I tried tinkering with the code, but with no results. Any help?

    Ty,
    Federico

    JonBJ 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Did you check the size of the model when initComboBoxes is called ?
      If it's valid, can you try not replacing the delegate ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      D 1 Reply Last reply
      2
      • D dammilo

        Hi guys,
        I have a table view that has to display some comboboxes on its 5th column.
        So, on init, i set a delegate (previewModeDelegate) and draw some comboboxes:

        def initComboBoxes(self):
        
            model = self.view.model()
            self.view.setItemDelegateForColumn(4,  previewModeDelegate(self))
        
            for r in range(model.rowCount()):
                res = self.view.openPersistentEditor(model.index(r, 4))
        

        the delegate:

        class previewModeDelegate(QItemDelegate):
        
            def __init__(self, parent):
                QItemDelegate.__init__(self, parent)
                self.items = []
                self.items.append("No FX")
                self.items.append("Motion Blur")
                self.items.append("DOF")
                self.parent = parent
        
            def createEditor(self, parent, option, index):
        
                self.editor = QComboBox(parent)
        
                for i in range(len(self.items)):
                    self.editor.addItem(self.items[i])
        
                # getting node, getting the data 
                cNode = index.model().cameraData[index.row()]
                value = cNode.GetProperty("previewMode")
        
                self.editor.connect(self.editor, SIGNAL("currentIndexChanged(int)"), self.cbCallback)
                self.editor.setCurrentIndex(value)
        
                return self.editor
        
            def cbCallback(self, value):
        
                editor = self.sender()
                self.parent.view.commitData(editor)
                self.parent.view.closeEditor(editor, QAbstractItemDelegate.NoHint)
        
            def setModelData(self, editor, model, index):
        
                value = editor.currentIndex()
                
                # getting the node, setting the data
                cNode = model.cameraData[index.row()]
                cNode.SetProperty("previewMode", value)
        
                return True
        

        everything works fine, i get some nice working comboboxes:

        0_1558020728670_6312abcc-746c-47cd-b8a0-6e00f43492c3-image.png

        problem is. during the sw usage, i have to reset the model and recreate the comboboxes.
        So, in the gui init, i connected the InitComboBoxes method to the the resetModel signal

        self.cameraModel.modelReset.connect(self.initComboBoxes)
        

        After i reset the model, the initComboBoxes method is launched but the comboboxes are not reinitialized correctly:

        0_1558020990731_8f1c6856-d5fa-4c50-a99c-5d11e71f0c04-image.png

        I am still very new to Pyside MVC. I tried tinkering with the code, but with no results. Any help?

        Ty,
        Federico

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #3

        @dammilo
        I use PyQt, but pretty similar to PySide. This probably has nothing to do with your problem, but...

        self.parent = parent
        

        QItemDelegate inherits all the way from QObject.parent() method. Is it a good idea to create an instance attribute with the same name as an inherited method?

        In your code, you could get rid of that anyway and simply use the existing parent() method instead. You won't need your own parent attribute in any of your classes deriving from QObject.

        1 Reply Last reply
        2
        • SGaistS SGaist

          Hi,

          Did you check the size of the model when initComboBoxes is called ?
          If it's valid, can you try not replacing the delegate ?

          D Offline
          D Offline
          dammilo
          wrote on last edited by dammilo
          #4

          thank you guys for your inputs

          @SGaist
          hi, i checked and the model looks always valid to me.
          My impression is that on a model level everything works - in fact, the numbers that i see on the combobox column are coming straight out of it.
          Also, I just noticed that if i double click the cell and go into edit mode, I still get my combobox delegate and I can correctly edit the values; the combobox disappears as soon as i'm done with the edit.

          I tried moving the setItemDelegateForColumn method around - only in init, after the openPersistentEditor, before it, duplicated...but no luck

          @JonB
          good call, didn't know that, I fixed it. But sadly, as you said, that didn't solve the issue

          1 Reply Last reply
          0
          • D Offline
            D Offline
            dammilo
            wrote on last edited by
            #5

            Breakthrough!

            If I connect the rebuild function to a custom signal that I emit just after the endResetModel(), everything works.

            Why is that?
            Maybe the modelReset signal is not emitted when I expect it to?

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              modelReset should be emit after endResetMode.

              What version of PySide are you using ?

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              D 1 Reply Last reply
              0
              • SGaistS SGaist

                modelReset should be emit after endResetMode.

                What version of PySide are you using ?

                D Offline
                D Offline
                dammilo
                wrote on last edited by
                #7

                @SGaist sorry if I'm so late, I wasn't notified about your post.
                I'm using PySide 1.2. I am working inside 3dsmax 2017 and I have to use what Autodesk provides me - I have no choice about this.

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Almoedi
                  wrote on last edited by
                  #8

                  I was facing exactly the same problem with PyQt5.
                  I was creating a QTableView and assigned a QStyledItemDelegate to one of the columns and opened the persistent editor for the column items.
                  Whenever I was programmatically adding a new row and reopening the persistent editor for all table rows, the tableview data did not change upon selecting a new item in the QComboBox of the item delegate.
                  It took me a while to figure out that persistent editor was actually behaving as expected for the very last row of the table.
                  I'm pretty sure that when opening the peristent editor after having added a row to the table, a second QComboBox is drawn ontop of the already existing ones in rows 0:(numberOfRowsOfOriginalTable).

                  I've been able to solve the problem by effectively closing the existing persistent editors and reopening them again for all rows of the updated table.

                  Nevertheless, I would expect PyQt to ignore the openPersistentEditor() command on rows where the persistent editor is already open or to give out an error message.

                  SGaistS 1 Reply Last reply
                  0
                  • A Almoedi

                    I was facing exactly the same problem with PyQt5.
                    I was creating a QTableView and assigned a QStyledItemDelegate to one of the columns and opened the persistent editor for the column items.
                    Whenever I was programmatically adding a new row and reopening the persistent editor for all table rows, the tableview data did not change upon selecting a new item in the QComboBox of the item delegate.
                    It took me a while to figure out that persistent editor was actually behaving as expected for the very last row of the table.
                    I'm pretty sure that when opening the peristent editor after having added a row to the table, a second QComboBox is drawn ontop of the already existing ones in rows 0:(numberOfRowsOfOriginalTable).

                    I've been able to solve the problem by effectively closing the existing persistent editors and reopening them again for all rows of the updated table.

                    Nevertheless, I would expect PyQt to ignore the openPersistentEditor() command on rows where the persistent editor is already open or to give out an error message.

                    SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #9

                    @Almoedi hi and welcome to devnet,

                    Thanks for the detailed analysis and solution !

                    Would it be possible for you to provide a minimal script showing the persistant editor issue ?

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    A 1 Reply Last reply
                    0
                    • SGaistS SGaist

                      @Almoedi hi and welcome to devnet,

                      Thanks for the detailed analysis and solution !

                      Would it be possible for you to provide a minimal script showing the persistant editor issue ?

                      A Offline
                      A Offline
                      Almoedi
                      wrote on last edited by
                      #10

                      @SGaist sorry, it took me a while to create the mwe. Here you are:

                      #!/usr/bin/env python3
                      # -*- coding: utf-8 -*-
                      
                      import sys
                      from PyQt5.QtCore import (Qt, QAbstractTableModel, pyqtSlot)
                      from PyQt5.QtWidgets import (QLabel, QTextEdit, QApplication,
                                                   QStyledItemDelegate, QComboBox, QMainWindow, 
                                                   QTableView, QFormLayout, QPushButton, QWidget,
                                                   QHBoxLayout, QCheckBox)
                      import copy
                      
                      
                      class ComboDelegate(QStyledItemDelegate):
                          def __init__(self, parent, items):
                              self.items = items
                              QStyledItemDelegate.__init__(self, parent)
                      
                          def createEditor(self, parent, option, index):
                              self.editor = QComboBox(parent)
                              font = self.editor.font()
                              font.setPointSize(8)
                              self.editor.setFont(font)
                              self.editor.setStyleSheet("background-color: white;\n"
                                                        "border: 1px solid gray;\n"
                                                        "padding: 1px 3px 1px 3px;")
                              self.editor.addItems(self.items)
                              self.editor.currentIndexChanged.connect(self.currentIndexChanged)
                              return self.editor
                      
                          def setEditorData(self, editor, index):
                              editor.blockSignals(True) # block signals that are not caused by the user
                              value = index.data(Qt.DisplayRole)
                              if value == "":
                                  editor.setStyleSheet("background-color: red")
                              else:
                                  editor.setStyleSheet("background-color: white")
                              num = self.items.index(value)
                              editor.setCurrentIndex(num)
                              if index.column() == 1:
                                  editor.showPopup()
                              editor.blockSignals(False)
                      
                          def setModelData(self, editor, model, index):
                              model.setData(index, editor.currentText())
                              
                          def updateEditorGeometry(self, editor, option, index):
                              editor.setGeometry(option.rect)
                              
                          @pyqtSlot()
                          def currentIndexChanged(self):
                              self.commitData.emit(self.sender())
                       
                              
                      class tableModel(QAbstractTableModel):
                          def __init__(self, table_data = [['', '', '', '', '']]):
                              QAbstractTableModel.__init__(self)
                              self.table_data = table_data
                              self.no_columns = 5
                              
                          def addRow(self, rowNbr, rowData):
                              if rowNbr == -1:
                                  self.table_data.append(rowData)
                              else:
                                  self.table_data.insert(rowNbr, rowData)
                              self.layoutChanged.emit()
                              
                          def deleteRow(self, rowNbr):
                              self.table_data.pop(rowNbr)
                              
                          def data(self, index, role=Qt.DisplayRole):
                              if not index.isValid():
                                  return None
                              
                              value = self.table_data[index.row()][index.column()]
                              
                              if role == Qt.EditRole:
                                  return value
                              
                              if role == Qt.DisplayRole:    
                                  if isinstance(value, float):
                                      # Render float to 2 dp
                                      return "%.2f" % value
                                  else:
                                      return value
                              
                              if role == Qt.TextAlignmentRole:
                                  if index.column() in [0, 1, 2]:
                                      return Qt.AlignVCenter + Qt.AlignLeft
                                  else:
                                      return Qt.AlignVCenter + Qt.AlignRight
                      
                          def rowCount(self, index):
                              return len(self.table_data)
                      
                          def columnCount(self, index):
                              return self.no_columns
                      
                          def headerData(self, section, orientation, role):
                              if role != Qt.DisplayRole:
                                  return None
                              if orientation == Qt.Horizontal:
                                  return ("Type", "ID", "Location", "Offset T [\xB0C]", 
                                          "Offset RH [%]")[section]
                              else:
                                  return "{}".format(section)
                           
                          def setData(self, index, value, role=Qt.EditRole):
                              if index.isValid():
                                  self.table_data[index.row()][index.column()] = value
                                  self.dataChanged.emit(index, index, [Qt.DisplayRole])
                                  return True
                              else:
                                  return False
                                  
                          def flags(self, index):
                              return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
                          
                      
                      class Main(QMainWindow):
                          def __init__(self):
                              super().__init__()
                              self.central_widget = QWidget()
                              self.setCentralWidget(self.central_widget)
                      
                              fbox = QFormLayout()
                              hbox = QHBoxLayout()
                              self.table = QTableView()
                              self.label = QLabel('Current table data as python list')
                              self.tedit = QTextEdit()
                              self.applyFixCheckBox = QCheckBox('Apply fix (close and reopen '
                                                                + 'persistent editor. To be checked '
                                                                + 'right after program start in '
                                                                + 'this example)')
                              fbox.addRow(self.table)
                              self.addRowBtn = QPushButton('Add row')
                              self.deleteRowBtn = QPushButton("Delete row")
                              self.addRowBtn.clicked.connect(self.add_row2table)
                              self.deleteRowBtn.clicked.connect(self.delete_row_from_table)
                              hbox.addWidget(self.addRowBtn)
                              hbox.addWidget(self.deleteRowBtn)
                              fbox.addRow(hbox)
                              fbox.addRow(self.label)
                              fbox.addRow(self.tedit)
                              fbox.addRow(self.applyFixCheckBox)
                              self.centralWidget().setLayout(fbox)
                              
                      
                              data = [['Option2', 'Teststring', 'Testlocation', 1, 2],
                                      ['Option1', 'Teststring', 'Testlocation', 0, 0]]
                      
                              self.model = tableModel(data)
                              self.table.setModel(self.model)
                              self.model.dataChanged.connect(self.tableChanged)
                              self.resize(600,400)
                              self.lastTableData = []
                              self.tableChanged()
                              self.setComboBoxes()
                              
                          def tableChanged(self):
                              if self.model.table_data != self.lastTableData:
                                  self.tedit.setText(str(self.model.table_data))
                                  self.table.model().layoutChanged.emit()
                                  self.lastTableData = copy.deepcopy(self.model.table_data)       
                                  
                          def setComboBoxes(self):
                              self.table.setItemDelegateForColumn(0, ComboDelegate(self, 
                                                                                         ["", "Option1", 
                                                                                          "Option2",
                                                                                          "Option3"]))
                              for row in range(0, self.model.rowCount(0)):
                                  self.table.openPersistentEditor(self.model.index(row, 0))
                                     
                          def add_row2table(self):
                              if self.applyFixCheckBox.isChecked():
                                  # have to actively close the persistent editor, otherwise when reopening
                                  # an editor it is drawn ontop of the old one
                                  for row in range(0, self.model.rowCount(0)):
                                      self.table.closePersistentEditor(self.model.index(row, 0))
                                  
                              index = sorted(self.table.selectionModel().selectedRows())
                              if index != []:
                                  self.model.addRow(index[0].row(), ['Option1', '', '', '', ''])
                              else: # add row at the end
                                  self.model.addRow(-1, ['Option1', '', '', '', ''])
                              self.tableChanged()
                              self.setComboBoxes()
                      
                          def delete_row_from_table(self):
                              indexes = sorted(self.table.selectionModel().selectedRows())
                              if indexes != []:
                                  if self.applyFixCheckBox.isChecked():
                                      # have to actively close the persistent editor, otherwise when
                                      # reopening an editor it is drawn ontop of the old one
                                      for row in range(0, self.model.rowCount(0)):
                                          self.table.closePersistentEditor(
                                              self.model.index(row, 0))
                                      
                                  ind = [elem.row() for elem in indexes]
                                  ind.reverse()
                                  for i in ind:
                                      self.model.deleteRow(i)
                                  self.tableChanged()   
                                  self.setComboBoxes()
                      
                          def closeEvent(self,event):
                              QApplication.quit()
                      
                       
                      if __name__ == '__main__':   
                          app = QApplication.instance()
                          # prevent kernel crash at every second run of the program due to bug 
                          # in iPython 
                          if app is None:
                              app = QApplication(sys.argv)
                          
                          main = Main()
                          main.show()
                          main.raise_()
                          app.exec_()
                          
                      
                      1 Reply Last reply
                      0
                      • SGaistS Offline
                        SGaistS Offline
                        SGaist
                        Lifetime Qt Champion
                        wrote on last edited by
                        #11

                        There's no need to close them first.

                        The issue here comes rather from the replacement of the delegate. There's no need for that. If you just set it in the constructor and trigger the persistent editors when you add a line, you should be fine.

                        One thing that might be worth checking is why replacing the delegate does not close de persistent editor. I do not know whether it's on purpose as you asked for a persistent editor after all.

                        Interested in AI ? www.idiap.ch
                        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                        1 Reply Last reply
                        0

                        • Login

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