PyQt5 从 main 开始第二个 window

PyQt5 opening second window from main

嗨,我是 python 的新手(直到第 3 讲和麻省理工学院 6001 计算机科学与编程导论

在 Python) 尽管我开始玩 PyQt5 和 Designer (Python3, PyQt5, on linux).

我阅读了一点 PyQt5,但对面向对象编程和 Qt 了解不多

文档就像我一样的克林贡语。无法弄清楚为什么这个脚本 test.py 无法打开第二个

window,其实是打开的,在第一个window

前面desolve之前我可以简单看到

当我按下退出按钮时?有什么帮助吗?以及在哪里可以找到

逻辑的任何一种简单资源

Qt 的不同 类 和像我这样没有受过教育的应用程序循环。

test.py

!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 14:25:12 2020

@author: Pietro
"""

import sys

from PyQt5 import QtWidgets, uic

from PyQt5.QtWidgets import QDesktopWidget


def main():

    class quitto(QtWidgets.QMainWindow):

        def __init__(self):

            super(quitto, self).__init__()

            uic.loadUi('exitmain.ui', self)

            self.center()

            self.show()

            print('inside quitting2 ' *5)

        def center(self):

            qr = self.frameGeometry()

            cp = QDesktopWidget().availableGeometry().center()

            qr.moveCenter(cp)

            self.move(qr.topLeft())



    class menu(QtWidgets.QMainWindow):


        def __init__(self):

            super(menu, self).__init__()

            uic.loadUi('main.ui', self)


            self.ButtonQ.clicked.connect(self.QPushButtonQPressed) 

            self.center()

            self.show() 

        def center(self):

            qr = self.frameGeometry()

            cp = QDesktopWidget().availableGeometry().center()

            qr.moveCenter(cp)

            self.move(qr.topLeft())     


        def QPushButtonQPressed(self):

            #This is executed when the button is pressed

            print('buttonB pressed' *5)

            pippo=quitto()

            pippo.show()




    app = QtWidgets.QApplication(sys.argv)


    window=menu()

    app.exec_()


if __name__ == '__main__':

和main.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="windowModality">
   <enum>Qt::WindowModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>520</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="windowIcon">
   <iconset resource="resource001.qrc">
    <normaloff>:/main/python.png</normaloff>
    <normalon>:/main/python.png</normalon>
    <disabledoff>:/main/python.png</disabledoff>
    <disabledon>:/main/python.png</disabledon>
    <activeoff>:/main/python.png</activeoff>
    <activeon>:/main/python.png</activeon>
    <selectedoff>:/main/python.png</selectedoff>
    <selectedon>:/main/python.png</selectedon>:/main/python.png</iconset>
  </property>
  <property name="styleSheet">
   <string notr="true">QPushButton{
    background-color: #9de650;
}


QPushButton:hover{
    background-color: green;
}

QPushButton#ButtonQ{
    background-color: orange;
}


QPushButton#ButtonQ:hover{
    background-color: red;
}</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>0</y>
      <width>471</width>
      <height>71</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>16</pointsize>
      <weight>75</weight>
      <bold>true</bold>
      <underline>true</underline>
     </font>
    </property>
    <property name="text">
     <string>House-Buying-Menu</string>
    </property>
    <property name="alignment">
     <set>Qt::AlignCenter</set>
    </property>
   </widget>
   <widget class="QPushButton" name="ButtonA">
    <property name="geometry">
     <rect>
      <x>170</x>
      <y>100</y>
      <width>151</width>
      <height>81</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>20</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="text">
     <string>A</string>
    </property>
   </widget>
   <widget class="QPushButton" name="ButtonB">
    <property name="geometry">
     <rect>
      <x>170</x>
      <y>210</y>
      <width>151</width>
      <height>81</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>20</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="text">
     <string>B</string>
    </property>
   </widget>
   <widget class="QPushButton" name="ButtonC">
    <property name="geometry">
     <rect>
      <x>170</x>
      <y>320</y>
      <width>151</width>
      <height>81</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>20</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="text">
     <string>C</string>
    </property>
   </widget>
   <widget class="QPushButton" name="ButtonQ">
    <property name="geometry">
     <rect>
      <x>170</x>
      <y>450</y>
      <width>151</width>
      <height>81</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>20</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="text">
     <string>QUIT</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>520</width>
     <height>29</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources>
  <include location="resource001.qrc"/>
 </resources>
 <connections/>
</ui>

和mainexit.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>631</width>
    <height>496</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QDialogButtonBox" name="buttonBox">
    <property name="geometry">
     <rect>
      <x>200</x>
      <y>310</y>
      <width>174</width>
      <height>33</height>
     </rect>
    </property>
    <property name="standardButtons">
     <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>631</width>
     <height>29</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

该行为的原因是您正在创建的 quitto 实例是 "garbage collected"。像许多高级语言一样,Python 有一个自动管理内存的系统,并负责释放它 "decides" 不再有用的对象的内存。

在函数内创建的任何局部变量总是在函数 returns 后立即删除,除非它以某种方式引用到持久对象。

下面的例子用更简单的方式解释了它:"my_variable"是在function中创建的,但是一旦它完成(它returns)python就会删除那个变量,这很好,因为如果不这样做,你很容易就会内存不足:

class DeletingObject(object):
    '''
    A basic class that prints a message whenever any of its instances
    is going to be deleted.
    '''
    def __del__(self):
        print('Goodbye cruel world...')

def function():
    my_variable = DeletingObject()
    print(my_variable)

>>> function()
<__main__.DeletingObject object at 0xb593a46c>
Goodbye cruel world...

同样的事情发生在你的情况下:你创建了 quitto,但是没有它的永久引用,所以一旦创建它的函数 returns,它就被删除了。

当您在 class 中创建对象时,避免这种情况的最常见方法是使新对象成为 实例属性 :

    def QPushButtonQPressed(self):
        <b>self.pippo</b> = quitto()
        self.pippo.show()

现在新对象不再被删除,window也不会再被销毁


由于您刚刚开始研究所有这些,我想分享一些关于您的示例的建议:

  • classes 不应在函数内声明;在某些情况下可以这样做,但它们是非常特殊的情况,通常不需要通用用法(主要原因是 classes 应该可以从任何地方访问,以便可以随时创建它们的实例,但也是因为在函数中定义一个class意味着每次函数都是运行时总是声明它,这对性能来说不是很好);
  • 仅在分隔代码的逻辑片段时才在函数中使用空行,而不是在每一行之间;
  • 大多数语言的共同约定是对 classes 使用大写名称,对函数、变量和实例使用小写名称,请阅读官方 style guide for Python(a.k.a.PEP-8);
  • 虽然从技术上讲它没有任何问题,但通常最好不要混合从模块导入的方式,尤其是当有多个级别的子模块时(例如 PyQt):您要么像在第一行 (from PyQt5 import QtWidgets) 或您将要使用的单个 classes,就像您在第二次导入时所做的那样 (from PyQt5.QtWidgets import QDesktopWidget);第二种方法通常适用于简单的情况,但如果您的代码增长很多,您可能会发现自己有数十个 class 必须手动导入,虽然您可以 from PyQt5.QtWidgets import * 通常更好导入子模块并从那里访问 classes:
from PyQt5 import QtWidgets
# ...
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
  • QDesktopWidget 已过时,请改用 QScreen

@musicamante 感谢您详尽详尽的 post(我投了赞成票,但不算数,对不起)不确定我是否必须再 post 关于下一个问题,但它是相关的到上面的那个。我一直在努力让我的脚本正常工作,结果是:

test2.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 14:25:12 2020

@author: Pietro
"""

