QT Designer:在尊重 window 重新缩放 PyQT5 的同时移动和调整小部件

QT Designer: Moving & Resizing widgets while respecting window rescaling PyQT5

TLDR: QT Designer 不允许我在尊重 window 缩放比例的同时移动和调整小部件的大小。当将它从图像 1 拖动到图像 2 时,我想让最右边的按钮更靠近 window 的边缘,也就是保持比例。

我正在将 PyQT5 与 QT Designer 一起使用。我正在尝试手动组织(移动和调整)我的小部件,但我的两种方法(接下来解释)都失败了。

当我使用网格布局时,我可以通过拖动其边缘来更改 window 大小来缩放小部件(正如预期的那样......太棒了!)。但是当我使用布局时,我无法更改小部件的相对位置。使用布局时,我也无法拖动单个小部件的边缘以在 window 内调整它的大小(真可惜,这对于构建我的 UI 很重要)。

另一方面,当我不使用任何布局时,我可以自由移动和调整各个小部件的大小(耶!)。但是如果不使用布局,我的小部件将不再缩放以适应 window 大小。

我的问题是,如何在 QT Designer 中移动和调整小部件的大小?我认为我可以在 QT Designer 本身中做到这一点,而不是依赖于 PyQT5 的混乱,因为即使是微小的变化也会变得复杂,而不是 QT Designer 的拖放功能?

附件是一些屏幕截图,以图形方式显示我的意思。前两张照片显示了在没有布局的情况下发生的情况。我将 window 变大了,但我的小部件仍以相同大小留在同一位置。第三张截图显示了当我使用网格布局时会发生什么。我无法调整任何小部件的大小,也无法移动它们。

换句话说,是否有更有效的方法来使用 QT Designer 移动和调整大小来解决这两个问题?

我希望将最右边的按钮从图像 1 拖动到图像 2 时靠近 window 的边缘,也就是保持比例。

如果要实现比例则:

  • 使用额外的 QWidgets(默认情况下是不可见的,但对于我的演示我会设置背景颜色)或 QSpacerItem,

  • 使用满足条件的拉伸因子,并且

  • 将所有小部件的 sizePolice 设置为忽略。

网格布局将使用该信息来重新缩放几何图形。

对于我的演示,window 的大小为 200x200,按钮的位置为 50x50,大小为 40x40。

  • 对于左侧的小部件,您必须将水平拉伸因子设置为 40

  • 对于右侧的中央,您必须将水平拉伸因子设置为 50,将水平拉伸因子设置为 50。

  • 对于右侧的小部件,您必须将水平拉伸因子设置为 90(200 - 50 -40)

  • 对于顶部的小部件,您必须将垂直拉伸系数设置为 50。

  • 对于底部的小部件,您必须将垂直拉伸系数设置为 90(200 - 50 -40)。

<?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>593</width>
    <height>439</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="1">
     <widget class="QWidget" name="top" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
        <horstretch>0</horstretch>
        <verstretch>50</verstretch>
       </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: rgb(85, 255, 127);</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
     <widget class="QWidget" name="left" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
        <horstretch>50</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: rgb(255, 0, 0);</string>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="pushButton">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
        <horstretch>40</horstretch>
        <verstretch>40</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
    <item row="1" column="2">
     <widget class="QWidget" name="right" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
        <horstretch>110</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: rgb(255, 85, 255);</string>
      </property>
     </widget>
    </item>
    <item row="2" column="1">
     <widget class="QWidget" name="bottom" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
        <horstretch>0</horstretch>
        <verstretch>110</verstretch>
       </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: rgb(85, 255, 255);</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>593</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

虽然过度拥挤的 UI 通常不是一个好主意,但有很多空 space 很少会更好。布局管理器考虑了这个方面,并尝试通过使小部件尽可能多地使用 space 来 优化 可用 space(假设它们可以调整大小)。
也就是说,显然有很多情况下 window 和很多 space 是好的(例如,登录访问界面足够大以引起注意)。

重要的是要理解布局管理器,顾名思义,管理布局,它通过很多复杂的计算,基于安装它的小部件和所有其子项的所有大小hints/policies/constraints,递归地。如果您对某些小部件应该如何调整大小有一些要求,则必须指定它们。

