Почему размер моего окна меняется после загрузки моего пользовательского интерфейса?

#python #pyqt #pyqt5

#python #pyqt #pyqt5

Вопрос:

Я пытаюсь создать что-то вроде игры, похожей на Google dinosaur. Проблема в том, что когда я запускаю свое приложение, высота моего окна меняется через несколько секунд, поэтому, когда я пытаюсь поместить «динозавра» внизу, он остается плавающим, потому что высота окна изменилась.

«Динозавр» — это поле, которое плавает:

quot;Динозаврquot; - это поле, которое плавает

Это главное окно

 import sys 
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QFrame
from PyQt5.QtCore import Qt, QTimer, QRect
from PyQt5.QtGui import QPen, QBrush, QPainter
from PyQt5 import uic
import random

class animation(QWidget):

    def __init__(self):
        super().__init__()
        
        self.shapes = []
        self.dimension = 50

        #hilo del juego
        self.game_timer = QTimer(self)
        self.game_timer.timeout.connect(self.animation)
        self.game_timer.start(10)


        #crear las figuras
        self.shapes_generator_timer = QTimer(self)
        self.shapes_generator_timer.timeout.connect(self.new_shape)
        self.shapes_generator_timer.start(500)

        #jugador
        coord_x = self.width()
        coord_y = self.height() - self.dimension
        
        self.player = QRect(200, coord_y-2, self.dimension, self.dimension)

        

        
    def new_shape(self):
        coord_x = self.width()
        coord_y = self.height() - self.dimension
        
        rect = QRect(coord_x, coord_y-2, self.dimension, self.dimension)
        
        self.shapes.append(rect)

    

    def animation(self):
        for index,shape in enumerate(self.shapes):
            shape.moveLeft(shape.left() - 5)

            self.delete_shape(index,shape)
        self.update()
        print(self.height())

    def delete_shape(self, index,shape):

        if shape.getCoords()[2] < 0:
            del self.shapes[index]
            print(len(self.shapes))
        


    def paintEvent(self, event):
        
        painter = QPainter(self)
        painter.setPen(QPen(Qt.white,0,Qt.SolidLine))
        painter.setBrush(QBrush(Qt.white,Qt.SolidPattern))
        painter.drawRect(self.rect())


        painter.setPen(QPen(Qt.black,1,Qt.SolidLine))
        painter.setBrush(QBrush(Qt.green,Qt.SolidPattern))
        for shape in self.shapes:
            painter.drawRect(shape)

        painter.drawRect(self.player)

    


ui, _ = uic.loadUiType("PYQT5interface-animation.ui")

class MyApp(QWidget,ui):

    def __init__(self):
        super().__init__()

        self.setupUi(self)
        
        

        self.animation = animation()
        frame = self.findChild(QFrame, "frame")

        layout = QHBoxLayout()
        layout.addWidget(self.animation)

        frame.setLayout(layout)
    

app = QApplication(sys.argv)
myApp = MyApp()
myApp.show()
sys.exit(app.exec_())
 

И это мой файл пользовательского интерфейса

 <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1201</width>
    <height>638</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QFrame" name="frame">
     <property name="styleSheet">
      <string notr="true">background-color:rgb(0,0,0);</string>
     </property>
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
 

Ответ №1:

Вы не можете полагаться на геометрию виджета в его создании, если не укажете фиксированный размер: по умолчанию все классы QWidget имеют размер 640×480 во время инициализации.

В вашем случае решение простое: переместите вычисление позиции игрока в resizeEvent() , которое является событием, получаемым каждый раз, когда изменяется размер виджета (системой, пользователем или любым родительским / дочерним требованием к макету):

     def resizeEvent(self, event):
        self.player = QRect(200, self.height() - self.dimension-2, self.dimension, self.dimension)
 

Обратите внимание, что ваша реализация не совсем корректна, так как в результате препятствия будут иметь неправильное положение, если изменить размер окна во время воспроизведения, поэтому я предлагаю другой подход: всегда создавайте элементы при y = 0 и переводите прямоугольники в событие рисования:

 class animation(QWidget):
    def __init__(self):
        # ...
        self.player = QRect(200, 0, self.dimension, self.dimension)

    def new_shape(self):
        coord_x = self.width()
        rect = QRect(coord_x, 0, self.dimension, self.dimension)
        self.shapes.append(rect)

    # ...
    def paintEvent(self, event):
        painter = QPainter(self)

        painter.setPen(QPen(Qt.white,0,Qt.SolidLine))
        painter.setBrush(QBrush(Qt.white,Qt.SolidPattern))
        painter.drawRect(self.rect())

        coord_y = self.height() - self.dimension

        painter.setPen(QPen(Qt.black,1,Qt.SolidLine))
        painter.setBrush(QBrush(Qt.green,Qt.SolidPattern))
        for shape in self.shapes:
            painter.drawRect(shape.translated(0, coord_y))

        painter.drawRect(self.player.translated(0, coord_y))
 

Пожалуйста, обратите внимание, что использование findChild для пользовательского интерфейса, созданного с помощью uic / pyuic, бессмысленно, поскольку атрибуты экземпляра для каждого виджета уже созданы в setupUi : вы уже можете получить доступ к фрейму с помощью self.frame :

 class MyApp(QWidget,ui):
    def __init__(self):
        # ...

        self.frame.setLayout(layout)
 

Вы, вероятно, узнали об этом подходе в каком-то онлайн-руководстве, к сожалению, они совершенно не правы в этом и предполагают, что это очень плохая практика.