PySide:为啥在另一个 QmainWINdow(Main) 中触发 QAction 时 QMainWindow 会烧毁并消失

Posted

技术标签:

【中文标题】PySide:为啥在另一个 QmainWINdow(Main) 中触发 QAction 时 QMainWindow 会烧毁并消失【英文标题】:PySide: why the QMainWindow flasked and disappeared when trigger a QAction in another QmainWIndow(Main)PySide:为什么在另一个 QmainWINdow(Main) 中触发 QAction 时 QMainWindow 会烧毁并消失 【发布时间】:2014-01-14 02:05:25 【问题描述】:

我是 PySide 的新手。我遇到了一个问题:当我从另一个 QmainWIdow(主 UI)触发 QAction 时,一个新的 QMainWindow 闪烁并消失。这里我发布示例代码来说明上述情况:

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
       super(MainWindow, self).__init__(parent)
       ...
       self.execTaskAct = QtGui.QAction("execute", self,  triggered=self.executeTask)
       ...

    def  executeTask(self):
      task = TaskWindow()

class  TaskWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
       super(TaskWindow, self).__init__(parent)
       ...
      self.show()


if __name__ == '__main__':

    import sys

    app = QtGui.QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

【问题讨论】:

只需在task 变量之前添加selfself.task = TaskWindow() 【参考方案1】:

在创建新的 QMainWindow 后,您不会保留对它的引用,并且由于该新窗口(@98​​7654321@)没有父窗口,因此一旦 executeTask 方法运行,该窗口就会被垃圾回收。

您只需保留对新窗口的引用,将executeTask 更改为:

def  executeTask(self):
    self.task = TaskWindow()

请注意,如果操作运行两次,这将覆盖第一个 TaskWindow 并使其消失,否则您的应用程序将崩溃。您可以计算出在这种情况下应该发生什么,或者存储多个 TaskWindows 的引用的更好方法(例如在列表中)。

请注意,如果 QWidgets 没有父级,您只需要存储它们的引用(在您当前的情况下是这样)。如果 QWidget 有父对象,Qt 会在内部存储一个引用。

【讨论】:

【参考方案2】:

首先,由于缩进不一致,您的代码在外观上包含语法错误,这是 python 中的一个问题。

更重要的是,您在 execute 方法中创建了一个本地对象,一旦您的方法完成,本地创建的对象就会被破坏。

这是因为 qwidgets 的 show() 方法没有阻塞。当事件循环可用时,他们只会为 Qt 事件循环排队一个事件来处理请求。

您可以将变量类范围设置为 MainWindow,但是您可以将创建 TaskWindow 的创建移动到 init 方法以避免每次发出信号时创建不必要的对象,并且您只会在执行时显示 TaskWindow方法如下:

def __init__(self, parent = None):
   super(MainWindow, self).__init__(parent)
   ...
   self.execTaskAct = QtGui.QAction("execute", self,  triggered=self.executeTask)
   ...
   self.task = TaskWindow()

def  executeTask(self):
  task.show()

如果您真的希望每次触发执行时都构造一个新对象,那么您可以在前面加上“self”关键字来使每个新创建的对象类都具有作用域。但是,请确保不要在未定义的行为中留下未引用的窗口。所以,你会这样写:

def  executeTask(self):
  self.task = TaskWindow()

您肯定需要从 TaskWindow 的 init 方法中删除 show() 调用。

话虽如此,为了完整起见,我还将为 TaskWindow 分配一个父级,无论是 MainWindow 还是应用程序对象本身。

self.task = TaskWindow(self)

另外请注意,如果您只想看到一个窗口,您可能希望通过调用 MainWindow 的 hide() 方法来隐藏它。如果您想同时显示两者,您可能希望考虑一个 TaskDialog 而不是两个 MainWindow。

此外,一旦您完成执行,如果您的用户不应该明确地关闭它,您可能希望明确地隐藏()您的任务窗口。这取决于您的用例。

【讨论】:

我能问一下为什么必须将TaskWindow 的创建移至__init__ 方法吗?在由信号触发的方法中创建新窗口似乎没有任何问题。 我对撒尿比赛不是特别感兴趣。我的目标只是改进所提供的答案,您现在已经通过编辑您的答案来删除/澄清我遇到问题的部分。但是,据我所知,您的回答仍然假设一次打开的窗口不会超过两个(MainWindow 和 TaskWindow),而是假设将重复显示/隐藏一个额外的窗口。发布者可能希望为我们从所提供的最少信息中所知道的所有内容打开 10 个任务窗口。 你说“需要”我解释为“必须”。反正你现在已经改了。 (我懒得再回复这个问题的唯一原因是因为我不想被歪曲。如果“必须”和“需要”之间的区别对你来说很重要,那么我很抱歉。)跨度> @three_pineapples:我无法理解你想要表达的观点。为了避免每次都进行构造(如我所写),您需要将其移至构造函数。这有什么问题?

以上是关于PySide:为啥在另一个 QmainWINdow(Main) 中触发 QAction 时 QMainWindow 会烧毁并消失的主要内容,如果未能解决你的问题,请参考以下文章

PySide/PyQt QMainWindow 如何关闭 QDockWidget?

如何通过 PySide 上的另一个线程从 QMainWindow 类中捕获信号?

PySide2 QMainWindow()捆绑在PyInstaller中后无法呈现[重复]

PySide2 QML - 如何在另一个 QML 文件中引用 QML 文件作为组件?

Python / Pyside 代码没有按顺序执行?

单独文件中的 PySide 信号