PyQt5桌面应用开发(4):界面设计

PyQt5桌面应用系列

前言

经过需求分析、事件循环和并行设计的介绍,终于还是要写一篇界面设计的内容。很不情愿,因为我这个部分很不擅长,设计的界面很丑很丑。

其实丑并不可怕,关了灯都一样……

首先,我们再回到需求分析的话题。

为什么又是需求分析?

需求分析,这条内裤,始终是要穿上的。

那么界面设计的内裤大概是什么样的呢,有很多人是从美观的角度出发设计出漂亮的界面。我们搬砖的找了一堆支持的理论,还是只能设计出巨丑的界面。一般而言,我们是从用户体验的角度出发,结合软件需求分析的过程来确定界面。
用户体验设计的蜂巢模型
从用户需求这里,基本上就能确定数据报表,从功能需求分析这里,就能确定用户交互。这两个部分最终构成用户界面的开发需求。

  1. 报表界面;
  2. 交互界面。

软件用户界面设计的分析过程
等到报表界面和交互界面确定后,就是利用PyQt5提供的工具来实现。

PyQt5的界面设计元素

在采用PyQt5设计界面时,我们常用和标准的工具就是Designer。如何安装和使用Designer,我实在不耐烦讲。网上教程(视频)和参考貌似比较多。就只是在CSDN上,就一大堆,CSDN搜索pyqt5 designer即可。如果英语好,也有更多参考内容,随便举个栗子。我就大概拉一拉界面设计元素,以作参考。

界面设计元素分类

在Designer里面,PyQt5的界面设计元素分为8个大类:

  1. Layouts:界面布局
  2. Spacers:空间间隔
  3. Buttons:动作按钮
  4. Item Views:数据视图
  5. Item Widgets:数据控件
  6. Containers:空间容器
  7. Input Widgets:输入控件
  8. Display Widgets:显示控件

直接使用的话,就大概是按照这这八类来布置。这个分类,基本上是按照控件的功能来分的。使用的时候,我们还应该根据前面所讲的开发需求来进行进一步的分类。
PyQt的UI设计元素
所以我们的分类是这样的:
a.报表功能类(共18个控件)
b.输入功能类(共22个控件)
c.布局功能类(共16个控件)

控件功能分类
各个具体的控件功能,已经一些教程进行详细的指导。

编译为Python代码使用

转换命令行

我们很随意的假设要展示三组数据(报表1:树表列),然后用Designer画了一个控件。

在这里插入图片描述
然后很随意的存成文件tree_data_view.ui,用下面的命令,就可以把界面文件转化成Python文件。这里增加一个参数-x,就给输出的文件增加一个运行的例子。

pyuic5 .\tree_data_view.ui -o .\tree_data_view.py -x

接下来就能运行,显示界面示例。

python .\tree_data_view.ui -o .\tree_data_view.py -x

在这里插入图片描述
跟我们在Designer里面画的界面一模一样。ui文件的源代码也很简单,很容易看懂。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1106</width>
    <height>584</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Item Widgets Demo</string>
  </property>
  <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
   <item>
    <widget class="QTreeWidget" name="treeWidget">
     <column>
      <property name="text">
       <string notr="true">1</string>
      </property>
     </column>
    </widget>
   </item>
   <item>
    <widget class="QTableWidget" name="tableWidget"/>
   </item>
   <item>
    <widget class="QListWidget" name="listWidget"/>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

当然转换得到的Python文件更容易读懂。

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file '.\tree_data_view.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(1106, 584)
        self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.treeWidget = QtWidgets.QTreeWidget(Form)
        self.treeWidget.setObjectName("treeWidget")
        self.treeWidget.headerItem().setText(0, "1")
        self.horizontalLayout.addWidget(self.treeWidget)
        self.tableWidget = QtWidgets.QTableWidget(Form)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)
        self.horizontalLayout.addWidget(self.tableWidget)
        self.listWidget = QtWidgets.QListWidget(Form)
        self.listWidget.setObjectName("listWidget")
        self.horizontalLayout.addWidget(self.listWidget)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Item Widgets Demo"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

一般而言,我们使用这个代码时,有一个原则:不更改由pyuic5生成的代码。

界面设计信息唯一保存于ui文件中。

大概见到的使用这个报表的方法有3种。

组合使用

我们先看pyuic5 -x定义的使用方法,声明一个Ui_Form,默认的名字总是这个;把一个QWidget作为参数调用Ui_Form.setupUi

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

要正经的使用我们的报表,可以设计一个按钮,按按钮就显示报表。

from tree_data_view import Ui_Form
from PyQt5 import QtWidgets, QtCore

