From 72b46d5078715f14bd806758d0d397073076c2d3 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 13 Aug 2023 15:44:52 +0800 Subject: [PATCH] bugfix --- ui/kivyblocks/README.md | 5 + ui/kivyblocks/blocks.md | 47 ++++++++ ui/kivyblocks/changelog.md | 29 +++++ ui/kivyblocks/cn/binds.md | 168 ++++++++++++++++++++++++++++ ui/kivyblocks/cn/cdf.md | 66 +++++++++++ ui/kivyblocks/cn/index.md | 9 ++ ui/kivyblocks/cn/intro.md | 16 +++ ui/kivyblocks/en/box.md | 31 +++++ ui/kivyblocks/imgs/hello_window.png | Bin 0 -> 14029 bytes ui/kivyblocks/index.md | 88 +++++++++++++++ 10 files changed, 459 insertions(+) create mode 100644 ui/kivyblocks/README.md create mode 100755 ui/kivyblocks/blocks.md create mode 100755 ui/kivyblocks/changelog.md create mode 100755 ui/kivyblocks/cn/binds.md create mode 100755 ui/kivyblocks/cn/cdf.md create mode 100755 ui/kivyblocks/cn/index.md create mode 100755 ui/kivyblocks/cn/intro.md create mode 100755 ui/kivyblocks/en/box.md create mode 100755 ui/kivyblocks/imgs/hello_window.png create mode 100644 ui/kivyblocks/index.md diff --git a/ui/kivyblocks/README.md b/ui/kivyblocks/README.md new file mode 100644 index 0000000..0fe66f3 --- /dev/null +++ b/ui/kivyblocks/README.md @@ -0,0 +1,5 @@ +# Kivyblocks documentation + +## introduction +Kivyblock is a cross platform native user interface programming tool, it base on kivy module, and provide +## diff --git a/ui/kivyblocks/blocks.md b/ui/kivyblocks/blocks.md new file mode 100755 index 0000000..0668666 --- /dev/null +++ b/ui/kivyblocks/blocks.md @@ -0,0 +1,47 @@ +# Blocks +Blocks是kivyblocks的核心类,负责将字典类型的数据转化为GUI的Widget。 +Blocks也注册到了kivy.factory.Factory中,可以使用 +``` +blocks = Factory.Blocks() +``` +初始化实例 + +## 方法 +### widgetBuild(desc) +#### 参数 +* desc 1) 字典类型, Widget描述的字典数据 + 2) 字符串,可通过json导入的字符串 + +#### 返回值 +成功返回创建的widget实例 +失败返回空 + +#### 事件 +如果创建widget成功,触发”on_built"事件, +如果创建widget失败,将触发“on_failed”事件 + +事件处理函数的例子如下: +创建成功: +``` +def on_built(o, w): + pass +``` +其中,o为Blocks实例, w为新建widget的实例 +创建失败 +``` +def on_failed(o,e): + pass +``` +其中, o为Blocks实例, e为例外实例 + +#### 功能描述 +此方法按照desc字典数据构建一个Widget,首先按照desc['options']中的参数初始化“widgettype”属性指定的类,并将“subwidgets”中的子Widget创建,并添加到Widget中,并且将非“options”,“subwidgets“和”binds“的其他属性也创建完成,并作为Widget的属性变量,最后创建”binds“中的事件处理。 + +widgetBuild按照执行结果的状态,会触发两个事件中的一个,如果创建成功,会触发"on_built" + +### getWidgetById(id, from_widget=None) +查找指定id的widget,可以指定从整个widget树上的哪个节点开始查找。 +#### 参数 +id:字符串,其中‘.'用于分割,节点id,如果id中存在‘.',那么 +## 使用用例 + diff --git a/ui/kivyblocks/changelog.md b/ui/kivyblocks/changelog.md new file mode 100755 index 0000000..9180ea6 --- /dev/null +++ b/ui/kivyblocks/changelog.md @@ -0,0 +1,29 @@ +# Change log + +## version 0.3.0 +add script to support local .tmpl and .dspy file translation. it can build dynamic user interface without remote server. + +## version 0.3.1 +* uses weakref to collect all the i18n widgets in i18n.py +* show video play position + +## version 0.4.5 +* add Behavior handle when build Widget by blocks +* support following behaviors +1 ButtonBehavior +2 CodeNavigationBehavior +3 CompoundSelectionBehavior +4 CoverBehavior +5 DragBehavior +6 EmacsBehavior +7 FocusBehavior +8 ToggleButtonBehavior +9 TouchRippleBehavior +10 TouchRippleButtonBehavior +11 BGColorBehavior +12 ModalBehavior +13 swipeBehavior +14 videoBehavior +* DataGrid class add checkbox support + + diff --git a/ui/kivyblocks/cn/binds.md b/ui/kivyblocks/cn/binds.md new file mode 100755 index 0000000..c6d93b0 --- /dev/null +++ b/ui/kivyblocks/cn/binds.md @@ -0,0 +1,168 @@ +# 事件绑定 +在构件数据格式文件中可以用”binds“属性来为构件定义一到多个事件绑定。 + +事件绑定是一个数组结构的数据,每个事件绑定为当前构件(及其下属命名子构件)的任何一个事件定义处理实体 + +一个处理实体可以是 +1. 一个构件的方法 +2. 一个”urlwidget“类型的构件 +3. 一个描述一个构件的构件数据 +4. 一个注册函数 +5. 一个python脚本 +6. 某个构件的事件 +7. 一个组合处理(可以是上述处理实体的组合数组) + +## 名称约定 + +* self 当前构件 + +* root App.root构件 + +* “-”(减号)开始的名称,从当前位置查找命名的祖先构件,如果到app.root还没有找到,则返回空 + +* app App.get_running_app + +## 事件绑定数据格式说明 + +### wid + 事件发生所在构件,如果wid所代表的构件不存在指定的事件,此事件绑定将作废, + 必选项,self为当前构件 + +### event + 事件名称,wid和event组合就可以唯一确定一个事件 + +### actiontype + 行动类型,分别是“blocks”、”urlwidget“、“registerfunction”,“script”、“method“、”event“和”multiple“之一。 + +* ”blocks“:值必须是一个符合构件数据格式要求的字典数据,用来创建一个构件。 + +* ”urlwidget“;值必须是一个url,从服务器下载一个构件数据格式的文件来创建构件 + +* ”Registerfunction“:注册函数,注册函数必须是def f(*args, **kwargs)参数格式,args[0] 为wid所代表的构件对象,如果事件带数据,则从args[1]开始。 + +* ”script“:python 脚本。在脚本中有两个内定变量可用: self为“target”指定的构件,args=[事件对象,s事件数据] + +* ”method“:构件的方法,构件由此事件绑定的”target“属性指定,此方法必须具有*args, **kwargs格式的参数定义方式 + +* ”event“:构件的事件,构件由此事件绑定的”target“属性指定,事件绑定的参数将传递给此事件 + +* “multiple”:组合绑定,参看例子 + +### datatarget + 指定数据获取的构件,此构件需有“getValue”方法,用来获取数据,数据需是字典类型 + +### target + 指定处理事件的构件,对于actiontype in ["blocks", "urlwidget"]的事件绑定, target为其父构件,缺省的插入方式为替代,即用事件构造的构件,替换所有target构件中的其他子构件,成为target构件的唯一子构件,可以在事件绑定定义了不等于”replace“的“mode“属性,使得新创建的构件添加在其他构件的后面。 + +### mode + 可选属性,只有在actiontype in ["blocks", "urlwidget"]情况下有效,指定事件创建的构件在target构件中的插入方式,mode==”replace“时,清空target的所有子构件后插入,否则添加到最后。 + +### params + 事件处理指定的静态参数,其值在构件创建时确定,不会改变,实际传递给事件处理的参数为params.update(d), 其中d为datatarget构件的getValue方法返回的结果。如果未定义datatarget,则d={}, + +### options + 可选项,只有在actiontype in ["blocks", "urlwidget"]情况下有效,指定创建构件的构件数据格式options的值需是一个数据字典,且需满足构件数据格式要求。参看[构件数据格式](./cdf.md) + +### conform + 可选项,如果存在,此事件处理前需要客户点击弹出窗体中的确认键,否则将不会处理 + conform的数据格式如下: +``` +"conform":{ + "size_hint":[0.6,0.6], + "title":"Conform for delete playlist", + "message":"Please conform to delete this play list" +} + +``` +其中 + * size_hint定义弹出窗体占屏幕宽,高的占比 + + * title是团出窗体的标题 + + * message是显示的内容 + +### rfname + 当actiontype==‘registedfunction’时有效,指定注册函数,在前端应用中需先注册, 使用以下代码注册一个registedfunction +``` +from appPublic.registerfunction import RegisterFunction + +def HelloRF(*args, **kwargs): + print('Hello ...') + +rf = RegisterFunciton() +rf.register('testrf', HelloRF) +``` +'testrf'即可用于rfname + +### script + 当actiontype=='script'时有效,其值是一行可执行的python代码 + +### dispatch_event + 当actiontype=='event'时有效,指定需要触发target构件上的事件名称 + + +## 例子 + +### urlwidget +``` + { + "actiontype":"urlwidget", + "wid":"delete", + "datawidget":"delete", + "target":"root.public_popup", + "event":"on_press", + "conform":{ + "size_hint":[0.6,0.6], + "title":"Conform for delete playlist", + "message":"Please conform to delete this play list" + }, + "options":{ + "method":"GET", + "mode":"replace", + "params":{{json.dumps(params_kw)}}, + "url":"{{entire_url('delete_pldetail_song.dspy')}}" + } + }, +``` +命名构件”delete“按钮的”on_press"事件被绑定到了一个“urlwidget”事件处理上,数据来源为“delete”构件, 目标窗体为app.root构件下面的一个”public_popup"的构件。此事件的处理需要用户在弹出窗体中点击确认键后才能处理。 + +### method +``` + { + "event":"on_press", + "actiontype":"method", + "wid":"delete", + "target":"root.public_popup", + "method":"open" + } +``` +命名构件“delete”的“on_press"事件调用app.root的public_popup构件的open函数来处理。 + +### registedfunction +``` + { + "actiontype":"registedfunction", + "wid":"playlist_add", + "event":"on_submit", + "datawidget":"playlist_add", + "target":"self", + "rfname":"playlist_add" + } +``` +"playlist_add"构件的"on_submit"事件绑定在一个registedfunction的处理函数上 +事件的数据来源与“playlist_add"构件。 + +### script +``` + { + "actiontype":"script", + "wid":"playlist_add", + "event":"on_submit", + "datawidget":"playlist_add", + "target":"self", + "script":"print(self, args)" + } +``` +"playlist_add"构件的"on_submit"事件绑定了一个script的处理,即打印出target和参数 + + diff --git a/ui/kivyblocks/cn/cdf.md b/ui/kivyblocks/cn/cdf.md new file mode 100755 index 0000000..b49fa2a --- /dev/null +++ b/ui/kivyblocks/cn/cdf.md @@ -0,0 +1,66 @@ +# 构件数据格式说明 +一个构件数据格式必须是一个符合python的字典数据类型,需要至少包含以下属性 + +## id + 定义一个构件的名称,含有此属性的构件为命名构件,命名构件可以通过Blocks.getWidgetById获得。 + 名称规则:名称由字母数字构成,理论上可以包含汉字,绝对不能包含”."(英文句号) + +## widgettype + 定义构件的名称,大部分常用的kivy的widget的类名都可以用,以及kivyblocks扩展的部件 + +## options + 类构建时的参数,字典数据类型 + +## subwidgets + 子构件数组,数组中的每个元素也必须是一个符合构件数据格式要求的数据字典,用于定义一个子构件。 + +## binds + 构件的事件绑定列表,每个构件(和其命名子构件)的任和事件都可以绑定一道多个处理,详细事件绑定请看[事件绑定](./binds.md) + +## 其他属性 + 非上述属性的其他的字典属性,blocks会试图用此属性值来创建构件,如果成功,则将此构件在当前构件中保存为此属性名称,并可用使用属性名称当变量来调用 Blocks.getWidgetById()函数来获取 + +## 例子 + +### HELLO +``` + { + "widgettype":"VBox", + "options":{}, + "subwidgets":[ + { + "widgettype":"Title1", + "options":{ + "text":"Say Hello", + "i18n":True, + "size_hint_y":None, + "height":"py::CSize(2)" + } + }, + { + "widgettype":"Text", + "options":{ + "text":"Hello KivyBlocks" + } + } + ] + } +``` +##### 说明 +创建一个VBox类型的构件,VBox创建一个子构件垂直排列的容器,子构件按照顺序从上而下排列,在这个例子中,本VBox容器中放了两个子构件,一个是“title1”的标题1构件,另一个是Text构件,VBox不带参数是表示占满父构件的全部空间。 + +##### 显示效果 +[Hello](../imgs/hello_window.png) + +### 服务器来的构件字典数据 +``` + { + "widgettype":"urlwidget", + "options":{ + "params":{}, + "url":"{{entire_url('ctrl.ui')}}" + } + } +``` +#### 说明 +urlwidget的一个特殊的构件类型 diff --git a/ui/kivyblocks/cn/index.md b/ui/kivyblocks/cn/index.md new file mode 100755 index 0000000..082186e --- /dev/null +++ b/ui/kivyblocks/cn/index.md @@ -0,0 +1,9 @@ +# kivyblocks中文文档 + +## 目录 +[kivyblocks简介](./intro.md) + +[构件数据格式说明](./cdf.md) + +[常用构件](./components.md) + diff --git a/ui/kivyblocks/cn/intro.md b/ui/kivyblocks/cn/intro.md new file mode 100755 index 0000000..7dd6304 --- /dev/null +++ b/ui/kivyblocks/cn/intro.md @@ -0,0 +1,16 @@ +# kivyblocks简介 + +kivyblocks是一个基于kivy的python GUI开发工具,得益于kivy的跨平台特性, kivyblocks可以支持Windows,Linux,macosx, iPhone,android, 以及X霉 + + +## kivyblocks的目标 +降低前端原生应用开发的复杂度,提高原生应用系统的开发和运维效率。 + +## kivyblocks的原理 +逻辑 + 数据 = 程序 + +相信程序员都了解上述等式,kviyblocks将复杂的业务逻辑封装在底层的构件库中,而将标准的数据接口提供给程序员,程序员不需要编写逻辑代码 + +程序要要创建一个显示构件,只需要按照[构件数据格式](./cdf.md)的要求填写字典数据即可 + + diff --git a/ui/kivyblocks/en/box.md b/ui/kivyblocks/en/box.md new file mode 100755 index 0000000..e87dbc3 --- /dev/null +++ b/ui/kivyblocks/en/box.md @@ -0,0 +1,31 @@ +# Box +Box is a BoxLayout widget with a BGColorBehavior class, when add a Text, or widget contains Text, it will set the Text color as Box's fgcolor. + +It use normal_bgcolor as the background color, and normal_fgcolor as the text color if the Box is not selected, when the Box is selected, it show selected_bgcolor background color, and reset the Text's color inside the Box with selected_fgcolor + +## options + +### color_level +default is 0, the main color seials, color level for this widget, please reference to block color for further information +### radius: default is [], means not Box corner without radius. if present, it must be a list of 4 float value, indicate radius for the four corners[ top=left, top-right, bottom-right, bottom-left] + +### others options +each options accepte by BoxLayout is also accepted by Box + +## Method + +### selected +selected draws Box's background with selected_bgcolor, set all the Text widgets's color with selected_fgcolor + +### unselected +this method draws Box's background with normal_bgcolor, set all Text widgets's color with normal_fgcolor + +## event +no new events + +## refence +Horizonal Box[HBox](./hbox.md) +Vertical Box[VBox](./vbox.md) +Text [Text](./text.md) + + diff --git a/ui/kivyblocks/imgs/hello_window.png b/ui/kivyblocks/imgs/hello_window.png new file mode 100755 index 0000000000000000000000000000000000000000..9ce13a8acbc3b3a25899805e3808c1e64d033425 GIT binary patch literal 14029 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