简体   繁体   English

pyqt5 与 .ui 文件选项卡菜单填充和设计器中的窗口大小

[英]pyqt5 with .ui file tab menu padding and window size in designer

my running app我正在运行的应用

here how i created my app step by step:在这里我如何一步一步地创建我的应用程序:

  1. i am created a tab through designer tool with 3 pages: a) Account b) Security c) Performance and save as tab.ui我通过设计器工具创建了一个包含 3 个页面的选项卡:a) 帐户 b) 安全性 c) 性能并另存为tab.ui

  2. then i generate tab.py file from tab.ui by using pyuic5然后我使用 pyuic5 从 tab.ui 生成tab.py文件

  3. now i added some classes manually in tab.py file >> TabBar, TabWidget and ProxyStyle classes, then change self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) to self.tabWidget = TabWidget(self.centralwidget), and add àpp.setStyle(ProxyStyle()) after app = QtWidgets.QApplication(sys.argv)现在我在tab.py文件中手动添加了一些类 >> TabBar、TabWidget 和 ProxyStyle 类,然后将 self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 更改为 self.tabWidget = TabWidget(self.centralwidget),并添加 àpp。 app = QtWidgets.QApplication(sys.argv) 之后的 setStyle(ProxyStyle())

my code is working as i shown in pic, but my tab menu padding is not looking good and window size is not full (fit with window if i maximized).我的代码按图片所示工作,但我的选项卡菜单填充看起来不太好,窗口大小不完整(如果我最大化,则适合窗口)。 someone please look in to it.有人请看一下。

  1. Now my question is if we add some other elements in tab.ui and if i generate tab.py file again my previous tab.py code is overlapped which classes i manually added.现在我的问题是我们是否在tab.ui 中添加了一些其他元素,如果我再次生成tab.py文件,我之前的 tab.py 代码与我手动添加的类重叠。 this is not fine.这不好。

  2. i know i am wrong .but tell me the procedure and give me a proper structure than i can start to create my tool in right way.我知道我错了。但是告诉我程序并给我一个正确的结构,以便我可以开始以正确的方式创建我的工具。

here is my tab.py code:这是我的tab.py代码:

from PyQt5 import QtCore, QtGui, QtWidgets

class TabBar(QtWidgets.QTabBar):
    def tabSizeHint(self, index):
        s = QtWidgets.QTabBar.tabSizeHint(self, index)
        s.transpose()
        return s

    def paintEvent(self, event):
        painter = QtWidgets.QStylePainter(self)
        opt = QtWidgets.QStyleOptionTab()

        for i in range(self.count()):
            self.initStyleOption(opt, i)
            painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
            painter.save()

            s = opt.rect.size()
            s.transpose()
            r = QtCore.QRect(QtCore.QPoint(), s)
            r.moveCenter(opt.rect.center())
            opt.rect = r

            c = self.tabRect(i).center()
            painter.translate(c)
            painter.rotate(90)
            painter.translate(-c)
            painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt);
            painter.restore()


class TabWidget(QtWidgets.QTabWidget):
    def __init__(self, *args, **kwargs):
        QtWidgets.QTabWidget.__init__(self, *args, **kwargs)
        self.setTabBar(TabBar(self))
        self.setTabPosition(QtWidgets.QTabWidget.West)

