uipy


Nameuipy JSON
Version 1.0.3 PyPI version JSON
download
home_pagehttps://github.com/pypa/sampleproject
SummaryA UI library based on PySide 6
upload_time2023-09-18 02:01:18
maintainer
docs_urlNone
authorjerry
requires_python>=3.8
license
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # uipy 编程指南

## 1. 概述

https://pypi.org/project/uipy/

uipy 是基于 pyside 6 的二次封装,包含如下特性:

- MVVM
  
  简单 mvvm 模型,开发更方便。

- .ui 生成
  
  依赖 webmix,用 js 语言撰写原生 .ui 文件

- DOM 操作
  
  模仿 jquery 对组件进行操作

- Dispatcher
  
  模仿 jquery 事件机制,将 pyside 的 Event、Action、Signal+Slot 改为 jquery 事件模式,写起来更顺手

- widget、layout
   
  包含一系列新的组件。

  包含了 FlowLayout 流式布局。

- assets
  
  包含一系列新的资源,比如各种小图标。

- themes

  包含一系列新的主题。

依靠这些特性,编程的感觉就与 javascript 开发类似了。

## 2. 第一步

通过 pypi.org 来安装本包:

`pip install uipy'

或者,通过本包源码编译包。转到源码目录 uipyPackage/uipy 执行命令:

`python compileUIPY.py develop`

```python
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
# 本文示例中 ui 均表示 uipy 模块
import uipy as ui

if __name__ == "__main__":

    # 全局只调用一次,推荐在主.py文件中调用
    ui.init()

    app = QApplication(sys.argv)
    window = QMainWindow()
    # 设定主窗口,方便后续使用,也可以调用 ui.getMW() 来获得
    ui.MW = window
    window.show()

    sys.exit(app.exec())
```

## 3. 功能



### 3.1 派发器

Dispatcher,融合 Action、Event、Signal & Slot,并提供类似 jquery 的使用方法。

- uuid
  唯一编号

- setSignals(signals)
  
  设置字符串类的信号。需要先设置,才能侦听。

  signals 是一个 list,元素可以是字符串(作为信号的名字),则创建 Signal(object),也可以是元组,元组的第一个元素是字符串(作为信号的名字),后接的元素是给 Signal 的参数。

  ```python
  x.setSignals(['change','undo','redo']) # 等于 Signal(object)
  x.setSignals([('change',int)]) # 等于 Signal(int)
  x.setSignals([('change',str,int)]) # 等于 Signal(str,int)
  ```

- on(signal,slot)
  
  侦听信号

- off(signal,slot)
  
  断开侦听

- fire(signal,*args)
  
  触发信号

- mixin(obj)
  
  静态方法,为对象提供 on/off/fire 方法。

简单示例:

```python
# 因 python 没有 js 那种匿名函数,只能这样写
def click1():
    pass

def click2(msg):
    print(msg)

Dispatcher.mixin(label1)

label1.on(label1.clicked,click1)
label1.setSignals(['signal1'])
label1.on('signal1',click2)

# 自定义信号
label1.fire('signal1','hello')
```

信号可以是 Qt 自带的,也可以是自定义的字符串。

在 ViewModel.bind 方法中会自动为组件进行 minxin,无需显式调用。


### 3.2 数据绑定

数据双向绑定器。

ViewModel 作为视图模型基类。

Binder 统管 DataBinder、Observable、ViewModel 三者,并且可以通过 ui.BINDER 全局访问到。

DataBinder 与组件绑定,为组件连通 Observable 提供桥梁,如:

```python
name=ui.Observable('jerry')
label1.databind={'str',name}
```

数据流只有两个方向,一是从值变更触发组件变更,如:

```python
name.set('jerry1979')
```

将会导致 label1 显示的文本改变。

二是从组件本身变更触发值的改变,如文本框里面的值被用户修改,会同时改变绑定的 Observable 里面的值。

数据流的两个方向的改变均由 Binder 总控。

除了数据流以外,还有信号流,信号流绑定的不再是 Observable 对象,而是一个函数,如:

```python
def click1():
    pass

button1.databind={'click',click1}
```

#### 3.2.1 绑定类型

- 值类型

  可以为:str/int/float/bool/color/date/datetime/time,

  绑定的是一个字符串、整数、浮点数、布尔、颜色、日期、日期时间、时间。

- 行为
  
  可以为:visible/enable,表明可见和可用。

- 信号

  可以为:click/dbclick/select/mousedown/mouseup,表明点击、双击、列表类(列表、树形、表格、单选钮组、复选框组、下拉框)选择、按下、弹起信号。双击仅用于 list/tree/table。

- 集合类

  可以为:list/tree/table/combobox/radioGroup/checkboxGroup,表明为列表、树形、表格、下拉框、单选钮组、复选框组提供的数据源(model)。

- 选择类
  
  可以为:selectItem/selectIndex/selectValue/selectText/selectId,表明当前选中的项/索引/值/文本/Id。
  要注意,list/tree/talbe 的 selectIndex 为 QModelIndex 类型(可以通过 QAbstractItemModel::createIndex 来创建),而 combobox/radioGroup/checkboxGroup 的 selectIndex 为选择的索引(从0开始)。

#### 3.2.2 Observable/ObservableArray

用于绑定,称为可绑定对象。

Observable 绑定值,ObservableArray 绑定 ui.Model 及子类(暂不支持普通 list)。

共有方法:

- get/set
  
  取值,赋值

ObservableArray 独有方法:

- add/insert/remove/update/clear

  添加、插入、移除、更新、清除。

  详见 table.py 示例。

举例:
```python
name=ui.Observable('jerry')
print(name.get())
name.set('jerry1979')
print(name.get())

shapes = ui.ObservableArray(ui.ListModel([{
    'text': '三角形',
    'icon': './imgs/triangle.png'
}, {
    'text': '圆形',
    'icon': './imgs/circle.png',
    'age': 12
}, {
    'text': '矩形',
    'icon': './imgs/rect.png'
}]))
shapes.add({
    'text': '矩形2',
    'icon': './imgs/rect.png'
})
```

绑定对象也支持显式侦听变化:

```python
# 前述 name
def print1(newValue):
  print(newValue)

