diff --git a/kivyblocks/anz/__init__.py b/kivyblocks/anz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kivyblocks/anz/bluetooth.py b/kivyblocks/anz/bluetooth.py new file mode 100644 index 0000000..b554eb2 --- /dev/null +++ b/kivyblocks/anz/bluetooth.py @@ -0,0 +1,46 @@ +from kivy.app import App + +from jnius import autoclass +import kivy +from android.broadcast import BroadcastReceiver +import sys + +from kivyblocks.baseWidget import VBox + +BluetoothManager = autoclass('android.bluetooth.BluetoothManager') +BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter') +BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice') + +class BluetoothFinder(VBox): + data = ListProperty([]) + def __init__(self, **kw): + super().__init__(**kw) + arg = context.getSystemService(context.BLUETOOTH_SERVICE) + self.bt_manager = BluetoothManager(arg) + self.bt_adapter = self.bt_manager.getAdaper() + self.unpairedBT() + + def get_paired_bt(self): + return self.bt_adapter.getBondedDevice() + + def __del__(self): + self.adapter.cancelDiscovery() + + def unpairedBT(self): + self.adapter = BluetoothAdapter.getDefaultAdapter() + # Search foir unpaired devices + print("Unpaired devices") + self.data=[{"text": "Unpaired Devices"}] + myReceiver = BroadcastReceiver(self.onReceive, actions = [BluetoothDevice.ACTION_FOUND, BluetoothAdapter.ACTION_DISCOVERY_STARTED, BluetoothAdapter.ACTION_DISCOVERY_FINISHED]) + myReceiver.start() + self.adapter.startDiscovery() + + # Called by Broadcastreceiver + def onReceive(self, context, intent): + print(f"*BT* On receive context{context}", flush = True) + print(f"*BT* On receive intent {intent}", flush = True) + sys.stdout.flush() + + self.myData.append({"text": f"Context {context}, intent {intent}"}) + self.layout.data = [item for item in self.myData] + diff --git a/kivyblocks/baseWidget.py b/kivyblocks/baseWidget.py index 9454f1a..a944e8a 100755 --- a/kivyblocks/baseWidget.py +++ b/kivyblocks/baseWidget.py @@ -67,7 +67,7 @@ from .widgetExt.inputext import FloatInput,IntegerInput, \ StrInput,SelectInput, BoolInput, Password from .widgetExt.messager import Messager from .bgcolorbehavior import BGColorBehavior -from .utils import NeedLogin, InsufficientPrivilege, HTTPError +from .utils import NeedLogin, InsufficientPrivilege, HTTPError, blockImage from .login import LoginForm from .tab import TabsPanel from .threadcall import HttpClient @@ -76,6 +76,7 @@ from .widget_css import WidgetCSS from .ready import WidgetReady from .utils import CSize, SUPER from .swipebehavior import SwipeBehavior +from .widgetExt.inputext import MyDropDown if platform == 'android': from .widgetExt.phonebutton import PhoneButton @@ -171,7 +172,6 @@ class Text(Label): SUPER(Text, self, kwargs) if self._i18n: - self.i18n = I18n() self.i18n.addI18nWidget(self) if self.wrap: self.size_hint_y = None @@ -220,7 +220,8 @@ class Text(Label): self.lang = lang def on_lang(self,o,lang): - self.text = self.i18n(self.otext) + if self._i18n and self.otext: + self.text = self.i18n(self.otext) class Title1(Text): def __init__(self, **kw): @@ -260,10 +261,11 @@ class Modal(VBox): position = OptionProperty('cc',options=['tl', 'tc', 'tr', 'cl', 'cc', 'cr', 'bl', 'bc', 'br']) - def __init__(self, **kw): self._target = None - SUPER(Modal, self, kw) + super(Modal, self).__init__(**kw) + self.set_size_position() + self._target.bind(size=self.set_size_position) self.register_event_type('on_open') self.register_event_type('on_pre_open') self.register_event_type('on_pre_dismiss') @@ -285,7 +287,16 @@ class Modal(VBox): return super().on_touch_down(touch) - def set_modal_position(self): + def on_target(self): + w = Window + if self.target is not None: + w = Factory.Blocks.getWidgetById(self.target) + if w is None: + w = Window + if w != self._target: + self._target = w + + def set_target(self): if self._target is None: if self.target is None: w = Window @@ -294,42 +305,56 @@ class Modal(VBox): if w is None: w = Window self._target = w + + def set_size_position(self, *args): + self.set_target() + if self.size_hint_x: + self.width = self.size_hint_x * self._target.width + if self.size_hint_y: + self.height = self.size_hint_y * self._target.height + print(self.width, self.height, + self.size_hint_x, self.size_hint_y, + self._target.size + ) + self.set_modal_position() + + def set_modal_position(self): + self.set_target() xn = self.position[1] yn = self.position[0] x, y = 0, 0 if xn == 'c': - x = (w.width - self.width) / 2 + x = (self._target.width - self.width) / 2 elif xn == 'r': - x = w.width - self.width + x = self._target.width - self.width if x < 0: x = 0 if yn == 'c': - y = (w.height - self.height) / 2 + y = (self._target.height - self.height) / 2 elif yn == 't': - y = w.height - self.height + y = self._target.height - self.height if y < 0: y = 0 - if w == Window: + if self._target == Window: self.pos = x, y else: - self.pos = w.pos[0] + x, w.pos[1] + y + self.pos = self._target.pos[0] + x, self._target.pos[1] + y - def open(self, widget=None): + def open(self): if self.parent: - return + self.parent.remove_widget(self) self.dispatch('on_pre_open') - self.set_modal_position() Window.add_widget(self) self.dispatch('on_open') if self._target != Window: - self._target.disable() + self._target.disabled = True def dismiss(self, *args): self.dispatch('on_pre_dismiss') self.dispatch('on_dismiss') Window.remove_widget(self) if self._target != Window: - self._Target.enable() + self._target.enabled = False def on_open(self, *args): pass @@ -369,6 +394,32 @@ class TimedModal(Modal): self.time_task = None super().dismiss() +class Running(AsyncImage): + def __init__(self, widget, **kw): + super().__init__(**kw) + self.host_widget = widget + self.source = blockImage('running.gif') + self.host_widget.bind(size=self.set_size) + self.set_size() + self.open() + + def open(self): + if self.parent: + self.parent.remove_widget(self) + Window.add_widget(self) + self.host_widget.disabled = True + + def dismiss(self): + if self.parent: + self.parent.remove_widget(self) + self.host_widget.disabled = False + + def set_size(self, *args): + self.size_hint = (None, None) + self.width = CSize(2) + self.height = CSize(2) + self.center = self.host_widget.center + class PressableImage(ButtonBehavior,AsyncImage): def on_press(self): pass @@ -377,6 +428,10 @@ class PressableLabel(ButtonBehavior, Text): def on_press(self): pass +class PressableText(ButtonBehavior, Text): + def on_press(self): + pass + class FILEDataHandler(EventDispatcher): def __init__(self, url, suffixs=[],params={}): self.url = url @@ -577,3 +632,28 @@ class Slider(Carousel): w = bk.widgetBuild(desc) self.add_widget(w) +i18n = I18n() + +class I18nWidget(PressableText): + lang = StringProperty(None) + def __init__(self, **kw): + super().__init__(**kw) + self.lang = i18n.lang + + def on_lang(self, *args): + self.otext = self.lang + + def on_press(self, *args): + langs = i18n.get_languages() + data = [ {'lang':l} for l in langs ] + mdd = MyDropDown(textField='lang', valueField='lang', + value=self.lang, + data=data) + mdd.bind(on_select=self.selected_lang) + mdd.showme(self) + + def selected_lang(self, o, v): + lang = v[0] + self.lang = lang + i18n.changeLang(self.lang) + diff --git a/kivyblocks/command_action.py b/kivyblocks/command_action.py index 93203eb..913e33c 100644 --- a/kivyblocks/command_action.py +++ b/kivyblocks/command_action.py @@ -15,7 +15,6 @@ def get_cmd_desc(cmd_desc): desc['datamethod'] = v['datamethod'] keys = v.keys() if 'url' in keys: - Logger.info('get_cmd_desc():cmd_desc=%s', cmd_desc) desc['actiontype'] = 'urlwidget' desc['mode'] = 'replace' options = { @@ -24,18 +23,22 @@ def get_cmd_desc(cmd_desc): } desc['options'] = options return desc + if 'rfname' in keys: desc['actiontype'] = 'registedfunction' desc['params'] = v.get('params',{}) return desc + if 'script' in keys: desc['actiontype'] = 'script' desc['script'] = v.get('script') return desc + if 'method' in keys: desc['actiontype'] = 'method' desc['method'] = v.get('method') return desc + if 'actions' in keys: desc['actiontype'] = 'multiple' desc['actions'] = v.get('actions') @@ -61,6 +64,6 @@ def cmd_action(cmd_desc, widget): }) w.bind(on_conform=partial(blocks.uniaction, widget, desc)) w.open() - Logger.info('cmd_action():desc=%s, conform and action', desc) + print('cmd_action():desc=', desc, ' conform and action', desc) diff --git a/kivyblocks/dg.py b/kivyblocks/dg.py index 545a2d0..c86861f 100755 --- a/kivyblocks/dg.py +++ b/kivyblocks/dg.py @@ -25,7 +25,7 @@ from appPublic.uniqueID import getID from appPublic.myTE import string_template_render from .utils import * -from .baseWidget import Text, HBox, VBox +from .baseWidget import Text, HBox, VBox, Running from .scrollpanel import ScrollPanel from .paging import Paging, RelatedLoader from .ready import WidgetReady @@ -64,7 +64,7 @@ class Cell(ButtonBehavior, WidgetCSS, BoxLayout): if self.row.header: self.csscls=self.row.part.datagrid.header_css if desc['header']: - bl = Text(i18n=True, text=str(desc['value']), + bl = Text(i18n=True, otext=str(desc['value']), font_size=CSize(1),wrap=True, halign='left', valign='middle' ) @@ -432,7 +432,7 @@ class DataGrid(VBox): desc = { "widgettype":"Text", "options":{ - "text":n, + "otext":n, "i18n":True, } } diff --git a/kivyblocks/i18n.py b/kivyblocks/i18n.py index 914a8d7..5ad2fc5 100644 --- a/kivyblocks/i18n.py +++ b/kivyblocks/i18n.py @@ -14,6 +14,9 @@ class I18n: def __init__(self): self.kvlang={} self.lang = locale.getdefaultlocale()[0] + if self.lang is None: + self.lang = 'en_US' + self.languages = None self.loadI18n(self.lang) self.i18nWidgets = [] @@ -21,8 +24,9 @@ class I18n: self.i18nWidgets.append(ref(w)) def loadI18nFromI18nFolder(self, lang): - config = gtConfig() + config = getConfig() fpath = os.path.join(config.i18n_folder,lang,'msg.txt') + print('fpath=', fpath, type(fpath)) with codecs.open(fpath,'r','utf-8') as f: line = f.readline() d = {} @@ -32,15 +36,39 @@ class I18n: continue k,v = line.split(':',1) d.update({k:v}) - line = readline() + line = f.readline() return d + def get_languages(self): + if self.languages: + return self.languages + self.languages = self.getLanguages() + return self.languages + + def getLanguages(self): + config = getConfig() + if config.i18n_folder: + langs = [] + for f in os.listdir(config.i18n_folder): + p = os.path.join(config.i18n_folder, f) + if os.path.isdir(p): + langs.append(os.path.basename(f)) + return langs + + if config.i18n_url: + url = '%s%s' % (config.uihome, config.i18n_url) + hc = HttpClient() + d = hc.get(url) + if isinstance(d, list): + return d + return [] + def loadI18n(self,lang): app = App.get_running_app() config = getConfig() self.kvlang[lang] = {} if config.i18n_folder: - self.kvlang[lang] = self.loadI18nFromFolder(lang) + self.kvlang[lang] = self.loadI18nFromI18nFolder(lang) return if config.i18n_url: @@ -63,8 +91,8 @@ class I18n: if not d: self.loadI18n(lang) self.lang = lang - ws = [ w for w in self.i18nWidgets if w is not None ] + ws = [ w for w in self.i18nWidgets if w() is not None ] + for w in ws: + w().changeLang(lang) self.i18nWidgets = ws - for w in self.i18nWidgets: - w.changeLang(lang) diff --git a/kivyblocks/pagepanel.py b/kivyblocks/pagepanel.py index 9a9261d..93c4aa5 100644 --- a/kivyblocks/pagepanel.py +++ b/kivyblocks/pagepanel.py @@ -5,7 +5,7 @@ from kivy.app import App from kivy.clock import Clock from kivy.factory import Factory from kivy.utils import platform -from .baseWidget import VBox, HBox +from .baseWidget import VBox, HBox, I18nWidget from .toggleitems import PressableBox from .utils import * from .swipebehavior import SwipeBehavior @@ -61,6 +61,7 @@ PagePanel description file format "bar_autohide": true when page is idle "bar_size": bar size in CSize unit "bar_at": "top" or "bottom" + "i18n": true of false "bar_css": "panel_css": "left_menu": if defined, it must be a widget instance or a dict @@ -136,6 +137,7 @@ sub-widget's description file format """ def __init__(self, bar_size=2, bar_css='default', + i18n=False, csscls='default', singlepage=False, fixed_before=None, @@ -147,6 +149,7 @@ sub-widget's description file format self.bar_size = bar_size self.bar_autohide = bar_autohide self.bar_at = bar_at + self.i18n = i18n self.singlepage = singlepage self.idle_status = False self.idle_threshold = 10 @@ -218,6 +221,10 @@ sub-widget's description file format }) self.bar.add_widget(self.bar_left_menu) self.bar_left_menu.bind(on_press=self.show_left_menu) + if self.i18n: + self.i18n_w = I18nWidget(size_hint_x=None, width=CSize(5)) + self.bar.add_widget(self.i18n_w) + self.bar_title = HBox(csscls=bar_css) self.bar.add_widget(self.bar_title) self.bar_right_menu = VBox(size_hint=(None,None),size=CSize(bcsize,bcsize)) diff --git a/kivyblocks/paging.py b/kivyblocks/paging.py index 453c207..ba95ad0 100644 --- a/kivyblocks/paging.py +++ b/kivyblocks/paging.py @@ -7,6 +7,7 @@ from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.clock import Clock from kivy.app import App +from kivy.properties import BooleanProperty from functools import partial from appPublic.dictObject import DictObject from appPublic.jsonConfig import getConfig @@ -40,8 +41,8 @@ it fires two type of event """ class PageLoader(EventDispatcher): + loading = BooleanProperty(False) def __init__(self,target=None, **options): - self.loading = False self.target = target self.options = options self.filter = None @@ -72,6 +73,12 @@ class PageLoader(EventDispatcher): self.loader.bind(on_success=self.show_page) self.loader.bind(on_error=self.onerror) + def on_loading(self, *args): + if self.loading: + self.running = Running(self.target) + else: + self.running.dismiss() + def on_newbegin(self): pass diff --git a/kivyblocks/version.py b/kivyblocks/version.py index 8d7ecef..c15a4ac 100644 --- a/kivyblocks/version.py +++ b/kivyblocks/version.py @@ -1 +1 @@ -__version__ = '0.3.23' +__version__ = '0.3.24'