如何使 QPushButtons 和 ToolBox 选项卡扩展到 100% 宽度

How to make the QPushButtons and ToolBox Tabs expand to 100% width

我正在尝试在 PyQt 中创建一个侧边菜单,类似于这个:https://www.youtube.com/watch?v=O9l75KOB2pE

为了在菜单中创建“子菜单”,我选择了“工具箱”,然后在工具箱之后添加了一个 QPushButton(这将是子菜单下方的一个按钮。它也有助于“非-打开”子菜单不会转到帧末尾)

但是,我在这样做时遇到了多个问题。在视频中,他在每个按钮下方添加了一个 border-bottom,并在鼠标悬停在按钮上时添加了一个 border-left。

我也做了同样的事情,但显然我的按钮没有完全展开,因此 border-bottom 仅添加在 QPushButton 的“文本”下方,而不是整个菜单。类似地,border-left 被添加到 QPushButton 文本的最左侧(而不是侧边菜单的左侧)。

此外,工具箱的选项卡也缩小了(也就是说,它们没有显示全文。这也可能是因为它没有获得侧边菜单框架的整个宽度)

我尝试使用不同小部件的“SizePolicy”以某种方式使按钮在宽度上完全展开,但对我来说没有任何效果。谁能帮我解决这个问题?

这是我当前程序的.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::NonModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1074</width>
    <height>751</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="styleSheet">
   <string notr="true">* {
border: none;
}

.QTableWidget
{
alternate-background-color: rgb(241, 241, 241);
background-color: white;
}

.QTableWidget:item
{
color: black;
padding: 5px;
}

.QHeaderView::section
{
background-color: white;
color: black;
font-weight: bold;
border: 0px;
border-bottom: 1px solid;
}</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <property name="styleSheet">
    <string notr="true">background-color: white;</string>
   </property>
   <layout class="QHBoxLayout" name="horizontalLayout">
    <property name="leftMargin">
     <number>0</number>
    </property>
    <property name="topMargin">
     <number>0</number>
    </property>
    <property name="rightMargin">
     <number>0</number>
    </property>
    <property name="bottomMargin">
     <number>0</number>
    </property>
    <item>
     <widget class="QFrame" name="side_menu_container">
      <property name="maximumSize">
       <size>
        <width>230</width>
        <height>16777215</height>
       </size>
      </property>
      <property name="styleSheet">
       <string notr="true">background-color: #1b1b1b;
color: white;
font-size: 25px;
font-weight: 600;
line-height: 65px;
text-align: center;
letter-spacing: 1px;

</string>
      </property>
      <property name="frameShape">
       <enum>QFrame::StyledPanel</enum>
      </property>
      <property name="frameShadow">
       <enum>QFrame::Raised</enum>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <property name="leftMargin">
        <number>0</number>
       </property>
       <property name="topMargin">
        <number>6</number>
       </property>
       <property name="rightMargin">
        <number>0</number>
       </property>
       <property name="bottomMargin">
        <number>0</number>
       </property>
       <item alignment="Qt::AlignTop">
        <widget class="QFrame" name="frame">
         <property name="frameShape">
          <enum>QFrame::StyledPanel</enum>
         </property>
         <property name="frameShadow">
          <enum>QFrame::Raised</enum>
         </property>
         <layout class="QHBoxLayout" name="horizontalLayout_42">
          <property name="spacing">
           <number>0</number>
          </property>
          <property name="topMargin">
           <number>0</number>
          </property>
          <property name="rightMargin">
           <number>0</number>
          </property>
          <property name="bottomMargin">
           <number>0</number>
          </property>
          <item alignment="Qt::AlignLeft|Qt::AlignTop">
           <widget class="QFrame" name="frame_46">
            <property name="styleSheet">
             <string notr="true">font-size: 21px;