每当要创建 任何 界面时,特别是如果正在考虑用户体验问题(这是他们应该考虑的)并且还有很多空的 space要按比例使用,至少有两个重要步骤:

  1. 准确理解在布局的“正常”条件下应如何放置小部件(位置、大小和比例);
  2. 考虑一下当调整容器大小时那些尺寸和位置应该改变;

第一点似乎很好解释,但即使创建一个使用布局考虑空白区域的基本界面也不是那么简单。
根据情况,网格布局可能就足够了,但在某些情况下,您可能需要使用 nested 布局。

在您的具体情况下,网格可能就足够了,但要确保保持适当的间距和比例,您需要使用 spacer items.

我已经为您的案例准备了一个基本的 UI 示例:比例可能不完全正确,但这只是一个开始。

这是它在正常尺寸下的样子:

这就是它变大后的样子:

如您所见,我添加了 spacers:靠近边缘的 each 边有一个,这是确保适当的“边界边距”所必需的保持不变,然后另外两个用于两个按钮之间的间距;由于网格变为 5x5,因此两个中心 spacers 的“单元格”位置可能在任何位置,只要垂直在中央行和水平在中央列即可。

现在小部件的比例和间距是通过以下方式实现的:

  • 两个按钮都有 Preferred 尺寸政策(垂直和水平);这确保布局知道如果有足够的 space 可以让它们变大,但不会让它们占用 all 可用的 space;
  • 左上按钮的大小策略 stretch 为 1(同样,垂直和水平),而右下按钮为 2;这样,他们的第二个按钮将始终 try 是第一个按钮宽度和高度的两倍;
  • 布局(通过单击中央小部件可访问)还为第三列(中间的列)设置了 2 的列拉伸;考虑到上述情况,结果将是布局将尝试使中间的 space 与右下按钮一样宽;

请注意,当使用网格时,按钮的行或列拉伸可以(有时 应该)直接在布局上设置。
还要考虑到边缘的 spacers 是基本的 QSpacerItems,它的功能非常有限(最小 width/height 和大小策略),如果你想要更大的比例边距,你必须设置它在拉伸中。另一种可能性是使用空 QWidgets 而不是 spacers.

基于以上,更好的选择是实际设置网格布局的延伸:

vertical (rows):      1, 2, 1, 4, 1
horizontal (columns): 1, 2, 4, 4, 1

请记住,拉伸值是相对比例,如果您想要更精确的尺寸,请使用更大的值。

显然,您还可以为任何小部件设置最大尺寸,以防您不想超过某个尺寸。

我将附上原始文件(仅在小部件上设置了拉伸),所以我会留给您更改这些值,看看会发生什么。

最后,请记住,如果您添加另一个具有 一个 PreferredIgnored 大小策略的小部件,布局将做出反应因此。尝试将按钮添加到现有按钮的同一行或同一列的空网格中,您将看到结果。

这是基本的 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>806</width>
    <height>507</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,2,0,0">
    <item row="1" column="2">
     <spacer name="horizontalSpacer_2">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>40</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="2" column="2">
     <spacer name="verticalSpacer_2">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
        <height>40</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="4" column="3">
     <spacer name="verticalSpacer_3">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
        <height>40</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="3" column="4">
     <spacer name="horizontalSpacer_3">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>40</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="pushButton">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
        <verstretch>1</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
    <item row="3" column="3">
     <widget class="QPushButton" name="pushButton_2">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>2</horstretch>
        <verstretch>2</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>...</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
     <spacer name="horizontalSpacer">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>40</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="0" column="1">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
        <height>40</height>
       </size>
      </property>
     </spacer>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>806</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

虽然基本上只有三个基本布局管理器(QFormLayout 和 QStackedLayout 有点不同),而且它们非常简单,但学习如何使用它们需要大量时间和经验。此外,了解尺寸提示和策略的工作原理对于更好地理解它们的行为 非常 非常重要。我强烈建议您在 Designer 中以及通过从代码创建 UI 来做 大量 实验。
出于 学习 目的,您可以在 Designer 中创建 UI,使用 pyuic 将其导出并研究其内容。但是,请记住,不要 evernever 编辑生产代码的这些文件。在关于 using Designer.

的官方指南中阅读更多关于它们的用法