pyqt5 с отступом меню вкладки файла .ui и размером окна в дизайнере - PullRequest
1 голос
/ 28 февраля 2020

мое работающее приложение

вот как я создал свое приложение шаг за шагом:

  1. я создал вкладку через дизайнер инструмент с 3 страницами: а) Учетная запись б) Безопасность c) Производительность и сохранить как tab.ui

  2. затем я генерирую tab.py файл из tab.ui с помощью pyuic5

  3. теперь я добавил некоторые классы вручную в tab.py file >> TabBar, TabWidget и ProxyStyle, затем изменил Селфи *

мой код работает так, как показано в pi c, но заполнение меню на вкладке выглядит не очень хорошо, а размер окна не полный (подходит для окна, если я развернул). кто-то, пожалуйста, посмотрите на это.

Теперь мой вопрос: добавим ли мы некоторые другие элементы в tab.ui и если я сгенерирую файл tab.py снова, мой предыдущий код tab.py будет перекрыт, что классы, которые я добавил вручную. это не хорошо.

Я знаю, что я неправ. Но скажите мне процедуру и дайте мне правильную структуру, чем я могу начать создавать свой инструмент правильным способом.

вот мой 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_())

1 Ответ

0 голосов
/ 28 февраля 2020

У вашего подхода есть две основные и общие проблемы.

* НИКОГДА * не редактируйте вывод pyuic

Это происходит очень часто: вы получаете хорошо отформатированный файл python и заставляют думать, что вы можете использовать этот файл для написания своей программы.

Возможно, вы также заметили предупреждение в этом файле:

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

Как вы уже узнали, как только вам нужно будет изменить пользовательский интерфейс, вы попадете в беспорядок объединения новых модификаций с уже написанным вами кодом.

Суть в том, что вы должны думать об этих файлах как о ресурсных файлах (в отличие от изображения, базы данных или файла конфигурации), и их нужно использовать как таковые: поскольку они python файлы, они могут быть импортированы как модуль, и их классы должны использоваться для создания интерфейса ваших актуальных виджетов и windows.

. Есть три основных способа сделать это все они объяснены в с использованием документа Designer
Я настоятельно рекомендую вам использовать третий метод (подход множественного наследования), поскольку он позволяет вам иметь ссылки на объекты пользовательского интерфейса в качестве прямых атрибутов экземпляра (self.tabWidget, et c.).

Кроме того, вы можете полностью отказаться от подхода pyui c и напрямую импортировать файлы .ui с помощью функции loadUI из модуля uic.

from PyQt5 import QtWidgets
from PyQt5 import uic

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

Это позволяет сэкономить некоторое время, поскольку вам не нужно перестраивать файл каждый раз, когда вы вносите изменения; также иногда можно забыть сделать этот шаг, что может создать некоторую путаницу.
Просто помните, что этот путь всегда относительно файла, который содержит класс, который его загрузит (и абсолютные пути никогда не должны использоваться).

Всегда используйте менеджер раскладки

Использование фиксированных размеров и позиций виджетов обычно не рекомендуется, так как то, что вы видите на вашем компьютере, вероятно, будет сильно отличаться от других.
Это может зависеть от ряда аспектов, но самое главное:

  • операционная система (и ее версия);
  • настройки экрана (разрешение, стандартное или высокое разрешение - например, экраны Retina) ;
  • пользовательская настройка (размеры шрифтов по умолчанию, некоторые «темы», которые используют разные поля и пробелы между объектами);

Все это потенциально делает фиксированный макет непригодным для использования, поскольку виджеты могут перекрываться или становятся невидимыми, потому что скрыты другими или потому что доступного размера экрана недостаточно для отображения интерфейса как Вы разработали его.

Использование менеджеров компоновки упрощает все это, потому что они автоматически изменят размер интерфейса, гарантируя, что все виджеты будут по крайней мере использовать столько места, сколько им нужно, оставляя место для тех, кто может воспользоваться преимуществами использования большего количества size.

Хотя это может показаться более сложным в управлении (особенно для сложных интерфейсов), это просто привычка.
В большинстве случаев вы получаете вложенных макетов, но с ними все в порядке.

В вашем случае вы, вероятно, будете использовать что-то вроде следующей структуры:

  • вертикальный макет в качестве основного макета;
    • горизонтальное расположение сверху;
      • горизонтальная прокладка с фиксированной шириной;
      • поле со списком;
      • еще одна горизонтальная прокладка для правого поля;
    • вкладка;
      • вертикальная раскладка для первой вкладки;
        • два вертикально выровненных групповых блока
      • горизонтальный макет для второй вкладки;
        • два горизонтально выровненных групповых блока;
      • ...

Как последнее замечание, я не думаю, что вам нужно использовать прокси-стиль для настройки размера прямоугольника: как вы можете видеть, текст обрезается, и это связано с тем, как вы рисуете и вращаетесь, что также приводит к к окраске вопроса о вкладке фона. Удалите размеры из таблицы стилей и прокси-стиля, затем используйте эти значения размеров в пределах 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

Поскольку существует небольшое перекрытие между панелью вкладок и содержимым вкладок, вы можете убедиться, что они правильно выровнены установив смещение псевдоэлементов ::pane и ::tab-bar:

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

Кроме того, убедитесь, что таблица стилей верхнего кадра применяется только к объектам QFrame (и, возможно, к имени объекта), в противном случае к каждому дочернему виджету будет наследовать.

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

Имея все это в виду, вы получите более чистый внешний вид, правильное отображение и расположение виджета / панели вкладок, гибкость компоновки и, что самое важное, возможность редактировать интерфейс на лету без дальнейших проблем и головной боли: -)

woooo, that's cool!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...