Android QPython3 可视化-基础窗口:BaseWindow.py
乘着船
编辑于 2022年01月14日 08:47
收录于文集
共64篇

代码块
Python
自动换行
复制代码
#要求 QPython 3C 3.5.0 或以上
#需要android.py  https://www.bilibili.com/read/cv8679914
from android import *
import abc,pickle
from time import time
from hashlib import md5
from json import dumps,loads
class BaseDict(dict):
    def __init__(self,data=None):
        if data:
            dict.__init__(self,data)
        else:
            dict.__init__(self)
    def __setattr__(self,name,val):
            self[name]=val
    def __getattr__(self,name):
            return self[name]
    def __getstate__(self):
        return self.__dict__.copy()
    def __setstate__(self,dict):
        self.__dict__.update(dict)
    def copy(self):
        return pickle.loads(pickle.dumps(self))
class EventHandler(object):
    def __init__(self,event_name,compare_attribute,compare_value,view=None,handler_function=None):
        self.view=view
        self.event_name=event_name
        self.compare_attribute=compare_attribute
        self.compare_value=compare_value
        self.event_handler_fn=handler_function
    def match_event_data(self,event_data):
        try:
            if event_data["name"]==self.event_name:
                if self.compare_attribute !=None:
                    return event_data["data"][self.compare_attribute]==self.compare_value
                else:
                    return True
        except:
            return False
        else:
            return False
    def __str__(self):
        return str(self.event_name)+":"+str(self.compare_attribute)+"="+str(self.compare_value)
class click_EventHandler(EventHandler):
    EVENT_NAME="click"
    COMPARE_ATTRIBUTE="id"
    def __init__(self,view,handler_function=None):
        super(click_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,view.view_id,view,handler_function)
class itemclick_EventHandler(EventHandler):
    EVENT_NAME="itemclick"
    COMPARE_ATTRIBUTE="id"
    def __init__(self,view,handler_function=None):
        super(itemclick_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,view.view_id,view,handler_function)
class key_EventHandler(EventHandler):
    EVENT_NAME="key"
    COMPARE_ATTRIBUTE="key"
    def __init__(self,key_match_id="4",view=None,handler_function=None):
        super(key_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,key_match_id,view,handler_function)
class _internal_exit_signal():
    EVENT_NAME="fullscreenwrapper2_internal_exit_signal"
    COMPARE_ATTRIBUTE="id"
    eventhandler=EventHandler(EVENT_NAME,COMPARE_ATTRIBUTE,str(os.getpid()),None,None)
    @classmethod
    def post_internal_exit_signal(cls):
        jsla('eventPost',cls.EVENT_NAME,dumps({cls.COMPARE_ATTRIBUTE:str(os.getpid())}),True)
class View(object):
    def __init__(self,view_id,view_type):
        self.view_type=view_type
        self.view_id=view_id
        self._events={}
    def add_event(self,eventhandler):
        self._events[eventhandler.event_name]=eventhandler
    def remove_event(self,event_name):
        self._events.pop(event_name)
    def set_listitems(self,listitems):
        jsla('fullSetList',self.view_id,listitems)
    def __setattr__(self,name,value):
        if name in ("view_type","view_id","_events"):
            object.__setattr__(self,name,value)
        else:
            return jsla('fullSetProperty',self.id,name,value)
    def __getattr__(self,name):
        return rsla('fullGetProperty',self.view_id,name)
    def __str__(self):
        try:
            return self.text
        except AttributeError:
            return None
class Layout(object):
    __metaclass__=abc.ABCMeta
    #默认主题常数0,更多主题常数,参考 https://developer.android.google.cn/reference/android/R.style.html
    defaultTheme=0
    defaultTitle='QSL4A未来小程序'
    #初始化:自身,布局xml,标题,主题常数
    def __init__(self,xml,title=None,theme=None):
        self.uid=md5(str(str(title)+str(os.getpid())+str(time())).encode("UTF-8")).hexdigest()
        self.xml=xml
        if title==None:
            self.title=Layout.defaultTitle
        else:
            self.title=title
        if theme==None:
            self.theme=Layout.defaultTheme
        else:
            self.theme=theme
        self.views=BaseDict()
        self._reset()
    def _reset(self):
        self.views.clear()
        self.views[self.uid]=View(self.uid,"Layout")
    def add_event(self,eventhandler):
        self.views[self.uid].add_event(eventhandler)
    def remove_event(self,event_name):
        self.views[self.uid].remove_event(event_name)
    @abc.abstractmethod
    def on_show(self):
        pass
    @abc.abstractmethod
    def on_close(self):
        pass
