Pyside6(3): 自动生成UI的Qt参数输入对话框

这篇具有很好参考价值的文章主要介绍了Pyside6(3): 自动生成UI的Qt参数输入对话框。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.前言

参数输入界面是桌面软件开发最繁琐的部分之一。特别是当系统中存在多种可编辑的数值模型时,由于各个模型的字段不同,每个字段的输入类型也不同,需要制作不同的UI,使用不同的UI控件,无疑会耗费大量时间,同时拓展性也非常差,因为每增加一个数值模型,都需要制作相应的参数输入界面。

本文提出的解决思路是将数值模型假定为类似于json的树形结构,采用递归遍历树的方式生成对应的Qt 控件树,从而实现根据不同的数值模型动态生成参数输入界面。当用户编辑完数值模型点击ok按钮时,再次遍历生成的Qt控件树,从而获取相应的输入数据。另外还具有合法值校验的功能,可以定位到非法输入对应的控件并以红色边框高亮显示的功能。效果如下:

pyside6 输入框,Qt,qt,pyside6,自动UI生成,Powered by 金山文档

2.将数值模型解析为Qt控件树

2.1 输入参数类型

本文涉及的数值模型字段包含字符串浮点数,整数布尔值,枚举值(用list表示)复合字段几种类型。参数类型和Qt控件之间的对应关系如下:

  • 字符串-QLineEdit

  • 浮点数- QDoubleSpinBox

  • 整数-QSpinBox

  • 布尔值-QCheckBox

  • 枚举值-QComboBox

  • 复合字段可以又可以分解成上述几种数据类型,所以对应于一个包含多个前述几种控件的QWidget。

2.2 可编辑数值模型定义

本文假设数值模型可以是不同的类,但是它们都有一个editableField属性,定义可编辑的字段信息。例如:

class A:
    @property
    def editableField(self) -> dict:
        return {
            "name": {
                "name": "名称",
                "value": "",
                "required": True
            },
            "B": {
                "name": "复合字段B",
                "value": B()
            },
        }


class B:
    @property
    def editableField(self) -> dict:
        return {
            "string": {
                "name": "字符串",
                "value": "111111"
            },
            "bool": {
                "name": "布尔值",
                "value": False,
                "required": True
            },
            "float": {
                "name": "浮点值",
                "value": 1.0,
                "unit": "km",
                "min": 0,
                "max": -1
            },
            "int": {
                "name": "整数",
                "value": 1,
                "unit": "km",
                "min": 0,
                "max": -1
            },
            "dataObj": {
                "name": "复合字段C",
                "value": C(),
                "required": True
            },
            "enum": {
                "name": "枚举值",
                "value": [1, 2, 3],
                "required": True
            }
        }


class C:
    @property
    def editableField(self) -> dict:
        return {
            "name": {
                "name": "名称",
                "value": "",
                "required": True
            },
            "gender": {
                "name": "性别",
                "value": "",
                "required": True
            }
        }


class Model:
    @property
    def editableField(self) -> dict:
        return {
            "name": {
                "name": "名称",
                "value": "",
                "required": False
            },
            "he": {
                "name": "人",
                "value": A(),
                "root": True
            },
        }

注意

editableField中定义的所有复合字段不能相互嵌套,比如B包含一个C类型的可编辑字段,C又包含一个B类型的可编辑字段,会导致解析时陷入无限循环。

2.3 对话框基本结构

对话框的基本定义如下:

from PySide6.QtGui import Qt
from PySide6.QtWidgets import QDialog, QMessageBox, QWidget, QTabWidget, QSpacerItem, QSizePolicy, QFormLayout, \
    QScrollArea, QFrame, QDialogButtonBox

from editDialog_utils import isDataObj, generate_widget, getContentLayout, checkInput, showErrorInputWidget


