布局不会扩展超过使用设计器设置的初始大小
Layout doesn't expand more than initial size it was set using designer
我正在尝试根据 GUI window 大小调整我的小部件的大小,但卡住了。
首先,我使用设计器放置了一些小部件,包括 QVBoxLayout
,并使用 PyQt5.uic.loadUi()
加载它。
之后我尝试通过 resizeEvent
:
调整一些小部件的大小
def resizeEvent(self, event):
print("Resized")
QtWidgets.QMainWindow.resizeEvent(self, event)
self.resize_widget(self.label, 'both', self.ratio_label)
self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)
def resize_widget(self, widget, mode, ratio):
if mode == 'width':
widget.resize(int(self.size().width() * ratio), widget.geometry().height())
elif mode == 'height':
widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
else:
try:
widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
except AttributeError:
widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))
self.label
、self.slider_cv2
和self.graph_layout
分别是Qlabel
、QSlider
和QVBoxLayout
的实例。
self.label
和 self.slider_cv2
的大小如我所料动态变化,但 self.graph_layout
没有。当 GUI window 变小时,它会变小,但不会增长到大于其初始大小。
添加了最少的可重现代码:
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QVideoFrame
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtGui import QPixmap, QImage
import sys
import os
import threading
app = QtWidgets.QApplication(sys.argv)
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from time import sleep
import numpy as np
class UI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('/workspace/demo_cv2_load_entire_layout.ui', self)
self.fig = plt.Figure()
self.canvas = FigureCanvas(self.fig)
self.graph_layout.addWidget(self.canvas)
#---------For resizeing widget-----------
w, h = self.size().width(), self.size().height()
self.graph_layout.activate()
self.ratio_graph_layout = (self.graph_layout.geometry().width() / w, self.graph_layout.geometry().height() / h)
self.ratio_label = (self.label.size().width() / w, self.label.size().height() / h)
self.ratio_slider_cv2 = self.slider_cv2.size().width() / w
self.initial_wh = (w, h)
#----------------------------------------
self.show()
def resizeEvent(self, event):
print("Resized")
QtWidgets.QMainWindow.resizeEvent(self, event)
self.resize_widget(self.label, 'both', self.ratio_label)
self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)
def resize_widget(self, widget, mode, ratio):
if mode == 'width':
widget.resize(int(self.size().width() * ratio), widget.geometry().height())
elif mode == 'height':
widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
else:
try:
widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
except AttributeError:
widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))
print(widget.geometry())
if __name__ == "__main__":
window = UI()
app.exec_()
和.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>1018</width>
<height>814</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>240</x>
<y>410</y>
<width>291</width>
<height>151</height>
</rect>
</property>
<layout class="QVBoxLayout" name="graph_layout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
</layout>
</widget>
<widget class="QSlider" name="slider_cv2">
<property name="geometry">
<rect>
<x>220</x>
<y>620</y>
<width>349</width>
<height>15</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>90</x>
<y>0</y>
<width>711</width>
<height>371</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>Video</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1018</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
如果您想手动调整元素的大小,则不应使用布局。在此您必须使用 QWidget 作为容器,然后使用布局将 FigureCanvas 放置在该容器中。
另一方面,我实现了一种逻辑,可以简化并允许轻松处理调整大小功能,而无需覆盖 resizeEvent。
*.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>1018</width>
<height>814</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QSlider" name="slider_cv2">
<property name="geometry">
<rect>
<x>220</x>
<y>620</y>
<width>349</width>
<height>15</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>90</x>
<y>0</y>
<width>711</width>
<height>371</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>Video</string>
</property>
</widget>
<widget class="QWidget" name="graph_container" native="true">
<property name="geometry">
<rect>
<x>240</x>
<y>390</y>
<width>291</width>
<height>151</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1018</width>
<height>28</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
*.py
import os
import sys
from dataclasses import dataclass
from enum import IntFlag, auto
from pathlib import Path
from PyQt5 import QtWidgets, uic, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
class ModeRatio(IntFlag):
NONE = auto()
WIDTH = auto()
HEIGHT = auto()
BOTH = WIDTH | HEIGHT
@dataclass
class SizeManager(QtCore.QObject):
widget: QtWidgets.QWidget
mode: ModeRatio
def __post_init__(self):
super().__init__(self.widget)
self.widget.window().installEventFilter(self)
self._width_ratio = self.widget.width() * 1.0 / self.widget.window().width()
self._height_ratio = self.widget.height() * 1.0 / self.widget.window().height()
def eventFilter(self, obj, event):
if obj is self.widget.window():
if event.type() == QtCore.QEvent.Resize:
self.apply_resize()
return super().eventFilter(obj, event)
def apply_resize(self):
width = self.widget.width()
height = self.widget.height()
if self.mode & ModeRatio.WIDTH:
width = self.widget.window().width() * self._width_ratio
if self.mode & ModeRatio.HEIGHT:
height = self.widget.window().height() * self._height_ratio
self.widget.resize(QtCore.QSizeF(width, height).toSize())
class UI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
filename = os.fspath(
Path(__file__).resolve().parent / "demo_cv2_load_entire_layout.ui"
)
uic.loadUi(filename, self)
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
lay = QtWidgets.QVBoxLayout(self.graph_container)
lay.addWidget(self.canvas)
self.label_size_manager = SizeManager(widget=self.label, mode=ModeRatio.BOTH)
self.slider_size_manager = SizeManager(
widget=self.slider_cv2, mode=ModeRatio.WIDTH
)
self.graph_size_manager = SizeManager(
widget=self.graph_container, mode=ModeRatio.BOTH
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = UI()
window.show()
app.exec_()
├── demo_cv2_load_entire_layout.ui
└── main.py
首先,感谢@eyllanesc 为问题提供了直接而优雅的解决方案。
这是使用 qt-designer 设置类似 属性 的方法:
将布局放在 QWidget
.
内
之前尝试过直接修改layout或者mainwindow的centralwidget,但是都失败了
很奇怪 self.graph_layout.geometry()
returns 类似 (0, 0, 200, 100)
的东西,尽管它在主窗口的视角下远低于 (0, 0)
。
所以我认为有一个不可见的父对象(即使 self.graph_layout.parent()
returns mainwindow 的 centralwidget),并通过明确地将布局放在一个小部件中来替换这个幽灵父对象。
此外,用户必须将小部件的布局(通过右键单击)设置为Layout Horizontally
或Layout Vertically
等,以使布局的大小自动跟随小部件。
我正在尝试根据 GUI window 大小调整我的小部件的大小,但卡住了。
首先,我使用设计器放置了一些小部件,包括 QVBoxLayout
,并使用 PyQt5.uic.loadUi()
加载它。
之后我尝试通过 resizeEvent
:
def resizeEvent(self, event):
print("Resized")
QtWidgets.QMainWindow.resizeEvent(self, event)
self.resize_widget(self.label, 'both', self.ratio_label)
self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)
def resize_widget(self, widget, mode, ratio):
if mode == 'width':
widget.resize(int(self.size().width() * ratio), widget.geometry().height())
elif mode == 'height':
widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
else:
try:
widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
except AttributeError:
widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))
self.label
、self.slider_cv2
和self.graph_layout
分别是Qlabel
、QSlider
和QVBoxLayout
的实例。
self.label
和 self.slider_cv2
的大小如我所料动态变化,但 self.graph_layout
没有。当 GUI window 变小时,它会变小,但不会增长到大于其初始大小。
添加了最少的可重现代码:
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QVideoFrame
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtGui import QPixmap, QImage
import sys
import os
import threading
app = QtWidgets.QApplication(sys.argv)
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from time import sleep
import numpy as np
class UI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('/workspace/demo_cv2_load_entire_layout.ui', self)
self.fig = plt.Figure()
self.canvas = FigureCanvas(self.fig)
self.graph_layout.addWidget(self.canvas)
#---------For resizeing widget-----------
w, h = self.size().width(), self.size().height()
self.graph_layout.activate()
self.ratio_graph_layout = (self.graph_layout.geometry().width() / w, self.graph_layout.geometry().height() / h)
self.ratio_label = (self.label.size().width() / w, self.label.size().height() / h)
self.ratio_slider_cv2 = self.slider_cv2.size().width() / w
self.initial_wh = (w, h)
#----------------------------------------
self.show()
def resizeEvent(self, event):
print("Resized")
QtWidgets.QMainWindow.resizeEvent(self, event)
self.resize_widget(self.label, 'both', self.ratio_label)
self.resize_widget(self.slider_cv2, 'width', self.ratio_slider_cv2)
self.resize_widget(self.graph_layout, 'both', self.ratio_graph_layout)
def resize_widget(self, widget, mode, ratio):
if mode == 'width':
widget.resize(int(self.size().width() * ratio), widget.geometry().height())
elif mode == 'height':
widget.resize(widget.geometry().width(), int(self.size().height() * ratio))
else:
try:
widget.resize(int(self.size().width() * ratio[0]), int(self.size().height() * ratio[1]))
except AttributeError:
widget.setGeometry(QtCore.QRect(widget.geometry().left(), widget.geometry().top(), int(self.geometry().width() * ratio[0]), int(self.geometry().height() * ratio[1])))
print(widget.geometry())
if __name__ == "__main__":
window = UI()
app.exec_()
和.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>1018</width>
<height>814</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>240</x>
<y>410</y>
<width>291</width>
<height>151</height>
</rect>
</property>
<layout class="QVBoxLayout" name="graph_layout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
</layout>
</widget>
<widget class="QSlider" name="slider_cv2">
<property name="geometry">
<rect>
<x>220</x>
<y>620</y>
<width>349</width>
<height>15</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>90</x>
<y>0</y>
<width>711</width>
<height>371</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>Video</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1018</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
如果您想手动调整元素的大小,则不应使用布局。在此您必须使用 QWidget 作为容器,然后使用布局将 FigureCanvas 放置在该容器中。
另一方面,我实现了一种逻辑,可以简化并允许轻松处理调整大小功能,而无需覆盖 resizeEvent。
*.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>1018</width>
<height>814</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QSlider" name="slider_cv2">
<property name="geometry">
<rect>
<x>220</x>
<y>620</y>
<width>349</width>
<height>15</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>90</x>
<y>0</y>
<width>711</width>
<height>371</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>Video</string>
</property>
</widget>
<widget class="QWidget" name="graph_container" native="true">
<property name="geometry">
<rect>
<x>240</x>
<y>390</y>
<width>291</width>
<height>151</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1018</width>
<height>28</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
*.py
import os
import sys
from dataclasses import dataclass
from enum import IntFlag, auto
from pathlib import Path
from PyQt5 import QtWidgets, uic, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
class ModeRatio(IntFlag):
NONE = auto()
WIDTH = auto()
HEIGHT = auto()
BOTH = WIDTH | HEIGHT
@dataclass
class SizeManager(QtCore.QObject):
widget: QtWidgets.QWidget
mode: ModeRatio
def __post_init__(self):
super().__init__(self.widget)
self.widget.window().installEventFilter(self)
self._width_ratio = self.widget.width() * 1.0 / self.widget.window().width()
self._height_ratio = self.widget.height() * 1.0 / self.widget.window().height()
def eventFilter(self, obj, event):
if obj is self.widget.window():
if event.type() == QtCore.QEvent.Resize:
self.apply_resize()
return super().eventFilter(obj, event)
def apply_resize(self):
width = self.widget.width()
height = self.widget.height()
if self.mode & ModeRatio.WIDTH:
width = self.widget.window().width() * self._width_ratio
if self.mode & ModeRatio.HEIGHT:
height = self.widget.window().height() * self._height_ratio
self.widget.resize(QtCore.QSizeF(width, height).toSize())
class UI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
filename = os.fspath(
Path(__file__).resolve().parent / "demo_cv2_load_entire_layout.ui"
)
uic.loadUi(filename, self)
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
lay = QtWidgets.QVBoxLayout(self.graph_container)
lay.addWidget(self.canvas)
self.label_size_manager = SizeManager(widget=self.label, mode=ModeRatio.BOTH)
self.slider_size_manager = SizeManager(
widget=self.slider_cv2, mode=ModeRatio.WIDTH
)
self.graph_size_manager = SizeManager(
widget=self.graph_container, mode=ModeRatio.BOTH
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = UI()
window.show()
app.exec_()
├── demo_cv2_load_entire_layout.ui
└── main.py
首先,感谢@eyllanesc 为问题提供了直接而优雅的解决方案。
这是使用 qt-designer 设置类似 属性 的方法:
将布局放在 QWidget
.
之前尝试过直接修改layout或者mainwindow的centralwidget,但是都失败了
很奇怪 self.graph_layout.geometry()
returns 类似 (0, 0, 200, 100)
的东西,尽管它在主窗口的视角下远低于 (0, 0)
。
所以我认为有一个不可见的父对象(即使 self.graph_layout.parent()
returns mainwindow 的 centralwidget),并通过明确地将布局放在一个小部件中来替换这个幽灵父对象。
此外,用户必须将小部件的布局(通过右键单击)设置为Layout Horizontally
或Layout Vertically
等,以使布局的大小自动跟随小部件。