</string>
            </property>
            <property name="frameShape">
             <enum>QFrame::StyledPanel</enum>
            </property>
            <property name="frameShadow">
             <enum>QFrame::Raised</enum>
            </property>
            <layout class="QVBoxLayout" name="verticalLayout_4">
             <property name="spacing">
              <number>0</number>
             </property>
             <property name="leftMargin">
              <number>0</number>
             </property>
             <property name="topMargin">
              <number>0</number>
             </property>
             <property name="rightMargin">
              <number>0</number>
             </property>
             <property name="bottomMargin">
              <number>0</number>
             </property>
             <item alignment="Qt::AlignLeft|Qt::AlignTop">
              <widget class="QLabel" name="label_2">
               <property name="font">
                <font>
                 <family>Arial</family>
                 <pointsize>-1</pointsize>
                 <weight>75</weight>
                 <bold>true</bold>
                </font>
               </property>
               <property name="text">
                <string>Timetable Manager</string>
               </property>
              </widget>
             </item>
            </layout>
           </widget>
          </item>
         </layout>
        </widget>
       </item>
       <item>
        <widget class="QFrame" name="frame_45">
         <property name="enabled">
          <bool>true</bool>
         </property>
         <property name="styleSheet">
          <string notr="true">background-color: #1e1e1e;</string>
         </property>
         <property name="frameShape">
          <enum>QFrame::StyledPanel</enum>
         </property>
         <property name="frameShadow">
          <enum>QFrame::Raised</enum>
         </property>
         <layout class="QVBoxLayout" name="verticalLayout_20">
          <property name="spacing">
           <number>0</number>
          </property>
          <property name="leftMargin">
           <number>0</number>
          </property>
          <property name="topMargin">
           <number>0</number>
          </property>
          <property name="rightMargin">
           <number>0</number>
          </property>
          <property name="bottomMargin">
           <number>0</number>
          </property>
          <item>
           <widget class="QFrame" name="side_menu">
            <property name="minimumSize">
             <size>
              <width>228</width>
              <height>0</height>
             </size>
            </property>
            <property name="styleSheet">
             <string notr="true">QPushButton
{
font-size: 16px;
line-height: 60px;
padding: 5px 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
color: #fff; 
border-left: 1px solid transparent;
}

QPushButton:hover
{
color: cyan;
background-color: #1e1e1e;
border-left: 1px solid cyan;
 
}


QToolBox::tab
{
font-size: 15px;
border: none;
}

