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

How to add sibling to any selected node



  • https://paste.ofcode.org/AruS2xQqNNLdadxtajnZEB

    I am looking to add a sibling node to a selected node.

    Can some help me with a logic how to add Sibling Node

    The tree ins coming as

    https://paste.ofcode.org/36i5953LNNa28A6VuV5kr5m

    D
    C
    B
    A

    I want to insert E after B and the python function to add the function is addSibling

    Can some one help me with the logic


  • Banned

    Okay that link you provided is PyQt4 which was deprecated a while back we are currently on PyQt5 -- what are you using to code with 4 or 5?



  • I am using pyqt4


  • Banned

    Okay and you are aware that PyQt4 has been deprecated since December of 2015 and that Python 2.7 which it only works with was deprecated January of this year??

    Further PyQt5 and Python3 are not backwards compatible with PyQt4 nor Python2 so not only can you expect issues going forward but it is only going to get worse and not better -- sorry to say you should convert this all to Python3 and PyQt5 going forward

    Note I can help you with this if you do not feel you can do it on your own -- but really the major difference are rather minor overall so you should be able to convert to the newer libraries with only need to change a few things that you might be doing



  • @Denni-0 said in How to add sibling to any selected node:

    Further PyQt5 and Python3 are not backwards compatible with PyQt4 nor Python2 so not only can you expect issues going forward but it is only going to get worse and not better -- sorry to say you should convert this all to Python3 and PyQt5 going forward

    Can we sync up for the soltion if you can guide me in Qt also that will be fine , how to add a sibling node


  • Banned

    Okay for extensive discourse you can PM me or post your email (if comfortable doing so) and I will contact you other than that I will take that linked code and turn it into Python3-Qt5 code but you will need to change your libraries and/or figure out to install both of them (should that be your choice) as I only run Python3 with PyQt5 on my machine and the old PyQt4 Python2 stuff is on a different machine for looking back on the old code that my current code is augmenting and replacing.

    I should be able to post a updated version within a few days so stay tuned to this same bat-channel ;)


  • Banned

    Okay got a basic version done that converts it from PyQt4 / Python2 -- to PyQt5 / Python3 -- you can use this to test if you have the necessary libraries added to handle the changes -- once you have this up and running we can work on making all the necessary connections within the code and perhaps breaking it apart into separate autonomous files for easier maintenance and/or duplication

    from PyQt5.QtCore    import *
    from PyQt5.QtGui     import *
    from PyQt5.QtWidgets import *
    
    # Always import anything not PyQt5 after you import PyQt5 in order
    # to make sure the references are associated with PyQt5
    import sys
    import os
    
    # Note as you begin to explore PyQt5 examples you will see alot of examples
    # incorrectly using `super( )` instead of the explicit method used within 
    # your original code and this example just be fore-warned that including this
    # feature introduces 4 major issues that you must fully understand and code 
    # for along with current bugs. This is because `super( )` was designed to work
    # in C++ and then ported to Python where it due to the major differences 
    # between these 2 languages it does not work all that well and I would argue 
    # not at allso you either go with the explicit method or choose to introduce 
    # unnecessary issues and potential bugs for no gain since the reason it was 
    # created in C++ was to handle a rather rare occurring complex inheritance 
    # issue which could  have been simply avoid under most situations by designing 
    # the code a bit differently and that is not just my opinion on that but one 
    # that is shared by me but feel free to investigate this on your own and make
    # your own decision on using or not using it
    
    # This class seems totally associated with the Menu but if not then it would 
    # needed to be adjusted to be handled properly within the new setup based on
    # which element it should be associated with.  I did not look closely at this
    # as it did not seem to be using PyQt since you did not use a QObject
    class GroupNode(object):
      # A group node in the tree of databases model
        def __init__(self, parent, name):
          # Create a group node for the tree of databases model
            self.children = []
            self.parent = parent
            self.name = name
    
      # Is this a property of this class??
        def __len__(self):
            return len(self.children)
    
        def insertChild(self, child, position=0):
          # Insert a child in a group node
            self.children.insert(position, child)
    
        def childAtRow(self, row):
          # The row-th child of this node
            assert 0 <= row <= len(self.children)
    
            return self.children[row]
    
        def row(self):
          # The position of this node in the parent's list of children
            RetVal = 0
            if self.parent:
                RetVal = self.parent.children.index(self)
    
            return RetVal
    
    # This is would be much better done using MVC Methodology which means
    # removing from this code and putting it into its own file and converting
    # from PyQt to straight Python as it ought to be which by the way I have
    # an example version of this that I share with my students in my free 
    # online lab-like classroom-like message server which you are welcome to
    # join -- however I am not going to change every aspect of this program in
    # this intial pass just the fundamentals that need to changed from going 
    # from old to new --- also I am not sure but this might also be part of 
    # your problem as you might be tying your data source to tightly to your GUI
    # which is the whole reason the MVC methodology was created a long time ago
    # Also this might still be using going forward because I still use this object
    # in my code and my database is not only not directly connected to the GUI but
    # resides in a completely different multiprocess within its own QThread so very
    # much separated from the GUI due to design reasons let alone just good MVC 
    # implementation purposes
    class DBsTreeModel(QAbstractItemModel):
      # The tree of databases model
        def __init__(self, parent):
          # This is not needed by the base object and serves
          # no purpose to pass it on but you may need it here
            self.Parent = parent
          # Create the model
            QAbstractItemModel.__init__(self)
    
            # Populate the model
            self.root = GroupNode(None, 'root')
            DirLst = ["A","B","C","D"]
            for Dir in DirLst:
                print("Dir:", Dir)
                self.addBranch(QModelIndex(), Dir)
                #for f in files:
                #    self.addBranch(QModelIndex(), f)
    
        def flags(self, index):
          # Returns the item flags for the given index
            return Qt.ItemIsEnabled|Qt.ItemIsSelectable
    
        def data(self, index, role):
          # Returns the data stored under the given role for the
          # item referred to by the index
            if not index.isValid():
                return QVariant()
            node = self.nodeFromIndex(index)
            if role == Qt.DisplayRole:
                return QVariant(node.name)
            else:
                return QVariant()
    
        def setData(self, index, value, role=Qt.DisplayRole):
          # Sets the role data for the item at index to value
            RetVal = False
            if index.isValid():
                node = self.nodeFromIndex(index)
                if role == Qt.DisplayRole:
                    node.name = value
                  # Okay not sure about this one I think this is different in PyQt5
                  # or might handled in a cleaner method would need to investigate it
                  # to be sure though
                  # self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index)
                    print('Emit Data Changed Signal',str(index),'::',str(index))
                    RetVal = True
    
            return RetVal
    
        def headerData(self, section, orientation, role):
          # Returns the data for the given role and section in the header
          # with the specified orientation
            if (orientation, role) == (Qt.Horizontal, \
                Qt.DisplayRole):
                return QVariant('Sample tree')
    
            return QVariant()
    
        def columnCount(self, parent):
          # The number of columns for the children of the given index
            return 1
    
        def rowCount(self, parent):
          # The number of rows of the given index
            if not parent.isValid():
                parent_node = self.root
            else:
                parent_node = parent.internalPointer()
            return len(parent_node)
    
        def hasChildren(self, index):
          # Finds out if a node has children
            if not index.isValid():  # self.root fulfils this condition
                return True
            parent = self.nodeFromIndex(index)
            if parent.children != []:
                return True
            else:
                return False
    
        def index(self, row, column, parent):
          # Creates an index in the model for a given node and returns it
            assert self.root
            branch = self.nodeFromIndex(parent)
            assert branch is not None
    
            return self.createIndex(row, column, branch.childAtRow(row))
    
        def nodeFromIndex(self, index):
          # Retrieves the tree node with a given index
            if index.isValid():
                return index.internalPointer()
            else:
                return self.root
    
        def parent(self, child):
          # The parent index of a given index
            node = self.nodeFromIndex(child)
            if node is None:
                return QModelIndex()
            parent = node.parent
            if parent is None:
                return QModelIndex()
            grandparent = parent.parent
            if grandparent is None:
                return QModelIndex()
            row = grandparent.rowOfChild(parent)
            assert row != -1
    
            return self.createIndex(row, 0, parent)
    
        def deleteNode(self, index):
          # Delete a node from the model
            node = self.nodeFromIndex(index)
            # Deletes the node from the tree of databases model/view
            parent = self.parent(index)
            position = node.row()
            self.removeRows(position, 1, parent)
    
        def addBranch(self, index, childname):
          # Create a new branch under the given parent
            rowCount = self.rowCount(index)
            self.insertRows(0, 1, index)
            child_idx = self.index(0, 0, index)
            print("child_idx",child_idx.row())
            print("childname",childname)
            self.setData(child_idx, childname)
             
            return True
    
        def insertRows(self, position=0, count=1, parent=QModelIndex()):
          # Insert `count` rows after the given row
            node = self.nodeFromIndex(parent)
            pos = len(node.children)
            self.beginInsertRows(parent, position,position + count - 1)
            child = GroupNode(node, 'Unknown')
            print("\n node",node.name,child.name)
            node.insertChild(child, position)
            self.endInsertRows()
    
            return True
    
        def removeRows(self, position, count=1, parent=QModelIndex()):
          # Removes `count` rows before the given row
            node = self.nodeFromIndex(parent)
            self.beginRemoveRows(parent, position,
                position + count - 1)
            for row in range(count):
                del node.children[position + row]
            self.endRemoveRows()
    
            return True
    
    class MenuToolBar(QDockWidget):
        def __init__(self, MainWin):
            QDockWidget.__init__(self)
            self.MainWin = MainWin
            self.MainMenu = MainWin.menuBar()
    
          # This helps facilitate adding a Tool Bar is you want
          # one later on
            self.MenuActRef = {'RemoveAct':0,
                               'AddSibAct':0,
                               'AddChldAct':0}
    
            # ******* Create the Main Menu *******
            self.FileMenu  = self.MainMenu.addMenu('Main')
    
            # ******* Create Main Menu Items *******
            self.RemoveAct = QAction('&Remove', self)
          # And in case you might want to add an icon (.ico) or png to this
          # self.RemoveAct = QAction(QIcon('Images/RemoveNode.png'), '&Remove', self)
            self.RemoveAct.setShortcut("Ctrl+R")
            self.RemoveAct.setStatusTip('Deletes a the Selected Node')
            self.RemoveAct.triggered.connect(self.RemoveBranch)
            self.MenuActRef['RemoveAct'] = self.RemoveAct
    
            self.AddSibAct = QAction('Add &Sibling', self)
          # And in case you might want to add an icon (.ico) or png to this
          # self.AddSibAct = QAction(QIcon('Images/AddCiblingNode.png'), 'Add &Sibling', self)
            self.AddSibAct.setShortcut("Ctrl+S")
            self.AddSibAct.setStatusTip('Adds a Sibling to the Selected Node')
            self.AddSibAct.triggered.connect(self.AddSibling)
            self.MenuActRef['AddSibAct'] = self.AddSibAct
    
            self.AddChldAct = QAction('Add &Child', self)
          # And in case you might want to add an icon (.ico) or png to this
          # self.AddChldAct = QAction(QIcon('Images/AddChildNode.png'), 'Add &Child', self)
            self.AddChldAct.setShortcut("Ctrl+C")
            self.AddChldAct.setStatusTip('Adds a Child to the Selected Node')
            self.AddChldAct.triggered.connect(self.AddChild)
            self.MenuActRef['AddChldAct'] = self.AddChldAct
    
            # ******* Setup the File Menu *******
            self.FileMenu.addAction(self.RemoveAct)
            self.FileMenu.addAction(self.AddSibAct)
            self.FileMenu.addAction(self.AddChldAct)
    
         # And this is where I would call the function that instantiates the Tool Bar
         # but this is only needed if you plan to implement a Tool Bar but everything
         # found in a Tool Bar comes from the Menu System
         #   self.InitToolBar()
    
        @pyqtSlot()
        def AddSibling(self):
          # This will need to be rerouted to the appropriate place for doing this
          # or you can create a Signal and emit that but unless your are planning
          # on using separate QThreads that is unnecessary overhead and directly
          # call backs work just fine
            print('This will add a Sibling to Current Node')
    
        @pyqtSlot()
        def AddChild(self):
          # This will need to be rerouted to the appropriate place for doing this
          # or you can create a Signal and emit that but unless your are planning
          # on using separate QThreads that is unnecessary overhead and directly
          # call backs work just fine
            print('This will add a Child to Current Node')
    
        @pyqtSlot()
        def RemoveBranch(self):
          # This will need to be rerouted to the appropriate place for doing this
          # or you can create a Signal and emit that but unless your are planning
          # on using separate QThreads that is unnecessary overhead and directly
          # call backs work just fine
          #
          # Delete a given branch from the tree model and its views
            print('This will remove the Current Node')
    #        current = self.dbs_tree_view.currentIndex()
    #        if current is None:
    #            return
    
          # Delete the Node
    #        self.dbs_tree_model.deleteNode(current)
    
    # It is easiest to sub-class this for many reasons one of which is that
    # you can separate into its own file and have your MainApplication file
    # be the same from program to the next since we can keep it generic
    class CentralPanel(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.MainWin = parent
    
            self.dbs_tree_view = QTreeView()
    
            # The tree of databases model/view
            self.dbs_tree_model = DBsTreeModel(self)
            self.dbs_tree_view.setModel(self.dbs_tree_model)
    
            VBox = QVBoxLayout()
            VBox.addWidget(self.dbs_tree_view)
    
            self.setLayout(VBox)
    
    # Test correctness of tree models/views
    class TreeModelTester(QMainWindow):
      # Initialize the Application -- yes this is NOT the constructor but the Initializer
      # just stating this because many seem to be confused about this __new__ is the constructor
      # which is why you must pass "self" to the __init__ method
        def __init__(self):
            QMainWindow.__init__(self)
    
            self.counter = 0
    
            # Make the GUI
            self.setWindowTitle('TreeView Tester')
    
            self.CenterPane = CentralPanel(self)
            self.setCentralWidget(self.CenterPane)
    
            self.MenuToolBar = MenuToolBar(self)
    
            self.SetStatusBar(self)
    
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
        def closeEvent(self, event):
          # Handle close events beyond just closing the Application
          # such as disconnecting and Signals that might have been
          # connected to
            self.close()
    
    # Status Bar Handler **********
        def SetStatusBar(self, parent):
          # Some of this for preparation to making it a stand alone
          # Class which it currently is not implemented as such
            StatusMsg = ''
            parent.StatBar = parent.statusBar()
    
            if len(StatusMsg) < 1:
                StatusMsg = 'Ready'
    
            parent.StatBar.showMessage(StatusMsg)
    
    if __name__ == '__main__':
      # Okay unless you are planning on handling Command Line arguments
      # then it is best not to include them. Further IF you plan to use
      # Command Line arguements I strongly suggest looking into argparser
      # as it handles these much cleaner, intuitively, and efficiently.
        MainEvntThred = QApplication([])
    
        MainApplication = TreeModelTester()
        MainApplication.show()
    
      # This PyQt5 version of this although some folks feel that perhaps
      # sys.exit should still wrap it but PyQt5 states that it is not due
      # to recent changes to how this function works -- note PySide2 still
      # uses the PyQt4 method of sys.exit(MainEvntThred.exec_()) where the
      # sys.exit is required to use it properly
        MainEvntThred.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