重新加载 PyQt5 应用程序而不先重新启动它

Posted

技术标签:

【中文标题】重新加载 PyQt5 应用程序而不先重新启动它【英文标题】:Reloading a PyQt5 App without Restarting it first 【发布时间】:2021-04-07 15:11:19 【问题描述】:

我创建了一个应用程序,并在其中一个 Windows (CreateProjectWindow(QDialog)) 上填写了一份表格,然后我使用 self.buttonBox.accepted.connect(self.getInfo) 提交了该表格。

提交时,我希望UpdateProjecWindow(QDialog) 中的self.tableComboBox 自动并立即更新,而无需我先重新启动应用程序。 CreateProjectWindow(QDialog) 中的最后四行代码是我尝试过的,但它们都不起作用。下面是sn-p的代码:

from __future__ import print_function
from datetime import date
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import * 
import sys 
import os
import subprocess
import pandas as pd
import time


Class CreateProjectWindow(QDialog):

     def __init__(self):
          super(CreateProjectWindow, self).__init__()

          self.setWindowTitle("Create a new project") 
          self.setGeometry(100, 100, 300, 400) 
          self.timeStampLineEdit = QDateTime.currentDateTime().toString('MM-dd-yyyy hh:mm:ss')
          self.surnameLineEdit = QLineEdit()
          self.firstnameLineEdit = QLineEdit()
          self.dateOfBirthLineEdit = QDateEdit(calendarPopup=True, displayFormat='MM-dd-yyyy') 
          self.createForm()
          self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
          self.buttonBox.accepted.connect(self.getInfo) 
          self.buttonBox.rejected.connect(self.reject) 

     def getInfo():
         output = 'timestamp': self.timeStampLineEdit,\ 
                   'surname' : self.surnameLineEdit,\
                   'firstname' : self.firstnameLineEdit,\
                   'dob' : self.dateOfBirthLineEdit
         df = pd.DataFrame(output, index=[0])
         df.to_excel('to/some/local/path/profiles_data.xlsx')

         return None

         QApplication.processEvents() # attempt 1 but doesn't work
         QApplication(sys.argv).reload() # attempt 2 but doesn't work
         subprocess.Popen([sys.executable, FILEPATH]) # attempt 3 restarts the app which I don't want
         os.execl(sys.executable, sys.executable, * sys.argv) # attempt 4 restarts the app which I don't want.

     def creatForm(self):
        layout = QFormLayout() 
        layout.addRow(QLabel("Surname"), self.surnameLineEdit) 
        layout.addRow(QLabel("First Name"), self.firstnameLineEdit)
        layout.addRow(QLabel("D.O.B"), self.dateOfBirthLineEdit)
        self.formGroupBox.setLayout(layout) 
        mainLayout = QVBoxLayout() 
        mainLayout.addWidget(self.formGroupBox) 
        mainLayout.addWidget(self.buttonBox) 
        self.setLayout(mainLayout) 

class UpdateProjectWindow(QDialog): 
  
    def __init__(self): 
        super(UpdateProjectWindow, self).__init__() 
        self.setWindowTitle("Parameter Inputs") 
        self.setGeometry(100, 100, 300, 400) 
        self.formGroupBox = QGroupBox("Some Window") 
        
        self.tableComboBox = QComboBox()
        
        df = pd.read_excel('to/some/local/path/profiles_data.xlsx')

        names = df['surname']

        self.tableComboBox.addItems(names)

        createForm()

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 
        self.buttonBox.accepted.connect(self.processInfo) 
        self.buttonBox.rejected.connect(self.reject) 
        mainLayout = QVBoxLayout() 
        mainLayout.addWidget(self.formGroupBox) 
        mainLayout.addWidget(self.buttonBox) 
        self.setLayout(mainLayout) 

    def processInfo(self):
        get_name = self.tableComboBox.currentText()
        print(get_name)

    def createForm(self):
        layout = QFormLayout() 
        layout.addRow(QLabel("Select Surname"), self.tableComboBox)
        self.formGroupBox.setLayout(layout) 

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # setting window title 
        self.setWindowTitle("MyApp") 
  
        # setting geometry to the window 
        self.setGeometry(200, 200, 400, 10) 

        self.window1 = CreateProjectWindow()
        self.window2 = UpdateProjectWindow()

        l = QVBoxLayout()
        button1 = QPushButton("Create a Project") 
        button1.clicked.connect(
            lambda checked: self.toggle_window(self.window1)
        )
        l.addWidget(button1)

        button2 = QPushButton("Update a Project")
        button2.clicked.connect(
            lambda checked: self.toggle_window(self.window2)
        )
        l.addWidget(button2)
        
        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def toggle_window(self, window):
        if window.isVisible():
            window.hide()
        else:
            window.show()

