Лучший подход к отображению / скрытию диалогов - PullRequest
0 голосов
/ 12 ноября 2018

Я столкнулся с теоретическим вопросом. Я использую pyqt5, но это, вероятно, очень универсальный и не зависящий от фреймворка вопрос.
У меня QMainwindow сидит в ожидании, когда пользователь что-то сделает. Пользователь может показывать / скрывать диалоги (подклассы QDockwidgets), когда он выбирает, используя QMenu и связанные с ним ярлыки (это проверяемый QAction для каждого отдельного диалога).
Я изо всех сил пытался показать / скрыть диалоги эффективно. В настоящее время я просто запускаю их все при запуске, скрывая те, которые я не хочу показывать в начале. Это облегчает запуск диалогов, так как я могу просто dialogue.show() / dialogue.hide() в зависимости от текущей видимости диалогов. Но я не могу поверить, что это лучшая практика и очень эффективная.

Я пытался ( В настоящее время у меня не настроена среда pyqt на этом компьютере, поэтому мне пришлось урезать мой фактический код, не имея возможности проверить, работает ли он ):

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class InfoPanel(QDockWidget):
    def __init__(self, title='Tool Box'):
        QDockWidget.__init__(self, title)
        self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        frame = QFrame()
        layout = QGridLayout()
        self.canvas = QGraphicsView()
        self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
        layout.addWidget(self.canvas)
        frame.setLayout(layout)
        self.setWidget(frame)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.showpanelAct = QAction("&Show Panel", self, enabled=True,checkable=True, shortcut="F10")
        self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.showpanelAct)
        self.setDockOptions(QMainWindow.AnimatedDocks)

    def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
        if i == 0: #infopanel
            dialogueExists = True
            try: self.infoPanel
            #except NameError: #does not catch the error
            except:
                dialogueExists = False
            if dialogueExists:
                print('destroy')
                self.infoPanel.destroy()
            else:
                print('create')
                self.infoPanel = InfoPanel() #init
                self.infoPanel.show()

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

Это работает в первый раз, но после этого кажется, что оно вызывает разрушение диалога (что, к удивлению, не приводит к сбою ничего, что просто продолжается).
Почему это и есть ли стандартный способ приблизиться к отображению скрытия диалогов?

Ответы [ 2 ]

0 голосов
/ 17 ноября 2018

После перерыва в кодировании решение моей проблемы стало очень очевидным. Возвращаясь к исходному коду (который не дал ожидаемого результата создания / уничтожения диалога self.infoPanel по требованию):

dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
  dialogueExists = False
if dialogueExists:
  print('destroy')
  self.infoPanel.destroy()
else:
  print('create')
  self.infoPanel = InfoPanel() #init
  self.infoPanel.show()

Моя главная проблема заключалась в том, что я перепутал две разные вещи. Qt уничтожил виджет, содержащийся в объекте self.infoPanel, когда я вызвал self.infoPanel.destroy(). Но это не означает, что объект self.infoPanel не существует (это именно то, для чего я использую try: ..., чтобы увидеть, существует ли объект). Простой и очевидный способ создания и уничтожения диалогов по требованию, очевидно, включает удаление объекта из среды (del self.infoPanel). Рабочий код:

dialogueExists = True
try:
  self.infoPanel.destroy() #not sure this is needed, but I guess it doesn't hurt
  del self.infoPanel #this is the deletion of the actual object
except:
  dialogueExists = False
if not dialogueExists : 
  self.infoPanel = InfoPanel()

Приветствия и большое спасибо за полезный совет при принятии решения о том, показывать / скрывать диалоги или создавать / уничтожать их!

0 голосов
/ 13 ноября 2018

Я взял MCVE OP и попытался запустить его в моем cygwin64 в Windows 10.

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

Сначала я вставил & ldquo; хижину & ​​rdquo; на первой строке для удобного запуска в bash:

#!/usr/bin/python3

Во-вторых, self.viewMenu не появился. Следовательно, я вставил строку после

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.showpanelAct)

для добавления viewMenu к строке главного меню:

        self.menuBar().addMenu(self.viewMenu)

который это исправил.

В-третьих, при нажатии на пункт меню я получил:

Traceback (most recent call last):
  File "./testQDockPanelShowHide.py", line 27, in <lambda>
    self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
  File "./testQDockPanelShowHide.py", line 45, in showPanel
    self.infoPanel = InfoPanel() #init
  File "./testQDockPanelShowHide.py", line 17, in __init__
    self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
NameError: name 'QtGui' is not defined
Aborted (core dumped)

Я должен признать, что мои знания Python очень ограничены. (Я - парень, который пишет привязки Python на C ++ для коллег. Итак, мои коллеги - настоящие эксперты. В большинстве случаев я немного играю в Python, когда проверяю, выполняют ли новые реализованные привязки то, что ожидается.) Однако Я модифицировал

    self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))

до:

    self.canvas.setBackgroundBrush(QBrush(QColor(40, 40, 40)))

которая исправила эту проблему.

После этого я понял поведение, описанное OP, и внимательно посмотрел, где я (и OP) заподозрил ошибку:

    def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
        if i == 0: #infopanel
            dialogueExists = True
            try: self.infoPanel
            #except NameError: #does not catch the error
            except:
                dialogueExists = False
            if dialogueExists:
                print('destroy')
                self.infoPanel.destroy()
            else:
                print('create')
                self.infoPanel = InfoPanel() #init
                self.infoPanel.show()

Я твердо верю, что try: self.infoPanel не делает то, что думает OP.

Он пытается получить доступ к self.infoPanel, который не существует до первого вызова этого метода. (Пожалуйста, имейте в виду, что переменная-член self.infoPanel не существует.) Таким образом, ветвь except: выполняется и устанавливает dialogueExists = False, что через несколько строк вызывает self.infoPanel = InfoPanel() #init. Теперь переменная-член self.infoPanel существует, и try: self.infoPanel никогда не будет повторяться до тех пор, пока не будет уничтожено это MainWindow.

Из любопытства я взглянул на QWidget.destroy() (чтобы не сказать что-то не так):

QWidget.destroy (self, bool destroyWindow = True, bool destroySubWindows = True)

Освобождает системные ресурсы окна. Уничтожает окно виджета, если destroyWindow имеет значение true.

destroy () вызывает себя рекурсивно для всех дочерних виджетов, передавая destroySubWindows для параметра destroyWindow. Чтобы лучше контролировать уничтожение подвиджетов, сначала выборочно уничтожайте подвиджеты.

Эта функция обычно вызывается из деструктора QWidget.

Это определенно не уничтожает переменную-член self.infoPanel.

Поняв это, исправить было легко и очевидно:

    def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
        if i == 0: #infopanel
            try: self.infoPanel
            #except NameError: #does not catch the error
            except:
                print('create')
                self.infoPanel = InfoPanel() #init
            if self.infoPanel.isVisible():
                self.infoPanel.hide()
            else:
                self.infoPanel.show()

Btw. Я заменил destroy() на hide(), что делает воссоздание InfoPanel() устаревшим.

Я проверил это, переключая пункт меню несколько раз & ndash; теперь он работает как положено (по крайней мере, похоже).

Полный образец наконец:

#!/usr/bin/python3

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class InfoPanel(QDockWidget):
    def __init__(self, title='Tool Box'):
        QDockWidget.__init__(self, title)
        self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        frame = QFrame()
        layout = QGridLayout()
        self.canvas = QGraphicsView()
#        self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
        self.canvas.setBackgroundBrush(QBrush(QColor(40, 40, 40)))
        layout.addWidget(self.canvas)
        frame.setLayout(layout)
        self.setWidget(frame)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.showpanelAct = QAction("&Show Panel", self, enabled=True,checkable=True, shortcut="F10")
        self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.showpanelAct)
        self.menuBar().addMenu(self.viewMenu)
        self.setDockOptions(QMainWindow.AnimatedDocks)

    def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
        if i == 0: #infopanel
            try: self.infoPanel
            #except NameError: #does not catch the error
            except:
                print('create')
                self.infoPanel = InfoPanel() #init
            if self.infoPanel.isVisible():
                self.infoPanel.hide()
            else:
                self.infoPanel.show()

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

Snapshot of MCVE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...