master
yumoqing 2022-10-09 13:14:34 +08:00
parent 4700ba32a3
commit ba29b30fe9
9 changed files with 202 additions and 31 deletions

View File

View File

@ -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]

View File

@ -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)

View File

@ -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)

View File

@ -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,
}
}

View File

@ -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)

View File

@ -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))

View File

@ -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

View File

@ -1 +1 @@
__version__ = '0.3.23'
__version__ = '0.3.24'