class AEditDialog(QDialog):
    def __init__(self, dataObj):
        """
        数据对象编辑对话框
        :param dataObj:  数据对象
        """
        super().__init__()
        self.__dataObj = dataObj
        # 用户数输入数据
        self.inputData = None
        self.resize(480, 360)
        # 控件树
        self.widget_tree = dict()
        self.__loadWidget()
        self.__connectWidget()

    def __loadWidget(self):
        """
        初始化控件
        :return:
        """
        tab = QTabWidget()
        self.tab = tab
        tab1 = QWidget()
        tab1.setLayout(getContentLayout(direction='v'))
        # 滚动视图
        tab1_scroll = QScrollArea()
        tab1_scroll.setObjectName(u"scroll")
        tab1_scroll.setWidgetResizable(True)
        tab1_scroll.setFrameShadow(QFrame.Raised)
        tab1.layout().addWidget(tab1_scroll)
        tab1_scrollArea = QWidget()
        tab1_layout = QFormLayout(tab1_scrollArea)
        tab.addTab(tab1, "common")
        # 控件所处tab索引,用于错误提示
        tab_index = 0
        # 根据数据对象动态生成ui
        for key, item in self.__dataObj.editableField.items():
            # 值
            value = item["value"]
            if isDataObj(value):
                # 顶级属性是数据对象时生成一个tab
                sub_tree = dict()
                self.widget_tree[key] = sub_tree
                # 新建tab
                tab2 = QWidget()
                tab2.setLayout(getContentLayout(direction='v'))
                # 布局和滚动视图
                scrollAreaWidgetContents = QWidget()
                content_layout = getContentLayout(direction='v')
                tab_index += 1
                # 添加控件
                w = generate_widget(sub_tree, key, item, tab_index)
                content_layout.addWidget(w)
                scrollAreaWidgetContents.setLayout(content_layout)
                # spacer
                vSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
                content_layout.addItem(vSpacer)
                # 滚动视图
                scroll = QScrollArea()
                scroll.setObjectName(u"scroll")
                scroll.setFrameShadow(QFrame.Raised)
                scroll.setWidgetResizable(True)
                tab2.layout().addWidget(scroll)
                scroll.setWidget(scrollAreaWidgetContents)
                # 添加tab
                tab.addTab(tab2, item["name"])
            else:
                w = generate_widget(self.widget_tree, key, item, 0)
                if isinstance(w, tuple):
                    tab1_layout.addRow(w[0], w[1])
                else:
                    tab1_layout.addWidget(w)
        # 添加控件到tab1 滚动视图
        tab1_scroll.setWidget(tab1_scrollArea)
        # 外部容器
        container_layout = getContentLayout(direction='v', margin=(4, 4, 4, 4))
        container_layout.addWidget(tab)
        # 添加底部按钮
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName(u"buttonBox")
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
        container_layout.addWidget(self.buttonBox)
        # 设置布局
        self.setLayout(container_layout)
        # 只有一页,隐藏tabbar
        if tab.count() == 1:
            tab.tabBar().setVisible(False)

    def __connectWidget(self):
        """
        连接槽函数
        :return:
        """
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

    def accept(self) -> None:
        """
        确定
        :return:
        """
        input_data = dict()
        # 输入数据校验
        inputData = checkInput(self.widget_tree, data_key="", input_data=input_data)
        if "widget" in inputData.keys():
            showErrorInputWidget(errorData=inputData, tab=self.tab)
            # 显示数据错误提示
            QMessageBox.critical(self, "错误", str("{} 必须填写!".format(inputData["name"])))
            return
        self.inputData = inputData
        super().accept()

上述QDialog中,控件树被保存在widget_tree字典中。这里最主要的函数是__loadWidget。这个函数首先创建一个QTabWidget添加到对话框中,然后开始遍历数值模型的editableField进行控件解析。弹字段是基本数据类型时,调用generate_widget生成控件添加到当前tab;当字段是个复合字段时,它会新建一个tab并设置滚动视图,然后将它添加到QTabWidget中。其中generate_widget正是递归遍历数值模型字段生成控件树的方法。

