PyQt5 opening second window from main

梦想与她 提交于 2020-06-16 17:06:45

问题


Hi I am new to python (up to lecture 3 and of MIT 6001 Introduction to Computer Science and Programming

in Python) nevetheless I started playing with PyQt5 and Designer (Python3, PyQt5, on linux).

I read, a little bit about PyQt5, but dont understand a lot about Object Oriented Programming and, Qt

documentation is like Klingon too me. Can't figure out why this script test.py fails to open the second

window, actually it does open it, I can see it briefly beefore desolve in front of the first window

when I push the Quit Button ? Any help ? And any kind of easy resource where to find the logic of the

different classes of Qt and the app loop for a non educated like me.

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__':

and 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>

and 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>


回答1:


The reason for that behavior is that the instance of quitto you are creating is "garbage collected". Like many higher-level languages, Python has a system that manages memory automatically, and is responsible of freeing the memory of objects that it "decides" are not useful anymore.

Any local variable created within a function is always deleted as soon as the function returns, unless it is referenced to a persistent object in some way.

The following example explains it in a simpler way: "my_variable" is created within function, but as soon as it is completed (it returns) python deletes that variable, and that's good, because if it wouldn't you'll easily go out of memory:

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...

The same thing happens in your case: you create quitto, but there's no persistent reference for it, so as soon as the function that creates it returns, it's deleted.

The most common way to avoid that, when you're creating objects within a class, is to make the new object an instance attribute:

    def QPushButtonQPressed(self):
        self.pippo = quitto()
        self.pippo.show()

Now the new object is not deleted anymore, and the window will not be destroyed anymore.


Since you've just begun to study all this, I'd like to share some suggestions about your example:

  • classes should not be declared within functions; there are some cases for which this can be done, but they are very specific situations it's usually not necessary for generic usage (the main reason is that classes should be accessible from anywhere so that their instances can be created anytime, but it's also because defining a class in a function means that it's always declared each time the function is run, which is not very good for performance);
  • use blank lines in functions only when separating logical fragments of your code, not between every line;
  • it's common convention for the majority of languages to use capitalized names for classes and lower case names for functions, variables and instances, read more on the official style guide for Python (a.k.a. PEP-8);
  • while technically there's nothing wrong with it, it's usually better to not mix the way you import from modules, especially when there are multiple levels of sub modules (like with PyQt): you either import the submodule like you did in the first line (from PyQt5 import QtWidgets) or the single classes you're going to use, like you did in the second import (from PyQt5.QtWidgets import QDesktopWidget); the second approach is usually fine for simple cases, but if your code grows a lot you might find yourself with tens of classes that have to be imported manually, and while you could do from PyQt5.QtWidgets import * it's usually better to import the submodules and access the classes from there:
from PyQt5 import QtWidgets
# ...
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
  • QDesktopWidget is obsolete, use QScreen instead;



回答2:


@musicamante thank for your long and detailed post (I voted fo it but it doesnt count, sorry) not sure if I have to make another post about the next question but its related to the one above. I kept trying to make my script work and I ended up with:

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()


and 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>

I'll try to split function and classes later (first I need to figure out classes more) nevertheless I changed class Quitto to :

class Quitto(QtWidgets.QDialog)

and changed:

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

in the previous script using:

-- window.hide() was shutting downt everything after showing briefly the two windows

while using

-- pippo.exec() instead of pippo.show() was showing the new 2nd window on top of the

other and after closing them I was getting a:

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

Using pippo.show() in the second script wasnt working either but pippo.exe_() did the

trick. How can it bypass the garbage collection ???

And why changing class Quitto() as QtWidgets.QMainWindow I get the Attribute error ?



来源:https://stackoverflow.com/questions/61123846/pyqt5-opening-second-window-from-main

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!