如何管理 "QMainWindow" 小部件内的框架?
How can I manage the frames inside a "QMainWindow" widget?
这些天我试图复制一个已经用 Tkinter 和 PyQt5 编写的 GUI,但我是这个框架的新手,我仍然不知道如何完成一些步骤。这是我的代码:
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
import sys
class MainWindow(qtw.QMainWindow):
def __init__(self):
"""MainWindow constructor"""
super().__init__()
self.title="Main Window"
self.setWindowTitle(self.title)
self.top, self.left, self.width, self.height = 250, 250, 760, 620
self.setGeometry(self.top, self.left, self.width, self.height)
self.setMinimumSize(760, 620)
self.CreateMenu()
self.CreateFrames()
self.show()
def CreateMenu(self):
MainMenu=self.menuBar()
EditMenu=MainMenu.addMenu("Test1")
HelpMenu=MainMenu.addMenu("Test2")
Test1_1=qtw.QAction("Test1-1", self)
Test1_1.triggered.connect(self.Test)
EditMenu.addAction(Test1_1)
Test2_1=qtw.QAction("Test2-1", self)
Test2_1.triggered.connect(self.Test)
HelpMenu.addAction(Test2_1)
def CreateFrames(self):
TheFrame=qtw.QWidget(self)
TheFrame.setStyleSheet("background-color:blue")
self.setCentralWidget(TheFrame)
TheLayout=qtw.QVBoxLayout()
TheFrame.setLayout(TheLayout)
MainFrame, ButtonsFrame = qtw.QFrame(), qtw.QFrame()
MainFrame.setStyleSheet("background-color:red"), ButtonsFrame.setStyleSheet("background-color:green")
TheLayout.addWidget(MainFrame)
TheLayout.addWidget(ButtonsFrame)
# the "resize" method doesn't work..
self.Button1=qtw.QPushButton("Button 2")
self.Button1.resize(50,100)
self.Button2=qtw.QPushButton("Button 2")
self.Button2.resize(50,100)
self.Button3=qtw.QPushButton("Button 2")
self.Button3.resize(50,100)
ButtonsLayout=qtw.QHBoxLayout()
ButtonsFrame.setLayout(ButtonsLayout)
ButtonsLayout.addWidget(self.Button1)
ButtonsLayout.addStretch()
ButtonsLayout.addWidget(self.Button2)
ButtonsLayout.addWidget(self.Button3)
def Test(self):
pass
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = MainWindow()
sys.exit(app.exec())
在主要 window 内,我创建了 TheFrame
(蓝框),在其中,我也创建了 ButtonsFrame
和 MainFrame
(绿框和红色的)。我的问题是:
我的做法是否正确?我的意思是,PyQt 让我创建 TheFrame
吗?因为我想,直接在mainwindow中创建ButtonsFrame
和MainFrame
不是更好吗?我试过了,但没用。我无法在 QMainWindow
小部件中创建新布局,有一个默认布局,但我不知道它的性质。这是什么布局?
当我将 ButtonsFrame
和 MainFrame
放入 TheFrame
时,我意识到小部件之间存在一些默认空间(我在所附的屏幕截图中对它们进行了签名).如何手动管理这些默认空间的大小?我在放置按钮小部件时看到了相同的空格..
当我使用 QHBoxLayout
对象时,如何为放置在其中的框架设置静态高度?例如,在我的代码中我想为 ButtonsFrame
设置一个默认高度,在我看来它没有向上扩展。我该怎么做?
如何更改按钮的大小?如您所见,resize
方法不起作用..
附截图:
TL; DR; 它要求的一切都在 the Qt docs 中。 Qt 文档是最好的文档之一,因为它们清楚地指出了限制和所涵盖的功能,因此建议任何 Qt 用户阅读它。
如果您检查 the docs of QMainWindow 它清楚地表明这个小部件有一个预定义的结构(检查 link 以获得更多信息以及这个问题已经在 SO 中多次出现).
如果您不想要该默认结构,请使用 QWidget 而不是 QMainWindow,它是构建任何类型的小部件的基本构建块。
可以使用 QHBoxLayout 的 setContentsMargins()
and setSpacing()
方法修改这些空间。
TheLayout.setContentsMargins(10, 20, 30, 40)
TheLayout.setSpacing(50)
布局是设置它的父小部件的子小部件的几何形状(位置和大小)的管理者,因此它们使用附加信息(例如 sizeHint、sizePolicy、拉伸因子、 ETC)。所以如果你想设置一个固定的高度然后在容器中设置那个高度:
ButtonsFrame.setFixedHeight(300)
或者在这种情况下,最好通过将系数设置为 1 来使顶部小部件尽可能地伸展。
TheLayout.addWidget(MainFrame, stretch=1)
TheLayout.addWidget(ButtonsFrame)
由于按钮的几何形状由布局处理,因此使用 resize() 不会更改任何内容,因为布局会将其恢复为默认大小。为避免这种情况,最好使用 setFixedSize()
:
self.Button1 = qtw.QPushButton("Button 2")
self.Button1.setFixedSize(50, 100)
self.Button2 = qtw.QPushButton("Button 2")
self.Button2.setFixedSize(50, 100)
self.Button3 = qtw.QPushButton("Button 2")
self.Button3.setFixedSize(50, 100)
QMainWindow(一种非常特殊的 QWidget,它是 Qt 中所有 ui 元素的基础)不允许设置您自己的布局,因为它有自己的 private 布局,用于管理通常在“main window”中创建的所有内容。您可以从 QMainWindow documentation:
中看到这一点
因为只要调整 window(或其任何内容)的大小时,这些对象都需要自动定位和调整大小,并且由于这种定位是 quite 复杂的性质布局在内部管理的一些小部件(停靠小部件和工具栏)。
这给您留下了创建“仅一个”中央小部件的“唯一”可能性。但这没什么大不了的:通常的做法是创建一个空的 QWidget 作为 容器 以防你的 UI 有多个“主要”小部件,就像在你的案例.
您在两个内部框架周围看到的边框是为任何布局默认创建的,可以使用 setContentsMargins()
. You can use getContentsMargins()
控制以获取布局的当前默认页边距,然后根据需要更改它们。例如,如果你只想避开顶部边框,万一有一个:
left, top, right, bottom = self.getContentsMargins()
if top:
self.setContentsMargins(left, 0, right, bottom)
请注意,即使 QWidget 子classes 也有内容边距(具有相同的函数名称),可以向小部件添加额外的边距,除了那些在布局上设置,但请注意,这些边距通常仅受某些小部件(通常是容器,如 QFrame)的尊重。
您看到的 在 这些框架之间的边框由布局的 间距 属性 控制。对于所有布局,它可以获取 (getSpacing()
) 和设置 (setSpacing()
) 作为全局值,为元素之间的所有间距保留,但也可以为垂直或水平间距单独设置QGridLayout (setHorizontalSpacing()
, setVerticalSpacing()
).
更改小部件的大小有点复杂。布局管理器实际上是布局的管理器,这意味着由他们决定元素有多大以及它们应该放在哪里,因此在由布局管理的小部件上使用 resize()
是完全没有意义的。
当小部件由布局管理时,布局会查询其 sizePolicy()
,这是一个描述当 Qt 必须决定其几何形状时小部件应如何行为的对象。
有些小部件的其中一个尺寸是固定的(按钮的垂直尺寸就是这种情况,默认情况下只能水平展开),其他小部件有 expanding 策略(这意味着他们会尝试占据尽可能多的 space),而其他人则有 首选 大小(因此他们会尽可能地尝试增长,但如果在相同的layout 有一个 expanding 元素,它将优先。
然后所有这些都基于小部件的 sizeHint()
,这是正常情况下小部件的 首选 大小(并且可以 return不同的值取决于情况和小部件类型),并且布局将使用它来决定如何根据小部件的策略设置几何图形。
可以通过多种方式覆盖该策略:
- 通过为小部件设置新大小策略;
- 通过获取小部件的大小策略,更改其属性之一,然后将其设置回小部件(单独更改大小策略不会影响小部件,因为大小策略不是发送的对象“通知”);
- 通过设置最小或最大大小限制(使用
set(Minimum|Maximum|Fixed)(Width|Height|Size)
);
- 将小部件添加到布局 和 设置 stretch 值,这是一个整数值,告诉布局 该元素与其他元素的比例;例如,如果您添加一个带有
addWidget(button1, stretch=1)
的按钮和另一个带有 addWidget(button2, stretch=2)
的按钮,第二个按钮的大小将是第一个的两倍;
最后,一个重要的考虑因素。
Qt 布局允许 嵌套 布局:例如,如果您不喜欢 QGridLayout 提供的 rows/columns 排列,您可以将水平布局添加到垂直布局。然后你可以在那个水平布局上添加另一个垂直布局。发生这种情况是因为添加到布局的所有项目实际上都是 QLayoutItem 对象,这些对象是用于表示添加到布局的“对象”的抽象项目:当使用 addWidget()
添加小部件时,布局会创建一个QLayoutItem 为它。
另请注意,每个 QLayout class 实际上是一个 QLayoutItem subclass;当您向框布局(或拉伸)添加间距时,您实际上是在添加具有固定或扩展大小策略的 QSpacerItem(同样,QLayoutItem subclass)。
题目比较复杂,我能理解如果从一个稍微简单的框架来,你可能会感到困惑,所以我建议你慢慢来,仔细阅读关于以下主题:
- QWidget size hints and size policies
- QSizePolicy
- Layout Management
- QLayout and QLayoutItem
- Using layouts in Designer
我还建议您使用 Qt Designer 大量 代码 和 实验,这将使您更好了解 Qt 布局管理的工作原理。
它并不总是完整的ui,所以请耐心等待并始终关注文档。
小提示:如果不存在子控件,设计器不允许为 父 控件设置布局。一个常见的错误是创建一个 main window,然后添加一个 widget 容器并为该容器设置布局;由于 Designer 在创建主 windows 时已经设置了中央小部件(并且无法更改),结果是创建的容器不会调整其大小以适应主 window。只需确保 centralwidget
具有布局(您可以从对象检查器中的图标中看到它)。如果您想为小部件设置布局但仍需要将其留空(因为您只想从代码中添加子小部件),只需将任意小部件添加到父部件,设置布局,然后删除该小部件。
这些天我试图复制一个已经用 Tkinter 和 PyQt5 编写的 GUI,但我是这个框架的新手,我仍然不知道如何完成一些步骤。这是我的代码:
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
import sys
class MainWindow(qtw.QMainWindow):
def __init__(self):
"""MainWindow constructor"""
super().__init__()
self.title="Main Window"
self.setWindowTitle(self.title)
self.top, self.left, self.width, self.height = 250, 250, 760, 620
self.setGeometry(self.top, self.left, self.width, self.height)
self.setMinimumSize(760, 620)
self.CreateMenu()
self.CreateFrames()
self.show()
def CreateMenu(self):
MainMenu=self.menuBar()
EditMenu=MainMenu.addMenu("Test1")
HelpMenu=MainMenu.addMenu("Test2")
Test1_1=qtw.QAction("Test1-1", self)
Test1_1.triggered.connect(self.Test)
EditMenu.addAction(Test1_1)
Test2_1=qtw.QAction("Test2-1", self)
Test2_1.triggered.connect(self.Test)
HelpMenu.addAction(Test2_1)
def CreateFrames(self):
TheFrame=qtw.QWidget(self)
TheFrame.setStyleSheet("background-color:blue")
self.setCentralWidget(TheFrame)
TheLayout=qtw.QVBoxLayout()
TheFrame.setLayout(TheLayout)
MainFrame, ButtonsFrame = qtw.QFrame(), qtw.QFrame()
MainFrame.setStyleSheet("background-color:red"), ButtonsFrame.setStyleSheet("background-color:green")
TheLayout.addWidget(MainFrame)
TheLayout.addWidget(ButtonsFrame)
# the "resize" method doesn't work..
self.Button1=qtw.QPushButton("Button 2")
self.Button1.resize(50,100)
self.Button2=qtw.QPushButton("Button 2")
self.Button2.resize(50,100)
self.Button3=qtw.QPushButton("Button 2")
self.Button3.resize(50,100)
ButtonsLayout=qtw.QHBoxLayout()
ButtonsFrame.setLayout(ButtonsLayout)
ButtonsLayout.addWidget(self.Button1)
ButtonsLayout.addStretch()
ButtonsLayout.addWidget(self.Button2)
ButtonsLayout.addWidget(self.Button3)
def Test(self):
pass
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = MainWindow()
sys.exit(app.exec())
在主要 window 内,我创建了 TheFrame
(蓝框),在其中,我也创建了 ButtonsFrame
和 MainFrame
(绿框和红色的)。我的问题是:
我的做法是否正确?我的意思是,PyQt 让我创建
TheFrame
吗?因为我想,直接在mainwindow中创建ButtonsFrame
和MainFrame
不是更好吗?我试过了,但没用。我无法在QMainWindow
小部件中创建新布局,有一个默认布局,但我不知道它的性质。这是什么布局?当我将
ButtonsFrame
和MainFrame
放入TheFrame
时,我意识到小部件之间存在一些默认空间(我在所附的屏幕截图中对它们进行了签名).如何手动管理这些默认空间的大小?我在放置按钮小部件时看到了相同的空格..当我使用
QHBoxLayout
对象时,如何为放置在其中的框架设置静态高度?例如,在我的代码中我想为ButtonsFrame
设置一个默认高度,在我看来它没有向上扩展。我该怎么做?如何更改按钮的大小?如您所见,
resize
方法不起作用..
附截图:
TL; DR; 它要求的一切都在 the Qt docs 中。 Qt 文档是最好的文档之一,因为它们清楚地指出了限制和所涵盖的功能,因此建议任何 Qt 用户阅读它。
如果您检查 the docs of QMainWindow 它清楚地表明这个小部件有一个预定义的结构(检查 link 以获得更多信息以及这个问题已经在 SO 中多次出现).
如果您不想要该默认结构,请使用 QWidget 而不是 QMainWindow,它是构建任何类型的小部件的基本构建块。
可以使用 QHBoxLayout 的
setContentsMargins()
andsetSpacing()
方法修改这些空间。TheLayout.setContentsMargins(10, 20, 30, 40) TheLayout.setSpacing(50)
布局是设置它的父小部件的子小部件的几何形状(位置和大小)的管理者,因此它们使用附加信息(例如 sizeHint、sizePolicy、拉伸因子、 ETC)。所以如果你想设置一个固定的高度然后在容器中设置那个高度:
ButtonsFrame.setFixedHeight(300)
或者在这种情况下,最好通过将系数设置为 1 来使顶部小部件尽可能地伸展。
TheLayout.addWidget(MainFrame, stretch=1) TheLayout.addWidget(ButtonsFrame)
由于按钮的几何形状由布局处理,因此使用 resize() 不会更改任何内容,因为布局会将其恢复为默认大小。为避免这种情况,最好使用
setFixedSize()
:self.Button1 = qtw.QPushButton("Button 2") self.Button1.setFixedSize(50, 100) self.Button2 = qtw.QPushButton("Button 2") self.Button2.setFixedSize(50, 100) self.Button3 = qtw.QPushButton("Button 2") self.Button3.setFixedSize(50, 100)
QMainWindow(一种非常特殊的 QWidget,它是 Qt 中所有 ui 元素的基础)不允许设置您自己的布局,因为它有自己的 private 布局,用于管理通常在“main window”中创建的所有内容。您可以从 QMainWindow documentation:
中看到这一点因为只要调整 window(或其任何内容)的大小时,这些对象都需要自动定位和调整大小,并且由于这种定位是 quite 复杂的性质布局在内部管理的一些小部件(停靠小部件和工具栏)。
这给您留下了创建“仅一个”中央小部件的“唯一”可能性。但这没什么大不了的:通常的做法是创建一个空的 QWidget 作为 容器 以防你的 UI 有多个“主要”小部件,就像在你的案例.
您在两个内部框架周围看到的边框是为任何布局默认创建的,可以使用 setContentsMargins()
. You can use getContentsMargins()
控制以获取布局的当前默认页边距,然后根据需要更改它们。例如,如果你只想避开顶部边框,万一有一个:
left, top, right, bottom = self.getContentsMargins()
if top:
self.setContentsMargins(left, 0, right, bottom)
请注意,即使 QWidget 子classes 也有内容边距(具有相同的函数名称),可以向小部件添加额外的边距,除了那些在布局上设置,但请注意,这些边距通常仅受某些小部件(通常是容器,如 QFrame)的尊重。
您看到的 在 这些框架之间的边框由布局的 间距 属性 控制。对于所有布局,它可以获取 (getSpacing()
) 和设置 (setSpacing()
) 作为全局值,为元素之间的所有间距保留,但也可以为垂直或水平间距单独设置QGridLayout (setHorizontalSpacing()
, setVerticalSpacing()
).
更改小部件的大小有点复杂。布局管理器实际上是布局的管理器,这意味着由他们决定元素有多大以及它们应该放在哪里,因此在由布局管理的小部件上使用 resize()
是完全没有意义的。
当小部件由布局管理时,布局会查询其 sizePolicy()
,这是一个描述当 Qt 必须决定其几何形状时小部件应如何行为的对象。
有些小部件的其中一个尺寸是固定的(按钮的垂直尺寸就是这种情况,默认情况下只能水平展开),其他小部件有 expanding 策略(这意味着他们会尝试占据尽可能多的 space),而其他人则有 首选 大小(因此他们会尽可能地尝试增长,但如果在相同的layout 有一个 expanding 元素,它将优先。
然后所有这些都基于小部件的 sizeHint()
,这是正常情况下小部件的 首选 大小(并且可以 return不同的值取决于情况和小部件类型),并且布局将使用它来决定如何根据小部件的策略设置几何图形。
可以通过多种方式覆盖该策略:
- 通过为小部件设置新大小策略;
- 通过获取小部件的大小策略,更改其属性之一,然后将其设置回小部件(单独更改大小策略不会影响小部件,因为大小策略不是发送的对象“通知”);
- 通过设置最小或最大大小限制(使用
set(Minimum|Maximum|Fixed)(Width|Height|Size)
); - 将小部件添加到布局 和 设置 stretch 值,这是一个整数值,告诉布局 该元素与其他元素的比例;例如,如果您添加一个带有
addWidget(button1, stretch=1)
的按钮和另一个带有addWidget(button2, stretch=2)
的按钮,第二个按钮的大小将是第一个的两倍;
最后,一个重要的考虑因素。
Qt 布局允许 嵌套 布局:例如,如果您不喜欢 QGridLayout 提供的 rows/columns 排列,您可以将水平布局添加到垂直布局。然后你可以在那个水平布局上添加另一个垂直布局。发生这种情况是因为添加到布局的所有项目实际上都是 QLayoutItem 对象,这些对象是用于表示添加到布局的“对象”的抽象项目:当使用 addWidget()
添加小部件时,布局会创建一个QLayoutItem 为它。
另请注意,每个 QLayout class 实际上是一个 QLayoutItem subclass;当您向框布局(或拉伸)添加间距时,您实际上是在添加具有固定或扩展大小策略的 QSpacerItem(同样,QLayoutItem subclass)。
题目比较复杂,我能理解如果从一个稍微简单的框架来,你可能会感到困惑,所以我建议你慢慢来,仔细阅读关于以下主题:
- QWidget size hints and size policies
- QSizePolicy
- Layout Management
- QLayout and QLayoutItem
- Using layouts in Designer
我还建议您使用 Qt Designer 大量 代码 和 实验,这将使您更好了解 Qt 布局管理的工作原理。
它并不总是完整的ui,所以请耐心等待并始终关注文档。
小提示:如果不存在子控件,设计器不允许为 父 控件设置布局。一个常见的错误是创建一个 main window,然后添加一个 widget 容器并为该容器设置布局;由于 Designer 在创建主 windows 时已经设置了中央小部件(并且无法更改),结果是创建的容器不会调整其大小以适应主 window。只需确保 centralwidget
具有布局(您可以从对象检查器中的图标中看到它)。如果您想为小部件设置布局但仍需要将其留空(因为您只想从代码中添加子小部件),只需将任意小部件添加到父部件,设置布局,然后删除该小部件。