Python and PyQt: run function from another class

后端 未结 1 390
臣服心动
臣服心动 2021-02-06 18:19

Update 3:

Here\'s the new code, trying to do what you told me to, with no results... I\'m starting to think that I have done something very foolish some

1条回答
  •  闹比i
    闹比i (楼主)
    2021-02-06 18:51

    Specific error related problem

    Remove the classmethod decorator on your update2 method. It makes "self" turn into the class object and there is no tableWidget attribute on your class. Only your widget. There is no use for the decorator here.

    General design and fundamental issues

    There are some severely major fundamental issues with this code. I realize you are new and learning, so its even more important that you address them now. This is a fantastic book, Rapid GUI Programming with Python and Qt that I can't praise enough, for learning PyQt

    What you are doing here is taking a UI design file generate by Qt Designer, and editing it to slot in your logic into the setupUi methods. What you need to understand about this ui file is that it creates generic classes containing the widget setup code to be applied to your own class. You have that part of the concept at the bottom where you create a new QMainWindow and then call setupUi on it. What you shouldn't be doing is creating a huge number of global variable or adding extra methods and logic within these UI setup classes. You also have an example of a wrong @classmethod in there.

    Object relationships in PyQt generally work like this... Parent classes can have attributes and child widgets. Child widgets can have attributes and generally don't know about their parents.

    An example of how you would properly set up this QMainWindow would be to first keep that UI autogenerate code in its own file (myappUI.py or something like that). Then you would create a subclass for your QMainWindow, which will have your custom methods and data attributes:

    class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
    
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
            self.setupUi(self)
    
            # create instance attributes to store data
            ...
            self.mothersname = []
            self.birthday = []
            self.placeofbirth = []
            self.nationality = []
            self.address = []
            ...
    
            r=0
            c=0
            for x in children:
                for i in x:
                    newItem = QtGui.QTableWidgetItem(i)
                    self.tableWidget.setItem(r, c, newItem)
                    r += 1
                r = 0
                c += 1
    
        def newFile(self):
            ...
    
        def openFile(self):
            ...
    
        def Quit(self):
            # Dont force the exit of the app like this.
            # Just close the main window
            # sys.exit(app.exec_())
    
        # this was in no way a classmethod      
        # @classmethod
        def update2(self): 
            ...
    
        def actionewchild(self):
            ...
            # if you want to create a form dialog on the fly
            # and kill it after you get results
            form = Form(parent=self)
            form.exec_()
    
        def lists(self):
            ...
    
    class Form(QtGui.QDialog, Ui_Form):
    
        def __init__(self, *args, **kwargs):
            super(Form, self).__init__(*args, **kwargs)
            self.setupUi(self)
    
        def amlist(self):
            ...
    
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec_())
    

    This was just a rough outline of how it would be reorganized into two classes, each inheriting from their UI design. Those large number of globals would be moved probably into your MainWindow as instance attributes. Don't use globals.

    The reason I have taken this approach, in suggesting you completely reorganize, is because you are headed down a very messy and potentially frustrating path. Things will not work as you expect and you will find yourself posting on here constantly about why that is happening. If you take the time to review that book I suggested, you will have a much better experience.

    Update #1: to match your code update

    The concept between your main window and your dialog are backwards. You have the main window connecting an action to a function within your dialog module to launch it and do the work. What really should be happening is your main window connection to its own method. This method will launch the dialog, get the results and modify itself.

    def addtolists(self):
        program.am.append(text)
        instance = MainWindow()
        instance.updatetable()
        dialog.close()  
    

    You have that code in your dialog. First of all, calling MainWindow like that will not get you the instance. It will try and create a second main window widget. Don't try and get a reference to the parent and modify it. The children should have no knowledge of the parents that are using it. For all you know, down the line you might use this dialog in a different place.

    # this is just a pseudo example of the right direction.
    # A method in your MainWindow, starting the child dialog,
    # waiting on its results, and updating itself.
    def showDialog(self):
        d = MyDialog(self)
        if d.exec_():
            self.updateStuff(d.values)
    
    myAction.triggered.connect(self.showDialog)
    

    As for the globals... You should not need a single global in your application design. They are for special situations where you know you need them. I would say to make it a point that if you are using a global right now you might be doing it wrong. The reason is that you are using them to pass around data in different directions, instead of following a parent-child relationship, or communicating data over signals/slots. If a parent starts a child widget, it will know about the child and be able to call methods or get values from it.

    Update #2: to match your code update

    The creation of the dialog from your main window is a bit off. It should be like this:

    def doupdate(self):
        d = NewChildDlg(self)
        # exec_ shows the dialog modally
        if d.exec_():
            # your dialog didn't have a pre-initialized
            # `text` attribute so it was not safe to assume
            text = d.lineEdit.text()
            # stop using globals. use attributes
            self.am.append(text)
            self.updatetable()
    

    For the global... again stop using them. Move them into your main window class:

    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self.am = []
            self.courtdate = []
            self.board_number = []
    

    And this part in your dialog class is wrong:

        def addtolists(self):
            # why use a global here? dialog == self
            # self.accept()
            dialog.accept()        
    
    # you don't need a top level function that
    # maintains a global instance and creates the dialog
    # It should be the callers responsibility
    def main():
        global dialog
        dialog = NewChildDlg()
        dialog.show()
        dialog.exec_()
    

    And finally, just a comment about your import statements... You don't need to first import the module to be able to import members of that module:

    # either you need the newchilddlg namespace
    # or you only want the members...
    # newchilddlg.NewChildDlg  vs  NewChildDlg
    import newchilddlg
    from newchilddlg import *
    

    To help you out a bit more, I have posted a cleaned up version of your main window code here: http://pastebin.com/9b8gcGhU

    0 讨论(0)
提交回复
热议问题