class FullScreenWrapper2App(object):
    _layouts=[]
    SHOW_LAYOUT_PUSH_OVER_CURRENT=0
    SHOW_LAYOUT_REPLACING_CURRENT=1
    _SHOW_LAYOUT_POP_CURRENT=2
    #默认全屏窗口显示
    @classmethod
    def show_layout(cls,layout,show_mode=SHOW_LAYOUT_REPLACING_CURRENT):
        if show_mode==cls.SHOW_LAYOUT_PUSH_OVER_CURRENT or show_mode==cls.SHOW_LAYOUT_REPLACING_CURRENT or show_mode==cls._SHOW_LAYOUT_POP_CURRENT:
            curlayoutidx=len(cls._layouts)-1
            if(curlayoutidx > -1):
                cls._layouts[curlayoutidx].on_close()
            jsla('fullShow',layout.xml,layout.title,layout.theme)
            viewsdict=rsla('fullQuery')
            layout._reset()
            views=layout.views
            for viewname in viewsdict:
                views[viewname]=View(viewname,viewsdict[viewname]["type"])
            if show_mode==cls.SHOW_LAYOUT_PUSH_OVER_CURRENT:
                cls._layouts.append(layout)
            elif show_mode==cls.SHOW_LAYOUT_REPLACING_CURRENT:
                if(curlayoutidx > -1):
                    cls._layouts.pop()
                cls._layouts.append(layout)
            elif show_mode==cls._SHOW_LAYOUT_POP_CURRENT:
                if(curlayoutidx > -1):
                    cls._layouts.pop() 
            layout.on_show()
    @classmethod
    def close_layout(cls):
        curlayoutidx=len(cls._layouts)-1
        if curlayoutidx >0:
            cls.show_layout(cls._layouts[curlayoutidx-1],cls._SHOW_LAYOUT_POP_CURRENT)
        elif curlayoutidx==0:
            cls.exit_FullScreenWrapper2App()
    @classmethod
    def exit_FullScreenWrapper2App(cls):
        jsla('fullDismiss')
        _internal_exit_signal.post_internal_exit_signal()
    @classmethod
    def eventloop(cls):
        if len(cls._layouts)<1:
            raise RuntimeError("Trying to start eventloop without a layout visible")
        while(True):
            evt=rsla('eventPoll')
            if(len(evt)>0):
                eventdata=evt[0]
                try:
                    eventdata["data"]=loads(eventdata["data"])
                except:
                    pass
                if _internal_exit_signal.eventhandler.match_event_data(eventdata):
                    break
                curlayout=cls._layouts[len(cls._layouts)-1]
                views=curlayout.views
                for viewname in views:
                    view=views[viewname]
                    _events=view._events
                    for eventname in _events:
                        event=_events[eventname]
                        if event.match_event_data(eventdata):
                            if event.event_handler_fn !=None:
                                event.event_handler_fn(event.view,eventdata)
                                event_handled=True
                                break
    @classmethod
    def set_list_contents(cls,id,list):
        return jsla('fullSetList',id,list)
    @classmethod
    def set_property_value(cls,id,property,value):
        return jsla('fullSetProperty',id,property,value)
    @classmethod
    def get_property_value(cls,id,property):
        return rsla('fullGetProperty',id,property)
    _show_layout=show_layout
    #特殊全屏窗口显示:背景批量替换
    @classmethod
    #显示布局替换背景:自身,布局对象,不透明度(0~255),背景图片,显示模式
    def show_layout_replace_background(cls,layout,opacity=255,background_image=None,show_mode=SHOW_LAYOUT_REPLACING_CURRENT):
        xml=layout.xml
        new=[];c=0;h=0
        o=hex(opacity)[2:]
        if len(o)==1:o='0'+o
        d=new.append;f=xml.find
        while True:
            a=xml.find('android:background=',c)
            if a==-1:
                d(xml[c:])
                break
            d(xml[c:a])
            b=f('#',a)
            h+=1
            if background_image==None or h>1:
                b+=1
                d(xml[a:b])
                c=f(xml[b-2],b)
                e=xml[b:c]
                if len(e)==6:
                    d(o+e)
                else:
                    d(o+e[2:])
            else:
                d(xml[a:b])
                c=f(xml[b-1],b)
                e=xml[b:c]
                d('file://'+background_image)
        layout.xml=''.join(new)
        return cls._show_layout(layout,show_mode)
def Str2Xml(s):
    t=[];r=t.append
    for i in s:
        j=ord(i)
        if j<256 and not (i.isalpha() or i.isdigit()):
            r('&#');r(str(j));r(";")
        else:
            r(i)
    return ''.join(t)
#默认按钮
CCC=('确认','复制','取消')
YCN=('是','复制','否')
#删改至:fullscreenwrapper2,Hariharan Srinath,Jul 19, 2012,https://srinathh.github.io/opensource/fullscreenwrapper ;
#修改目的:减少冗余,提高速度,增加功能。
#最后修改:2021-12-29 by 乘着船
复制成功

#该基础窗口模块看上去没什么用,但它却是其他QPython3可视化窗口的必备条件。

#增加功能:QPython3可视化窗口主题(theme)设置、背景替换(show_layout_replace_background)

#提高速度:属性获取(fullGetProperty)速度与属性写入(fullSetProperty)速度

作者:乘着船@Bilibili

更多文章+下载链接:网页链接​