accpet函数是用户点击ok按钮时调用的方法。检查用户输入和高亮显示错误输入的方法checkInputshowErrorInputWidget将在后面介绍。

isDataObjgetContentLayout是辅助方法,分别用于判断复合字段和生成样式统一的QVBoxLayoutQHBoxLayout。定义如下:

def isDataObj(data):
    """
    判断数据是否是基本类型之外的数据对象
    :param data:
    :return:
    """
    return type(data) not in [str, float, int, list, tuple, bool]


def getContentLayout(direction: str = "v", margin: tuple = (0, 0, 0, 0)) -> QVBoxLayout:
    """
    生成存放内容的布局
    :param margin:
    :param direction:
    :return:
    """
    if direction == "v":
        layout = QVBoxLayout()
    else:
        layout = QHBoxLayout()
    layout.setContentsMargins(*margin)
    return layout

2.4 解析得到Qt控件树

首先根据基本数据类型生成控件:

def generateFloatWidget(item: dict) -> QDoubleSpinBox:
    """
    生成浮点数值控件
    :param item:
    :return:
    """
    dSpinbox = QDoubleSpinBox()
    minVal = 0
    maxVal = 0
    if "min" in item.keys():
        minVal = item["min"]
    if "max" in item.keys():
        maxVal = item["max"]
    if minVal > maxVal:
        # 设置无穷大
        maxVal = 1e20
        minVal = -maxVal
    dSpinbox.setMinimum(minVal)
    dSpinbox.setMaximum(maxVal)
    dSpinbox.setValue(item["value"])
    dSpinbox.setMinimumHeight(24)
    return dSpinbox

def generateIntWidget(item: dict) -> QSpinBox:
    """
    生成整数控件
    :param item:
    :return:
    """
    spinbox = QSpinBox()
    minVal = 0
    maxVal = 0
    if "min" in item.keys():
        minVal = item["min"]
    if "max" in item.keys():
        maxVal = item["max"]
    if minVal > maxVal:
        # 设置无穷大
        maxVal = int(1e9)
        minVal = -maxVal
    spinbox.setMinimum(minVal)
    spinbox.setMaximum(maxVal)
    spinbox.setValue(item["value"])
    spinbox.setMinimumHeight(24)
    return spinbox


def generateStrWidget(item: dict) -> QLineEdit:
    """
    生成单行文本输入控件
    :param item:
    :return:
    """
    lineEdit = QLineEdit()
    lineEdit.setMinimumHeight(24)
    value = item["value"]
    lineEdit.setText(value)
    return lineEdit


def generateBoolWidget(item: dict) -> QCheckBox:
    """
    生成布尔值输入控件
    :param item:
    :return:
    """
    checkBox = QCheckBox()
    name = item["name"]
    value = item["value"]
    checkBox.setChecked(value)
    checkBox.setText(name)
    return checkBox


def generateEnumWidget(item: dict) -> QComboBox:
    """
    生成枚举值对应的列表控件
    :param item:
    :return:
    """
    comboBox = QComboBox()
    comboBox.setMinimumHeight(24)
    value = item["value"]
    value = [str(i) for i in value]
    comboBox.addItems(value)
    return comboBox

然后递归遍历数值模型字段:

# 展开和收起图标
ic_down_arrow = "ic_down_arrow.svg"
ic_right_arrow = "ic_right_arrow.svg"