import sys
from PyQt5 import QtWidgets, uic
#import resource


def main():

    class Quitto(QtWidgets.QDialog):

        def quitbuttonboxquit(self):
            print('system exit')
            app.quit()            

        def quitbuttonboxnonquit(self):
            print('return to main')
            window.show()
            self.close()    

        def __init__(self):
            super(Quitto, self).__init__()
            uic.loadUi('exitdialog2.ui', self)
            self.center()
            self.show()
            print('inside quitting2 ' *5)
            self.QuitbuttonBox.accepted.connect(self.quitbuttonboxquit)
            self.QuitbuttonBox.rejected.connect(self.quitbuttonboxnonquit)

        def center(self):
            qr = self.frameGeometry()
            cp = QtWidgets.QDesktopWidget().availableGeometry().center()
            qr.moveCenter(cp)
            self.move(qr.topLeft())

        def closeEvent(self, event): 
                event.ignore()

    class Menu(QtWidgets.QMainWindow):

        def __init__(self): 
            super(Menu, self).__init__()
            uic.loadUi('main.ui', self)
            self.ButtonQ.clicked.connect(self.qpushbuttonqpressed) 
            self.center()
            self.show() 

        def center(self):
            qr = self.frameGeometry()
            cp = QtWidgets.QDesktopWidget().availableGeometry().center()
            qr.moveCenter(cp)
            self.move(qr.topLeft())   


        def qpushbuttonqpressed(self): #This is executed when the button is pressed
            print('buttonB pressed' *5)
            window.hide()
            pippo=Quitto()
            pippo.exec_()


        def closeEvent(self, event): #Your desired functionality here 
                event.ignore()




    app = QtWidgets.QApplication(sys.argv)

    sshFile="coffee.qss"
    with open(sshFile,"r") as fh:
        app.setStyleSheet(fh.read())
    window=Menu()
    app.exec_()