QToolBox::tab:selected
{
background-color: #1e1e1e;
border-left: 1px solid cyan;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}</string>
            </property>
            <property name="frameShape">
             <enum>QFrame::StyledPanel</enum>
            </property>
            <property name="frameShadow">
             <enum>QFrame::Raised</enum>
            </property>
            <layout class="QVBoxLayout" name="verticalLayout_3">
             <property name="spacing">
              <number>0</number>
             </property>
             <property name="leftMargin">
              <number>0</number>
             </property>
             <property name="topMargin">
              <number>10</number>
             </property>
             <property name="rightMargin">
              <number>0</number>
             </property>
             <property name="bottomMargin">
              <number>0</number>
             </property>
             <item>
              <widget class="QFrame" name="frame_4">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="styleSheet">
                <string notr="true"/>
               </property>
               <property name="frameShape">
                <enum>QFrame::StyledPanel</enum>
               </property>
               <property name="frameShadow">
                <enum>QFrame::Raised</enum>
               </property>
               <layout class="QVBoxLayout" name="verticalLayout_5">
                <property name="spacing">
                 <number>0</number>
                </property>
                <property name="leftMargin">
                 <number>0</number>
                </property>
                <property name="topMargin">
                 <number>0</number>
                </property>
                <property name="rightMargin">
                 <number>0</number>
                </property>
                <property name="bottomMargin">
                 <number>0</number>
                </property>
                <item alignment="Qt::AlignLeft|Qt::AlignTop">
                 <widget class="QToolBox" name="toolBox">
                  <property name="styleSheet">
                   <string notr="true"/>
                  </property>
                  <property name="frameShape">
                   <enum>QFrame::NoFrame</enum>
                  </property>
                  <property name="currentIndex">
                   <number>0</number>
                  </property>
                  <widget class="QWidget" name="page_2">
                   <property name="geometry">
                    <rect>
                     <x>0</x>
                     <y>0</y>
                     <width>230</width>
                     <height>201</height>
                    </rect>
                   </property>
                   <property name="styleSheet">
                    <string notr="true"/>
                   </property>
                   <attribute name="icon">
                    <iconset resource="icons.qrc">
                     <normaloff>:/icons/icons/down-arrrow-navigate.png</normaloff>
                     <normalon>:/icons/icons/chevron-down.svg</normalon>:/icons/icons/down-arrrow-navigate.png</iconset>
                   </attribute>
                   <attribute name="label">
                    <string>Registration Details</string>
                   </attribute>
                   <layout class="QVBoxLayout" name="verticalLayout_6">
                    <item alignment="Qt::AlignTop">
                     <widget class="QFrame" name="frame_5">
                      <property name="styleSheet">
                       <string notr="true"/>
                      </property>
                      <property name="frameShape">
                       <enum>QFrame::StyledPanel</enum>
                      </property>
                      <property name="frameShadow">
                       <enum>QFrame::Raised</enum>
                      </property>
                      <layout class="QFormLayout" name="formLayout">
                       <property name="leftMargin">
                        <number>15</number>
                       </property>
                       <property name="rightMargin">
                        <number>0</number>
                       </property>
                       <property name="bottomMargin">
                        <number>0</number>
                       </property>
                       <item row="1" column="0">
                        <widget class="QPushButton" name="course_menu_button">
                         <property name="styleSheet">
                          <string notr="true"/>
                         </property>
                         <property name="text">
                          <string>Courses</string>
                         </property>
                        </widget>
                       </item>
                       <item row="2" column="0">
                        <widget class="QPushButton" name="room_menu_button">
                         <property name="text">
                          <string>Rooms</string>
                         </property>
                        </widget>
                       </item>
                       <item row="3" column="0">
                        <widget class="QPushButton" name="teacher_menu_button">
                         <property name="text">
                          <string>Teachers</string>
                         </property>
                        </widget>
                       </item>
                       <item row="4" column="0">
                        <widget class="QPushButton" name="registered_menu_button">
                         <property name="text">
                          <string>Registered Courses</string>
                         </property>
                        </widget>
                       </item>
                       <item row="0" column="0">
                        <widget class="QPushButton" name="section_menu_button">
                         <property name="sizePolicy">
                          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
                           <horstretch>0</horstretch>
                           <verstretch>0</verstretch>
                          </sizepolicy>
                         </property>
                         <property name="font">
                          <font>
                           <family>Arial</family>
                           <pointsize>-1</pointsize>
                           <weight>75</weight>
                           <bold>true</bold>
                          </font>
                         </property>
                         <property name="styleSheet">
                          <string notr="true"/>
                         </property>
                         <property name="text">
                          <string>Sections</string>
                         </property>
                         <property name="iconSize">
                          <size>
                           <width>32</width>
                           <height>32</height>
                          </size>
                         </property>
                        </widget>
                       </item>
                      </layout>
                     </widget>
                    </item>
                   </layout>
                  </widget>
                  <widget class="QWidget" name="page_3">
                   <property name="geometry">
                    <rect>
                     <x>0</x>
                     <y>0</y>
                     <width>230</width>
                     <height>102</height>
                    </rect>
                   </property>
                   <attribute name="icon">
                    <iconset resource="icons.qrc">
                     <normaloff>:/icons/icons/chevron-down.svg</normaloff>:/icons/icons/chevron-down.svg</iconset>
                   </attribute>
                   <attribute name="label">
                    <string>Teacher Preferences</string>
                   </attribute>
                   <layout class="QVBoxLayout" name="verticalLayout_13">
                    <item alignment="Qt::AlignTop">
                     <widget class="QFrame" name="frame_32">
                      <property name="frameShape">
                       <enum>QFrame::StyledPanel</enum>
                      </property>
                      <property name="frameShadow">
                       <enum>QFrame::Raised</enum>
                      </property>
                      <layout class="QVBoxLayout" name="verticalLayout_14">
                       <item>
                        <widget class="QPushButton" name="room_preference_menu_button">
                         <property name="text">
                          <string>Room Preferences</string>
                         </property>
                        </widget>
                       </item>
                       <item>
                        <widget class="QPushButton" name="slot_preference_menu_button">
                         <property name="text">
                          <string>Slot Preferences</string>
                         </property>
                        </widget>
                       </item>
                      </layout>
                     </widget>
                    </item>
                   </layout>
                  </widget>
                  <widget class="QWidget" name="page">
                   <property name="geometry">
                    <rect>
                     <x>0</x>
                     <y>0</y>
                     <width>230</width>
                     <height>138</height>
                    </rect>
                   </property>
                   <attribute name="label">
                    <string>Page</string>
                   </attribute>
                   <layout class="QVBoxLayout" name="verticalLayout_17">
                    <item alignment="Qt::AlignTop">
                     <widget class="QFrame" name="frame_43">
                      <property name="frameShape">
                       <enum>QFrame::StyledPanel</enum>
                      </property>
                      <property name="frameShadow">
                       <enum>QFrame::Raised</enum>
                      </property>
                      <layout class="QVBoxLayout" name="verticalLayout_18">
                       <item>
                        <widget class="QPushButton" name="student_clash_menu_button">
                         <property name="text">
                          <string>Student Clashes</string>
                         </property>
                        </widget>
                       </item>
                       <item>
                        <widget class="QPushButton" name="room_clash_menu_button">
                         <property name="text">
                          <string>Room Clashes</string>
                         </property>
                        </widget>
                       </item>
                       <item>
                        <widget class="QPushButton" name="instructor_clash_menu_button">
                         <property name="text">
                          <string>Instructor Clashes</string>
                         </property>
                        </widget>
                       </item>
                      </layout>
                     </widget>
                    </item>
                   </layout>
                  </widget>
                 </widget>
                </item>
               </layout>
              </widget>
             </item>
             <item>
              <widget class="QFrame" name="frame_44">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="frameShape">
                <enum>QFrame::StyledPanel</enum>
               </property>
               <property name="frameShadow">
                <enum>QFrame::Raised</enum>
               </property>
               <layout class="QVBoxLayout" name="verticalLayout_19">
                <item alignment="Qt::AlignTop">
                 <widget class="QPushButton" name="pushButton_2">
                  <property name="text">
                   <string>PushButton</string>
                  </property>
                 </widget>
                </item>
               </layout>
              </widget>
             </item>
            </layout>
           </widget>
          </item>
         </layout>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QFrame" name="main_body">
      <property name="frameShape">
       <enum>QFrame::StyledPanel</enum>
      </property>
      <property name="frameShadow">
       <enum>QFrame::Raised</enum>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout">
       <property name="spacing">
        <number>0</number>
       </property>
       <property name="leftMargin">
        <number>0</number>
       </property>
       <property name="topMargin">
        <number>0</number>
       </property>
       <property name="rightMargin">
        <number>0</number>
       </property>
       <property name="bottomMargin">
        <number>0</number>
       </property>
      </layout>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources>
  <include location="icons.qrc"/>
 </resources>
 <connections/>
</ui>

所提供的 UI 存在各种问题。

按钮的问题有多种原因,并且无法从 Designer 轻松解决,尤其是因为您更改了很多属性(其中许多属性已传播,包括样式表)。

首先,左边框被移动,因为在大多数系统中所有布局都有默认边距,因此您必须明确将其设置为0。

在 Designer 中,select 来自 object 检查器的每个 ,滚动到 属性 编辑器的底部并更改 layoutLeftMargin 属性 到 0。如果 属性 已经是 0 但未显示在 bold 中(表示 属性 是否使用默认值或明确的值),更改为另一个数字,然后再次将其设置为 0。然后对您在该页面中添加的框架执行相同的操作。

第一帧底部边框“不完整”的问题是由于您使用了 QFormLayout,在某些样式中,它使按钮仅占据最小尺寸,而不是展开。
将其更改为垂直布局,然后将以下内容添加到 QPushButton 的 side_menu 样式表中 select 或者:

text-align: left;

另请注意,对于如此复杂的小部件结构,您应该注意不要在不同的地方设置太多样式表:不仅通常没有必要,而且还难以跟踪它们(在某些时候您可能面对一些意想不到的行为,找到问题的实际根源会变得非常困难)。考虑到通常 在 parents 上设置通用样式表属性通常会更好,就像您在 side_menu_container 上所做的那样,甚至 window。滚动区域和组合框等复杂小部件要求,如果您设置 属性,则必须设置 all 属性。使用特定的 select 或 而不是 通配符!)是更好更安全的选择。另请注意,letter-spacing 不是 QSS 可识别的 属性,因此您应该将其删除。