if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

【问题讨论】:

附代码时请注意:您的示例包含多个问题(“Class”应该是小写的,缩进问题很多,对createForm的函数调用都是错误的,并且有属性在声明之前使用)。在粘贴之前总是尝试你的代码,我们应该专注于你的代码做了什么,而不是纠正你的语法。 【参考方案1】:

通常不需要重新启动 Qt 应用程序,当然这对于您的情况也不是必需的:重新启动 QApplication 几乎就像关闭程序并再次打开它,这样做只是为了更新一些数据并不会产生任何影响完全有感觉。

一种可能的解决方案是为第一个窗口类创建一个自定义信号,该信号将在数据库更新时发出,然后将该信号连接到另一个窗口中加载数据的函数(这意味着该函数将也可以在开头调用)。

class CreateProjectWindow(QDialog):
    dataChanged = pyqtSignal()

    # ...

    def getInfo(self):
        # note that your original data was completely wrong, as you tried to add
        # the *widgets* to the frame, while you need to add their *values*
        output = 'timestamp': self.timeStampLineEdit,
               'surname' : self.surnameLineEdit.text(),
               'firstname' : self.firstnameLineEdit.text(),
               'dob' : self.dateOfBirthLineEdit.date().toString()
        df = pd.DataFrame(output, index=[0])
        df.to_excel('to/some/local/path/profiles_data.xlsx')
        self.dataChanged.emit()
        self.accept()


class UpdateProjectWindow(QDialog): 
    def __init__(self): 
        # ...
        self.tableComboBox = QComboBox()
        self.updateData()

        self.createForm()
        # ...

    def updateData(self):
        self.tableComboBox.clear()
        df = pd.read_excel('to/some/local/path/profiles_data.xlsx')
        names = df['surname']
        self.tableComboBox.addItems(names)


class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.window1.dataChanged.connect(self.window2.updateData)

除此之外,你必须对你的代码更加小心:我花了更多的时间来纠正你的语法和错误,而不是真正给你这个答案。请注意,您的示例还有其他各种问题,但我不会在这里解决它们,因为它不在主题范围内。本质上,写程序的时候一定要多加注意。

【讨论】:

谢谢@musicamante。我已尝试实施您的解决方案,但我认为 getInfo() 中的输出在连接时有空白字段,因为我收到以下错误:ValueError: No engine for filetype: '' 再次感谢@musicamante,问题已解决。我更改了以下内容以使其正常工作:从:self.window1.dataChanged.connect(self.window2.updateData) 到:self.window1.dataChanged.connect(self.window2.__init__) @MichaelKateregga 不,不!这真是一个糟糕可怕的想法!如果你有那个错误,那么你可能做错了什么,从那个错误可能是路径(如上所述,你应该更加小心你的语法)。因为这样的错误而调用__init__ 是完全和绝对错误的(实际上,无论如何它都是错误的,除非你真的,真的知道你在做什么)。错误应该根据他们的信息来解决,而不是试图做随机的事情。如果您在开车时看到燃油灯亮起,您不会跳下车窗去买新车。 嗨@musicamante,该错误是由于在尚未填充数据时调用updateData,即姓氏,名字字段等是空字符串''所以我的其余代码不起作用。调用 init 会以某种方式填充所有字段并更新第二个窗口。我没有给你完整的代码,但是 updateData 函数有几个组合框,它们在其他组合框中的 currentTextChanged 上得到更新。您可以通过 michaelk@aims.ac.za 给我发送电子邮件,以便进一步讨论。 我看不出应该讨论什么。如果在__init__ 期间数据为空,请添加正确避免问题的检查,或者您正确实现 updateData 函数。 再次调用 init 作为解决方法只是懒惰,它不是解决方案,而且迟早肯定会产生问题。

以上是关于重新加载 PyQt5 应用程序而不先重新启动它的主要内容,如果未能解决你的问题,请参考以下文章

重新启动 gif 动画而不重新加载文件

注销时重新启动expo应用程序而不弹出

使用 nodemon 或 supervisor 仅重新加载文件而不重新启动所有服务器 - WebStorm

在 IntelliJ 中强制重新加载环境变量...而不重新启动?

重新加载 .env 变量而不重新启动服务器(Laravel 5,共享主机)

在sails.js 中更改配置参数而不重新加载服务器