繁体   English   中英

如何从 PyQt5 中的对话框 window 中触发主 Window 中的方法?

[英]How to trigger a method in a main Window from a dialog window in PyQt5?

我是 PyQt 的初学者,并尝试使用用户在另一个对话框 window 中提供的信息更新主 window 中的小部件。

这是我的主要 window:

class Window(QtWidgets.QMainWindow):    
    def __init__(self):        
        super(Window, self).__init__()
        uic.loadUi('GUI_MainWindow.ui',self)
        self.setWindowTitle("BR")

        self.statusBar()

        #save File Action
        saveFile= QtWidgets.QAction('&Profil speichern',self)
        saveFile.setShortcut('CTRL+S')
        saveFile.setStatusTip('Save File')
        saveFile.triggered.connect(self.file_save)

        mainMenu = self.menuBar()

        fileMenu = mainMenu.addMenu('&Datei')
        fileMenu.addAction(saveFile)

        self.home()

    def home(self):

        self.dlg = self.findChild(QtWidgets.QPushButton, 'addfile')
        self.dlg.clicked.connect(self.opensecondwindow)
        self.show()

    # this is the method that I want to call
    def updatebez(self,widgetname,filename):
        self.widg = self.findChild(QLabel, str(self.widgetname))
        self.widg.setText(filename)
        self.update()
        print(self.widg.Text())

    #calling the dialog window
    def opensecondwindow(self):
        self.sw = lesen(self)
        self.sw.show()

    def file_save(self):
        name, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File',options=QFileDialog.DontUseNativeDialog)
        file = open(name, 'w')
        text = self.textEdit.toPlainText()
        file.write(text)
        file.close()

这是对话框 window:

class lesen(QtWidgets.QDialog):        
    def __init__(self,parent):
        global afl
        global calendar
        global fn
        super(lesen, self).__init__(parent)
        uic.loadUi('Kal.ui',self)
        self.setWindowTitle("Parametrierung")
        afl = self.findChild(QtWidgets.QLineEdit, 'Aufloesung')
        calendar = self.findChild(QtWidgets.QCalendarWidget, 'Calendar')

        self.addfile = self.findChild(QtWidgets.QPushButton, 'chooseFile')
        slot = self.findChild(QtWidgets.QSpinBox, 'spinBox')
        slotnr = slot.text()
        widgetname = 'ZRName'+ slotnr
        self.filename = self.findChild(QtWidgets.QLineEdit, 'Bez')
        self.addfile.clicked.connect(self.updatebez(widgetname,self.filename))
        self.addfile.clicked.connect(self.file_open)

    def Datenanpassung(self, tempfile):
        list=[]
        zeitabstand = int(afl.text())*60
        datum = calendar.selectedDate()
        a = datetime.datetime(datum.year(),datum.month(),datum.day(),00,00,00)
        for row in tempfile:
            a = a + datetime.timedelta(0,zeitabstand)
            datetimestr= str(a.date()) + ' ' + str(a.time())
            row = [datetimestr, row[0]]
            list.append(row)
        return list


    def file_open(self):
        #Dateiauswahl
        global name
        global tempfile
        global fn
        tempfile = []

        filters = ""
        selected_filter = "csv or json (*.csv *.json)"
        name, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Datei auswählen', filters, selected_filter,  
                                                        options=QFileDialog.DontUseNativeDialog)
        file = open(name, 'r', newline='')
        for row in csv.reader(file):
            tempfile.append(row)
        self.anpassen = self.findChild(QtWidgets.QCheckBox, 'checkBox')
        if self.anpassen.isChecked():
            newfile = self.Datenanpassung(tempfile)
            with open(os.path.basename(file.name)[:-4] +'_mit_DateTime.csv', 'w', newline='') as csvFile:
                writer = csv.writer(csvFile)
                writer.writerows(newfile)
            file = open(os.path.basename(file.name)[:-4] +'_mit_DateTime.csv', 'r', newline='')
            reader = csv.DictReader( file, fieldnames = ( "DateTime","Wert"))

            out = json.dumps(list(reader))
            f = open( os.path.basename(file.name)[:-4] +'_mit_DateTime.json', 'w')
            f.write(out)
        else:
            pass

编辑:我得到的错误是:(不知何故它只将第一行粘贴为代码)

Traceback (most recent call last):

  File "D:/Data/Zeitreihen_1.0x2.py", line 141, in opensecondwindow
    self.sw = lesen(self)

  File "D:/Data/Zeitreihen_1.0x2.py", line 35, in __init__
    self.addfile.clicked.connect(self.updatebez(widgetname,self.filename))

AttributeError: 'lesen' object has no attribute 'updatebez'


An exception has occurred, use %tb to see the full traceback.
SystemExit: 0

方法 updatez 背后的目标是从用户在对话框 window 中输入的文本中更新主 window 中的 QLabel object。 在添加方法并尝试在对话框 window 中调用它之前,一切正常。 当我尝试单击显示对话框 window 的按钮时,现在会出现错误。

我知道最好的解决方案是为主要 window 和对话框 window 之间的信号设置一个新的 class ,但我无法正确处理。 因此,我想知道是否可以让代码在不使用信号的情况下完成它必须做的事情。

在此先感谢,互联网向导!

您的代码存在各种问题,我会在回答时尝试解决这些问题。

该错误的主要原因是,正如错误报告的那样,您正在尝试调用不存在的 class 实例的属性: updatebez是主 window 的成员,而不是对话框的成员(记住: self约定命名参考class 实例)。