if __name__ == '__main__':
    main()


和exitdialog.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="windowModality">
   <enum>Qt::WindowModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>586</width>
    <height>522</height>
   </rect>
  </property>
  <property name="font">
   <font>
    <italic>true</italic>
   </font>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <property name="styleSheet">
   <string notr="true"/>
  </property>
  <widget class="QDialogButtonBox" name="QuitbuttonBox">
   <property name="geometry">
    <rect>
     <x>40</x>
     <y>460</y>
     <width>341</width>
     <height>32</height>
    </rect>
   </property>
   <property name="font">
    <font>
     <pointsize>18</pointsize>
     <weight>75</weight>
     <bold>true</bold>
    </font>
   </property>
   <property name="styleSheet">
    <string notr="true">QPushButton{                 
    background-color: #17eb3e;
}



QPushButton:hover{                 
    background-color: red;
}


</string>
   </property>
   <property name="orientation">
    <enum>Qt::Horizontal</enum>
   </property>
   <property name="standardButtons">
    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
   </property>
  </widget>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>50</x>
     <y>30</y>
     <width>491</width>
     <height>341</height>
    </rect>
   </property>
   <property name="styleSheet">
    <string notr="true">image: url(:/main/alert.png);</string>
   </property>
   <property name="text">
    <string/>
   </property>
  </widget>
  <widget class="QLabel" name="label_2">
   <property name="geometry">
    <rect>
     <x>170</x>
     <y>400</y>
     <width>271</width>
     <height>41</height>
    </rect>
   </property>
   <property name="font">
    <font>
     <pointsize>18</pointsize>
     <weight>75</weight>
     <italic>false</italic>
     <bold>true</bold>
    </font>
   </property>
   <property name="styleSheet">
    <string notr="true">QLabel { 
    color : red
 }</string>
   </property>
   <property name="text">
    <string>Are you sure to Quit ?!?</string>
   </property>
  </widget>
 </widget>
 <resources>
  <include location="resource001.qrc"/>
 </resources>
 <connections>
  <connection>
   <sender>QuitbuttonBox</sender>
   <signal>accepted()</signal>
   <receiver>Dialog</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>248</x>
     <y>254</y>
    </hint>
    <hint type="destinationlabel">
     <x>157</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>QuitbuttonBox</sender>
   <signal>rejected()</signal>
   <receiver>Dialog</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>316</x>
     <y>260</y>
    </hint>
    <hint type="destinationlabel">
     <x>286</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

稍后我将尝试拆分函数和 classes(首先我需要弄清楚 classes) 尽管如此,我还是将 class Quitto 更改为 :

class Quitto(QtWidgets.QDialog)

并更改:

        def qpushbuttonqpressed(self): #This is executed when the button is pressed
            print('buttonB pressed' *5)
            window.hide()
            pippo=Quitto()
            pippo.exec_() 

在之前的脚本中使用:

-- window.hide() 在短暂显示两个 windows

后关闭了所有内容

同时使用

-- pippo.exec() 而不是 pippo.show() 在

之上显示了新的第二个 window

其他,关闭它们后我得到了:

AttributeError: 'quitto' object has no attribute 'exec_'

在第二个脚本中使用 pippo.show() 也没有用,但是 pippo.exe_() 做了

技巧。它如何绕过垃圾收集???

以及为什么将 class Quitto() 更改为 QtWidgets.QMainWindow 我收到属性错误?

在我的项目中,我使用它从主 window 打开另一个 window。我粘贴了具有额外中心功能的全功能脚本,将 main windows 放在屏幕中间。从 Designer 创建两个文件,“UIMain.ui”(主 windows)和第二个 window“notes.ui”并保存它们。

# Open Second Window/Dialog/Form from Main Window

from PyQt5 import uic
from PyQt5.QtWidgets import (
                            QMainWindow,
                            QApplication,
                            QDesktopWidget
                            )

form_2, base_2 = uic.loadUiType('notes.ui')


class MainNotes(base_2, form_2):
    def __init__(self, parent=None):
        super(base_2, self).__init__(parent)
        self.setupUi(self)


class MainApp(QMainWindow):
    """ Main Class
    """
    def __init__(self):
        super(MainApp, self).__init__()
        self.mainnotes = MainNotes()
        self.ui = uic.loadUi('UIMain.ui', self)

        self.initapp()

    def initapp(self):
        self.ui.bt_notes.clicked.connect(self.notes)

    def notes(self):
        """
        Put your code here
        """
        self.mainnotes.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())


def main():
    import sys
    try:
        myapp = QApplication([])
        mywindow = MainApp()
        mywindow.center()
        mywindow.show()
        myapp.exec_()
    except SystemExit:
        sys.exit(0)


if __name__ == '__main__':
    main()