class TreeData_View(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(TreeData_View, self).__init__(parent)
        # 设置界面
        self.ui = Ui_Form()
        self.ui.setupUi(self)
		
		# 重新定义变量,为了更好地获得IDE的支持,更容易编写程序
        self.treeWidget: QtWidgets.QTreeWidget = self.ui.treeWidget
        self.listWidget: QtWidgets.QListWidget = self.ui.listWidget
        self.tableWidget: QtWidgets.QTableWidget = self.ui.tableWidget
		
		# fake data
        self._init_tree()
        self._init_table()
        self._init_list()

    def _init_tree(self):
        # prepare data for QTreeWidget
        self.treeWidget.setHeaderLabels(["Name", "Value"])
        self.treeWidget.setColumnCount(2)
        self.treeWidget.setColumnWidth(0, 200)
        self.treeWidget.setColumnWidth(1, 200)
        self.treeWidget.setSortingEnabled(True)
        self.treeWidget.setAlternatingRowColors(True)
        self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)

        root = QtWidgets.QTreeWidgetItem(self.treeWidget)
        root.setText(0, "Root")
        root.setText(1, "Root Value")
        child1 = QtWidgets.QTreeWidgetItem(root)
        child1.setText(0, "Child 1")
        child1.setText(1, "Child 1 Value")
        child2 = QtWidgets.QTreeWidgetItem(root)
        child2.setText(0, "Child 2")
        child2.setText(1, "Child 2 Value")

        grandchild1 = QtWidgets.QTreeWidgetItem(child1)
        grandchild1.setText(0, "Grandchild 1")
        grandchild1.setText(1, "Grandchild 1 Value")
        grandchild2 = QtWidgets.QTreeWidgetItem(child1)
        grandchild2.setText(0, "Grandchild 2")
        grandchild2.setText(1, "Grandchild 2 Value")
        
        self.treeWidget.addTopLevelItem(root)
        self.treeWidget.expandAll()

    def _init_table(self):
        # prepare data for QTableWidget
        self.tableWidget.setColumnCount(2)
        self.tableWidget.setRowCount(3)
        self.tableWidget.setHorizontalHeaderLabels(["Name", "Value"])
        self.tableWidget.setColumnWidth(0, 200)
        self.tableWidget.setColumnWidth(1, 200)
        self.tableWidget.setSortingEnabled(True)
        self.tableWidget.setAlternatingRowColors(True)
        self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.tableWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)

        for row in range(3):
            for col in range(2):
                item = QtWidgets.QTableWidgetItem(f"Item {row}, {col}")
                self.tableWidget.setItem(row, col, item)

    def _init_list(self):
        # prepare data for QListWidget
        self.listWidget.setSortingEnabled(True)
        self.listWidget.setAlternatingRowColors(True)
        self.listWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.listWidget.addItems([f"Item {i}" for i in range(10)])


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    win = QtWidgets.QMainWindow()
    Form = TreeData_View()

    button = QtWidgets.QPushButton("Show TreeData_View")
    button.clicked.connect(lambda: Form.show())
    win.setCentralWidget(button)

    win.show()
    sys.exit(app.exec_())

这个也是相当直观的。

继承使用方式

我们还可以采用继承的方式,仅有小小的不同。

class TreeData_View(QtWidgets.QWidget, Ui_Form):
    def __init__(self, parent=None):
        super(TreeData_View, self).__init__(parent)
        self.setupUi(self)

        self.treeWidget: QtWidgets.QTreeWidget
        self.listWidget: QtWidgets.QListWidget
        self.tableWidget: QtWidgets.QTableWidget

此时,代码稍微简单一点点,可读性也没有变差。

直接使用ui文件的方法

直接使用ui的方法也很简单,也只要更改一点点代码。

from PyQt5 import QtWidgets, QtCore, uic

class TreeData_View(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(TreeData_View, self).__init__(parent)
        uic.loadUi("tree_data_view.ui", self)
        self.treeWidget: QtWidgets.QTreeWidget
        self.listWidget: QtWidgets.QListWidget
        self.tableWidget: QtWidgets.QTableWidget

这里的loadUi函数的第二个参数是一个QWidget对象,如果不输入这个参数,就直接新建一个QWidget。这里值得注意的是,ui文件中定义的控件,直接就赋给了QWidget对象,与组合方式中略有不同,更类似与多继承的方式。

总结

  1. 8类控件可以分为3大类;
  2. 三种使用ui文件的方式;
  3. 每种方式都差不多,为了IDE支持代码,可以把控件的类型都声明一遍。
  • 8
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大福是小强

除非你钱多烧得慌……

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值