class ProxyStyle(QtWidgets.QProxyStyle):
    def drawControl(self, element, opt, painter, widget):
        if element == QtWidgets.QStyle.CE_TabBarTabLabel:
            ic = self.pixelMetric(QtWidgets.QStyle.PM_TabBarIconSize)
            r = QtCore.QRect(opt.rect)
            w =  0 if opt.icon.isNull() else opt.rect.width() + self.pixelMetric(QtWidgets.QStyle.PM_TabBarIconSize)
            r.setHeight(opt.fontMetrics.width(opt.text) + w)
            r.moveBottom(opt.rect.bottom())
            opt.rect = r
        QtWidgets.QProxyStyle.drawControl(self, element, opt, painter, widget)


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        #self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget = TabWidget(self.centralwidget)
        self.tabWidget.setGeometry(QtCore.QRect(0, 40, 800, 541))
        self.tabWidget.setStyleSheet("\n"
"    QTabBar::tab { height: 100px; width: 50px; }\n"
"    QTabBar::tab {background-color: rgb(34, 137, 163);}\n"
"    QTabBar::tab:selected {background-color: rgb(48, 199, 184);}\n"
"    QTabWidget>QWidget>QWidget{background: WHITE;}\n"
"  ")
        self.tabWidget.setTabPosition(QtWidgets.QTabWidget.West)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.groupBox_3 = QtWidgets.QGroupBox(self.tab)
        self.groupBox_3.setGeometry(QtCore.QRect(20, 10, 681, 80))
        self.groupBox_3.setObjectName("groupBox_3")
        self.groupBox_4 = QtWidgets.QGroupBox(self.tab)
        self.groupBox_4.setGeometry(QtCore.QRect(20, 100, 681, 80))
        self.groupBox_4.setObjectName("groupBox_4")
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.groupBox = QtWidgets.QGroupBox(self.tab_2)
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 251, 191))
        self.groupBox.setObjectName("groupBox")
        self.groupBox_2 = QtWidgets.QGroupBox(self.tab_2)
        self.groupBox_2.setGeometry(QtCore.QRect(290, 20, 271, 191))
        self.groupBox_2.setObjectName("groupBox_2")
        self.tabWidget.addTab(self.tab_2, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.tabWidget.addTab(self.tab_3, "")
        self.frame = QtWidgets.QFrame(self.centralwidget)
        self.frame.setGeometry(QtCore.QRect(-1, 0, 801, 41))
        self.frame.setStyleSheet("background-color: rgb(59, 118, 150);")
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.comboBox = QtWidgets.QComboBox(self.frame)
        self.comboBox.setGeometry(QtCore.QRect(50, 10, 141, 22))
        self.comboBox.setObjectName("comboBox")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(2)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.groupBox_3.setTitle(_translate("MainWindow", "GroupBox"))
        self.groupBox_4.setTitle(_translate("MainWindow", "GroupBox"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Account"))
        self.groupBox.setTitle(_translate("MainWindow", "GroupBox"))
        self.groupBox_2.setTitle(_translate("MainWindow", "GroupBox"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Security"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Performance"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(ProxyStyle())
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

There are two main and common issues with your approach.你的方法有两个主要和常见的问题。

*NEVER* edit the output of pyuic *永远*编辑pyuic的输出

This happens very often: you get a well formatted python file, and are led to think that you can use that file to write your program.这种情况经常发生:您得到一个格式良好的 python 文件,并认为您可以使用该文件来编写您的程序。

You may have noticed the warning in that file too:您可能也注意到该文件中的警告:

# WARNING! All changes made in this file will be lost!

As you've already found out, as soon as you need to modify the UI, you'll be caught in the mess of merging the new modifications with the code you already wrote.正如您已经发现的那样,一旦您需要修改 UI,您就会陷入将新修改与您已经编写的代码合并的混乱中。

The bottom line is that you've to think about those files as resource files (not unlike an image, a database or a configuration file), and they have to be used as such: since they are python files, they can be imported as a module, and their classes have to be used to build the interface of your actual widgets and windows.最重要的是,您必须将这些文件视为资源文件(与图像、数据库或配置文件不同),并且必须如此使用它们:由于它们是 python 文件,因此可以将它们导入为一个模块,它们的类必须用于构建实际小部件和窗口的界面。

There are three main ways to do that, all of them explained in the using Designer documentation.有三种主要方法可以做到这一点,所有这些方法都在使用设计器文档中进行了解释。
I strongly suggest you to use the third method (the multiple inheritance approach), as it allows you to have references to UI objects as direct attributes of the instance ( self.tabWidget , etc.).我强烈建议您使用第三种方法(多重继承方法),因为它允许您将 UI 对象的引用作为实例的直接属性( self.tabWidget等)。

Alternatively, you can completely avoid the pyuic approach at all, and directly import the .ui files using the loadUI function from the uic module.或者,您可以完全避免使用 pyuic 方法,直接使用uic模块中的loadUI函数导入.ui文件。

from PyQt5 import QtWidgets
from PyQt5 import uic

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUI('mywindow.ui', self)

This allows you to save some time, since you don't have to rebuild the file each time you make modifications;这使您可以节省一些时间,因为您不必每次进行修改时都重新构建文件; also, sometimes one may forget to do that step, which might create some confusion.此外,有时人们可能会忘记执行该步骤,这可能会造成一些混乱。
Just remember that that path is always relative to the file that contains the class that will load it (and absolute paths should never be used).请记住,该路径始终相对于包含将加载它的类的文件(绝对路径不应使用)。

Always use a layout manager始终使用布局管理器

Using fixed widget sizes and positions is usually discouraged, as what you see on your computer will probably be very, if not drastically, different on another's.通常不鼓励使用固定的小部件大小和位置,因为您在计算机上看到的内容在其他计算机上可能会非常不同,如果不是完全不同的话。
That can depend on a series of aspects, but most importantly:这可能取决于一系列方面,但最重要的是:

  • operating system (and its version);操作系统(及其版本);
  • screen settings (resolution, standard or High DPI - such as retina screens);屏幕设置(分辨率、标准或高 DPI - 例如视网膜屏幕);
  • user customization (default font sizes, some "themes" that use different margins and spaces between objects);用户自定义(默认字体大小,一些“主题”在对象之间使用不同的边距和空格);

All this potentially makes a fixed layout unusable, as widgets could overlap or become invisible because hidden by others or because the available screen size is not enough to show the interface as you designed it.所有这些都可能使固定布局无法使用,因为小部件可能会重叠或变得不可见,因为被其他人隐藏,或者因为可用的屏幕尺寸不足以显示您设计的界面。

Using layout managers simplifies all this, because they will automatically resize the interface ensuring that all widgets will at least use as much space as they need, leaving space for those that might take advantage in using more size.使用布局管理器简化了这一切,因为它们会自动调整界面大小,确保所有小部件至少使用它们需要的空间,为那些可能会利用更大尺寸的小部件留出空间。

While this could seem a bit more difficult to manage (especially for complex interfaces), it's just a matter of habit.虽然这看起来有点难以管理(特别是对于复杂的界面),但这只是习惯问题。
Most of the times you'll end up with nested layouts, but there's nothing wrong with them.大多数情况下,您最终会得到嵌套布局,但它们没有任何问题。

In your case, you'll probably use something like the following structure:在您的情况下,您可能会使用类似以下结构的内容:

  • a vertical layout as the main layout;以垂直布局为主布局;
    • a horizontal layout for the top;顶部的水平布局;
      • a horizontal spacer, with a fixed width;一个水平间隔,宽度固定;
      • the combobox;组合框;
      • another horitonzal spacer for the right margin;右边距的另一个水平间隔;
    • the tabwidget;小工具;
      • a vertical layout for the first tab;第一个标签的垂直布局;
        • the two vertically aligned group boxes两个垂直对齐的分组框
      • a horizontal layout for the second tab;第二个选项卡的水平布局;
        • the two horizontally aligned group boxes;两个水平对齐的分组框;
      • ... ...

As a final note, I don't think you need to use the proxystyle to adjust the size of the rectangle: as you can see, the text is cropped, and that's due to the way you paint and rotate, which also leads to the painting issue of the tab background.最后一点,我认为您不需要使用 proxystyle 来调整矩形的大小:如您所见,文本被裁剪,这是由于您绘制和旋转的方式,这也导致选项卡背景的绘制问题。 Remove the sizes from the stylesheet and the proxystyle, then use those size values within tabSizeHint() :从样式表和tabSizeHint()样式中删除大小,然后在tabSizeHint()使用这些大小值:

    def tabSizeHint(self, index):
        s = QtWidgets.QTabBar.tabSizeHint(self, index)
        s.transpose()
        s.setHeight(max(s.height(), 50))
        s.setWidth(max(s.width(), 100))
        return s

Since there's a small overlap between the tabbar and the tabwidget contents, you can ensure that they are correctly aligned by setting the offset of the ::pane and ::tab-bar pseudo elements:由于 tabbar 和 tabwidget 内容之间有少量重叠,您可以通过设置::pane::tab-bar伪元素的偏移量来确保它们正确对齐:

            QTabWidget::pane {top: 0px;}
            QTabWidget::tab-bar {right: 0px;}

Also, ensure to apply the top frame stylesheet to QFrame objects only (and possibly its object name), otherwise every child widget will inherit it.此外,确保仅将顶部框架样式表应用于 QFrame 对象(可能还有它的对象名称),否则每个子部件都将继承它。

self.frame.setStyleSheet("QFrame#frame {background-color: rgb(59, 118, 150);}")

With all of this in mind, you'll end up with a cleaner look, correct tab widget/bar painting and positioning, layout flexibility and, most importantly, the possibility to edit the UI on the fly without further problems and headaches :-)考虑到所有这些,您最终将获得更干净的外观、正确的选项卡小部件/栏绘制和定位、布局灵活性,最重要的是,可以即时编辑 UI,而不会出现更多问题和头痛:-)

呜呜,这很酷!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM