关于参数的界面化

关于参数的界面化

开始

最开始,可能有不少人写的python源码都是跟我一样,将所有的变量都写死在源码里面(比如我的上一篇博客:用 matplotlib 绘制会动的雪花 就是这样的。),每次需要修改都是直接改源码的。但这样总感觉哪里不对的说。
接着,我知道了启动带参数的用法(python a.py 参数),发现了argparse,这个会自动处理命令行参数,并格式化,而且带帮助提示,终于可以在源码里面大胆用参数了。
终于有一天,我脚本里写的处理argparse部分超过了100行!虽然我这个是因为pep8的原因,将代码进行处理之后的结果。如果不用pep8处理,也有20行了。
我当时很惊讶,然后我关上了那个源码的编辑界面。嗯,刚刚啥都没发生,嗯。
终于,在这几天,我发现了一个利器,终于可以再次正视那个问题。

源码

#!/usr/bin/python
# vim: set fileencoding=utf8 :

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui

app = QtGui.QApplication([])
import pyqtgraph.parametertree.parameterTypes as pTypes
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType


# 这一组变量会同时变化, a和b的值互为倒数
class ComplexParameter(pTypes.GroupParameter):
    def __init__(self, **opts):
        opts['type'] = 'bool'
        opts['value'] = True
        pTypes.GroupParameter.__init__(self, **opts)

        self.addChild({
            'name': 'A = 1/B',
            'type': 'float',
            'value': 7,
            'suffix': 'Hz',
            'siPrefix': True
        })
        self.addChild({
            'name': 'B = 1/A',
            'type': 'float',
            'value': 1 / 7.,
            'suffix': 's',
            'siPrefix': True
        })
        self.a = self.param('A = 1/B')
        self.b = self.param('B = 1/A')
        self.a.sigValueChanged.connect(self.aChanged)
        self.b.sigValueChanged.connect(self.bChanged)

    def aChanged(self):
        self.b.setValue(1.0 / self.a.value(), blockSignal=self.bChanged)

    def bChanged(self):
        self.a.setValue(1.0 / self.b.value(), blockSignal=self.aChanged)


# 这个用来实时添加项目
class ScalableGroup(pTypes.GroupParameter):
    def __init__(self, **opts):
        opts['type'] = 'group'
        opts['addText'] = "Add"
        opts['addList'] = ['str', 'float', 'int']
        pTypes.GroupParameter.__init__(self, **opts)

    def addNew(self, typ):
        val = {'str': '', 'float': 0.0, 'int': 0}[typ]
        self.addChild(
            dict(
                name="ScalableParam %d" % (len(self.childs) + 1),
                type=typ,
                value=val,
                removable=True,
                renamable=True))


params = [
    {
        'name':
        'Basic parameter data types',
        'type':
        'group',
        'children': [
            {
                'name': 'Integer',
                'type': 'int',
                'value': 10
            },
            {
                'name': 'Float',
                'type': 'float',
                'value': 10.5,
                'step': 0.1
            },
            {
                'name': 'String',
                'type': 'str',
                'value': "hi"
            },
            {
                'name': 'List',
                'type': 'list',
                'values': [1, 2, 3],
                'value': 2
            },
            {
                'name': 'Named List',
                'type': 'list',
                'values': {
                    "one": 1,
                    "two": "twosies",
                    "three": [3, 3, 3]
                },
                'value': 2
            },
            {
                'name': 'Boolean',
                'type': 'bool',
                'value': True,
                'tip': "This is a checkbox"
            },
            {
                'name': 'Color',
                'type': 'color',
                'value': "FF0",
                'tip': "This is a color button"
            },
            {
                'name': 'Gradient',
                'type': 'colormap'
            },
            {
                'name':
                'Subgroup',
                'type':
                'group',
                'children': [
                    {
                        'name': 'Sub-param 1',
                        'type': 'int',
                        'value': 10
                    },
                    {
                        'name': 'Sub-param 2',
                        'type': 'float',
                        'value': 1.2e6
                    },
                ]
            },
            {
                'name': 'Text Parameter',
                'type': 'text',
                'value': 'Some text...'
            },
            {
                'name': 'Action Parameter',
                'type': 'action'
            },
        ]
    },
    {
        'name':
        'Numerical Parameter Options',
        'type':
        'group',
        'children': [
            {
                'name': 'Units + SI prefix',
                'type': 'float',
                'value': 1.2e-6,
                'step': 1e-6,
                'siPrefix': True,
                'suffix': 'V'
            },
            {
                'name': 'Limits (min=7;max=15)',
                'type': 'int',
                'value': 11,
                'limits': (7, 15),
                'default': -6
            },
            {
                'name': 'DEC stepping',
                'type': 'float',
                'value': 1.2e6,
                'dec': True,
                'step': 1,
                'siPrefix': True,
                'suffix': 'Hz'
            },
        ]
    },
    {
        'name':
        'Save/Restore functionality',
        'type':
        'group',
        'children': [
            {
                'name': 'Save State',
                'type': 'action'
            },
            {
                'name':
                'Restore State',
                'type':
                'action',
                'children': [
                    {
                        'name': 'Add missing items',
                        'type': 'bool',
                        'value': True
                    },
                    {
                        'name': 'Remove extra items',
                        'type': 'bool',
                        'value': True
                    },
                ]
            },
        ]
    },
    {
        'name':
        'Extra Parameter Options',
        'type':
        'group',
        'children': [
            {
                'name': 'Read-only',
                'type': 'float',
                'value': 1.2e6,
                'siPrefix': True,
                'suffix': 'Hz',
                'readonly': True
            },
            {
                'name': 'Renamable',
                'type': 'float',
                'value': 1.2e6,
                'siPrefix': True,
                'suffix': 'Hz',
                'renamable': True
            },
            {
                'name': 'Removable',
                'type': 'float',
                'value': 1.2e6,
                'siPrefix': True,
                'suffix': 'Hz',
                'removable': True
            },
        ]
    },
    ComplexParameter(name='Custom parameter group (reciprocal values)'),
    ScalableGroup(
        name="Expandable Parameter Group",
        children=[
            {
                'name': 'ScalableParam 1',
                'type': 'str',
                'value': "default param 1"
            },
            {
                'name': 'ScalableParam 2',
                'type': 'str',
                'value': "default param 2"
            },
        ]),
]

# 创建Parameter对象
p = Parameter.create(name='params', type='group', children=params)


def change(param, changes):
    # 这个用来监视输出哪个项改变了
    print("tree 改变:")
    for param, change, data in changes:
        path = p.childPath(param)
        if path is not None:
            childName = '.'.join(path)
        else:
            childName = param.name()
        print('  变量: %s' % childName)
        print('  改变:    %s' % change)
        print('  数据:      %s' % str(data))
        print('  ----------')


p.sigTreeStateChanged.connect(change)


def valueChanging(param, value):
    # 这个监视正在改变,但是没有改变完成的
    print(" %s值正在变化(没有完成): %s" % (param, value))


# Too lazy for recursion:
# 将所有对象的信号连接到相应的函数
for child in p.children():
    child.sigValueChanging.connect(valueChanging)
    for ch2 in child.children():
        ch2.sigValueChanging.connect(valueChanging)


def save():
    # 这里可以将数据state存储到文件里,可以重新读入
    state = p.saveState()
    fn = pg.QtGui.QFileDialog.getSaveFileName(None, "保存到文件..", "conf/gui.cfg",
                                              "Config Files (*.cfg)")
    fn, _ = fn
    if not fn:
        return
    pg.configfile.writeConfigFile(state, fn)


def restore():
    global state
    add = p['Save/Restore functionality', 'Restore State', 'Add missing items']
    rem = p['Save/Restore functionality', 'Restore State',
            'Remove extra items']
    p.restoreState(state, addChildren=add, removeChildren=rem)


p.param('Save/Restore functionality', 'Save State').sigActivated.connect(save)
p.param('Save/Restore functionality',
        'Restore State').sigActivated.connect(restore)

# 创建两个相同的对象,他们应该显示同样的值
t = ParameterTree()
t.setParameters(p, showTop=False)
t.setWindowTitle('Parameter Tree例子')
t2 = ParameterTree()
t2.setParameters(p, showTop=False)
# 用来存储初始值,方便重置
state = p.saveState()

win = QtGui.QWidget()
layout = QtGui.QGridLayout()
win.setLayout(layout)
layout.addWidget(QtGui.QLabel("两边显示同样的数据. 它们一直显示同样的值."), 0, 0, 1, 2)
layout.addWidget(t, 1, 0, 1, 1)
layout.addWidget(t2, 1, 1, 1, 1)
win.show()
win.resize(800, 800)

## test save/restore
s = p.saveState()
p.restoreState(s)

## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

界面

在这里插入图片描述

源码说明

以上代码是pyqtgraph里面关于parametertree的示例代码,我只把一点英文改成了中文而已。这个示例大家运行起来就知道那些功能都有什么了。初始的功能有:

  1. 限制变量的类型
  2. 可以方便的设定颜色,只要一行代码
  3. 指定设置的值的范围,在定义的时候加上’limits’: (最小值, 最大值)
  4. 如果参数只有有限的几个值,只要设置类型为list, 然后在values里面指定就行
  5. 可以非常方便的自定义自己特定的一组项目
  6. 可以任意嵌套,包括自定义的

我的一个自定义

因为经常用到划图表,画线条,所以自定义了一个1

class Line(pTypes.GroupParameter):
    def __init__(self, **opts):
        opts['type'] = 'group'
        opts['value'] = True
        super().__init__(**opts)
        print(opts)
        aa = {"name": "_check", "type": "bool", "value": True}
        title = {}
        title['title'] = opts["title"] if opts.get("title") else opts["name"]
        self.addChild({**opts, **aa, **title})
        self.addChild({
            "name":
            "_moresit",
            "title":
            "设置",
            "type":
            "group",
            "visible":
            True,
            "children": [{
                "name": "width",
                "title": "线宽",
                "type": "float",
                "value": 1.5,
                "step": 0.5
            },
                         {
                             "name": "color",
                             "type": "color",
                             "title": "颜色",
                             "value": "993399"
                         }]
        })
        self.a = self.param("_check")
        self.b = self.param("_moresit")
        self.a.sigValueChanged.connect(self.ischange)

    def ischange(self):
        # 如果a被选择则添加后面的设置,否则删除.
        if self.a.value():
            self.addChild(self.b)
        else:
            self.b.remove()

这个可以实现根据需要自动增删后面的设置。比如,一个线条要显示的时候,才需要定义后面的线条的宽度,颜色,如果不显示,则不需要。

如何获取自己想要的变量的值

将示例里面的params改成下面这样:

params = [{
    "name":
    "a",
    "type":
    "group",
    "children": [{
        "name": "b",
        "type": "int",
        "value": 10
    }, Line(name="c")]
},
          {
              "name":
              "保存/重置",
              "type":
              "group",
              "children": [{
                  "name": "保存",
                  'type': 'action'
              },
                           {
                               "name":
                               "重置",
                               "type":
                               "action",
                               'children': [
                                   {
                                       'name': '添加删除项目',
                                       'type': 'bool',
                                       'value': True
                                   },
                                   {
                                       'name': '移除添加项目',
                                       'type': 'bool',
                                       'value': True
                                   },
                               ]
                           }]
          }]

那么, 读取b的值只要 p[“a”, “b”]就行了。


  1. 这段代码里面用到了一个有用的技巧,那就是字典合并, 即代码中的{**opts, **aa, **title}。里面的opts、 aa、 title都是字典,依次用后面的字典内容更新前面的字典。
    这个有用的地方很多,比如,最前面的是默认设置,后面的是用户配置,用户没有配置的项目就是默认值。 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值