Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Create a sibling of a row in QtreeView



  • I have a QTreeView
    I have a QtreeView a

    1. I want to get current selected row

    the following line is not giving to current selected row

     index = self.selectionModel().currentIndex(). 
    

    And
    Also I was looking for sample code how to add a new row , to create sibling of a new row.

    Regards,


  • Banned

    Okay I think this might cover the items and/or issues you are having and just one of the examples I have available in my free python-qt classroom

    from PyQt5.QtCore    import Qt, QModelIndex, pyqtSlot
    
    from PyQt5.QtGui     import QStandardItemModel, QStandardItem, QBrush, QFont, QCursor, QIcon, QPixmap
    
    from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QSplitter, QVBoxLayout, QHBoxLayout
    from PyQt5.QtWidgets import QAbstractItemView, QHeaderView, QLabel, QDockWidget, QAction
    from PyQt5.QtWidgets import QStyleFactory, QAbstractScrollArea, QPushButton, QTreeView
    
    # This is your Menu and Tool Bar class it does not handle the Tool Bar
    # at this time but it could be expanded to do so fairly easily just 
    # keep in mind everything on a Tool Bar should come from the Menu    
    class MenuToolBar(QDockWidget):
        def __init__(self, MainWin):
            QDockWidget.__init__(self)
            self.MainWin = MainWin
            self.MainMenu = MainWin.menuBar()
    
          # This is used to have a handle to the Menu Items
          # should you implement a Tool Bar
            self.MenuActRef = {'SetCatGrpAct':0,
                               'ResetViewAct':0}
    
            # ******* Create the Catalog Menu *******
            self.CatalogMenu  = self.MainMenu.addMenu('Catalog')
    
            # ******* Create Catalog Menu Items *******
            self.SetCatGrpAct = QAction('&Set CatGroups', self)
          # In case you have or want to include an Icon
          #  self.SetCatGrpAct = QAction(QIcon('Images/setcatgroup.ico'), '&Set CatGroups', self)
            self.SetCatGrpAct.setShortcut("Ctrl+S")
            self.SetCatGrpAct.setStatusTip('Initialize Category Groups')
            self.SetCatGrpAct.triggered.connect(self.SetCatGroups)
            self.MenuActRef['SetCatGrpAct'] = self.SetCatGrpAct
    
            self.ResetViewAct = QAction('&Reset View', self)
          #  self.ResetAct = QAction(QIcon('Images/resetview.ico'), '&Reset View', self)
            self.ResetViewAct.setShortcut("Ctrl+R")
            self.ResetViewAct.setStatusTip('Reset the View to All')
            self.ResetViewAct.triggered.connect(self.ResetView)
            self.MenuActRef['ResetViewAct'] = self.ResetViewAct
    
            # ******* Setup the World Menu *******
            self.CatalogMenu.addAction(self.SetCatGrpAct)
            self.CatalogMenu.addSeparator()
            self.CatalogMenu.addAction(self.ResetViewAct)
    
    # These are the Menu/Tool Bar Actions
        def SetCatGroups(self):
          # This would getting the data from the Data Source but is hard-coded here
            CatGroups = {100: {0: 'All'}, '1000': {0: 'Group-1000', '1001': 'Item-1001', '1002': 'Item-1002', '1003': 'Item-1003'}, '2000': {0: 'Group-2000', '2001': 'Item-2001', '2002': 'Item-2002', '2003': 'Item-2003'}}
            self.MainWin.SetCatGroups(CatGroups)
    
        def ResetView(self):
          # This would handle receiving the data from the Data Source but is hard-coded here
            ViewData = {'ItemDesc1':['Units', '1'],'ItemDesc2':['Units', '2'],'ItemDesc3':['Units', '3'],'ItemDesc4':['Units', '4'],'ItemDesc5':['Units', '5'],'ItemDesc6':['Units', '6'],'ItemDesc7':['Units', '7']}
            self.MainWin.SetViewItems(ViewData)
    
    ############################## Formatting Override for StandardItemModel ##############################
    class CustomItemModel(QStandardItemModel):
        def headerData(self, section, orientation, role):
            if role == Qt.ForegroundRole:
                brush = QBrush()
                brush.setColor(Qt.blue)
                brush.setStyle(Qt.SolidPattern)
                return brush
                  
            elif role == Qt.BackgroundRole:
                brush = QBrush()
                brush.setColor(Qt.yellow)
                brush.setStyle(Qt.SolidPattern)
                return brush
              
            elif role == Qt.FontRole:
                font = QFont()
                font.setBold(True)
                font.setPointSize(10)
                return font
                  
    #        return super().headerData(section, orientation, role)
            return QStandardItemModel(self).headerData(section, orientation, role)
    
    ############################## Category Groups Tree List ##############################
    class CatGroupTree(QTreeView):
        def __init__(self, CentrPane):
            QTreeView.__init__(self, CentrPane)
            self.CntrPane = CentrPane
    
            self.model = CustomItemModel()
            self.model.setHorizontalHeaderLabels(['Category/Group'])
    
            self.setModel(self.model)
            self.setMinimumWidth(110)
            self.clicked.connect(self.ItemSingleClicked)
            self.setEditTriggers(QAbstractItemView.NoEditTriggers)
    
        @property
        def CntrPane(self):
            return self.__parent
    
        @CntrPane.setter
        def CntrPane(self, value):
            self.__parent = value
    
        def SetContent(self, tmpCatGrpList):
            self.model.setRowCount(0)
            TreeRoot = self.model.invisibleRootItem()
            SeenCat = {}
            
            for CatIdx in tmpCatGrpList:
                Groups = tmpCatGrpList[CatIdx]
                for GrpIdx in Groups:
                    CurNode = TreeRoot
                    if GrpIdx == 0:
                        CatNam = Groups[0]
                        GrpNam = ''
                        CurNode.appendRow([QStandardItem(CatNam)])
    
                        SeenCat[CatNam] = CurNode
                    else:
                        CurNode = SeenCat[CatNam]
                        CurNode = CurNode.child(CurNode.rowCount() - 1)
    
                        GrpNam = Groups[GrpIdx]
                        CurNode.appendRow([QStandardItem(GrpNam)])
            
            # Initialize Selection to First Row 'All'
            self.setCurrentIndex(self.model.index(0, 0))
    
        @pyqtSlot(QModelIndex)
        def ItemSingleClicked(self, ModlIdx):
            ItemIdxs = self.selectedIndexes()[0]
            Item = ItemIdxs.model().itemFromIndex(ModlIdx)
            CatItm = ItemIdxs.model().itemFromIndex(ModlIdx).text()
            if CatItm == 'All' or Item.hasChildren():
                self.CntrPane.RefrshTreeSelctd(CatItm)
            else:
                CatItm = ItemIdxs.model().itemFromIndex(ModlIdx).parent().text()
                GrpItm = ItemIdxs.model().itemFromIndex(ModlIdx).text()
                self.CntrPane.RefrshTreeSelctd(CatItm, GrpItm)
    
    ############################## Item Data List ##############################
    class ItemDsplyr(QTreeView):
        def __init__(self, CentrPane):
            QTreeView.__init__(self, CentrPane)
            self.CntrPane = CentrPane
    
          # Editable cannot be turned off for an individual column all must be turned off
            self.setEditTriggers(QTreeView().NoEditTriggers)
            self.model = CustomItemModel(0, 4)
    
          # The 1st Column's Data is Idented for some currently unknown reason so 
          # to keep it looking good an additional column has been added to be that
          # 1st Column and then this 1st Column is hidden which fixes this issue
            self.model.setHorizontalHeaderLabels(['', 'ItemName', 'Value', 'Units'])
            self.model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
            self.model.dataChanged.connect(self.onValueChanged)
            self.setModel(self.model)
            self.setMinimumWidth(250)
            self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
          # Settings for each individual column 0 to 3
            self.setColumnWidth(0, 1)
            self.setColumnHidden(0, True)
            self.header().setSectionResizeMode(0, QHeaderView.Fixed)
          # Although this is supposed to default to false it does not
            self.header().setStretchLastSection(False)
            self.header().setSectionResizeMode(1, QHeaderView.Stretch)
            self.setColumnWidth(2, 75)
            self.setColumnWidth(3, 100)
    
          # And the left click event then must be handled within code
            self.clicked.connect(self.ItemLeftClicked)
    
    # DJ -- Not sure why this is in here or what I was going to do with it need to *************************************
    #       revisit this and either finish it or remove it
    # -- Okay I think this is to handle the editting of the data items within the Item View 
        @pyqtSlot("QModelIndex", "QModelIndex", "QVector<int>")
        def onValueChanged(self, top_left, bottom_right, roles):
            if top_left == bottom_right and Qt.DisplayRole in roles:
                newValue = top_left.data()
    
        @property
        def CntrPane(self):
            return self.__parent
    
        @CntrPane.setter
        def CntrPane(self, value):
            self.__parent = value
    
        def SetContent(self, tmpItmList):
            self.model.setRowCount(0)
    
            idx = 0
            for Item in tmpItmList:
              # Required to avoid getting a duplicate data insert message
                idx += 1
                blnkVal = QStandardItem(str(idx))
    
                ItmNam = QStandardItem(Item)
                ItmNam.setTextAlignment(Qt.AlignLeft)
                ItmNam.isEditable = False
    
                ItmVal = QStandardItem(tmpItmList[Item][0])
                ItmVal.setTextAlignment(Qt.AlignCenter)
    
                ItmUnt = QStandardItem(tmpItmList[Item][1])
                ItmUnt.setTextAlignment(Qt.AlignLeft)
                ItmUnt.isEditable = False
    
                self.model.appendRow([blnkVal, ItmNam, ItmVal, ItmUnt])
    
        def mousePressEvent (self, event):
            if event.button() == Qt.RightButton:
                print("Right Clicked Launch Process")
    
                return True
    
            QTreeView.mousePressEvent(self, event)
    
        def ItemLeftClicked(self, index):
            Item = self.selectedIndexes()[0]
            # Get row index
            CurRow = index.row()
            # Set index to Current Row and Column 1 (Item)
            ItemIdx = index.sibling(CurRow, 1)
            # Set index to Current Row and Column 2 (Value)
            ValuIdx = index.sibling(CurRow, 2)
            # Allow Value to be Editted
            self.edit(ValuIdx)
    
            ItemVal = Item.model().itemFromIndex(ItemIdx).text()
            ValuVal = Item.model().itemFromIndex(ValuIdx).text()
            self.CntrPane.RefrshListSelctd(ItemVal, ValuVal)
    
    ############################## Window Center Pane Class ##############################
    class CenterPanel(QWidget):
        def __init__(self, MainWin):
            QWidget.__init__(self)
    
            self.MainWin = MainWin
    
            self.CatGroups = {}
            self.ViewItems = {}
    
            self.CatItem = 'All'
            self.ListItem = ''
    
            self.CatGrpTree = CatGroupTree(self)
            self.ItemDsply = ItemDsplyr(self)
    
            CntrPane = QSplitter(Qt.Horizontal, self)
            CntrPane.addWidget(self.CatGrpTree)
            CntrPane.addWidget(self.ItemDsply)
            CntrPane.setSizes([50,200])
            CntrPane.setCollapsible(0, False)
            CntrPane.setCollapsible(1, False)
    
            hbox = QHBoxLayout(self)
            hbox.addWidget(CntrPane)
    
            self.setLayout(hbox)
    
        @property
        def MainWin(self):
            return self.__parent
    
        @MainWin.setter
        def MainWin(self, value):
            self.__parent = value
    
        @property
        def CatItem(self):
            return self.__CatItmSelctd
    
        @CatItem.setter
        def CatItem(self, value):
            self.__CatItmSelctd = value
    
        @property
        def GrpItem(self):
            return self.__GroupItmSelctd
    
        @GrpItem.setter
        def GrpItem(self, value):
            self.__GroupItmSelctd = value
    
        @property
        def ListItem(self):
            return self.__listItmSelctd
    
        @ListItem.setter
        def ListItem(self, value):
            self.__listItmSelctd = value
    
        def RefrshTreeSelctd(self, CatSlctd, GrpSlctd=None):
            UpdateItem = False
            if CatSlctd != self.CatItem:
                self.CatItem = CatSlctd
                self.GrpItem = GrpSlctd
                UpdateItem = True
            elif GrpSlctd != self.GrpItem:
                self.GrpItem = GrpSlctd
                UpdateItem = True
    
            if UpdateItem:
                #Retrieve new data and repopulate List
                self.MainWin.GetViewItems(self.CatItem, self.GrpItem)
    
        def RefrshListSelctd(self, LstItm, LstVal):
            if LstItm != self.ListItem:
                self.ListItem = LstItm
    
        def SetCatGroup(self, CatGrpList):
            self.CatGrpTree.SetContent(CatGrpList)
    
        def SetItmDsply(self, ItemList):
            self.ItemDsply.SetContent(ItemList)
    
    # QMainWindow which is actually more a Controller/Handler of all the elements
    # contained within
    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
    
          # Basic Main Window Items aka this is your Handler or Controller for M-V-C
            self.setWindowTitle('Main Window')
            WinLeft = 150; WinTop = 150; WinWidth = 500; WinHigh = 500
            self.setGeometry(WinLeft, WinTop, WinWidth, WinHigh)
    
          # QMainWindow contains a Central Widget, Menu/Tool Bar, Status Bar, and 
          # dockable window border this covers 3 of the 4 items
            self.CenterPane = CenterPanel(self)
            self.setCentralWidget(self.CenterPane)
    
          # The Menu and Tool Bar for your MainWindow should you want one or both
            self.MenuBar = MenuToolBar(self)
    
          # The call to the Status Bar method would be placed here
    
          # Not 100% sure what this all does but it does remove oddities from the window
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
        def SetCatGroups(self, CatGroups):
            self.CenterPane.SetCatGroup(CatGroups)
    
        def SetViewItems(self, ViewData):
            self.CenterPane.SetItmDsply(ViewData)
    
        def GetViewItems(self, CatItem, GrpItem):
          # This is where the call to the database would occur
            print('Cat/Group',CatItem,GrpItem)
            if CatItem == 'All':
                ViewData = {'ItemDesc0':['Units', '0'],'ItemDesc1':['Units', '1'],'ItemDesc2':['Units', '2'],'ItemDesc3':['Units', '3'],'ItemDesc4':['Units', '4'],'ItemDesc5':['Units', '5'],'ItemDesc6':['Units', '6'],'ItemDesc7':['Units', '7'],'ItemDesc8':['Units', '8'],'ItemDesc9':['Units', '9']}
            elif CatItem == 'Group-1000':
                if GrpItem == 'Item-1001':
                    ViewData = {'ItemDesc1':['Units', '1'],'ItemDesc3':['Units', '3'],'ItemDesc5':['Units', '5']}
                elif GrpItem == 'Item-1002':
                    ViewData = {'ItemDesc1':['Units', '1'],'ItemDesc2':['Units', '2'],'ItemDesc3':['Units', '3'],}
                elif GrpItem == 'Item-1003':
                    ViewData = {'ItemDesc2':['Units', '2'],'ItemDesc4':['Units', '4']}
                else:
                    ViewData = {'ItemDesc1':['Units', '1'],'ItemDesc2':['Units', '2'],'ItemDesc3':['Units', '3'],'ItemDesc4':['Units', '4'],'ItemDesc5':['Units', '5']}
            elif CatItem == 'Group-2000':
                if GrpItem == 'Item-2001':
                    ViewData = {'ItemDesc5':['Units', '5'],'ItemDesc7':['Units', '7'],'ItemDesc9':['Units', '9']}
                elif GrpItem == 'Item-2002':
                    ViewData = {'ItemDesc7':['Units', '7'],'ItemDesc8':['Units', '8'],'ItemDesc9':['Units', '9']}
                elif GrpItem == 'Item-2003':
                    ViewData = {'ItemDesc6':['Units', '6'],'ItemDesc8':['Units', '8']}
                else:
                    ViewData = {'ItemDesc5':['Units', '5'],'ItemDesc6':['Units', '6'],'ItemDesc7':['Units', '7'],'ItemDesc8':['Units', '8'],'ItemDesc9':['Units', '9']}
            else:
                print('Main Window Error with Category',CatItem)
    
            self.SetViewItems(ViewData)
    
    # The basic format of the Main which handles things outside the application
    # which many cases is virtually nothing
    if __name__ == "__main__":
        MainThred = QApplication([])
    
        MainGUI = MainWindow()
        MainGUI.show()
    
        MainThred.exec()
    
      # If anyone wants more extensive free help I run an online lab-like classroom-like 
      # message server feel free and drop by you will not be able to post until I clear 
      # you as a student as this prevents spammers so if interested here is the invite
      # https://discord.gg/3D8huKC
    

Log in to reply