name.on('change', print1)
```

#### 3.2.3 ViewModel

视图模型基类。

**注:此类的实例在全局中只能一个。**

```python
# MyUserControl.py
class MyUserControl:
  def click2():
    pass
```

```python
import uipy as ui
from UserControl import *

class User:
  username=''
  gender=1
  age=38

class VM(ViewModel):
  # 对象转绑定对象
  user=ui.fromObj(User())

  # 绑定对象
  userId=ui.Observable(-1)

  # 列表绑定对象
  genders = ui.ObservableArray(ui.ListModel([{
        'id': 0,
        'text': '女性'
    }, {
        'id': 1,
        'text': '男性'
    }]))

  # 绑定事件的响应
  def click1():
    pass

  # 绑定导入文件对象的方法
  uctl=ui.fromObj(UserControl())

class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()

        # Ui_MainWindow 为已经布局好的窗口
        self.ui1 = Ui_MainWindow()
        self.ui1.setupUi(self)

        ui.MW = self

        self.ui1.radiobuttonGroup1.databind = {
            'radioGroup': vm.genders,
            'selectId': vm.user.gender
        }
        self.ui1.pushButton1.databind = {
            'click': vm.click1
        }
        self.ui1.label1.databind = {
            'int': vm.userId
        }
        self.ui1.pushButton2.databind = {
            'click': vm.uctl.click1
        }

if __name__ == "__main__":

    ui.init()

    vm = VM()

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()

    vm.bind()

    sys.exit(app.exec())
