diff --git a/README.md b/README.md index c99e9b4..8ada7a9 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,270 @@ -# 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 - -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 - -[appPublic](https://github.com/yumoqing/appPublic) -[kivycalendar](https://github.com/yumoqing/kivycalendar) -[kivy](https://github.com/kivy/kivy) -... -see the [requirements.txt](./requirements.txt) - -## 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. -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 -``` -pip install git+https://github.com/yumoqing/kivyblocks -``` -Use above command to install the newest version of kivyblocks -## How to use -see the simple example below: -``` -import sys -import os -from appPublic.folderUtils import ProgramPath -from appPublic.jsonConfig import getConfig - -from kivyblocks.blocksapp import BlocksApp -from kivyblocks.blocks import Blocks - -class TestApp(BlocksApp): - def build(self): - b = super(TestApp, self).build() - widget_desc = { - "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" - } - } - ] - } - blocks = Blocks() - x = blocks.widgetBuild(widget_desc) - return x - - -if __name__ == '__main__': - pp = ProgramPath() - workdir = pp - if len(sys.argv) > 1: - workdir = sys.argv[1] - print('ProgramPath=',pp,'workdir=',workdir) - - config = getConfig(workdir,NS={'workdir':workdir,'ProgramPath':pp}) - myapp = TestApp() - myapp.run() -``` -if you running it on window, it will show the following: -![hello](./docs/imgs/hello_window.png) - -## 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) +# 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 + +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 + +[appPublic](https://github.com/yumoqing/appPublic) +[kivycalendar](https://github.com/yumoqing/kivycalendar) +[kivy](https://github.com/kivy/kivy) +... +see the [requirements.txt](./requirements.txt) + +## 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. +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 +``` +pip install git+https://github.com/yumoqing/kivyblocks +``` +Use above command to install the newest version of kivyblocks +## How to use +see the simple example below: +``` +import sys +import os +from appPublic.folderUtils import ProgramPath +from appPublic.jsonConfig import getConfig + +from kivyblocks.blocksapp import BlocksApp +from kivyblocks.blocks import Blocks + +class TestApp(BlocksApp): + def build(self): + b = super(TestApp, self).build() + widget_desc = { + "widgettype":"VBox", + "options":{}, + "subwidgets":[ + { + "widgettype":"Title1", + "options":{ + "otext":"Say Hello", + "i18n":True, + "size_hint_y":None, + "height":"py::CSize(2)" + } + }, + { + "widgettype":"Text", + "options":{ + "i18n":True, + "otext":"Hello KivyBlocks" + } + } + ] + } + blocks = Blocks() + x = blocks.widgetBuild(widget_desc) + return x + +if __name__ == '__main__': + pp = ProgramPath() + workdir = pp + if len(sys.argv) > 1: + workdir = sys.argv[1] + print('ProgramPath=',pp,'workdir=',workdir) + + config = getConfig(workdir,NS={'workdir':workdir,'ProgramPath':pp}) + myapp = TestApp() + myapp.run() +``` +if you running it on window, it will show the following: +![hello](./docs/imgs/hello_window.png) + +## BlocksApp +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. + +## Blocks +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: +``` +from kivy.factory import Factory + +Blocks = Factory.Blocks +``` +### 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) diff --git a/kivyblocks/circle_progress.py b/kivyblocks/circle_progress.py new file mode 100644 index 0000000..56e9c97 --- /dev/null +++ b/kivyblocks/circle_progress.py @@ -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() + diff --git a/kivyblocks/register.py b/kivyblocks/register.py index a1fa35d..39cb3ab 100644 --- a/kivyblocks/register.py +++ b/kivyblocks/register.py @@ -1,151 +1,153 @@ -import kivy -from kivy.utils import platform -from kivy.uix.textinput import TextInput - -from appPublic.registerfunction import RegisterFunction - -import kivyblocks.clickable -import kivyblocks.multi_select - -from .baseWidget import * -from .tree import Tree, TextTree, MenuTree, PopupMenu -from .toolbar import ToolPage, Toolbar -from .dg import DataGrid -from .vplayer import VPlayer -from .aplayer import APlayer -from .form import Form, StrSearchForm -from .boxViewer import BoxViewer -from .pagescontainer import PageContainer -from .hostimage import HostImage -from .toggleitems import PressableBox, ToggleItems -from .twosides import TwoSides -from .tab import TabsPanel -from .qrdata import QRCodeWidget -# from .kivycamera import KivyCamera -from .filebrowser import FileLoaderBrowser -from .mapview import MapView -from .message import Conform -from .pagepanel import PagePanel -from .markdown import Markdown -# from .custom_camera import CustomCamera, QrReader -from .defaultimage import * -from .price import * - -#if kivy.platform in ['win','linux', 'macosx']: -# from .camerawithmic import ScreenWithMic -#from .camerawithmic import CameraWithMic -from .scrollpanel import ScrollPanel -from .udp_widget import UdpWidget -from .paging import PageLoader -from .dateinput import DateInput -from .block_test import BlockTest -from .hierarchy import Hierarchy -from .price import PriceView -from .ffpyplayer_video import FFVideo -from .upload import UploadFile +import kivy +from kivy.utils import platform +from kivy.uix.textinput import TextInput + +from appPublic.registerfunction import RegisterFunction + +import kivyblocks.clickable +import kivyblocks.multi_select + +from .baseWidget import * +from .tree import Tree, TextTree, MenuTree, PopupMenu +from .toolbar import ToolPage, Toolbar +from .dg import DataGrid +from .vplayer import VPlayer +from .aplayer import APlayer +from .form import Form, StrSearchForm +from .boxViewer import BoxViewer +from .pagescontainer import PageContainer +from .hostimage import HostImage +from .toggleitems import PressableBox, ToggleItems +from .twosides import TwoSides +from .tab import TabsPanel +from .qrdata import QRCodeWidget +# from .kivycamera import KivyCamera +from .filebrowser import FileLoaderBrowser +from .mapview import MapView +from .message import Conform +from .pagepanel import PagePanel +from .markdown import Markdown +# from .custom_camera import CustomCamera, QrReader +from .defaultimage import * +from .price import * + +#if kivy.platform in ['win','linux', 'macosx']: +# from .camerawithmic import ScreenWithMic +#from .camerawithmic import CameraWithMic +from .scrollpanel import ScrollPanel +from .udp_widget import UdpWidget +from .paging import PageLoader +from .dateinput import DateInput +from .block_test import BlockTest +from .hierarchy import Hierarchy +from .price import PriceView +from .ffpyplayer_video import FFVideo +from .upload import UploadFile from .pyinterpreter import PyInterpreter - -r = Factory.register -# if kivy.platform in ['win','linux', 'macosx']: -# r('ScreenWithMic', ScreenWithMic) +from .circle_progress import CircleProgress + +r = Factory.register +# if kivy.platform in ['win','linux', 'macosx']: +# r('ScreenWithMic', ScreenWithMic) +r('CircleProgress', CircleProgress) r('PyInterpreter', PyInterpreter) -r('UploadFile', UploadFile) -r('FFVideo', FFVideo) -r('AnchorBox', AnchorBox) -r('FloatBox', FloatBox) -r('RelativeBox', RelativeBox) -r('GridBox', GridBox) -r('PageBox', PageBox) -r('ScatterBox', ScatterBox) -r('StackBox', StackBox) -r('DateInput', DateInput) -r('HTTPSeriesData', HTTPSeriesData) -r('HTTPDataHandler', HTTPDataHandler) -r('PageLoader', PageLoader) -r('UdpWidget', UdpWidget) -r('ScrollPanel', ScrollPanel) -r('TextInput', TextInput) -# r('CameraWithMic', CameraWithMic) -# r('CustomCamera', CustomCamera) -# r('QrReader', QrReader) -r('Markdown', Markdown) -r('PagePanel', PagePanel) -r('Conform', Conform) -r('Popup', Popup) -r('MapView', MapView) -r('DataGrid',DataGrid) -r('FileLoaderBrowser',FileLoaderBrowser) -# r('KivyCamera',KivyCamera) -r('QRCodeWidget',QRCodeWidget) -r('TabsPanel',TabsPanel) -r('TwoSides',TwoSides) -r('PageContainer', PageContainer) -r('BoxViewer', BoxViewer) -r('Form', Form) -r('StrSearchForm', StrSearchForm) -r('VPlayer', VPlayer) -r('DataGrid', DataGrid) -r('Toolbar', Toolbar) -r('ToolPage',ToolPage) -r('HTTPDataHandler',HTTPDataHandler) -r('Text',Text) -r('ScrollWidget',ScrollWidget) -r('BinStateImage',BinStateImage) -r('JsonCodeInput',JsonCodeInput) -r('FloatInput',FloatInput) -r('IntegerInput',IntegerInput) -r('StrInput',StrInput) -r('SelectInput',SelectInput) -r('BoolInput',BoolInput) -r('Messager',Messager) -r('LoginForm',LoginForm) -r('PressableImage', PressableImage) -r('PressableLabel', PressableLabel) -r('Tree',Tree) -r('TextTree',TextTree) -r('MenuTree',MenuTree) -r('PopupMenu',PopupMenu) -r('HostImage',HostImage) -r('APlayer',APlayer) -r('WrapText',WrapText) -r('PressableBox',PressableBox) -r('Title1',Title1) -r('Title2',Title2) -r('Title3',Title3) -r('Title4',Title4) -r('Title5',Title5) -r('Title6',Title6) -r('Modal',Modal) -r('TimedModal',TimedModal) -r('HBox',HBox) -r('VBox',VBox) -r('SwipeBox',SwipeBox) -r('ToggleItems',ToggleItems) -r('ExAccordion', ExAccordion) -r('Slider', Slider) -if platform == 'android': - r('PhoneButton',PhoneButton) - r('AWebView',AWebView) - - -def register_widget(name, klass): - try: - Factory.regiter(name, klass) - except: - Logger.info(f'Plugin : register_widget():{name} register error') - -def register_registerfunction(name, func): - rf = RegisterFunction() - try: - rf.register(name, func) - except Exception as e: - Logger.info(f'Plugin : register_registerfunction():{name} register error({e})') - print_exc() - -def register_blocks(name, value): - b = Factory.Blocks() - try: - b.register_widget(name, value) - except: - Logger.info(f'plugin : register_blocks():{name} register error') - +r('UploadFile', UploadFile) +r('FFVideo', FFVideo) +r('AnchorBox', AnchorBox) +r('FloatBox', FloatBox) +r('RelativeBox', RelativeBox) +r('GridBox', GridBox) +r('PageBox', PageBox) +r('ScatterBox', ScatterBox) +r('StackBox', StackBox) +r('DateInput', DateInput) +r('HTTPSeriesData', HTTPSeriesData) +r('HTTPDataHandler', HTTPDataHandler) +r('PageLoader', PageLoader) +r('UdpWidget', UdpWidget) +r('ScrollPanel', ScrollPanel) +r('TextInput', TextInput) +# r('CameraWithMic', CameraWithMic) +# r('CustomCamera', CustomCamera) +# r('QrReader', QrReader) +r('Markdown', Markdown) +r('PagePanel', PagePanel) +r('Conform', Conform) +r('Popup', Popup) +r('MapView', MapView) +r('DataGrid',DataGrid) +r('FileLoaderBrowser',FileLoaderBrowser) +# r('KivyCamera',KivyCamera) +r('QRCodeWidget',QRCodeWidget) +r('TabsPanel',TabsPanel) +r('TwoSides',TwoSides) +r('PageContainer', PageContainer) +r('BoxViewer', BoxViewer) +r('Form', Form) +r('StrSearchForm', StrSearchForm) +r('VPlayer', VPlayer) +r('DataGrid', DataGrid) +r('Toolbar', Toolbar) +r('ToolPage',ToolPage) +r('HTTPDataHandler',HTTPDataHandler) +r('Text',Text) +r('ScrollWidget',ScrollWidget) +r('BinStateImage',BinStateImage) +r('JsonCodeInput',JsonCodeInput) +r('FloatInput',FloatInput) +r('IntegerInput',IntegerInput) +r('StrInput',StrInput) +r('SelectInput',SelectInput) +r('BoolInput',BoolInput) +r('Messager',Messager) +r('LoginForm',LoginForm) +r('PressableImage', PressableImage) +r('PressableLabel', PressableLabel) +r('Tree',Tree) +r('TextTree',TextTree) +r('MenuTree',MenuTree) +r('PopupMenu',PopupMenu) +r('HostImage',HostImage) +r('APlayer',APlayer) +r('WrapText',WrapText) +r('PressableBox',PressableBox) +r('Title1',Title1) +r('Title2',Title2) +r('Title3',Title3) +r('Title4',Title4) +r('Title5',Title5) +r('Title6',Title6) +r('Modal',Modal) +r('TimedModal',TimedModal) +r('HBox',HBox) +r('VBox',VBox) +r('SwipeBox',SwipeBox) +r('ToggleItems',ToggleItems) +r('Accordion', ExAccordion) +r('Slider', Slider) +if platform == 'android': + r('PhoneButton',PhoneButton) + r('AWebView',AWebView) + + +def register_widget(name, klass): + try: + Factory.regiter(name, klass) + except: + Logger.info(f'Plugin : register_widget():{name} register error') + +def register_registerfunction(name, func): + rf = RegisterFunction() + try: + rf.register(name, func) + except Exception as e: + Logger.info(f'Plugin : register_registerfunction():{name} register error({e})') + print_exc() + +def register_blocks(name, value): + b = Factory.Blocks() + try: + b.register_widget(name, value) + except: + Logger.info(f'plugin : register_blocks():{name} register error') + diff --git a/kivyblocks/version.py b/kivyblocks/version.py index 908c0bb..9a8e054 100644 --- a/kivyblocks/version.py +++ b/kivyblocks/version.py @@ -1 +1 @@ -__version__ = '0.4.3' +__version__ = '0.4.4'