【问题标题】:How to launch the QMainwindow from QDialog after the interval selected by user on QDialog combobox using QTimer如何在用户使用 QTimer 在 QDialog 组合框上选择的时间间隔后从 QDialog 启动 QMainwindow
【发布时间】:2020-03-23 05:49:58
【问题描述】:

我有一个名为 Main 的 QMainWIdow,它调用名为 popup_on_waiver 的 QDialog。 QDialog 有一个组合框来选择小时数。一旦用户选择小时并单击确定,我想关闭弹出窗口,隐藏 QMainwindow 并在从组合框中选择的小时数后启动 QMainwindow。程序一直运行,直到用户选择小时并单击确定。它关闭弹出窗口并隐藏主窗口。(要求应用程序必须永远在隐藏状态下运行,因此隐藏主窗口)。当它调用launch_after_interval 时,它失败并出现错误“进程完成,退出代码1073741845”。请告知正确的步骤。 我在下面未提供的某些其他条件下启动主窗口,因此我正在编写一个单独的块,以便在用户选择的豁免时间后再次启动主窗口。另外,我尝试获取弹出窗口的结果,接受或拒绝,但它没有返回任何内容。

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import QUrl, Qt, QTimer, QSize, QRect
import sys


class popup_on_waiver(QDialog):
    #pop up window
    def __init__(self, parent=None):
        super(QDialog,self).__init__(parent)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.setMinimumSize(QSize(660, 340))
        self.setWindowTitle("Waiver")

        self.cb = QComboBox()   #combobox
        self.cb.setGeometry(QRect(40, 40, 100, 30))
        self.cb.addItems(["1", "2", "3", "4"])
        self.cb.currentIndexChanged[str].connect(self.returnInterval)
        self.cb.setObjectName("combobox")
        self.cb.move(80, 80)

        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        self.buttons.accepted.connect(self.hide_main)
        self.buttons.rejected.connect(self.reject)  #buttons

        vbox = QVBoxLayout(self)   #layout
        vbox.addWidget(self.cb)
        vbox.addWidget(self.buttons)
        self.setLayout(vbox)


    def hide_main(self, hours):
        self.accept
        self.parent().hide()
        launch_after_interval(self.interval)  #calling timer function


    def returnInterval(self, hours):      #combobox value that is number of hours
        self.interval = int(hours) * 3600 * 1000

