This commit is contained in:
yumoqing 2022-11-12 20:41:12 +08:00
parent 9e0510d4b7
commit ebfbfb115f
4 changed files with 481 additions and 238 deletions

358
README.md
View File

@ -1,88 +1,270 @@
# KivyBlocks # KivyBlocks
Can you ever image build a gui application like play Lego blocks? kivyblocks just try to provide programmer a tool to build a application like play lego blocks Can you ever image build a gui application like play Lego blocks? kivyblocks just try to provide programmer a tool to build a application like play lego blocks
kivyblocks base on the python package 'kivy', which is a cross platform GUI package and can play on window, linux, mac OS x, android and iphone kivyblocks base on the python package 'kivy', which is a cross platform GUI package and can play on window, linux, mac OS x, android and iphone
## Requirement ## Requirement
[appPublic](https://github.com/yumoqing/appPublic) [appPublic](https://github.com/yumoqing/appPublic)
[kivycalendar](https://github.com/yumoqing/kivycalendar) [kivycalendar](https://github.com/yumoqing/kivycalendar)
[kivy](https://github.com/kivy/kivy) [kivy](https://github.com/kivy/kivy)
... ...
see the [requirements.txt](./requirements.txt) see the [requirements.txt](./requirements.txt)
## Principle ## Principle
There is a BlocksApp(inherited from App) in kivyblocks contains a all widgets can be created by Blocks, and the Blocks creates widgets according a customized json data, the data can download from application server or local filesystem. There is a BlocksApp(inherited from App) in kivyblocks contains a all widgets can be created by Blocks, and the Blocks creates widgets according a customized json data, the data can download from application server or local filesystem.
The Customized json data has it own format to descript the UI and it's interaction. please see the "Customized json data" section for further information. The Customized json data has it own format to descript the UI and it's interaction. please see the "Customized json data" section for further information.
## installation ## installation
``` ```
pip install git+https://github.com/yumoqing/kivyblocks pip install git+https://github.com/yumoqing/kivyblocks
``` ```
Use above command to install the newest version of kivyblocks Use above command to install the newest version of kivyblocks
## How to use ## How to use
see the simple example below: see the simple example below:
``` ```
import sys import sys
import os import os
from appPublic.folderUtils import ProgramPath from appPublic.folderUtils import ProgramPath
from appPublic.jsonConfig import getConfig from appPublic.jsonConfig import getConfig
from kivyblocks.blocksapp import BlocksApp from kivyblocks.blocksapp import BlocksApp
from kivyblocks.blocks import Blocks from kivyblocks.blocks import Blocks
class TestApp(BlocksApp): class TestApp(BlocksApp):
def build(self): def build(self):
b = super(TestApp, self).build() b = super(TestApp, self).build()
widget_desc = { widget_desc = {
"widgettype":"VBox", "widgettype":"VBox",
"options":{}, "options":{},
"subwidgets":[ "subwidgets":[
{ {
"widgettype":"Title1", "widgettype":"Title1",
"options":{ "options":{
"text":"Say Hello", "otext":"Say Hello",
"i18n":True, "i18n":True,
"size_hint_y":None, "size_hint_y":None,
"height":"py::CSize(2)" "height":"py::CSize(2)"
} }
}, },
{ {
"widgettype":"Text", "widgettype":"Text",
"options":{ "options":{
"text":"Hello KivyBlocks" "i18n":True,
} "otext":"Hello KivyBlocks"
} }
] }
} ]
blocks = Blocks() }
x = blocks.widgetBuild(widget_desc) blocks = Blocks()
return x x = blocks.widgetBuild(widget_desc)
return x
if __name__ == '__main__': if __name__ == '__main__':
pp = ProgramPath() pp = ProgramPath()
workdir = pp workdir = pp
if len(sys.argv) > 1: if len(sys.argv) > 1:
workdir = sys.argv[1] workdir = sys.argv[1]
print('ProgramPath=',pp,'workdir=',workdir) print('ProgramPath=',pp,'workdir=',workdir)
config = getConfig(workdir,NS={'workdir':workdir,'ProgramPath':pp}) config = getConfig(workdir,NS={'workdir':workdir,'ProgramPath':pp})
myapp = TestApp() myapp = TestApp()
myapp.run() myapp.run()
``` ```
if you running it on window, it will show the following: if you running it on window, it will show the following:
![hello](./docs/imgs/hello_window.png) ![hello](./docs/imgs/hello_window.png)
## Documents ## BlocksApp
[中文文档](./docs/cn/index.md) inherited from kivy.app.App, for kivyblocks, it get root widget description dictionary from kivyblocks app's json configuration file, and uses Blocks to build the app's root widget.
[English](./docs/en/index.md)
## Blocks
## references A class to constructs all the GUI Widgets in kivyblocks from a widget description dictionary,
The Blocks class is register in kivy.factory.Factory, so you can get Blocks class use following script:
Build app for android please see [Buildozer](https://github.com/kivy/buildozer) ```
kivy introduct and API please see [kivy](https://kivy.org) from kivy.factory import Factory
## Changelog Blocks = Factory.Blocks
[changelog](docs/changelog.md) ```
### getWidgetById get widget by id
#### Syntax
getWidgetById(id:str, from_widget:Widget) -> Widget
#### Description
getWidgetById find the widget identified by "id", the widgets can be found, cause it have a "id" attribute in the widget description dictionary.
#### Use Case
get app
```
app = Factory.Blocks.getWidgetById('app')
```
get root widget
```
root = Factory.blocks.getWidgetById('root')
```
get Window
```
w = Factory.Blocks.getWidgetById('Window')
```
find app.root descendant widget with widget_id is 'myid'
```
Factory.Blocks.getWidgetById('root.myid')
```
find specified widget's descendant widget
```
from_w = Factory.Blocks.getWidgetById('root.one_id')
w = Factory.Blocks.getWidgetById('mychild', from_widget=from_w)
```
find a widget, widget_id is 'descendant' which has a ancester widget_id is 'myancester' and it is from_widget widget's ancester.
```
from_w = Factory.Blocks.getWidgetById('root.one_id')
w = Factory.Blocks.getWidgetById('-myancester.descendant', from_widget=from_w)
```
getWidgetById(id:str, from_widget:Widget) -> Widget
#### Arguments:
* id a '.' splited string, each part must be a widget_id in the widget tree
if id part is start with a '-', it mean to find widget upward, else it find widget downward
* from_widget, default is None, it mean find widget from app.root
#### Return
if widget found, return the found widget, else return None
### widgetBuild()
#### Syntax
x = widgetBuild(desc)
#### Use Case
#### Arguments
* desc
widget description dictionary(wdd), it has the following attributes:
** widgettype
A string, the name of widget class registered by kivyblocks into kviy.factory.Factory
** options
A dictionary, it is the **kwargs argument of __init__() method of the class
** subwidgets:
a list, contains one or more wdd or id, when wdd mean widget description dictionary, and string mean the id can be find with Factory.Blocks.getWidgetById()
** binds
a list of dictionary, each items bind a event to a action or actions
for further information, please read use case of this function
** any other attributes will be handle like a wdd, use to build a widget and the attributes key will be the attribute name of the class instance.
#### Return
if success, return the widget, else return None
## Registered class
### Hierarchy
### Menu
### DataGrid
### Toolbar
### PagePanel
### ToolPage
### Form
### Charts
### Video
### FFVideo
### CircleProgress
### BlocksTest
### DefaultImage
### CommandBox
### Text
### TinyText
### PriceView
### SingleCheckBox
### ClickableBox
### ClickableText
### ClickableIconText
### ToggleText
### ToggleIconText
### ClickableImage
### ToggleImage
### CircleProgress
### PyInterpreter
### UploadFile
### FFVideo
### AnchorBox
### FloatBox
### RelativeBox
### GridBox
### PageBox
### ScatterBox
### StackBox
### DateInput
### HTTPSeriesData
### HTTPDataHandler
### PageLoader
### UdpWidget
### ScrollPanel
### TextInput
### CameraWithMic
### CustomCamera
### QrReader
### Markdown
### PagePanel
### Conform
### Popup
### MapView
### DataGrid
### FileLoaderBrowser
### KivyCamera
### QRCodeWidget
### TabsPanel
### TwoSides
### PageContainer
### BoxViewer
### Form
### StrSearchForm
### VPlayer
### DataGrid
### Toolbar
### ToolPage
### HTTPDataHandler
### Text
### ScrollWidget
### BinStateImage
### JsonCodeInput
### FloatInput
### IntegerInput
### StrInput
### SelectInput
### BoolInput
### Messager
### LoginForm
### PressableImage
### PressableLabel
### Tree
### TextTree
### MenuTree
### PopupMenu
### HostImage
### APlayer
### WrapText
### PressableBox
### Title1
### Title2
### Title3
### Title4
### Title5
### Title6
### Modal
### TimedModal
### HBox
### VBox
### SwipeBox
### ToggleItems
### ExAccordion
### Slider
### PhoneButton
### AWebView
## Documents
[中文文档](./docs/cn/index.md)
[English](./docs/en/index.md)
## references
Build app for android please see [Buildozer](https://github.com/kivy/buildozer)
kivy introduct and API please see [kivy](https://kivy.org)
## Changelog
[changelog](docs/changelog.md)

View File

@ -0,0 +1,59 @@
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.graphics import Color, Line
from kivy.properties import NumericProperty, ColorProperty
from kivy.app import App
from kivyblocks.baseWidget import VBox
class CircleProgress(VBox):
total_cnt = NumericProperty(100)
cur_cnt = NumericProperty(0)
circle_color = ColorProperty([0.7,0.7,0.7,1])
present_color = ColorProperty([1,0,0,1])
line_width = NumericProperty(2)
def __init__(self, **kw):
self.present_w = Label(text='0%', color=self.present_color)
super().__init__(**kw)
self.draw_circle()
self.add_widget(self.present_w)
def draw_circle(self):
r = min(*self.size) / 2 - self.line_width
rate = self.cur_cnt / self.total_cnt
angle_end = rate * 360
print(f'angle={angle_end}, {self.total_cnt} - {self.cur_cnt}, {r}, {self.size}')
# self.canvas.before.clear()
# with self.canvas.before:
self.canvas.after.clear()
with self.canvas.after:
Color(*self.circle_color)
Line(circle=(self.center_x,self.center_y, r),
width=4)
Color(*self.present_color)
Line(circle=(self.center_x,self.center_y, r, 0, angle_end),
width=4)
self.present_w.text = '%d%%' % int(rate * 100)
def progress(self, cur_cnt):
if cur_cnt >= self.total_cnt:
self.cur_cnt = self.total_cnt
elif cur_cnt < 0:
self.cur_cnt = 0
else:
self.cur_cnt = cur_cnt
def on_cur_cnt(self, o, v):
self.draw_circle()
if __name__ == '__main__':
class TestApp(App):
def build(self):
x = CircleProgress(total_cnt=1000)
self.task = Clock.schedule_interval(self.inc_v, 0.5)
return x
def inc_v(self, t=None):
cnt = self.root.cur_cnt + 5
self.root.progress(cnt)
TestApp().run()

View File

@ -1,151 +1,153 @@
import kivy import kivy
from kivy.utils import platform from kivy.utils import platform
from kivy.uix.textinput import TextInput from kivy.uix.textinput import TextInput
from appPublic.registerfunction import RegisterFunction from appPublic.registerfunction import RegisterFunction
import kivyblocks.clickable import kivyblocks.clickable
import kivyblocks.multi_select import kivyblocks.multi_select
from .baseWidget import * from .baseWidget import *
from .tree import Tree, TextTree, MenuTree, PopupMenu from .tree import Tree, TextTree, MenuTree, PopupMenu
from .toolbar import ToolPage, Toolbar from .toolbar import ToolPage, Toolbar
from .dg import DataGrid from .dg import DataGrid
from .vplayer import VPlayer from .vplayer import VPlayer
from .aplayer import APlayer from .aplayer import APlayer
from .form import Form, StrSearchForm from .form import Form, StrSearchForm
from .boxViewer import BoxViewer from .boxViewer import BoxViewer
from .pagescontainer import PageContainer from .pagescontainer import PageContainer
from .hostimage import HostImage from .hostimage import HostImage
from .toggleitems import PressableBox, ToggleItems from .toggleitems import PressableBox, ToggleItems
from .twosides import TwoSides from .twosides import TwoSides
from .tab import TabsPanel from .tab import TabsPanel
from .qrdata import QRCodeWidget from .qrdata import QRCodeWidget
# from .kivycamera import KivyCamera # from .kivycamera import KivyCamera
from .filebrowser import FileLoaderBrowser from .filebrowser import FileLoaderBrowser
from .mapview import MapView from .mapview import MapView
from .message import Conform from .message import Conform
from .pagepanel import PagePanel from .pagepanel import PagePanel
from .markdown import Markdown from .markdown import Markdown
# from .custom_camera import CustomCamera, QrReader # from .custom_camera import CustomCamera, QrReader
from .defaultimage import * from .defaultimage import *
from .price import * from .price import *
#if kivy.platform in ['win','linux', 'macosx']: #if kivy.platform in ['win','linux', 'macosx']:
# from .camerawithmic import ScreenWithMic # from .camerawithmic import ScreenWithMic
#from .camerawithmic import CameraWithMic #from .camerawithmic import CameraWithMic
from .scrollpanel import ScrollPanel from .scrollpanel import ScrollPanel
from .udp_widget import UdpWidget from .udp_widget import UdpWidget
from .paging import PageLoader from .paging import PageLoader
from .dateinput import DateInput from .dateinput import DateInput
from .block_test import BlockTest from .block_test import BlockTest
from .hierarchy import Hierarchy from .hierarchy import Hierarchy
from .price import PriceView from .price import PriceView
from .ffpyplayer_video import FFVideo from .ffpyplayer_video import FFVideo
from .upload import UploadFile from .upload import UploadFile
from .pyinterpreter import PyInterpreter from .pyinterpreter import PyInterpreter
from .circle_progress import CircleProgress
r = Factory.register
# if kivy.platform in ['win','linux', 'macosx']: r = Factory.register
# r('ScreenWithMic', ScreenWithMic) # if kivy.platform in ['win','linux', 'macosx']:
# r('ScreenWithMic', ScreenWithMic)
r('CircleProgress', CircleProgress)
r('PyInterpreter', PyInterpreter) r('PyInterpreter', PyInterpreter)
r('UploadFile', UploadFile) r('UploadFile', UploadFile)
r('FFVideo', FFVideo) r('FFVideo', FFVideo)
r('AnchorBox', AnchorBox) r('AnchorBox', AnchorBox)
r('FloatBox', FloatBox) r('FloatBox', FloatBox)
r('RelativeBox', RelativeBox) r('RelativeBox', RelativeBox)
r('GridBox', GridBox) r('GridBox', GridBox)
r('PageBox', PageBox) r('PageBox', PageBox)
r('ScatterBox', ScatterBox) r('ScatterBox', ScatterBox)
r('StackBox', StackBox) r('StackBox', StackBox)
r('DateInput', DateInput) r('DateInput', DateInput)
r('HTTPSeriesData', HTTPSeriesData) r('HTTPSeriesData', HTTPSeriesData)
r('HTTPDataHandler', HTTPDataHandler) r('HTTPDataHandler', HTTPDataHandler)
r('PageLoader', PageLoader) r('PageLoader', PageLoader)
r('UdpWidget', UdpWidget) r('UdpWidget', UdpWidget)
r('ScrollPanel', ScrollPanel) r('ScrollPanel', ScrollPanel)
r('TextInput', TextInput) r('TextInput', TextInput)
# r('CameraWithMic', CameraWithMic) # r('CameraWithMic', CameraWithMic)
# r('CustomCamera', CustomCamera) # r('CustomCamera', CustomCamera)
# r('QrReader', QrReader) # r('QrReader', QrReader)
r('Markdown', Markdown) r('Markdown', Markdown)
r('PagePanel', PagePanel) r('PagePanel', PagePanel)
r('Conform', Conform) r('Conform', Conform)
r('Popup', Popup) r('Popup', Popup)
r('MapView', MapView) r('MapView', MapView)
r('DataGrid',DataGrid) r('DataGrid',DataGrid)
r('FileLoaderBrowser',FileLoaderBrowser) r('FileLoaderBrowser',FileLoaderBrowser)
# r('KivyCamera',KivyCamera) # r('KivyCamera',KivyCamera)
r('QRCodeWidget',QRCodeWidget) r('QRCodeWidget',QRCodeWidget)
r('TabsPanel',TabsPanel) r('TabsPanel',TabsPanel)
r('TwoSides',TwoSides) r('TwoSides',TwoSides)
r('PageContainer', PageContainer) r('PageContainer', PageContainer)
r('BoxViewer', BoxViewer) r('BoxViewer', BoxViewer)
r('Form', Form) r('Form', Form)
r('StrSearchForm', StrSearchForm) r('StrSearchForm', StrSearchForm)
r('VPlayer', VPlayer) r('VPlayer', VPlayer)
r('DataGrid', DataGrid) r('DataGrid', DataGrid)
r('Toolbar', Toolbar) r('Toolbar', Toolbar)
r('ToolPage',ToolPage) r('ToolPage',ToolPage)
r('HTTPDataHandler',HTTPDataHandler) r('HTTPDataHandler',HTTPDataHandler)
r('Text',Text) r('Text',Text)
r('ScrollWidget',ScrollWidget) r('ScrollWidget',ScrollWidget)
r('BinStateImage',BinStateImage) r('BinStateImage',BinStateImage)
r('JsonCodeInput',JsonCodeInput) r('JsonCodeInput',JsonCodeInput)
r('FloatInput',FloatInput) r('FloatInput',FloatInput)
r('IntegerInput',IntegerInput) r('IntegerInput',IntegerInput)
r('StrInput',StrInput) r('StrInput',StrInput)
r('SelectInput',SelectInput) r('SelectInput',SelectInput)
r('BoolInput',BoolInput) r('BoolInput',BoolInput)
r('Messager',Messager) r('Messager',Messager)
r('LoginForm',LoginForm) r('LoginForm',LoginForm)
r('PressableImage', PressableImage) r('PressableImage', PressableImage)
r('PressableLabel', PressableLabel) r('PressableLabel', PressableLabel)
r('Tree',Tree) r('Tree',Tree)
r('TextTree',TextTree) r('TextTree',TextTree)
r('MenuTree',MenuTree) r('MenuTree',MenuTree)
r('PopupMenu',PopupMenu) r('PopupMenu',PopupMenu)
r('HostImage',HostImage) r('HostImage',HostImage)
r('APlayer',APlayer) r('APlayer',APlayer)
r('WrapText',WrapText) r('WrapText',WrapText)
r('PressableBox',PressableBox) r('PressableBox',PressableBox)
r('Title1',Title1) r('Title1',Title1)
r('Title2',Title2) r('Title2',Title2)
r('Title3',Title3) r('Title3',Title3)
r('Title4',Title4) r('Title4',Title4)
r('Title5',Title5) r('Title5',Title5)
r('Title6',Title6) r('Title6',Title6)
r('Modal',Modal) r('Modal',Modal)
r('TimedModal',TimedModal) r('TimedModal',TimedModal)
r('HBox',HBox) r('HBox',HBox)
r('VBox',VBox) r('VBox',VBox)
r('SwipeBox',SwipeBox) r('SwipeBox',SwipeBox)
r('ToggleItems',ToggleItems) r('ToggleItems',ToggleItems)
r('ExAccordion', ExAccordion) r('Accordion', ExAccordion)
r('Slider', Slider) r('Slider', Slider)
if platform == 'android': if platform == 'android':
r('PhoneButton',PhoneButton) r('PhoneButton',PhoneButton)
r('AWebView',AWebView) r('AWebView',AWebView)
def register_widget(name, klass): def register_widget(name, klass):
try: try:
Factory.regiter(name, klass) Factory.regiter(name, klass)
except: except:
Logger.info(f'Plugin : register_widget():{name} register error') Logger.info(f'Plugin : register_widget():{name} register error')
def register_registerfunction(name, func): def register_registerfunction(name, func):
rf = RegisterFunction() rf = RegisterFunction()
try: try:
rf.register(name, func) rf.register(name, func)
except Exception as e: except Exception as e:
Logger.info(f'Plugin : register_registerfunction():{name} register error({e})') Logger.info(f'Plugin : register_registerfunction():{name} register error({e})')
print_exc() print_exc()
def register_blocks(name, value): def register_blocks(name, value):
b = Factory.Blocks() b = Factory.Blocks()
try: try:
b.register_widget(name, value) b.register_widget(name, value)
except: except:
Logger.info(f'plugin : register_blocks():{name} register error') Logger.info(f'plugin : register_blocks():{name} register error')

View File

@ -1 +1 @@
__version__ = '0.4.3' __version__ = '0.4.4'