应该为顶级小部件(甚至是应用程序)设置一个编写良好的样式表,并且只有在 那个 确实需要时才最终为小部件设置一个特定的样式表小部件。

最后,您在某些小部件中设置了布局对齐方式,这会阻止它们正确显示,因为设置对齐方式会使小部件仅使用其基本尺寸提示,即使有更多 space可用。

删除 工具框按钮文本的问题有点棘手。问题源于 QStyleSheetStyle(一种特殊的私有 QStyle,自动设置在具有样式表或继承样式表的小部件上)自动使用省略文本,而且似乎实现得不好,因为样式选项没有' 正确初始化可用的文本矩形。我坚信这是一个错误。

如果未设置样式表,解决方案很简单:使用 QProxyStyle 并重写 drawControl 以自行绘制按钮文本。不幸的是,这对于样式表是不可能的,因为 drawControl 从未被调用,因为它被已安装的 QStyleSheetStyle 覆盖。

但是drawItemText() 调用的,所以有可能,有点hacky 和复杂的解决方案。

不幸的是,drawItemText() 没有对小部件的引用,因为它只使用给定的选项绘制文本。
诀窍是为工具箱使用提升的小部件,为其设置自定义代理样式,并覆盖其某些功能以使用标识符跟踪“实际”名称,然后使用真实名称调用基本实现。

class ToolBox(QtWidgets.QToolBox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.names = []
        self.nameDict = {}
        self.setStyle(ProxyStyle(self))

    def updateNames(self):
        self.nameDict.clear()
        for i, realName in enumerate(self.names):
            fakeName = '__{}'.format(i)
            super().setItemText(i, fakeName)
            self.nameDict[fakeName] = realName

    def itemInserted(self, index):
        self.names.insert(index, super().itemText(index))
        self.updateNames()

    def itemRemoved(self, index):
        self.names.pop(index)
        self.updateNames()

    def setItemText(self, index, text):
        self.names[index] = text
        self.update()

    def itemText(self, index):
        return self.names[index]


class ProxyStyle(QtWidgets.QProxyStyle):
    def __init__(self, toolBox):
        super().__init__(QtWidgets.QStyleFactory.create('fusion'))
        self.toolBox = toolBox

    def drawItemText(self, painter, rect, alignment, palette, enabled, text, role=QtGui.QPalette.NoRole):
        text = self.toolBox.nameDict.get(text, text)
        super().drawItemText(painter, rect, alignment, palette, enabled, text, role)

要升级小部件,请右键单击 object 检查器中的工具框,select“升级为...”,然后键入“工具箱”(我们的子class) 在“升级的 class 名称”字段中,键入存储 class 的文件的名称( 不带 扩展名!),然后单击“添加”,最后“推广”。

请注意 class 的文件甚至可以是主脚本,但在那种情况下您 必须 使用最后的 if __name__ == '__main__': 块作为应用程序,因为促销加载“header”作为导入。