#QMainwindow
class Main(QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.initUI()

    def initUI(self):
        self.centralwidget = QWidget(self)
        self.Waiver = QPushButton('Waiver')
        self.Waiver.clicked.connect(lambda: self.popup())

        hbox = QHBoxLayout()
        hbox.addWidget(self.Waiver)
        self.centralwidget.setLayout(hbox)
        self.setGeometry(50, 50, 1200, 600)
        self.setWindowTitle("Timesheet")
        self.setWindowIcon(QIcon(""))
        self.setStyleSheet("background-color:")
        self.setCentralWidget(self.centralwidget)
        self.show()

    def popup(self):
        self.p = popup_on_waiver()
        self.p.exec_()


def launch_after_interval(interval): 
    timer = QTimer()
    timer.setSingleShot(True)
    timer.setInterval(interval)
    timer.timeout().connect(lambda: Main())
    timer.start()

【问题讨论】:

    标签: pyqt5


    【解决方案1】:

    您的代码存在各种问题:

    1. 你在没有设置父对话框的情况下创建对话框,所以当你尝试调用self.parent().hide()时它不会起作用,因为parent()返回None,它显然没有hide属性;
    2. 您已将accepted 信号连接到hide_main,这需要一个参数,但accepted 信号没有任何参数;
    3. 你错过了hide_mainaccepted的括号,所以它不会被调用;
    4. self.interval 仅在组合索引更改时设置,但如果用户不更改它(保留默认值),则不会设置任何 self.interval
    5. 您正在设置WindowStaysOnTopHint 标志,这将重置任何其他窗口标志;结果将是您不会有一个新窗口,而是一个“嵌入”在父窗口中的小部件;正确设置你应该使用self.setWindowFlags(self.flags() | Qt.WindowStaysOnTopHint)的标志;
    6. 信号不能被“调用”,所以timer.timeout().connect中不能有括号;
    7. timer对象在launch_after_interval范围之外没有引用,也没有设置父对象,所以函数一返回就被删除,永远不会被触发;

    修改后的代码(修改以粗体显示):

    class popup_on_waiver(QDialog):
        def __init__(self, parent=None):
            super(QDialog,self).__init__(parent)
            self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
            self.setMinimumSize(QSize(660, 340))
            self.setWindowTitle("Waiver")
    
            self.cb = QComboBox()   #combobox
            self.cb.setGeometry(QRect(40, 40, 100, 30))
            self.cb.addItems(["1", "2", "3", "4"])
            self.cb.currentIndexChanged[str].connect(self.returnInterval)
            self.cb.setObjectName("combobox")
            self.cb.move(80, 80)
    
            self.buttons = QDialogButtonBox(
                QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
            self.buttons.accepted.connect(self.hide_main)
            self.buttons.rejected.connect(self.reject)
    
            vbox = QVBoxLayout(self)
            vbox.addWidget(self.cb)
            vbox.addWidget(self.buttons)
            self.setLayout(vbox)
    
            # set the default interval
            self.interval = 3600000
    
    
        # no arguments here!
        def hide_main(self):
            self.accept() # <-- the parentheses!
            self.parent().hide()
            launch_after_interval(self.interval)
    
        def returnInterval(self, hours):
            self.interval = int(hours) * 3600 * 1000
    
    class Main(QMainWindow):
    
        def __init__(self):
            super().__init__()
            self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
            self.initUI()
    
        def initUI(self):
            self.centralwidget = QWidget(self)
            self.Waiver = QPushButton('Waiver')
            # if the function does not have arguments, lambda is usually not required
            self.Waiver.clicked.connect(self.popup)
    
            hbox = QHBoxLayout()
            hbox.addWidget(self.Waiver)
            self.centralwidget.setLayout(hbox)
            self.setGeometry(50, 50, 1200, 600)
            self.setWindowTitle("Timesheet")
            self.setWindowIcon(QIcon(""))
            self.setStyleSheet("background-color:")
            self.setCentralWidget(self.centralwidget)
            self.show()
    
        def popup(self):
            # the parent is required to make the dialog modal *and* allow it
            # to call parent().hide()!
            self.p = popup_on_waiver(self)
            self.p.exec_()
    
    
    def launch_after_interval(interval):
        # set a parent QObject for the timer, so that it's not removed
        # when the function returns
        timer = QTimer(QApplication.instance())
        timer.setSingleShot(True)
        timer.setInterval(interval)
        timer.timeout.connect(lambda: Main())
        timer.start()
    

    其他相对小问题:

    • 应该避免类似的属性名称(如centralwidget类似于 QMainWindow 的centralWidget()),因为它们会造成混淆并导致难以发现的错误和问题;李>
    • 不应在最终调用/访问/显示它的对象之外创建作用于对象的计时器(即使是间接的);虽然从技术上讲这并没有什么问题,但通常最好保持对象“有条理”,以便在需要时可以访问它们(例如,显示窗口并在超时之前停止计时器);
    • 不建议创建主窗口的新实例,因为已经存在;这和上一点有关:如果你有直接引用定时器窗口,也可以调用self.someWindow.show()
    • 避免混淆和混淆命名风格:您使用大写的属性名称(Waiver)和小写的类名称(popup_on_waiver),而应该相反;然后还有混合大小写(returnInterval)和under_score(hide_main);选择一种风格并保持这种风格(在style guide for Python code,又名 PEP-8 中了解更多信息);

    我更喜欢只编辑阻止程序运行的代码部分,但您应该牢记上述方面,即使它们“相对较小”(强调相对 )。

    最后,(微不足道,但并非无关紧要):应避免混合来自相同模块的导入模式:您要么使用通配符导入,如from module import *(但通常是shouldn't)或显式导入,如from module import ClassA, ClassB, [...];对于像 PyQt5 这样的大模块,导入子模块是很常见的,比如from PyQt5 import QtWidgets:

    好:

    from PyQt5 import QtWidgets
    
    class SomeWidget(QtWidgets.QWidget):
        # ...
    

    也不错,但往往非常令人讨厌,因为您必须记住在每次需要新类时添加类,并且您最终可能会得到很长的导入列表,可能会导致不必要的类,因为您最终不使用一些(另外,我怀疑至少在 Qt 上是否有实质性的好处):

    from PyQt5.QtWidgets import QWidget, QHBoxLayout # etc...
    
    class SomeWidget(QWidget):
        # ...
    

    不太好,但它可以工作(保留子模块名称有助于记住它们的“范围”)并且行为与前一个相同:

    from PyQt5.QtWidgets import *
    

    这,这简直是错误(我的意思是,它有效,但没有任何意义):

    from PyQt5 import QtWidgets
    from PyQt5.QtWidgets import QWidget
    

    【讨论】:

    • 非常感谢您的详细回复。它按预期工作。谢谢。
    猜你喜欢
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    相关资源
    最近更新 更多