```

上述示例,演示了一个实体类 User + 实体控制器 UserControl,前者用于封装属性,后者用于控制这个实体类的各种方法。

这是一种比较好的实践。当然,将实体类和实体控制器放在一个类里面也是可以的。

vm.bind 可以传递父组件,如果整个界面中只有一部分需要绑定,可以这样做,默认是整个窗口。

#### 3.2.4 相关工具

- fromObj

  从对象转换为可绑定对象。


## 4. 其他API

### MW

本应用主窗口。

```python
print(ui.MW.name)
```

### F

依据名称或类型找控件。

```python
label1=ui.F('label1')
label1=ui.F(QLabel)
``` 
 
### ListModel

给列表用的数据源。

- `__init__(items,options)`

  构造函数。
  
  items 为集合。

  其中每一项,可以是自定义的数据结构(可以是字典和对象),但 id/text/value/icon/checked 有特定意义,分别表示 id/文本/值/图标/复选否。

  其中每一项,也可以是简单的 list,比如 ['a','b','c','d']
   
  options 为选项。

  也可以通过 defaultIcon 来指定默认图标,defaultAlign 来指定默认对齐方式。

  还可以通过 textCallback/iconCallback/alignCallback 来自处理每个字段。

  ```python
  # 用字典方式
  lm=ListModel([
    {
      'text':'hello',
      'checked': True
    },
    {
      'text':'world'
    }
  ],{'defaultIcon': './imgs/1.jpg'})

  # 用对象方式
  class Item:
    text=''
    checked=False

    def __init__(self,text,checked):
        self.text = text
        self.checked = checked

  items=[]
  for i in range(5):
    item=Item(str(i),True if i%2==0 else False)
    items.append(item)

  lm=ListModel(items,{'defaultIcon': './imgs/1.jpg'})
  ```

### TableModel

给表格用的数据源。

- `__init__(items, hHeaderItems=[], vHeaderItems=[], options={}):`

  构造函数。

- hHeaderItems

  设定水平头,字符串组成的 list。

- vHeaderItems

  设定垂直头,字符串组成的 list。

- options
  - vHeaderAutoIncrement
  
    垂直头是否采用数字自增,为 True 时,vHeaderItems 失效。

```python
# 两行两列
model1=ui.TableModel([
  [{
    'text': 'Using',
    'icon': mu.myPath() + './imgs/triangle.png',
    'checked': True
  }, {
    'text': 'Connecting widgets using a naming scheme',
    'icon': mu.myPath() + './imgs/circle.png',
    'checked': False
  }], 
  [{
    'text': 'Form'
  }, {
    'text': 'How to edit a form in Qt Designer'
  }]], 

  ['Title', 'Description'], 
  [], 
  {'defaultAlign': Qt.AlignCenter}
)
```

### TreeModel

给树形结构用的数据源。

### eachChild

遍历孩子控件。

### eachChildLayout

遍历孩子布局。

### findLayoutByWidget

通过组件找到自己的或所在的布局。

### checkButtons

选择按钮组中的按钮。

### center2Screen/center2Parent/center2MW

居中对齐,相对于屏幕、父亲、主窗口。


## 5. 扩展组件和布局

### ColorButton

点击可以弹出颜色选择框的按钮。

### ButtonGroup

按钮组,与 QButtonGroup 不同的是,本类继承 QWidget,通过 setModel 就可以自动形成多个按钮。

exclusive 为 True,则是单选按钮组,否则是复选框组。

并且,本组件也可以作为 QT Designer 的自定义组件,拷贝 widgetButtonGroup.py、registerButtonGroup.py、pluginButtonGroup.py 到你指定的某个目录,再设置环境变量 PYSIDE_DESIGNER_PLUGINS 指向该目录,重启 QT Designer 即可看到,在 uipy 组下面。

示例可以查看 mainButtonGroup.py 文件。

- exclusive()/setExclusive(val)
  
  取值和赋值,可以用这两个函数,也可以直接当做属性用。

  ```python
  bg1.exclusive=True
  print(bg1.exclusive) 
  ```

- setModel(items)

  设置模型,可以是 list['a','b','c'],也可以是 list[{'id':1,'text':'hello'},...]

- clear()

  清除里面的按钮。

### FlowLayout

流式布局。

例子 examples/flow.py

## 6. 资源

```python
icon=QIcon(':/edit/conference_64px.png')
```

包含 160 个图标,具体详见 assets/imgs 目录下。

在 QT Designer 的资源面板中也可以导入 assets/imgs/imgs.qrc 来使用。


## 7. 其他

### 全局异常

对于全局未捕获异常,自动抓取,并弹出消息框。

如果未弹出,程序退出了,可以查看应用程序下的 UncaughtHook.txt 文件。

### 示例

详见 examples 下面。

## 8. 依赖

- pyside 6

- mupy
  
  一套 python 的各类工具集

- webmix
  
  一套生成 .ui 文件的界面撰写语言。依赖于 pyside6 designer.exe 和 uic.exe。

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/pypa/sampleproject",
    "name": "uipy",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "",
    "author": "jerry",
    "author_email": "6018421@qq.com",
    "download_url": "https://files.pythonhosted.org/packages/02/a3/457d52a15fb151fdf5df864a063120e71809694aad10582fc1ab03e24b23/uipy-1.0.3.tar.gz",
    "platform": null,
    "description": "# uipy \u7f16\u7a0b\u6307\u5357\r\n\r\n## 1. \u6982\u8ff0\r\n\r\nhttps://pypi.org/project/uipy/\r\n\r\nuipy \u662f\u57fa\u4e8e pyside 6 \u7684\u4e8c\u6b21\u5c01\u88c5\uff0c\u5305\u542b\u5982\u4e0b\u7279\u6027\uff1a\r\n\r\n- MVVM\r\n  \r\n  \u7b80\u5355 mvvm \u6a21\u578b\uff0c\u5f00\u53d1\u66f4\u65b9\u4fbf\u3002\r\n\r\n- .ui \u751f\u6210\r\n  \r\n  \u4f9d\u8d56 webmix\uff0c\u7528 js \u8bed\u8a00\u64b0\u5199\u539f\u751f .ui \u6587\u4ef6\r\n\r\n- DOM \u64cd\u4f5c\r\n  \r\n  \u6a21\u4eff jquery \u5bf9\u7ec4\u4ef6\u8fdb\u884c\u64cd\u4f5c\r\n\r\n- Dispatcher\r\n  \r\n  \u6a21\u4eff jquery \u4e8b\u4ef6\u673a\u5236\uff0c\u5c06 pyside \u7684 Event\u3001Action\u3001Signal+Slot \u6539\u4e3a jquery \u4e8b\u4ef6\u6a21\u5f0f\uff0c\u5199\u8d77\u6765\u66f4\u987a\u624b\r\n\r\n- widget\u3001layout\r\n   \r\n  \u5305\u542b\u4e00\u7cfb\u5217\u65b0\u7684\u7ec4\u4ef6\u3002\r\n\r\n  \u5305\u542b\u4e86 FlowLayout \u6d41\u5f0f\u5e03\u5c40\u3002\r\n\r\n- assets\r\n  \r\n  \u5305\u542b\u4e00\u7cfb\u5217\u65b0\u7684\u8d44\u6e90\uff0c\u6bd4\u5982\u5404\u79cd\u5c0f\u56fe\u6807\u3002\r\n\r\n- themes\r\n\r\n  \u5305\u542b\u4e00\u7cfb\u5217\u65b0\u7684\u4e3b\u9898\u3002\r\n\r\n\u4f9d\u9760\u8fd9\u4e9b\u7279\u6027\uff0c\u7f16\u7a0b\u7684\u611f\u89c9\u5c31\u4e0e javascript \u5f00\u53d1\u7c7b\u4f3c\u4e86\u3002\r\n\r\n## 2. \u7b2c\u4e00\u6b65\r\n\r\n\u901a\u8fc7 pypi.org \u6765\u5b89\u88c5\u672c\u5305\uff1a\r\n\r\n`pip install uipy'\r\n\r\n\u6216\u8005\uff0c\u901a\u8fc7\u672c\u5305\u6e90\u7801\u7f16\u8bd1\u5305\u3002\u8f6c\u5230\u6e90\u7801\u76ee\u5f55 uipyPackage/uipy \u6267\u884c\u547d\u4ee4\uff1a\r\n\r\n`python compileUIPY.py develop`\r\n\r\n```python\r\nimport sys\r\nfrom PySide6.QtWidgets import QApplication, QMainWindow\r\n# \u672c\u6587\u793a\u4f8b\u4e2d ui \u5747\u8868\u793a uipy \u6a21\u5757\r\nimport uipy as ui\r\n\r\nif __name__ == \"__main__\":\r\n\r\n    # \u5168\u5c40\u53ea\u8c03\u7528\u4e00\u6b21\uff0c\u63a8\u8350\u5728\u4e3b.py\u6587\u4ef6\u4e2d\u8c03\u7528\r\n    ui.init()\r\n\r\n    app = QApplication(sys.argv)\r\n    window = QMainWindow()\r\n    # \u8bbe\u5b9a\u4e3b\u7a97\u53e3\uff0c\u65b9\u4fbf\u540e\u7eed\u4f7f\u7528\uff0c\u4e5f\u53ef\u4ee5\u8c03\u7528 ui.getMW() \u6765\u83b7\u5f97\r\n    ui.MW = window\r\n    window.show()\r\n\r\n    sys.exit(app.exec())\r\n```\r\n\r\n## 3. \u529f\u80fd\r\n\r\n\r\n\r\n### 3.1 \u6d3e\u53d1\u5668\r\n\r\nDispatcher\uff0c\u878d\u5408 Action\u3001Event\u3001Signal & Slot\uff0c\u5e76\u63d0\u4f9b\u7c7b\u4f3c jquery \u7684\u4f7f\u7528\u65b9\u6cd5\u3002\r\n\r\n- uuid\r\n  \u552f\u4e00\u7f16\u53f7\r\n\r\n- setSignals(signals)\r\n  \r\n  \u8bbe\u7f6e\u5b57\u7b26\u4e32\u7c7b\u7684\u4fe1\u53f7\u3002\u9700\u8981\u5148\u8bbe\u7f6e\uff0c\u624d\u80fd\u4fa6\u542c\u3002\r\n\r\n  signals \u662f\u4e00\u4e2a list\uff0c\u5143\u7d20\u53ef\u4ee5\u662f\u5b57\u7b26\u4e32\uff08\u4f5c\u4e3a\u4fe1\u53f7\u7684\u540d\u5b57\uff09\uff0c\u5219\u521b\u5efa Signal(object)\uff0c\u4e5f\u53ef\u4ee5\u662f\u5143\u7ec4\uff0c\u5143\u7ec4\u7684\u7b2c\u4e00\u4e2a\u5143\u7d20\u662f\u5b57\u7b26\u4e32\uff08\u4f5c\u4e3a\u4fe1\u53f7\u7684\u540d\u5b57\uff09\uff0c\u540e\u63a5\u7684\u5143\u7d20\u662f\u7ed9 Signal \u7684\u53c2\u6570\u3002\r\n\r\n  ```python\r\n  x.setSignals(['change','undo','redo']) # \u7b49\u4e8e Signal(object)\r\n  x.setSignals([('change',int)]) # \u7b49\u4e8e Signal(int)\r\n  x.setSignals([('change',str,int)]) # \u7b49\u4e8e Signal(str,int)\r\n  ```\r\n\r\n- on(signal,slot)\r\n  \r\n  \u4fa6\u542c\u4fe1\u53f7\r\n\r\n- off(signal,slot)\r\n  \r\n  \u65ad\u5f00\u4fa6\u542c\r\n\r\n- fire(signal,*args)\r\n  \r\n  \u89e6\u53d1\u4fe1\u53f7\r\n\r\n- mixin(obj)\r\n  \r\n  \u9759\u6001\u65b9\u6cd5\uff0c\u4e3a\u5bf9\u8c61\u63d0\u4f9b on/off/fire \u65b9\u6cd5\u3002\r\n\r\n\u7b80\u5355\u793a\u4f8b\uff1a\r\n\r\n```python\r\n# \u56e0 python \u6ca1\u6709 js \u90a3\u79cd\u533f\u540d\u51fd\u6570\uff0c\u53ea\u80fd\u8fd9\u6837\u5199\r\ndef click1():\r\n    pass\r\n\r\ndef click2(msg):\r\n    print(msg)\r\n\r\nDispatcher.mixin(label1)\r\n\r\nlabel1.on(label1.clicked,click1)\r\nlabel1.setSignals(['signal1'])\r\nlabel1.on('signal1',click2)\r\n\r\n# \u81ea\u5b9a\u4e49\u4fe1\u53f7\r\nlabel1.fire('signal1','hello')\r\n```\r\n\r\n\u4fe1\u53f7\u53ef\u4ee5\u662f Qt \u81ea\u5e26\u7684\uff0c\u4e5f\u53ef\u4ee5\u662f\u81ea\u5b9a\u4e49\u7684\u5b57\u7b26\u4e32\u3002\r\n\r\n\u5728 ViewModel.bind \u65b9\u6cd5\u4e2d\u4f1a\u81ea\u52a8\u4e3a\u7ec4\u4ef6\u8fdb\u884c minxin\uff0c\u65e0\u9700\u663e\u5f0f\u8c03\u7528\u3002\r\n\r\n\r\n### 3.2 \u6570\u636e\u7ed1\u5b9a\r\n\r\n\u6570\u636e\u53cc\u5411\u7ed1\u5b9a\u5668\u3002\r\n\r\nViewModel \u4f5c\u4e3a\u89c6\u56fe\u6a21\u578b\u57fa\u7c7b\u3002\r\n\r\nBinder \u7edf\u7ba1 DataBinder\u3001Observable\u3001ViewModel \u4e09\u8005\uff0c\u5e76\u4e14\u53ef\u4ee5\u901a\u8fc7 ui.BINDER \u5168\u5c40\u8bbf\u95ee\u5230\u3002\r\n\r\nDataBinder \u4e0e\u7ec4\u4ef6\u7ed1\u5b9a\uff0c\u4e3a\u7ec4\u4ef6\u8fde\u901a Observable \u63d0\u4f9b\u6865\u6881\uff0c\u5982\uff1a\r\n\r\n```python\r\nname=ui.Observable('jerry')\r\nlabel1.databind={'str',name}\r\n```\r\n\r\n\u6570\u636e\u6d41\u53ea\u6709\u4e24\u4e2a\u65b9\u5411\uff0c\u4e00\u662f\u4ece\u503c\u53d8\u66f4\u89e6\u53d1\u7ec4\u4ef6\u53d8\u66f4\uff0c\u5982\uff1a\r\n\r\n```python\r\nname.set('jerry1979')\r\n```\r\n\r\n\u5c06\u4f1a\u5bfc\u81f4 label1 \u663e\u793a\u7684\u6587\u672c\u6539\u53d8\u3002\r\n\r\n\u4e8c\u662f\u4ece\u7ec4\u4ef6\u672c\u8eab\u53d8\u66f4\u89e6\u53d1\u503c\u7684\u6539\u53d8\uff0c\u5982\u6587\u672c\u6846\u91cc\u9762\u7684\u503c\u88ab\u7528\u6237\u4fee\u6539\uff0c\u4f1a\u540c\u65f6\u6539\u53d8\u7ed1\u5b9a\u7684 Observable \u91cc\u9762\u7684\u503c\u3002\r\n\r\n\u6570\u636e\u6d41\u7684\u4e24\u4e2a\u65b9\u5411\u7684\u6539\u53d8\u5747\u7531 Binder \u603b\u63a7\u3002\r\n\r\n\u9664\u4e86\u6570\u636e\u6d41\u4ee5\u5916\uff0c\u8fd8\u6709\u4fe1\u53f7\u6d41\uff0c\u4fe1\u53f7\u6d41\u7ed1\u5b9a\u7684\u4e0d\u518d\u662f Observable \u5bf9\u8c61\uff0c\u800c\u662f\u4e00\u4e2a\u51fd\u6570\uff0c\u5982\uff1a\r\n\r\n```python\r\ndef click1():\r\n    pass\r\n\r\nbutton1.databind={'click',click1}\r\n```\r\n\r\n#### 3.2.1 \u7ed1\u5b9a\u7c7b\u578b\r\n\r\n- \u503c\u7c7b\u578b\r\n\r\n  \u53ef\u4ee5\u4e3a\uff1astr/int/float/bool/color/date/datetime/time\uff0c\r\n\r\n  \u7ed1\u5b9a\u7684\u662f\u4e00\u4e2a\u5b57\u7b26\u4e32\u3001\u6574\u6570\u3001\u6d6e\u70b9\u6570\u3001\u5e03\u5c14\u3001\u989c\u8272\u3001\u65e5\u671f\u3001\u65e5\u671f\u65f6\u95f4\u3001\u65f6\u95f4\u3002\r\n\r\n- \u884c\u4e3a\r\n  \r\n  \u53ef\u4ee5\u4e3a\uff1avisible/enable\uff0c\u8868\u660e\u53ef\u89c1\u548c\u53ef\u7528\u3002\r\n\r\n- \u4fe1\u53f7\r\n\r\n  \u53ef\u4ee5\u4e3a\uff1aclick/dbclick/select/mousedown/mouseup\uff0c\u8868\u660e\u70b9\u51fb\u3001\u53cc\u51fb\u3001\u5217\u8868\u7c7b\uff08\u5217\u8868\u3001\u6811\u5f62\u3001\u8868\u683c\u3001\u5355\u9009\u94ae\u7ec4\u3001\u590d\u9009\u6846\u7ec4\u3001\u4e0b\u62c9\u6846\uff09\u9009\u62e9\u3001\u6309\u4e0b\u3001\u5f39\u8d77\u4fe1\u53f7\u3002\u53cc\u51fb\u4ec5\u7528\u4e8e list/tree/table\u3002\r\n\r\n- \u96c6\u5408\u7c7b\r\n\r\n  \u53ef\u4ee5\u4e3a\uff1alist/tree/table/combobox/radioGroup/checkboxGroup\uff0c\u8868\u660e\u4e3a\u5217\u8868\u3001\u6811\u5f62\u3001\u8868\u683c\u3001\u4e0b\u62c9\u6846\u3001\u5355\u9009\u94ae\u7ec4\u3001\u590d\u9009\u6846\u7ec4\u63d0\u4f9b\u7684\u6570\u636e\u6e90\uff08model\uff09\u3002\r\n\r\n- \u9009\u62e9\u7c7b\r\n  \r\n  \u53ef\u4ee5\u4e3a\uff1aselectItem/selectIndex/selectValue/selectText/selectId\uff0c\u8868\u660e\u5f53\u524d\u9009\u4e2d\u7684\u9879/\u7d22\u5f15/\u503c/\u6587\u672c/Id\u3002\r\n  \u8981\u6ce8\u610f\uff0clist/tree/talbe \u7684 selectIndex \u4e3a QModelIndex \u7c7b\u578b\uff08\u53ef\u4ee5\u901a\u8fc7 QAbstractItemModel::createIndex \u6765\u521b\u5efa\uff09\uff0c\u800c combobox/radioGroup/checkboxGroup \u7684 selectIndex \u4e3a\u9009\u62e9\u7684\u7d22\u5f15\uff08\u4ece0\u5f00\u59cb\uff09\u3002\r\n\r\n#### 3.2.2 Observable/ObservableArray\r\n\r\n\u7528\u4e8e\u7ed1\u5b9a\uff0c\u79f0\u4e3a\u53ef\u7ed1\u5b9a\u5bf9\u8c61\u3002\r\n\r\nObservable \u7ed1\u5b9a\u503c\uff0cObservableArray \u7ed1\u5b9a ui.Model \u53ca\u5b50\u7c7b\uff08\u6682\u4e0d\u652f\u6301\u666e\u901a list\uff09\u3002\r\n\r\n\u5171\u6709\u65b9\u6cd5\uff1a\r\n\r\n- get/set\r\n  \r\n  \u53d6\u503c\uff0c\u8d4b\u503c\r\n\r\nObservableArray \u72ec\u6709\u65b9\u6cd5\uff1a\r\n\r\n- add/insert/remove/update/clear\r\n\r\n  \u6dfb\u52a0\u3001\u63d2\u5165\u3001\u79fb\u9664\u3001\u66f4\u65b0\u3001\u6e05\u9664\u3002\r\n\r\n  \u8be6\u89c1 table.py \u793a\u4f8b\u3002\r\n\r\n\u4e3e\u4f8b\uff1a\r\n```python\r\nname=ui.Observable('jerry')\r\nprint(name.get())\r\nname.set('jerry1979')\r\nprint(name.get())\r\n\r\nshapes = ui.ObservableArray(ui.ListModel([{\r\n    'text': '\u4e09\u89d2\u5f62',\r\n    'icon': './imgs/triangle.png'\r\n}, {\r\n    'text': '\u5706\u5f62',\r\n    'icon': './imgs/circle.png',\r\n    'age': 12\r\n}, {\r\n    'text': '\u77e9\u5f62',\r\n    'icon': './imgs/rect.png'\r\n}]))\r\nshapes.add({\r\n    'text': '\u77e9\u5f622',\r\n    'icon': './imgs/rect.png'\r\n})\r\n```\r\n\r\n\u7ed1\u5b9a\u5bf9\u8c61\u4e5f\u652f\u6301\u663e\u5f0f\u4fa6\u542c\u53d8\u5316\uff1a\r\n\r\n```python\r\n# \u524d\u8ff0 name\r\ndef print1(newValue):\r\n  print(newValue)\r\n\r\nname.on('change', print1)\r\n```\r\n\r\n#### 3.2.3 ViewModel\r\n\r\n\u89c6\u56fe\u6a21\u578b\u57fa\u7c7b\u3002\r\n\r\n**\u6ce8\uff1a\u6b64\u7c7b\u7684\u5b9e\u4f8b\u5728\u5168\u5c40\u4e2d\u53ea\u80fd\u4e00\u4e2a\u3002**\r\n\r\n```python\r\n# MyUserControl.py\r\nclass MyUserControl:\r\n  def click2():\r\n    pass\r\n```\r\n\r\n```python\r\nimport uipy as ui\r\nfrom UserControl import *\r\n\r\nclass User:\r\n  username=''\r\n  gender=1\r\n  age=38\r\n\r\nclass VM(ViewModel):\r\n  # \u5bf9\u8c61\u8f6c\u7ed1\u5b9a\u5bf9\u8c61\r\n  user=ui.fromObj(User())\r\n\r\n  # \u7ed1\u5b9a\u5bf9\u8c61\r\n  userId=ui.Observable(-1)\r\n\r\n  # \u5217\u8868\u7ed1\u5b9a\u5bf9\u8c61\r\n  genders = ui.ObservableArray(ui.ListModel([{\r\n        'id': 0,\r\n        'text': '\u5973\u6027'\r\n    }, {\r\n        'id': 1,\r\n        'text': '\u7537\u6027'\r\n    }]))\r\n\r\n  # \u7ed1\u5b9a\u4e8b\u4ef6\u7684\u54cd\u5e94\r\n  def click1():\r\n    pass\r\n\r\n  # \u7ed1\u5b9a\u5bfc\u5165\u6587\u4ef6\u5bf9\u8c61\u7684\u65b9\u6cd5\r\n  uctl=ui.fromObj(UserControl())\r\n\r\nclass MainWindow(QMainWindow):\r\n\r\n    def __init__(self):\r\n        super(MainWindow, self).__init__()\r\n\r\n        # Ui_MainWindow \u4e3a\u5df2\u7ecf\u5e03\u5c40\u597d\u7684\u7a97\u53e3\r\n        self.ui1 = Ui_MainWindow()\r\n        self.ui1.setupUi(self)\r\n\r\n        ui.MW = self\r\n\r\n        self.ui1.radiobuttonGroup1.databind = {\r\n            'radioGroup': vm.genders,\r\n            'selectId': vm.user.gender\r\n        }\r\n        self.ui1.pushButton1.databind = {\r\n            'click': vm.click1\r\n        }\r\n        self.ui1.label1.databind = {\r\n            'int': vm.userId\r\n        }\r\n        self.ui1.pushButton2.databind = {\r\n            'click': vm.uctl.click1\r\n        }\r\n\r\nif __name__ == \"__main__\":\r\n\r\n    ui.init()\r\n\r\n    vm = VM()\r\n\r\n    app = QApplication(sys.argv)\r\n    window = MainWindow()\r\n    window.show()\r\n\r\n    vm.bind()\r\n\r\n    sys.exit(app.exec())\r\n```\r\n\r\n\u4e0a\u8ff0\u793a\u4f8b\uff0c\u6f14\u793a\u4e86\u4e00\u4e2a\u5b9e\u4f53\u7c7b User + \u5b9e\u4f53\u63a7\u5236\u5668 UserControl\uff0c\u524d\u8005\u7528\u4e8e\u5c01\u88c5\u5c5e\u6027\uff0c\u540e\u8005\u7528\u4e8e\u63a7\u5236\u8fd9\u4e2a\u5b9e\u4f53\u7c7b\u7684\u5404\u79cd\u65b9\u6cd5\u3002\r\n\r\n\u8fd9\u662f\u4e00\u79cd\u6bd4\u8f83\u597d\u7684\u5b9e\u8df5\u3002\u5f53\u7136\uff0c\u5c06\u5b9e\u4f53\u7c7b\u548c\u5b9e\u4f53\u63a7\u5236\u5668\u653e\u5728\u4e00\u4e2a\u7c7b\u91cc\u9762\u4e5f\u662f\u53ef\u4ee5\u7684\u3002\r\n\r\nvm.bind \u53ef\u4ee5\u4f20\u9012\u7236\u7ec4\u4ef6\uff0c\u5982\u679c\u6574\u4e2a\u754c\u9762\u4e2d\u53ea\u6709\u4e00\u90e8\u5206\u9700\u8981\u7ed1\u5b9a\uff0c\u53ef\u4ee5\u8fd9\u6837\u505a\uff0c\u9ed8\u8ba4\u662f\u6574\u4e2a\u7a97\u53e3\u3002\r\n\r\n#### 3.2.4 \u76f8\u5173\u5de5\u5177\r\n\r\n- fromObj\r\n\r\n  \u4ece\u5bf9\u8c61\u8f6c\u6362\u4e3a\u53ef\u7ed1\u5b9a\u5bf9\u8c61\u3002\r\n\r\n\r\n## 4. \u5176\u4ed6API\r\n\r\n### MW\r\n\r\n\u672c\u5e94\u7528\u4e3b\u7a97\u53e3\u3002\r\n\r\n```python\r\nprint(ui.MW.name)\r\n```\r\n\r\n### F\r\n\r\n\u4f9d\u636e\u540d\u79f0\u6216\u7c7b\u578b\u627e\u63a7\u4ef6\u3002\r\n\r\n```python\r\nlabel1=ui.F('label1')\r\nlabel1=ui.F(QLabel)\r\n``` \r\n \r\n### ListModel\r\n\r\n\u7ed9\u5217\u8868\u7528\u7684\u6570\u636e\u6e90\u3002\r\n\r\n- `__init__(items,options)`\r\n\r\n  \u6784\u9020\u51fd\u6570\u3002\r\n  \r\n  items \u4e3a\u96c6\u5408\u3002\r\n\r\n  \u5176\u4e2d\u6bcf\u4e00\u9879\uff0c\u53ef\u4ee5\u662f\u81ea\u5b9a\u4e49\u7684\u6570\u636e\u7ed3\u6784\uff08\u53ef\u4ee5\u662f\u5b57\u5178\u548c\u5bf9\u8c61\uff09\uff0c\u4f46 id/text/value/icon/checked \u6709\u7279\u5b9a\u610f\u4e49\uff0c\u5206\u522b\u8868\u793a id/\u6587\u672c/\u503c/\u56fe\u6807/\u590d\u9009\u5426\u3002\r\n\r\n  \u5176\u4e2d\u6bcf\u4e00\u9879\uff0c\u4e5f\u53ef\u4ee5\u662f\u7b80\u5355\u7684 list\uff0c\u6bd4\u5982 ['a','b','c','d']\r\n   \r\n  options \u4e3a\u9009\u9879\u3002\r\n\r\n  \u4e5f\u53ef\u4ee5\u901a\u8fc7 defaultIcon \u6765\u6307\u5b9a\u9ed8\u8ba4\u56fe\u6807\uff0cdefaultAlign \u6765\u6307\u5b9a\u9ed8\u8ba4\u5bf9\u9f50\u65b9\u5f0f\u3002\r\n\r\n  \u8fd8\u53ef\u4ee5\u901a\u8fc7 textCallback/iconCallback/alignCallback \u6765\u81ea\u5904\u7406\u6bcf\u4e2a\u5b57\u6bb5\u3002\r\n\r\n  ```python\r\n  # \u7528\u5b57\u5178\u65b9\u5f0f\r\n  lm=ListModel([\r\n    {\r\n      'text':'hello',\r\n      'checked': True\r\n    },\r\n    {\r\n      'text':'world'\r\n    }\r\n  ],{'defaultIcon': './imgs/1.jpg'})\r\n\r\n  # \u7528\u5bf9\u8c61\u65b9\u5f0f\r\n  class Item:\r\n    text=''\r\n    checked=False\r\n\r\n    def __init__(self,text,checked):\r\n        self.text = text\r\n        self.checked = checked\r\n\r\n  items=[]\r\n  for i in range(5):\r\n    item=Item(str(i),True if i%2==0 else False)\r\n    items.append(item)\r\n\r\n  lm=ListModel(items,{'defaultIcon': './imgs/1.jpg'})\r\n  ```\r\n\r\n### TableModel\r\n\r\n\u7ed9\u8868\u683c\u7528\u7684\u6570\u636e\u6e90\u3002\r\n\r\n- `__init__(items, hHeaderItems=[], vHeaderItems=[], options={}):`\r\n\r\n  \u6784\u9020\u51fd\u6570\u3002\r\n\r\n- hHeaderItems\r\n\r\n  \u8bbe\u5b9a\u6c34\u5e73\u5934\uff0c\u5b57\u7b26\u4e32\u7ec4\u6210\u7684 list\u3002\r\n\r\n- vHeaderItems\r\n\r\n  \u8bbe\u5b9a\u5782\u76f4\u5934\uff0c\u5b57\u7b26\u4e32\u7ec4\u6210\u7684 list\u3002\r\n\r\n- options\r\n  - vHeaderAutoIncrement\r\n  \r\n    \u5782\u76f4\u5934\u662f\u5426\u91c7\u7528\u6570\u5b57\u81ea\u589e\uff0c\u4e3a True \u65f6\uff0cvHeaderItems \u5931\u6548\u3002\r\n\r\n```python\r\n# \u4e24\u884c\u4e24\u5217\r\nmodel1=ui.TableModel([\r\n  [{\r\n    'text': 'Using',\r\n    'icon': mu.myPath() + './imgs/triangle.png',\r\n    'checked': True\r\n  }, {\r\n    'text': 'Connecting widgets using a naming scheme',\r\n    'icon': mu.myPath() + './imgs/circle.png',\r\n    'checked': False\r\n  }], \r\n  [{\r\n    'text': 'Form'\r\n  }, {\r\n    'text': 'How to edit a form in Qt Designer'\r\n  }]], \r\n\r\n  ['Title', 'Description'], \r\n  [], \r\n  {'defaultAlign': Qt.AlignCenter}\r\n)\r\n```\r\n\r\n### TreeModel\r\n\r\n\u7ed9\u6811\u5f62\u7ed3\u6784\u7528\u7684\u6570\u636e\u6e90\u3002\r\n\r\n### eachChild\r\n\r\n\u904d\u5386\u5b69\u5b50\u63a7\u4ef6\u3002\r\n\r\n### eachChildLayout\r\n\r\n\u904d\u5386\u5b69\u5b50\u5e03\u5c40\u3002\r\n\r\n### findLayoutByWidget\r\n\r\n\u901a\u8fc7\u7ec4\u4ef6\u627e\u5230\u81ea\u5df1\u7684\u6216\u6240\u5728\u7684\u5e03\u5c40\u3002\r\n\r\n### checkButtons\r\n\r\n\u9009\u62e9\u6309\u94ae\u7ec4\u4e2d\u7684\u6309\u94ae\u3002\r\n\r\n### center2Screen/center2Parent/center2MW\r\n\r\n\u5c45\u4e2d\u5bf9\u9f50\uff0c\u76f8\u5bf9\u4e8e\u5c4f\u5e55\u3001\u7236\u4eb2\u3001\u4e3b\u7a97\u53e3\u3002\r\n\r\n\r\n## 5. \u6269\u5c55\u7ec4\u4ef6\u548c\u5e03\u5c40\r\n\r\n### ColorButton\r\n\r\n\u70b9\u51fb\u53ef\u4ee5\u5f39\u51fa\u989c\u8272\u9009\u62e9\u6846\u7684\u6309\u94ae\u3002\r\n\r\n### ButtonGroup\r\n\r\n\u6309\u94ae\u7ec4\uff0c\u4e0e QButtonGroup \u4e0d\u540c\u7684\u662f\uff0c\u672c\u7c7b\u7ee7\u627f QWidget\uff0c\u901a\u8fc7 setModel \u5c31\u53ef\u4ee5\u81ea\u52a8\u5f62\u6210\u591a\u4e2a\u6309\u94ae\u3002\r\n\r\nexclusive \u4e3a True\uff0c\u5219\u662f\u5355\u9009\u6309\u94ae\u7ec4\uff0c\u5426\u5219\u662f\u590d\u9009\u6846\u7ec4\u3002\r\n\r\n\u5e76\u4e14\uff0c\u672c\u7ec4\u4ef6\u4e5f\u53ef\u4ee5\u4f5c\u4e3a QT Designer \u7684\u81ea\u5b9a\u4e49\u7ec4\u4ef6\uff0c\u62f7\u8d1d widgetButtonGroup.py\u3001registerButtonGroup.py\u3001pluginButtonGroup.py \u5230\u4f60\u6307\u5b9a\u7684\u67d0\u4e2a\u76ee\u5f55\uff0c\u518d\u8bbe\u7f6e\u73af\u5883\u53d8\u91cf PYSIDE_DESIGNER_PLUGINS \u6307\u5411\u8be5\u76ee\u5f55\uff0c\u91cd\u542f QT Designer \u5373\u53ef\u770b\u5230\uff0c\u5728 uipy \u7ec4\u4e0b\u9762\u3002\r\n\r\n\u793a\u4f8b\u53ef\u4ee5\u67e5\u770b mainButtonGroup.py \u6587\u4ef6\u3002\r\n\r\n- exclusive()/setExclusive(val)\r\n  \r\n  \u53d6\u503c\u548c\u8d4b\u503c\uff0c\u53ef\u4ee5\u7528\u8fd9\u4e24\u4e2a\u51fd\u6570\uff0c\u4e5f\u53ef\u4ee5\u76f4\u63a5\u5f53\u505a\u5c5e\u6027\u7528\u3002\r\n\r\n  ```python\r\n  bg1.exclusive=True\r\n  print(bg1.exclusive) \r\n  ```\r\n\r\n- setModel(items)\r\n\r\n  \u8bbe\u7f6e\u6a21\u578b\uff0c\u53ef\u4ee5\u662f list['a','b','c']\uff0c\u4e5f\u53ef\u4ee5\u662f list[{'id':1,'text':'hello'},...]\r\n\r\n- clear()\r\n\r\n  \u6e05\u9664\u91cc\u9762\u7684\u6309\u94ae\u3002\r\n\r\n### FlowLayout\r\n\r\n\u6d41\u5f0f\u5e03\u5c40\u3002\r\n\r\n\u4f8b\u5b50 examples/flow.py\r\n\r\n## 6. \u8d44\u6e90\r\n\r\n```python\r\nicon=QIcon(':/edit/conference_64px.png')\r\n```\r\n\r\n\u5305\u542b 160 \u4e2a\u56fe\u6807\uff0c\u5177\u4f53\u8be6\u89c1 assets/imgs \u76ee\u5f55\u4e0b\u3002\r\n\r\n\u5728 QT Designer \u7684\u8d44\u6e90\u9762\u677f\u4e2d\u4e5f\u53ef\u4ee5\u5bfc\u5165 assets/imgs/imgs.qrc \u6765\u4f7f\u7528\u3002\r\n\r\n\r\n## 7. \u5176\u4ed6\r\n\r\n### \u5168\u5c40\u5f02\u5e38\r\n\r\n\u5bf9\u4e8e\u5168\u5c40\u672a\u6355\u83b7\u5f02\u5e38\uff0c\u81ea\u52a8\u6293\u53d6\uff0c\u5e76\u5f39\u51fa\u6d88\u606f\u6846\u3002\r\n\r\n\u5982\u679c\u672a\u5f39\u51fa\uff0c\u7a0b\u5e8f\u9000\u51fa\u4e86\uff0c\u53ef\u4ee5\u67e5\u770b\u5e94\u7528\u7a0b\u5e8f\u4e0b\u7684 UncaughtHook.txt \u6587\u4ef6\u3002\r\n\r\n### \u793a\u4f8b\r\n\r\n\u8be6\u89c1 examples \u4e0b\u9762\u3002\r\n\r\n## 8. \u4f9d\u8d56\r\n\r\n- pyside 6\r\n\r\n- mupy\r\n  \r\n  \u4e00\u5957 python \u7684\u5404\u7c7b\u5de5\u5177\u96c6\r\n\r\n- webmix\r\n  \r\n  \u4e00\u5957\u751f\u6210 .ui \u6587\u4ef6\u7684\u754c\u9762\u64b0\u5199\u8bed\u8a00\u3002\u4f9d\u8d56\u4e8e pyside6 designer.exe \u548c uic.exe\u3002\r\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "A UI library based on PySide 6",
    "version": "1.0.3",
    "project_urls": {
        "Bug Tracker": "https://github.com/pypa/sampleproject/issues",
        "Homepage": "https://github.com/pypa/sampleproject"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "165fcc579fcf82d61273cb02b066aa44ffd99b221d746e1bd2b52e8345edffbc",
                "md5": "8d8862d07d63d80974aefb3493bf98f8",
                "sha256": "51c48243deb9bf8e01d3da5207988b0681ff15cd8a5d5b4295c069f66d9b90a9"
            },
            "downloads": -1,
            "filename": "uipy-1.0.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8d8862d07d63d80974aefb3493bf98f8",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 6473,
            "upload_time": "2023-09-18T02:01:16",
            "upload_time_iso_8601": "2023-09-18T02:01:16.763171Z",
            "url": "https://files.pythonhosted.org/packages/16/5f/cc579fcf82d61273cb02b066aa44ffd99b221d746e1bd2b52e8345edffbc/uipy-1.0.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "02a3457d52a15fb151fdf5df864a063120e71809694aad10582fc1ab03e24b23",
                "md5": "4cfed2904475e488f5c82c117d051e15",
                "sha256": "5aded773de530f3a43d8965bdc3e16adc56059f68e66426e573bd0170988638a"
            },
            "downloads": -1,
            "filename": "uipy-1.0.3.tar.gz",
            "has_sig": false,
            "md5_digest": "4cfed2904475e488f5c82c117d051e15",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 7377,
            "upload_time": "2023-09-18T02:01:18",
            "upload_time_iso_8601": "2023-09-18T02:01:18.632081Z",
            "url": "https://files.pythonhosted.org/packages/02/a3/457d52a15fb151fdf5df864a063120e71809694aad10582fc1ab03e24b23/uipy-1.0.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-18 02:01:18",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "pypa",
    "github_project": "sampleproject",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "uipy"
}
        
Elapsed time: 0.13450s