Как запустить QMainwindow из QDialog после интервала, выбранного пользователем в выпадающем списке QDialog с использованием QTimer - PullRequest
0 голосов
/ 22 марта 2020

У меня есть QMainWIndow с именем Main, который вызывает QDialog с именем popup_on_waiver. 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()

1 Ответ

0 голосов
/ 22 марта 2020

Существуют различные проблемы с вашим кодом:

  1. вы создаете диалог без установки родителя, поэтому при попытке вызвать self.parent().hide() он не будет работать, потому что parent() возвращает None, который явно не имеет атрибута hide;
  2. вы подключили сигнал accepted к hide_main, который требует аргумента, но сигнал accepted не имеет;
  3. вы пропустили скобки accepted в hide_main, поэтому он не будет вызываться;
  4. self.interval устанавливается только всякий раз, когда индекс комбо изменяется, но если пользователь не меняет его (оставляя значение по умолчанию), self.interval set не будет;
  5. вы устанавливаете флаг WindowStaysOnTopHint only , который сбросит все остальные флаги окна; В результате у вас будет не новое окно, а виджет, который «встроен» в родительский элемент; чтобы правильно установить флаг, вы должны использовать 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(<b>self.windowFlags() | Qt.WindowStaysOnTopHint</b>)
        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
        <b>self.interval = 3600000</b>


    # no arguments here!
    def <b>hide_main(self)</b>:
        self.accept<b>()</b> # <-- 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(<b>self.windowFlags() | Qt.WindowStaysOnTopHint</b>)
        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
        <b>self.Waiver.clicked.connect(self.popup)</b>

        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<b>(self)</b>
        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 = <b>QTimer(QApplication.instance())</b>
    timer.setSingleShot(True)
    timer.setInterval(interval)
    <b>timer.timeout.connect(lambda: Main())</b>
    timer.start()

Другие относительно незначительные проблемы:

  • аналогичные имена атрибутов (например, centralwidget, что слишком аналогично centralWidget()) QMainWindow следует избегать, поскольку они могут создавать путаницу и приводить к трудностям в обнаружении ошибок и проблем;
  • таймер, который действует на объект, не должен создаваться вне объекта, который в конечном итоге вызовет / получить доступ / показать его (даже если косвенно); хотя с технической точки зрения в этом нет ничего плохого, обычно лучше сохранять объекты «организованными», чтобы к ним можно было обращаться при необходимости (например, показывать окно и останавливать таймер до истечения времени ожидания);
  • создание новый экземпляр главного окна не предлагается, так как он уже существует; это связано с предыдущим пунктом: если у вас есть прямая ссылка на таймер и окна, вы также можете вызвать self.someWindow.show();
  • , избегая смешивания и путаницы стилей имен: вы ' мы использовали имена в верхнем регистре для атрибутов (Waiver) и нижние для классов (popup_on_waiver), в то время как это должно быть наоборот; тогда есть также mixedCase (returnInterval) и under_score (hide_main); выберите стиль и сохраните его (подробнее об этом см. в руководстве по стилю для Python code , он же PEP-8);

Я предпочел редактировать только части вашего кода, который мешал работе программы, но вы должны действительно помнить вышеупомянутые аспекты, даже если они «относительно незначительны» (акцент на относительно ).

Наконец, ( тривиально, но не несущественно): следует избегать смешивания режимов импорта из одних и тех же модулей: либо вы используете импорт с подстановочными знаками, например from module import * (но обычно не должен ), либо явные, например 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...