def generate_widget(widget_tree: dict, key, item, tab_index: int):
    """
    根据数据对象生成控件
    :param tab_index: 顶层tab索引
    :param item: 数据对象
    :param key: 数据键
    :param widget_tree: 控件树
    :return:
    """
    # 数据项名称
    name = item["name"]
    # 值
    value = item["value"]
    # 单位
    unit = None
    if "unit" in item.keys():
        # 单位
        unit = item["unit"]

    # 控件容器
    w = QWidget()
    w.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

    # 字段名称和单位
    if unit is not None:
        label = QLabel("{}({}):".format(name, unit))
    else:
        label = QLabel("{}:".format(name))
    label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)

    required = False
    if "required" in item.keys():
        # 必填字段
        required = item["required"]

    widget_tree_node = {"tab_index": tab_index, "required": required, "name": name}

    if type(value) is float:
        doubleSpinBox = generateFloatWidget(item)
        widget_tree_node["widget"] = doubleSpinBox
        widget_tree[key] = widget_tree_node
        return label, doubleSpinBox

    if type(value) is int:
        intSpinBox = generateIntWidget(item)
        widget_tree_node["widget"] = intSpinBox
        widget_tree[key] = widget_tree_node
        return label, intSpinBox

    if type(value) is str:
        lineEdit = generateStrWidget(item)
        widget_tree_node["widget"] = lineEdit
        widget_tree[key] = widget_tree_node
        return label, lineEdit

    if type(value) is bool:
        checkBox = generateBoolWidget(item)
        widget_tree_node["widget"] = checkBox
        widget_tree[key] = widget_tree_node
        return checkBox

    if type(value) is list:
        comboBox = generateEnumWidget(item)
        widget_tree_node["widget"] = comboBox
        widget_tree[key] = widget_tree_node
        return label, comboBox

    if isDataObj(value):
        # 复合字段容器
        container = QWidget()
        container_layout = getContentLayout(direction='v')
        container_layout.setSpacing(0)
        container.setLayout(container_layout)
        # 子控件容器
        contentWidget = QWidget()
        contentWidget.setObjectName(u"contentWidget")
        if "root" not in item.keys() or not item['root']:
            # 非顶级对象添加收放按钮
            btnCollapse = QPushButton()
            btnCollapse.setObjectName(u"btnCollapse")
            btnCollapse.setLayout(getContentLayout(direction='h'))
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            btnCollapse.setSizePolicy(sizePolicy)
            btnCollapse.setFocusPolicy(Qt.NoFocus)
            btnCollapse.setFlat(True)
            # 图标
            iconBtn = QPushButton(btnCollapse)
            iconBtn.setObjectName(u"iconBtn")
            iconBtn.setFlat(True)
            iconBtn.setIconSize(QSize(20, 20))
            iconBtn.setIcon(QIcon(ic_down_arrow))
            btnCollapse.layout().addWidget(iconBtn)
            # 名称
            labelName = QLabel(name)
            labelName.setStyleSheet("font-size:12pt")
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            labelName.setSizePolicy(sizePolicy)
            btnCollapse.layout().addWidget(labelName)
            container.layout().addWidget(btnCollapse)
            # 收放槽函数
            iconBtn.clicked.connect(partial(collapseWidget, iconBtn, contentWidget))
            btnCollapse.clicked.connect(partial(collapseWidget, iconBtn, contentWidget))

        contentWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        contentWidget_layout = QFormLayout()
        # formlayout换行策略
        contentWidget_layout.setRowWrapPolicy(QFormLayout.WrapLongRows)
        contentWidget.setLayout(contentWidget_layout)
        # 添加子控件
        for key, item in value.editableField.items():
            sub_tree = dict()
            widget_tree[key] = sub_tree
            w = generate_widget(sub_tree, key, item, tab_index)
            if isinstance(w, tuple):
                # 子控件有label
                contentWidget_layout.addRow(w[0], w[1])
            else:
                # 无label,跨列
                row_index = contentWidget_layout.rowCount()
                contentWidget_layout.setWidget(row_index, QFormLayout.SpanningRole, w)
        container.layout().addWidget(contentWidget)
        return container

上述函数中,当字段类型是基本数据类型时,生成上述几种基本控件,添加到控件树中;当字段是复合字段时,新建一个QWidget作为容器,向其中添加一个收放按钮,然后新建一个contentWidget,递归生成子控件放入其中。每次遇到一个复合字段类型就向控件树中添加一个树枝,即sub_tree字典。函数collapseWidget用来收放控件,定义为:

def collapseWidget(iconBtn: QPushButton, collapsedWidget: QWidget, hold: bool = None):
    """
    收放控件
    :param hold: 保持某个状态
    :param iconBtn: 指示图标控件
    :param collapsedWidget:
    :return:
    """
    show = not collapsedWidget.isVisible()
    if hold is not None:
        show = hold
    if show:
        icon = QIcon(ic_down_arrow)
    else:
        icon = QIcon(ic_right_arrow)
    collapsedWidget.setVisible(show)
    iconBtn.setIcon(icon)

3.检查并获取用户输入

3.1 遍历控件树获取并检查用户输入

获取用户输入其实就是把控件树,即widget_tree这个字典再遍历一遍。首先定义获取单个控件输入的函数:

def getWidgetInput(widget):
    """
    获取单个控件输入
    :param widget:
    :return:
    """
    if type(widget) == QLineEdit:
        return widget.text()
    if type(widget) == QCheckBox:
        return widget.isChecked()
    if type(widget) == QDoubleSpinBox:
        return widget.value()
    if type(widget) == QSpinBox:
        return widget.value()
    if type(widget) == QComboBox:
        return widget.currentText()

然后定义检查单个控件输入的函数:

def checkWidget(widget, required: bool) -> bool:
    """
    检查单个控件输入
    :param widget:
    :param required: 是否必填
    :return:
    """
    if not required:
        # 非必填
        return True
    # 检查必填
    filled = False
    if type(widget) == QLineEdit:
        filled = widget.text() != ""
    if type(widget) == QCheckBox:
        filled = widget.isChecked()
    if type(widget) == QComboBox:
        filled = widget.currentText() != ""
    return filled

然后递归遍历控件树,获取并检查用户输入:

def checkInput(widget_tree: dict, data_key: str, input_data: dict):
    """
    获取并检查用户输入
    :param input_data: 存储输入数据
    :param data_key: 数据键,a.b.c
    :param widget_tree: 控件树
    :return:
    """
    for key, value in widget_tree.items():
        if "widget" not in value.keys():
            # value是个树节点
            if data_key != "":
                key = "{}.{}".format(data_key, key)
            input_data = checkInput(value, key, input_data)
            if "widget" in input_data.keys():
                # 出错终止
                return input_data
        else:
            # value是叶
            widget = value["widget"]
            tab_index = value["tab_index"]
            required = value["required"]
            name = value["name"]
            if not checkWidget(widget, required):
                # 边框显示为红色
                widget.setStyleSheet("border:1px solid red;")
                # 检查到输入错误,切换到出错的那个tab
                return {"name": name, "widget": widget, "tab_index": tab_index}
            else:
                # 清空错误样式
                widget.setStyleSheet(QWidget().styleSheet())

            if data_key == "":
                # 顶层数据字段
                input_data[key] = getWidgetInput(widget)
            else:
                # 控件树叶的数据字段
                input_data[data_key] = getWidgetInput(widget)
    return input_data

获取到的用户输入如下:

{
  'name': 'comm名称',
  'he.name': '人名',
  'he.dataObj_B.string': '111111',
  'he.dataObj_B.bool': True,
  'he.dataObj_B.float': 1.0,
  'he.dataObj_B.int': 1,
  'he.dataObj_B.dataObj_C.name': 'C名称',
  'he.dataObj_B.dataObj_C.gender': 'C性别',
  'he.dataObj_B.enum': '1'
}

因为python对象有setattr函数可以设置属性,所以这里用户输入不用嵌套成树形结构,可以少用递归,改用循环,提高运行速度,具体见第4节。

3.2 定位并高亮显示错误输入

当发现某个控件存在非法输入时,checkInput函数停止递归并返回出错控件的信息,例如:

{
  'name': '名称',
  'widget': <PySide6.QtWidgets.QLineEdit(0x1b9b2830)at 0x000000001C4A9200>,
  'tab_index': 1
}

其中tab_index表示该控件所处的tab索引。高亮显示错误输入位置的函数如下:

def showErrorInputWidget(errorData: dict, tab: QTabWidget):
    """
    显示输入错误的控件
    :param tab: 顶层tab
    :param errorData: 错误数据
    :return:
    """
    # 切换到所处tab
    tab.setCurrentIndex(errorData["tab_index"])
    # 取出对应错误控件
    widget = errorData["widget"]
    if not widget.isVisible():
        # 展开未展开的父节点显示控件
        parent = widget
        while True:
            parent = parent.parent()
            if parent is None:
                break
            # 查找下拉图标按钮和收放控件
            iconBtn = parent.findChild(QPushButton, "iconBtn")
            contentWidget = parent.findChild(QWidget, u"contentWidget")
            if contentWidget is not None and not contentWidget.isVisible():
                #展开
                collapseWidget(iconBtn, contentWidget, hold=True)

    # 滚动到控件所在位置
    tab_page = tab.currentWidget()
    scrollArea = tab_page.findChild(QScrollArea, "scroll")
    pointTab = tab_page.mapToGlobal(QPoint(0, 0))
    # 计算控件是否在滚动区可视范围内
    y1 = pointTab.y()
    h1 = tab_page.height()
    pointW = widget.mapToGlobal(QPoint(0, 0))
    y2 = pointW.y()
    h2 = widget.height()
    cond1 = y2 + h2 < y1 - 4
    cond2 = y1 + h1 < y2 + 4
    dy = 0
    # 计算滚动距离
    if cond1:
        dy = y2 - y1 - 4
    if cond2:
        dy = (y2 + h2) - (y1 + h1) + 4
    # 滚动到错误控件
    verticalScrollBar = scrollArea.verticalScrollBar()
    verticalScrollBar.setValue(verticalScrollBar.value() + dy)

4.将用户输入写入数值模型对象

将用户输入写入数值模型对象的函数如下,由于Python是个解释型语言,可以动态为对象设置属性,所以这里用了循环而不是递归来提高效率。

class Field:
    """
    字段属性
    """


def setField(self, data: dict):
    """
    设置字段
    :param self: 数值模型
    :param data:
    :return:
    """
    for key, value in data.items():
        if "." not in key:
            setattr(self, key, value)
            continue
        # 根据键定位到复合字段类型
        dataObj = self
        sub_keys = str(key).split(".")
        for sub_key in sub_keys[:-1]:
            try:
                dataObj = getattr(dataObj, sub_key)
            except AttributeError:
                # 没有预创建该属性则新建属性
                field = Field()
                setattr(dataObj, sub_key, field)
                dataObj = field
        setattr(dataObj, sub_keys[-1], value)

可以采用如下方式统一为数值模型添加setField方法:

class A:
    def __init__(self):
        self.setField = partial(setField, self)


class B:
    def __init__(self):
        self.setField = partial(setField, self)


class C:
    def __init__(self):
        self.setField = partial(setField, self)



class Model:
    def __init__(self):
        self.setField = partial(setField, self)

运行结果如下:文章来源地址https://www.toymoban.com/news/detail-768140.html

pyside6 输入框,Qt,qt,pyside6,自动UI生成,Powered by 金山文档
pyside6 输入框,Qt,qt,pyside6,自动UI生成,Powered by 金山文档
pyside6 输入框,Qt,qt,pyside6,自动UI生成,Powered by 金山文档

到了这里,关于Pyside6(3): 自动生成UI的Qt参数输入对话框的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity编辑器扩展——自动生成UI界面脚本

    一:前言 对于面板赋值或Find绑定UI组件,我们可以使用一种工具化的方式去自动生成代码并绑定对象,增加效率 分为logic和view,view层是UI界面上的组件,每次都会自动生成并覆盖,logic层是逻辑 二:使用 例如一个UI界面,我们只需要做成预制体并在Project下右键预制体,选择

    2024年02月11日
    浏览(31)
  • Python Qt PySide6简介

    自今天起开学学习教程,有网页介绍,有视频,非常的详细。 现将主要内容摘录如下: (结合自己的实际情况,略有增删和变动)(采用边实践边写的模式) 如果用  Python  语言开发  跨平台  的图形界面的程序,主要有3种选择: Tkinter 基于Tk的Python库,这是Python官方采用

    2024年02月14日
    浏览(32)
  • 【vscode】生成函数参数@param注释 及 自动添加头注释和函数注释

    自动添加头部、函数注释 方法一:输入/**,IDE会自动弹出完整的多行注释demo: 方法二:下载安装koroFileHeader, 一个vscode插件, 用于生成文件头部注释和函数注释的插件,效果如下: 用户设置文件settings.json,输入以下配置: 配置完成保存后, 重启vscode 完成插件环境生成。

    2024年02月13日
    浏览(38)
  • postman中使用formdata数据获取参数编写pre-request Script获取参数自动生成sign

    postman中header部分变量利用脚本生成相应的数据 timestamp:通过脚本获取生成的变量 sign:通过脚本获取生成的变量 body中的参数是form-data格式: 所有的参数需要根据实际的接口参数填写   程序片段: 由于本人的接口的加密方式是入参的参数按照字母排序然后加密字符串,所以

    2024年02月11日
    浏览(47)
  • 【无标题】PySide6在非UI线程更新UI界面实例

            写一个SonThread类继承于QThread,直接在run函数中写我们要执行的动作(更新UI界面)。优点比较简单易懂,缺点只能被一种事件使用,不能重复利用。         使用threading来创建新线程,在新线程中使用SonThread来更新UI界面。只用写一个更新UI界面的类,可以被很多的

    2024年02月16日
    浏览(40)
  • Selenium IDE :安装、配置、录制 轻松生成UI自动化测试脚本

    Selenium IDE 是一个用于 Web 测试的集成开发环境。是 Chrome 和 Firefox 的插件,可以记录和回放与浏览器的交互过程。 可以从SeleniumIDE官方地址下载:https://www.selenium.dev/selenium-ide/ 打开浏览器,这里使用的是chrome浏览器。选择\\\"更多工具扩展程序\\\" **注意:**必须要打开“开发者模式

    2024年02月14日
    浏览(42)
  • PySide6 将.ui文件编译为.py文件

    1. 制作UI 平时使用QT creator设计器设计UI,然后可将其转化成 .py 文件,以供 .py 脚本使用。 2.Pyside6 转换ui为 .py 文件 pyside6使用过程: 生成的py文件: 3. .py 文件国际化 通过pyside6-uic生成的 .py 代码中,中文或文本将使用unicode表示,即:“机器人设置”(u\\\"u673au5668u4ebau8bbeu

    2024年02月12日
    浏览(39)
  • 使用pyside6将ui文件转换为py

    在MainWindow.ui文件所在文件夹搜索栏输入cmd,点击回车键; 示意图如下: 直接输入下述代码: 示意图如下:

    2024年04月23日
    浏览(22)
  • Pyside6入门教学——编写一个UI界面并显示

    1、安装Pyside6 输入下列命令安装Pyside6。 2、设计UI 打开Qt设计工具(在安装Pyside6包的目录下)。 【注】我这用的是anaconda虚拟环境,所以我的路径是D:AppAnaconda3envssnakeLibsite-packagesPySide6。 设计一个界面,如何设计这里不详细讲解。 设计完后保存,得到一个ui文件。(另存

    2024年01月20日
    浏览(33)
  • STM32G070RBT6基于STM32CubeMX自动生成ADC输入电压采样工程全过程讲解

    📌相关篇《【硬件开源电路】STM32G070RBT6开发板》 🛠STM32CubeMX工程配置过程演示 📢Keil工程完成业务代码以及烧录演示放在资源中,gif文件过大无法上传。 📚工程概要 本工程是基于STM32CubeMX工具来配置项目,功能需求:在PA0引脚接入一个外部模拟量信号(单片机能承受的电

    2024年02月01日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包