无论如何,即使解决了这个问题(通过调用parent.updatebez ),它也不会起作用:信号/插槽连接通过使用可调用函数(插槽)来工作,因为连接是“交互式接口”,每次都必须做出不同的反应根据到信号的参数; 当你连接一个信号时,你实际上传递了一个对 function 的引用(它还没有运行,也不应该运行),并且只有在发出该信号时才会实际调用 function。 可能使用信号 arguments 作为其参数。
在您的情况下,您连接到 function调用,而不是它的引用,并且该调用可能会返回某个值或 None (所有未显式返回值的函数都隐式返回 None),这将导致连接错误:Qt期望对可调用对象的引用,但由于 function 正在就地调用,它取而代之的是返回其返回值,从而导致“未处理的 TypeError”异常。

“交互式界面”概念很重要:即使解决了上述问题,您也将始终以slotnrwidgetname变量的“静态”值结束。 在 python 中,变量和属性通常是 static:如果 spinbox 值更改,则slotnr将永远不会更改。

您需要创建一个widgetname ,每次单击按钮时“即时”计算小部件名称,然后调用主 Z05B8C74CBD96FBF2DE4C1A352702FFF4Z 的 function。

class Lesen(QtWidgets.QDialog):
    def __init__(self, parent):
        # ...
        self.addfile.clicked.connect(self.sendUpdate)

    def sendUpdate(self):
        widgetname = 'ZRName'+ self.spinBox.text()
        self.parent().updatebez(widgetname, self.Bez.text()))

或者,可以使用lambda函数。 但是,您应该小心使用它们:虽然它们是有用的“一次性快捷方式”,但有时为了可读性和调试目的,最好编写一个实际的 function 来代替。
最后但同样重要的是,lambda 是匿名函数,因此如果您不保留对它们的引用,您将无法单独断开连接到它们的信号。

class Lesen(QtWidgets.QDialog):
    def __init__(self, parent):
        # ...
        self.addfile.clicked.connect(lambda: self.parent().updatebez(
            'ZRName' + self.spinBox.text(), self.Bez.text()))

关于上述示例和您的代码的一些注释:

  • 无需使用findChild来获取您要查找的小部件; when using loadUi (or self.setupUi(self) with the multiple inheritance approach ) Qt automatically creates attribute names for all objects of the UI using their Qt object name: if your QSpinBox object name is "spinBox" in Designer, you can simply access它与self.spinBox ; 如果使用单个 inheritance 方法,则小部件将是self.ui的子级(因此,上面示例中的self.ui.spinBox );
  • 由于上述原因,每次调用updatebez function 插槽时,无需设置self.widg
  • 使用setText()后不需要调用update() ) ,因为它会自动刷新 label;
  • 您可能希望在updatebez()self.Bez.text() )中使用行编辑text() ) ,否则您将获得行编辑 object (不是字符串);
  • 如果对话框是持久的(不是模态的),即使显示对话框,主 window 也将始终获得鼠标/键盘交互,这将导致用户能够打开多个对话框; 你可能不想要那个;
  • 通常最好避免在__init__中调用show()
  • while, for simple situations, there's nothing wrong with implementations that use static function names of other class instancies , interfaces are usually preferred, and that's what signals and slot exist for: besides object reusability, the major benefit are readability, debugging and further editing (特别是在重构时); 所以,是的,最好为对话框创建自己的信号,然后从主 window 连接它;
  • 正如PEP 8所建议的那样: class 名称应始终大写,逗号后应始终有一个空格,并且mixedCase实例和变量名称应小写(在处理 Qt 时可能使用混合大小写);

这是一个基于您的代码的示例,其中包含上面编写的所有内容(我只显示不同之处):

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        uic.loadUi('GUI_MainWindow.ui', self)
        self.setWindowTitle("BR")

        self.statusBar()

        #save File Action
        saveFile= QtWidgets.QAction('&Profil speichern', self)
        saveFile.setShortcut('CTRL+S')
        saveFile.setStatusTip('Save File')
        saveFile.triggered.connect(self.file_save)

        mainMenu = self.menuBar()

        fileMenu = mainMenu.addMenu('&Datei')
        fileMenu.addAction(saveFile)

        # create the dialog, just once
        self.lesenDialog = Lesen(self)
        self.sw.dataChanged.connect(self.updatebez)

        # show the dialog when clicking on the button, no need to create a
        # new one each time;
        self.addfile.clicked.connect(self.sw.show)

    def updatebez(self, index, filename):
        widgetname = 'ZRName{}'.format(index)
        widget = getattr(self, widgetname)
        widget.setText(filename)
        print(self.widg.Text())


class Lesen(QtWidgets.QDialog):
    dataChanged = QtCore.pyqtSignal(int, str)
    def __init__(self, parent):
        # ...
        self.addfile.clicked.connect(lambda: self.dataChanged.emit(
            self.spinBox.value(), self.Bez.text()))

最后,在 StackOverflow 上发布问题时,您应该花一些时间仔细编辑您的示例(甚至到编写全新代码的程度,这通常有助于在提出问题之前找到解决方案); 此外,最好坚持使用英语并避免本地化 function 和变量名称,因为它确实提高了人们专注于您的问题的能力(因此更容易帮助您),而不是被可能对您没有意义的名称分心他们:你会惊讶于有那么多人因为这个原因而放弃帮助别人。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM