#!/usr/bin/python # -*- mode: python; coding: utf-8; -*- # ---------------------------------------------------------------------------# # # Copyright (C) 2017 LB # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # ---------------------------------------------------------------------------# from __future__ import division import logging import math import traceback from kivy.animation import Animation from kivy.app import App from kivy.base import EventLoop from kivy.base import stopTouchApp from kivy.cache import Cache from kivy.clock import Clock from kivy.config import Config from kivy.core.audio import SoundLoader from kivy.core.window import Window from kivy.graphics import Color from kivy.graphics import Line from kivy.graphics import Rectangle from kivy.graphics import Triangle from kivy.properties import NumericProperty from kivy.properties import StringProperty from kivy.uix.actionbar import ActionButton from kivy.uix.actionbar import ActionPrevious from kivy.uix.actionbar import ActionView from kivy.uix.behaviors import ButtonBehavior from kivy.uix.boxlayout import BoxLayout from kivy.uix.floatlayout import FloatLayout from kivy.uix.label import Label from kivy.uix.scrollview import ScrollView from kivy.uix.treeview import TreeView from kivy.uix.treeview import TreeViewLabel from kivy.uix.widget import Widget from kivy.utils import platform from pysollib.kivy.LBase import LBase from pysollib.kivy.LTask import LTask, LTaskQ from pysollib.kivy.androidperms import requestStoragePerm from pysollib.kivy.androidrot import AndroidScreenRotation from pysollib.resource import CSI if platform != 'android': Config.set('input', 'mouse', 'mouse,multitouch_on_demand') # ============================================================================= def get_platform(): return platform # ============================================================================= def get_menu_size_hint(): sh = (0.5, 1.0) if Window.size[0] < Window.size[1]: sh = (1.0, 1.0) return sh # ============================================================================= def set_fullscreen(fullscreen=True): if get_platform() == 'android': from jnius import autoclass else: return SDLActivity = autoclass('org.libsdl.app.SDLActivity') SDLActivity.setWindowStyle(fullscreen) # ============================================================================= def get_screen_ori(): if get_platform() == 'android': from jnius import autoclass from jnius import cast else: return None PythonActivity = autoclass('org.kivy.android.PythonActivity') currentActivity = cast('android.app.Activity', PythonActivity.mActivity) # Display = autoclass('android.view.Display') # WindowManager = autoclass('android.view.WindowManager') wm = currentActivity.getWindowManager() d = wm.getDefaultDisplay() so = None if d.getWidth() > d.getHeight(): so = 'landscape' else: so = 'portrait' logging.info("LApp: ori = %s" % so) return so # ============================================================================= class LPopCommander(LBase): def __init__(self, **kw): super(LPopCommander, self).__init__() self.pop_command = kw['pop_command'] def pop(self): if self.pop_command is not None: self.pop_command(0) return True return False # ============================================================================= class LAnimationTask(LTask, LBase): def __init__(self, spos, widget, **kw): super(LAnimationTask, self).__init__(widget.card) self.spos = spos self.widget = widget x = 0.0 y = 0.0 duration = 0.2 transition = 'in_out_quad' bindE = None bindS = None if 'x' in kw: x = kw['x'] if 'y' in kw: y = kw['y'] if 'duration' in kw: duration = kw['duration'] if 'transition' in kw: transition = kw['transition'] if 'bindE' in kw: bindE = kw['bindE'] if 'bindS' in kw: bindS = kw['bindS'] self.delay = duration / 3.0 self.xdest = x self.ydest = y self.duration = duration self.transition = transition self.bindE = bindE self.bindS = bindS print(self.widget.card) def start(self): super(LAnimationTask, self).start() anim = Animation( x=self.xdest, y=self.ydest, duration=self.duration, transition=self.transition) if self.bindE is not None: anim.bind(on_complete=self.bindE) if self.bindS is not None: anim.bind(on_start=self.bindS) self.widget.pos = self.spos anim.bind(on_complete=self.stop) anim.start(self.widget) def updateDestPos(self, pos): self.xdest = pos[0] self.ydest = pos[1] # ============================================================================= class LAnimationMgr(object): def __init__(self, **kw): super(LAnimationMgr, self).__init__() self.tasks = [] self.callbacks = [] self.taskQ = LTaskQ() def checkRunning(self): return len(self.tasks) > 0 def addEndCallback(self, cb): self.callbacks.append(cb) def taskEnd(self, task, value): if value: # print('LAnimationMgr: taskEnd = %s %s' % (task, value)) self.tasks.remove(task) if not self.checkRunning(): # print('LAnimationMgr: taskEnd ->', len(self.callbacks), 'callbacks') # noqa for cb in self.callbacks: cb() # print('LAnimationMgr: taskEnd -> callbacks done') self.callbacks = [] # print('Clock.get_fps() ->', Clock.get_fps()) def taskInsert(self, task): self.tasks.append(task) task.bind(done=self.taskEnd) Clock.schedule_once(lambda dt: self.taskQ.taskInsert(task), 0.016) LAnimationManager = LAnimationMgr() # ============================================================================= def LAfterAnimation(task, delay=0.0): def dotask(): # noqa Clock.schedule_once(lambda dt: task()) def mkcb(task): # noqa def cb(dt): if LAnimationManager.checkRunning(): LAnimationManager.addEndCallback(dotask) else: dotask() return cb Clock.schedule_once(mkcb(task), delay) # ============================================================================= LSoundLoader = SoundLoader # ============================================================================= class LBoxLayout(BoxLayout, LBase): def __init__(self, **kw): super(LBoxLayout, self).__init__(**kw) def winfo_screenwidth(self): return self.size[0] def winfo_screenheight(self): return self.size[1] # ============================================================================= def addAnchorOffset(pos, size, anchor): # print ('MfxCanvas: anchor=%s' % (anchor)) x = pos[0] y = pos[1] xa = 0 ya = 0 if anchor == "n": ya = -1 elif anchor == "w": xa = -1 elif anchor == "s": ya = 1 elif anchor == "e": xa = 1 elif anchor == "ne": ya = -1 xa = 1 elif anchor == "nw": ya = -1 xa = -1 elif anchor == "se": ya = 1 xa = 1 elif anchor == "sw": ya = 1 xa = -1 if xa == 0: x = x - size[0] / 2.0 elif xa == 1: x = x - size[0] if ya == 0: y = y - size[1] / 2.0 elif ya == 1: y = y - size[1] return (x, y) # ============================================================================= def LColorToLuminance(color): kc = color if isinstance(color, str): kc = LColorToKivy(color) r = kc[0] g = kc[1] b = kc[2] Y = 0.2989*r + 0.5866*g + 0.1145*b return Y # ============================================================================= def LTextureToLuminance(texture): b = texture.pixels s = 4 n = len(b)/1000 if n > 4: s = n - n % 4 n = 0 ll = 0 for i in range(0, len(b), int(s)): rr = int.from_bytes(b[i:i+1], byteorder='big', signed=False) / 256.0 # noqa gg = int.from_bytes(b[i+1:i+2], byteorder='big', signed=False) / 256.0 # noqa bb = int.from_bytes(b[i+2:i+3], byteorder='big', signed=False) / 256.0 # noqa ll = ll + LColorToLuminance([rr, gg, bb, 1]) n += 1 return (ll/n) # ============================================================================= def LColorToKivy(outline): if (outline[0] == '#'): outline = outline[1:] ou0 = float(int(outline[0:2], 16)) / 256.0 ou1 = float(int(outline[2:4], 16)) / 256.0 ou2 = float(int(outline[4:6], 16)) / 256.0 ou3 = 1.0 if len(outline) >= 8: ou3 = float(int(outline[6:8], 16)) / 256.0 return ou0, ou1, ou2, ou3 # ============================================================================= def cardfactor(canvas): # heuristic to find some sort of 'fontsize' out of the cardset. # We take the original small cardsets as reference and calculate # a correction factor. def pyth(a, b): return math.sqrt(a*a+b*b) cardscale = 1.0 try: cs = canvas.wmain.app.images.cs # print('Cardset:', cs) # print('Cardset:', cs.type) cardbase = pyth(73, 97) if cs.type == CSI.TYPE_FRENCH: cardbase = pyth(73, 97) elif cs.type == CSI.TYPE_HANAFUDA: cardbase = pyth(64, 102) elif cs.type == CSI.TYPE_MAHJONGG: cardbase = pyth(38, 54) elif cs.type == CSI.TYPE_TAROCK: cardbase = pyth(80, 80) elif cs.type == CSI.TYPE_HEXADECK: cardbase = pyth(50, 80) elif cs.type == CSI.TYPE_MUGHAL_GANJIFA: cardbase = pyth(80, 80) elif cs.type == CSI.TYPE_NAVAGRAHA_GANJIFA: cardbase = pyth(80, 80) elif cs.type == CSI.TYPE_DASHAVATARA_GANJIFA: cardbase = pyth(80, 80) elif cs.type == CSI.TYPE_TRUMP_ONLY: cardbase = pyth(35, 35) si = canvas.wmain.app.images.getSize() cardsize = pyth(si[0], si[1]) cardscale = cardsize/cardbase except: # noqa: E722 pass return cardscale # ============================================================================= class LText(Widget, LBase): text = StringProperty('') def __init__(self, canvas, x, y, **kwargs): super(LText, self).__init__(**kwargs) # super(LText, self).__init__() if 'text' not in kwargs: kwargs['text'] = 'X' font = 'helvetica' fontsize = 18.0 if 'font' in kwargs: font = kwargs['font'][0] fontsize = kwargs['font'][1] del kwargs['font'] self.anchor = 'nw' if 'anchor' in kwargs: self.anchor = kwargs['anchor'] self.text = kwargs['text'] self.coreFontSize = fontsize self.coreFont = font # print('LText: font = %s, font_size = %s' % (font, fontsize)) # print('LText: text = %s' % (self.text)) kwargs['font'] = font kwargs['font_size'] = fontsize * cardfactor(canvas) class MyLabel(Label, LBase): pass self.label = MyLabel(**kwargs) self.label.texture_update() self.coreSize = self.label.texture_size self.corePos = (x, y) self.prnt = canvas # print('LText: corePos = %s, coreSize = %s' # % (self.corePos, self.coreSize)) self.size = self.label.texture_size self.bind(size=self.updateCanvas) self.bind(pos=self.updateCanvas) self.bind(text=self.updateCanvas) def updateCanvas(self, inst, val): self.label.text = self.text self.label.texture_update() self.coreSize = self.label.texture_size cp = addAnchorOffset(self.corePos, self.coreSize, self.anchor) cs = self.coreSize pos, size = self.prnt.CoreToKivy(cp, cs) # print('LText: pos = %s, size = %s' % (pos, size)) color = LColorToKivy(self.prnt._text_color) # print('LText: color = %s' % str(color)) self.canvas.clear() with self.canvas: Color(color[0], color[1], color[2], color[3]) Rectangle(texture=self.label.texture, pos=pos, size=size) # ============================================================================= class LEvent(object): def __init__(self): self.x = 0 self.y = 0 self.cardid = -1 self.char = False pass # ============================================================================= class LLine(Widget, LBase): def __init__(self, canvas, args, **kw): super(LLine, self).__init__(**kw) print('kw = %s%s' % (args, kw)) lwidth = 10 fill = '#ee3344' ashape = () arrow = 'none' self.prnt = canvas xmin = 100000 ymin = 100000 xmax = -100000 ymax = -100000 self.corePoly = [] if isinstance(args[0], list): kww = args[1] if ('width' in kww): lwidth = kww['width'] self.lwidth = lwidth if ('fill' in kww): fill = kww['fill'] self.fill = fill if ('arrowshape' in kw): ashape = kw['arrowshape'] self.ashape = ashape if ('arrow' in kw): arrow = kw['arrow'] self.arrow = arrow pts = args[0] ipts = iter(pts) for x, y in zip(ipts, ipts): print('%s.%s' % (x, y)) self.corePoly.append(x) self.corePoly.append(y) if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y else: if ('width' in kw): lwidth = kw['width'] self.lwidth = lwidth if ('fill' in kw): fill = kw['fill'] self.fill = fill if ('arrowshape' in kw): ashape = kw['arrowshape'] self.ashape = ashape if ('arrow' in kw): arrow = kw['arrow'] self.arrow = arrow for i in range(0, 2): x = args[2 * i] y = args[2 * i + 1] self.corePoly.append(x) self.corePoly.append(y) if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y print('width = %s' % self.lwidth) print('color = %s' % self.fill) print('arrow = %s' % self.arrow) print('ashape = %s' % str(self.ashape)) self.alist = [] if self.arrow == 'last': self.alist.append(self.corePoly[-2]) self.alist.append(self.corePoly[-1]) self.alist.append(self.corePoly[-4]) self.alist.append(self.corePoly[-3]) elif self.arrow != 'none': self.alist.append(self.corePoly[0]) self.alist.append(self.corePoly[1]) self.alist.append(self.corePoly[2]) self.alist.append(self.corePoly[3]) self.corePos = (xmin, ymin) self.coreSize = (xmax - xmin, ymax - ymin) self.pos = self.corePos self.size = self.coreSize self.bcolor = LColorToKivy(self.fill) self.bind(size=self.updateCanvas) self.bind(pos=self.updateCanvas) def updateCanvas(self, instance, value): # size = self.size # pos = self.pos # Linie: poly = None poly = [] dmy, sxy = self.prnt.CoreToKivy( (0.0, 0.0), (self.lwidth, self.lwidth)) wpoly = sxy[1] ipts = iter(self.corePoly) for x, y in zip(ipts, ipts): print('%s.%s' % (x, y)) xy, dmy = self.prnt.CoreToKivy((x, y)) poly.append(xy[0]) poly.append(xy[1]) def rot(x, y, a): x1 = x * math.cos(a) + y * math.sin(a) y1 = y * math.cos(a) - x * math.sin(a) return (x1, y1) # Pfeil: PI = 3.1415926 atrio = None atrio = [] if (len(self.ashape) > 2): dx = (self.alist[0] - self.alist[2]) dy = (self.alist[1] - self.alist[3]) if (dx == 0.0): if (dy > 0.0): ang = -PI / 2.0 else: ang = PI / 2.0 else: ang = math.atan(dy / dx) if (dx > 0.0): ang = ang + PI # (kante, winkel?) x = self.ashape[0] * math.cos(self.ashape[1] * PI / 360.0) y = 2.0 * self.ashape[0] * math.sin(self.ashape[1] * PI / 360.0) # (länge, breite?) # x = self.ashape[0] # y = self.ashape[1] o = self.ashape[2] axy, dmy = self.prnt.CoreToKivy((self.alist[0], self.alist[1])) dmy, asxy = self.prnt.CoreToKivy((0, 0), (x, y)) dmy, aoff = self.prnt.CoreToKivy((0, 0), (o, o)) print('asxy=%s' % str(asxy)) x1, y1 = rot(-aoff[0], 0.0, ang) atrio.append(x1 + axy[0]) atrio.append(y1 + axy[1]) x1, y1 = rot(asxy[0] - aoff[0], asxy[1], ang) atrio.append(x1 + axy[0]) atrio.append(y1 + axy[1]) x1, y1 = rot(asxy[0] - aoff[0], -asxy[1], ang) atrio.append(x1 + axy[0]) atrio.append(y1 + axy[1]) self.canvas.clear() with self.canvas: Color(self.bcolor[0], self.bcolor[1], self.bcolor[2], self.bcolor[3]) Line(points=poly, width=wpoly, cap='none', joint='bevel') if (len(atrio) > 2): Triangle(points=atrio) # ============================================================================= class LRectangle(Widget, LBase): def __init__(self, prnt, args, **kw): super(LRectangle, self).__init__(**kw) self.prnt = prnt # print('width %s' % kw['width']) # print('outline %s' % kw['outline']) # print('fill %s' % kw['fill']) width = 10.0 if ('width' in kw): width = float(kw['width']) bcolor = '#ffa000a0' if ('outline') in kw: bcolor = kw['outline'] if (not bcolor or len(bcolor) < 7): bcolor = '#ffa000a0' fcolor = '#00aaff20' if ('fill') in kw: fcolor = kw['fill'] if (not fcolor or len(fcolor) < 7): fcolor = '#00aaff20' self.group = None if 'group' in kw: self.group = kw['group'] xmin = float(args[0]) ymin = float(args[1]) xmax = float(args[2]) ymax = float(args[3]) # print ('LRectangle: min = %s.%s' % (xmin, ymin)) # print ('LRectangle: max = %s.%s' % (xmax, ymax)) # print ('LRectangle: border = %s' % (width)) self.border = width self.fcolor = LColorToKivy(fcolor) self.bcolor = LColorToKivy(bcolor) self.corePos = (xmin, ymin) self.coreSize = (xmax - xmin, ymax - ymin) self.pos = self.corePos self.size = self.coreSize self.topleft = (xmin + width / 2.0, ymin + width / 2.0) self.bottomright = (xmax - width / 2.0, ymax - width / 2.0) self.poly = None self.bind(size=self.updateCanvas) self.bind(pos=self.updateCanvas) def updateCanvas(self, instance, value): # print('LRectangle: updateCanvas') pos, size = self.prnt.CoreToKivy(self.corePos, self.coreSize) bpos, dmy = self.prnt.CoreToKivy(self.topleft) tpos, dmy = self.prnt.CoreToKivy(self.bottomright) poly = [bpos[0], bpos[1], tpos[0], bpos[1], tpos[0], tpos[1], bpos[0], tpos[1], bpos[0], bpos[1]] cf = cardfactor(self.prnt) dmy, brd = self.prnt.CoreToKivy( (0.0, 0.0), (self.border, self.border)) border = brd[1] * cf self.canvas.clear() with self.canvas: Color(self.fcolor[0], self.fcolor[1], self.fcolor[2], self.fcolor[3]) Rectangle(pos=pos, size=size) Color(self.bcolor[0], self.bcolor[1], self.bcolor[2], self.bcolor[3]) Line(points=poly, width=border) def on_touch_down(self, touch): if self.collide_point(*touch.pos): if self.group is not None: logging.info('LRectangle: self=%s group=%s' % (self, self.group)) if '<1>' in self.group.bindings: # logging.info('LRectangle: size=%s' % (self.size)) ppos, psize = self.group.canvas.KivyToCore(touch.pos) event = LEvent() event.x = ppos[0] event.y = ppos[1] if touch.is_double_tap: self.group.bindings[''](event) else: self.group.bindings['<1>'](event) return True return False def on_touch_up(self, touch): if self.collide_point(*touch.pos): if self.group is not None: logging.info('LRectangle: self=%s group=%s' % (self, self.group)) if '' in self.group.bindings: ppos, psize = self.group.canvas.KivyToCore(touch.pos) event = LEvent() event.x = ppos[0] event.y = ppos[1] self.group.bindings[''](event) return True return False # ============================================================================= # Represents a Card as Kivy Window. Will contain an LImage item as child. # Images are managed in cards.py according to the cards state. Processes # Events/Action on the card or other images, as LImage is designed to not # process events. Should not take more than one child (LImage) at a time. class LImageItem(BoxLayout, LBase): def __init__(self, **kw): super(LImageItem, self).__init__(**kw) self.game = None self.card = None self.group = None self.image_type = "undefined" if 'game' in kw: self.game = kw['game'] if 'card' in kw: self.card = kw['card'] self.image_type = "card" if 'group' in kw: self.group = kw['group'] if 'image_type' in kw: self.image_type = kw['image_type'] self.dragstart = None # ev. noch globales cache für stacks->game und cards->stack # einrichten. Aber: stacks hängt vom jeweiligen spiel ab. def __str__(self): return f'' def get_image_type(self): return self.image_type def send_event_pressed_n(self, event, n): if self.group and n in self.group.bindings: self.group.bindings[n](event) def send_event_pressed(self, touch, event): if touch.is_double_tap: self.send_event_pressed_n(event, '') else: button = 'left' if 'button' in touch.profile: button = touch.button if button == 'left': self.send_event_pressed_n(event, '<1>') return if button == 'middle': self.send_event_pressed_n(event, '<2>') return if button == 'right': self.send_event_pressed_n(event, '<3>') return def on_touch_down(self, touch): # print('LCardImage: size = %s' % self.size) if self.collide_point(*touch.pos): if (self.game): for stack in self.game.allstacks: for i in range(len(stack.cards)): if stack.cards[i] == self.card: print('LCardImage: stack = %s' % stack) print('LCardImage: touch = %s' % str(touch)) print('grab') # grab the touch! touch.grab(self) ppos, psize = self.game.canvas.KivyToCore( touch.pos, self.size) event = LEvent() event.x = ppos[0] event.y = ppos[1] self.dragstart = touch.pos event.cardid = i self.send_event_pressed(touch, event) AndroidScreenRotation.lock(toaster=False) return True if self.group is not None: print('LCardImage: self=%s group=%s' % (self, self.group)) if '<1>' in self.group.bindings: ppos, psize = self.group.canvas.KivyToCore( touch.pos, self.size) event = LEvent() event.x = ppos[0] event.y = ppos[1] self.group.bindings['<1>'](event) return True if self.card is None: return False if self.game is None: return False # print('LCardImage: touch_down on %s' % str(touch.pos)) return False def send_event_released_1(self, event): if self.group and '' in self.group.bindings: self.group.bindings[''](event) def on_touch_up(self, touch): if touch.grab_current is self: # release my grabbed touch! print('ungrab') touch.ungrab(self) return True if self.collide_point(*touch.pos): if (self.game): for stack in self.game.allstacks: for i in range(len(stack.cards)): if stack.cards[i] == self.card: print('LCardImage: stack = %s' % stack) ppos, psize = self.game.canvas.KivyToCore( touch.pos, self.size) event = LEvent() event.x = ppos[0] event.y = ppos[1] event.cardid = i self.send_event_released_1(event) return True if self.group is not None: print('LCardImage: self=%s group=%s' % (self, self.group)) if '' in self.group.bindings: ppos, psize = self.group.canvas.KivyToCore( touch.pos, self.size) event = LEvent() event.x = ppos[0] event.y = ppos[1] self.group.bindings[''](event) return True if self.card is None: return False if self.game is None: return False # print('LCardImage: touch_up on %s' % str(touch.pos)) return False def on_touch_move(self, touch): # behandeln nur wenn grabbed if touch.grab_current is not self: return False if 'pos' not in touch.profile: return False print('LCardImage: touch_move on %s' % str(touch.pos)) for stack in self.game.allstacks: for i in range(len(stack.cards)): if stack.cards[i] == self.card: print('LCardImage: stack = %s/%s' % (stack, touch)) ppos, psize = self.game.canvas.KivyToCore( touch.pos, self.size) event = LEvent() event.x = ppos[0] event.y = ppos[1] event.cardid = i stack._motionEventHandler(event) return True # print('LCardImage: touch_move on %s' % str(touch.pos)) return False # ============================================================================= # Treeview class LTreeRoot(TreeView, LBase): def __init__(self, **kw): super(LTreeRoot, self).__init__(**kw) self.kw = kw def closeLastNode(self): ret = False lastopen = None for ti in reversed(self.children): if isinstance(ti, LTreeNode): if ti.is_open: lastopen = ti if lastopen is not None: self.toggle_node(lastopen) self.select_node(lastopen) ret = True return ret class LTreeNode(ButtonBehavior, TreeViewLabel, LBase): # def __init__(self, gameview, **kw): def __init__(self, **kw): self.command = None if 'command' in kw: self.command = kw['command'] self.variable = None if 'variable' in kw: self.variable = kw['variable'] if 'value' in kw: self.value = kw['value'] if ('text' in kw): self.title = kw['text'] super(LTreeNode, self).__init__(markup=True, **kw) if self.variable: self.variable.bind(value=self.onVarChange) self.onVarChange(self.variable, self.variable.value) # self.gameview = gameview self.coreFont = self.font_size # self.scaleFont(self.gameview.size[1]) # self.gameview.bind(size=self.scaleFontCB) # nicht skalieren! self.bind(on_release=self.on_released) self.bind(is_selected=self.onSelect) self.bind(is_open=self.onOpen) def onVarChange(self, instance, value): # print('LTreeNode: onVarChange(%s, %s, %s)' # % (instance, value, type(value))) if type(value) is bool: self.setCheck(value) if type(value) is int: self.setVal(value) if type(value) is str: self.setVal(value) # if type(value) is unicode: # self.setVal(value) def setCheck(self, value): # print('LTreeNode: setCheck(%s)' % value) if value: # self.text = '+ '+self.title self.text = '[b]+[/b] ' + self.title else: self.text = '- ' + self.title self.texture_update() def setVal(self, value): # print('LTreeNode: setVal(%s)' % value) if value == self.value: # fs = str(int(self.font_size+2)) # print ('%s.%s' % (self.font_size, fs)) # self.text = '[size='+fs+'][b]'+self.title+'[/b][/size]' # self.text = 'o '+self.title self.text = '[b]o[/b] ' + self.title # self.text = u'\u25cf '+self.title # unicode filled circle else: self.text = self.title self.text = u' ' + self.title # self.text = u'\u25cb '+self.title # unicode open circle self.texture_update() # font skalierung. def scaleFont(self, value): self.font_size = int(self.coreFont * value / 550.0) def scaleFontCB(self, instance, value): self.scaleFont(value[1]) # benutzer interaktion. def onSelect(self, instance, val): if val: print('select %s' % self.title) else: print('deselect %s' % self.title) pass def collapseChildren(self, deep=False): def cc(p, n): for c in n.nodes: if c.is_open: cc(p, c) p.toggle_node(c) p = self.parent if p and isinstance(p, LTreeRoot): for n in self.nodes: if n.is_open: # n.collapseChildren() # ginge nur mit LTreeNode! if deep: cc(p, n) # geht mit allen TreeViewNode p.toggle_node(n) def collapseSiblings(self, deep=True): def cc(p, n): for c in n.nodes: if c.is_open: cc(p, c) p.toggle_node(c) p = self.parent if p and isinstance(p, LTreeRoot): # print('expand: LTreeRoot') for n in p.root.nodes: # print('expand: -> check %s' % n.title) if n != self and n.is_open and n.level >= self.level: # print('expand: -> close %s' % n.title) if deep: cc(p, n) p.toggle_node(n) pn = self.parent_node if pn and isinstance(pn, LTreeNode): # print('expand: LTreeNode') for n in pn.nodes: # print('expand: -> check %s' % n.title) if n != self and n.is_open and n.level >= self.level: # print('expand: -> close %s' % n.title) if deep: cc(p, n) p.toggle_node(n) def onOpen(self, instance, val): if val: # print('expand %s, %s' % (self.level, self.title)) self.collapseSiblings(deep=False) else: # print('collapse %s, %s' % (self.level, self.title)) pass def on_released(self, v): if self.command: Clock.schedule_once(self.commandCB, 0.1) else: Clock.schedule_once(self.toggleCB, 0.1) def commandCB(self, d): self.command() def toggleCB(self, d): # hier könnte der knoten ev. auch neu aufgebaut werden ?! self.parent.toggle_node(self) # ============================================================================= class LTopLevelContent(BoxLayout, LBase): def __init__(self, **kw): super(LTopLevelContent, self).__init__(**kw) # Macht die Hintergrundfarbe der TopLevel (Dialog-) Fenster. with self.canvas.before: Color(0.45, 0.5, 0.5, 1.0) self.rect = Rectangle(pos=self.pos, size=self.size) self.bind(pos=self.update_rect) self.bind(size=self.update_rect) def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size def wm_minsize(self, w, h): pass # ============================================================================= class LTopLine(ButtonBehavior, Label, LBase): def __init__(self, **kw): super(LTopLine, self).__init__(**kw) with self.canvas.before: Color(0.45, 0.3, 0.3, 1.0) self.rect = Rectangle(pos=self.pos, size=self.size) self.bind(pos=self.update_rect) self.bind(size=self.update_rect) self.maxlines = 0 self.halign = 'center' self.valign = 'center' def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size def on_size(self, o, s): self.text_size = s def on_press(self): print('press') def on_release(self): print('release') # ============================================================================= class LTopLevelBase(BoxLayout, LBase): def __init__(self, title='', **kw): super(LTopLevelBase, self).__init__(orientation="vertical", **kw) self.title = title self.titleline = LTopLine(text=title, size_hint=[1.0, 0.15]) self.content = LTopLevelContent(orientation="vertical", **kw) self.add_widget(self.titleline) self.add_widget(self.content) # ============================================================================= class LTopLevel0(LTopLevelBase, LBase): def __init__(self, top, title='', **kw): super(LTopLevel0, self).__init__(title=title, **kw) self.main = top self.titleline.bind(on_press=self.onClick) self.main.pushWork(self.title, self) def onClick(self, event): print('LTopLevel: onClick') self.main.popWork(self.title) # ============================================================================= class LTopLevel(LTopLevelBase, LBase): def __init__(self, parent, title='', **kw): super(LTopLevel, self).__init__(title=title, **kw) self.mainwindow = parent def processAndroidBack(self): ret = False # try to collapse the last open tree node # the treeview will be located inside of a scrollview # (-> menubar.py) for c in self.content.children: # print("childitem: %s" % str(c)) if isinstance(c, LScrollView): for t in reversed(c.children): # print(" childitem: %s" % str(t)) if isinstance(t, LTreeRoot): ret = t.closeLastNode() if isinstance(c, BoxLayout): for t in reversed(c.children): # print(" childitem: %s" % str(t)) if isinstance(t, LPopCommander): ret = t.pop() pass return ret # ============================================================================= class LMenuBar(BoxLayout, LBase): def __init__(self, **kw): super(LMenuBar, self).__init__(**kw) self.menu = None self.size_hint = (1.0, 0.08) def setMenu(self, menu): print('LMenuBar: setMenu %s, %s' % (self, menu)) # Letztes Menu entfernen last = self.menu if (last is not None): self.remove_widget(last) self.menu = None # Neues Menu einfügen if (menu is not None): self.add_widget(menu) self.menu = menu menu.setBar(self) def getMenu(self): return self.menu # ============================================================================= class LMenu(ActionView, LBase): def __init__(self, prev, **kw): super(LMenu, self).__init__(**kw) class MyActionPrev(ActionPrevious, LBase): pass kw['app_icon'] = 'data/images/icons/48x48/pysol.png' kw['with_previous'] = prev kw['size_hint'] = (.01, 1) self.ap = MyActionPrev(**kw) self.add_widget(self.ap) self.bar = None self.uppermenu = None def addItem(self, mi): # print ('LMenu: addItem '+str(mi)+' '+str(self.bar)) mi.setBar(self.bar) self.add_widget(mi) def setBar(self, bar): # print ('LMenu: setBar %s, %s' % (self, bar)) self.bar = bar def prev(self, menu): # print ('LMenu: prev = %s' % menu) self.uppermenu = menu self.ap.bind(on_release=self.upper) pass def upper(self, event): print('upper') self.bar.setMenu(self.uppermenu) def delete(self, pos, mode): # print ('LMenu(%s): delete(%s, %s)' % (self, pos, mode)) items = [] menues = [] for c in self.children: if (type(c) is LMenuItem): # print ('LMenu: to delete child %s' % c) items.append(c) elif (type(c) is LMenu): # print ('LMenu: to delete child %s' % c) menues.append(c) else: # print ('LMenu: unknown child %s' % c) pass for c in items: # print ('LMenu: delete child %s' % c) self.clear_widgets([c]) for c in menues: # print ('LMenu: delete child %s' % c) self.clear_widgets([c]) c.delete(pos, mode) # def __str__(self): # return hex(id(self)) # ============================================================================= class LMenuItem(ActionButton, LBase): def __init__(self, menu, **kw): super(LMenuItem, self).__init__(**kw) # super(LMenuItem, self).__init__() self.bar = None self.submenu = None self.menu = menu self.menu.addItem(self) self.minimum_width = '200sp' if 'command' in kw: self.setCommand(kw['command']) if 'submenu' in kw: self.setSubMenu(kw['submenu']) def setBar(self, bar): # print ('LMenuItem: setBar %s, %s' % (self, bar)) self.bar = bar def onClick(self, event): # print('LMenuItem: onClick') # print('LMenuItem: submenu vorh: '+str(self.submenu)) self.bar.setMenu(self.submenu) return True def setSubMenu(self, submenu): # print('LMenuItem: setSubMenu') self.submenu = submenu # print('LMenuItem: setSubMenu: '+str(self.submenu)) self.submenu.prev(self.menu) self.submenu.setBar(self.bar) self.bind(on_release=self.onClick) pass def setCommand(self, cmd): print('LMenuItem: setCommand') self.bind(on_release=cmd) # def __str__(self): # return hex(id(self)) # ============================================================================= class LScrollView(ScrollView, LBase): def __init__(self, **kw): super(LScrollView, self).__init__(**kw) self.delayDown = False self.touch = None def delayReset(self, dt): if not self.delayDown: return self.delayDown = False ScrollView.on_touch_down(self, self.touch) # Scroll ist original viel zu flink auf den Touchgeräten. # Wir versuchen das hier etwas abzuschwächen. def on_touch_down(self, touch): self.delayDown = True self.touch = touch Clock.schedule_once(self.delayReset, 0.15) def on_touch_up(self, touch): if self.delayDown: ScrollView.on_touch_down(self, self.touch) self.delayDown = False return ScrollView.on_touch_up(self, touch) def on_touch_move(self, touch): return ScrollView.on_touch_move(self, touch) # ============================================================================= class LWorkWindow(Widget): def __init__(self): super(LWorkWindow, self).__init__() # beispiel zu canvas (hintergrund) with self.canvas.before: Color(0, 1, 1, 0.4) self.rect = Rectangle(pos=self.pos, size=self.size) self.bind(pos=self.update_rect) self.bind(size=self.update_rect) def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size def on_touch_down(self, touch): print('LWorkWindow: touch_down on %s' % str(touch.pos)) # return True # ============================================================================= # TkBase: # When using (introducing) new methods of the main tk window in the tk-version # please check if that method is catched here. And provide appropriate # implementation if needed. Otherwise the android version will crash. # LB241029. class LTkBase: # Tk Emulation needs. def __init__(self): self.title = "default title" self.icontitle = "default title" logging.info("LTkBase: __init__()") self.sleeping = False self.in_loop = False self.screenSize = (1000, 1000) def cget(self, strg): return False def wm_title(self, strg): self.title = strg logging.info("LTkBase: wm_title %s" % strg) if (self.app): # self.app.top.topLine.text = strg self.app.top.getMenu().ap.title = strg def wm_iconname(self, strg): self.icontitle = strg logging.info("LTkBase: wm_iconname %s" % strg) def eval_screen_dim(self, size): self.screenSize = size if get_platform() == 'android': from jnius import autoclass from jnius import cast else: return PythonActivity = autoclass('org.kivy.android.PythonActivity') currentActivity = cast( 'android.app.Activity', PythonActivity.mActivity) wm = currentActivity.getWindowManager() d = wm.getDefaultDisplay() self.screenSize = (d.getWidth(), d.getHeight()) def winfo_screenwidth(self): logging.info("LTkBase: winfo_screenwidth %s" % str(self.size[0])) return self.size[0] def winfo_screenheight(self): logging.info("LTkBase: winfo_screenheight %s" % str(self.size[1])) return self.size[1] def winfo_screendepth(self): return 32 def wm_minsize(self, x, y): pass def option_add(self, a, b, c): pass def option_get(self, a, b): return 0 def wm_withdraw(self): logging.info("LTkBase: wm_withdraw") pass def busyUpdate(self): print('LTkBase: busyUpdate()') pass def grid_columnconfigure(self, a, weight): pass def grid_rowconfigure(self, a, weight): pass def connectApp(self, app): logging.info("LTkBase: connectApp %s" % str(app)) self.app = app pass def wm_geometry(self, val): logging.info("LTkBase: wm_geometry %s" % str(val)) pass def update_idletasks(self): # logging.info("LTkBase: update_idletasks") try: if len(EventLoop.event_listeners) > 0: self.in_loop = True EventLoop.idle() self.in_loop = False else: logging.info("LTkBase: update_idletasks: terminating") except Exception: self.in_loop = False logging.info("LTkBase: update_idletasks: exception") def wm_state(self): return "" def wm_deiconify(self): pass def mainloop(self): logging.info("LTkBase: mainloop") pass def quit(self): logging.info("LTkBase: quit") stopTouchApp() def interruptSleep(self): # logging.info('LTkBase: interruptSleep') self.update_idletasks() # self.sleep_var = 1 return def mainquit(self): logging.info('LTkBase: mainquit') lapp = App.get_running_app() lapp.mainloop.send(None) # Spielprozess verlassen return def onWakeUp(self, dt): self.sleeping = False def sleep(self, seconds): logging.info('LTkBase: sleep %s seconds' % seconds) self.sleeping = True Clock.schedule_once(self.onWakeUp, seconds) while self.sleeping: # time.sleep(0.05) self.in_loop = True EventLoop.idle() self.in_loop = False def waitCondition(self, condition, swallow=False, pickup=False): # logging.info('LTkBase: wait condition start') while condition(): self.in_loop = True if swallow: # eat picked input up for provider in EventLoop.input_providers: provider.update(dispatch_fn=lambda *x: None) EventLoop.idle() if pickup: # pick input from os if EventLoop.window: EventLoop.window.mainloop() self.in_loop = False # logging.info('LTkBase: wait condition end') def waitAnimation(self, swallow=False, pickup=False): self.waitCondition(LAnimationManager.checkRunning, swallow=swallow, pickup=pickup) def tkraise(self): pass def winfo_ismapped(self): return True # ??? def attributes(self, *args): pass # ============================================================================= class LStack: def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def push(self, key, item): self.items.append((key, item)) def pop(self, key): for i in range(len(self.items)): t = self.items[i] if (t[0] == key): self.items.pop(i) return t[1] return None def peek(self, key): for i in range(len(self.items)): t = self.items[i] if (t[0] == key): return t return None def size(self): return len(self.items) # ============================================================================= class LMainWindow(BoxLayout, LTkBase): longPress = NumericProperty(0) def __init__(self, **kw): super(LMainWindow, self).__init__(orientation='vertical') LTkBase.__init__(self) self.menuArea = LMenuBar() self.workContainer = LBoxLayout(orientation='horizontal') self.workContainerO = LBoxLayout(orientation='horizontal') self.workContainer1 = LBoxLayout(orientation='vertical') self.workArea = None self.toolBar = None self.toolBarPos = 0 self.bindings = {} self._w = '.' self.add_widget(self.menuArea) self.add_widget(self.workContainerO) self.workContainerO.add_widget(self.workContainer) self.workStack = LStack() self.app = None ''' from kivy.graphics import opengl_utils print('OPENGL support:') print(opengl_utils.gl_get_extensions()) ''' # self.touches = [] with self.canvas.before: Color(0.15, 0.15, 0.15, 1) self.rect = Rectangle(pos=self.pos, size=self.size) self.bind(pos=self.update_rect) self.bind(size=self.update_rect) def update_rect(self, *args): self.pos = (0, 0) self.rect.pos = self.pos self.rect.size = self.size def on_motion(self, m): print('on_motion', m) pass # Events. def on_touch_down(self, touch): ret = False if super().on_touch_down(touch): return True # print(dir(touch)) # multitouch detection ''' #print("MainWindow touch_down",touch.ox,touch.oy) #print("MainWindow touch_down",touch.sx,touch.sy) #print("MainWindow touch_down",touch.px,touch.py) self.touches.append(touch) print("touches cnt = ",len(self.touches)) ''' # multiclick detection if touch.is_double_tap: # print('Touch is a double tap !') # print(' - interval is', touch.double_tap_time) # print(' - distance betw. previous is', touch.double_tap_distance) AndroidScreenRotation.unlock() ''' if touch.is_triple_tap: print('Touch is a triple tap !') print(' - interval is', touch.triple_tap_time) print(' - distance between previous is', touch.triple_tap_distance) ''' # (Eventloop reentrancy check) if self.in_loop: return ret # (demo mode stop - nur auf spielfläche) if '' in self.bindings: pgs = self.workStack.peek('playground') if pgs: pg = pgs[1] if pg.collide_point(*touch.pos): event = LEvent() event.char = True self.bindings[''](event) # standard notifikation: for c in self.children: ret = c.on_touch_down(touch) if ret: break return ret def on_touch_up(self, touch): ret = False if super().on_touch_up(touch): return True # long press only on playground. pgs = self.workStack.peek('playground') if pgs: pg = pgs[1] if pg.collide_point(*touch.pos): if (touch.time_end-touch.time_start) > 2.5: self.longPress = touch.time_end # standard notifikation: for c in self.children: ret = c.on_touch_up(touch) if ret: break # multitouch support ''' self.touches = [xx for xx in self.touches if xx != touch] print("touches cnt = ",len(self.touches)) ''' return ret def on_longPress(self, instance, timestamp): print('longPressed at {time}'.format(time=timestamp)) # Menubar: def setMenu(self, menu): self.menuArea.setMenu(menu) def getMenu(self): return self.menuArea.getMenu() # Toolbar: def setTool(self, toolbar, pos=0): if (toolbar is not None): self.toolBar = toolbar self.toolBarPos = pos self.rebuildContainer() # Workarea: def removeContainer(self): self.workContainer.clear_widgets() self.workContainerO.clear_widgets() self.workContainer1.clear_widgets() def buildContainer(self): # (hbox) if self.toolBar is not None and self.toolBarPos == 3: self.workContainerO.add_widget(self.toolBar) self.workContainerO.add_widget(self.workContainer1) if self.toolBar is not None and self.toolBarPos == 4: self.workContainerO.add_widget(self.toolBar) # (vbox) if self.toolBar is not None and self.toolBarPos == 1: self.workContainer1.add_widget(self.toolBar) self.workContainer1.add_widget(self.workContainer) if self.toolBar is not None and self.toolBarPos == 2: self.workContainer1.add_widget(self.toolBar) # (workcontainer) for w in self.workStack.items: self.workContainer.add_widget(w[1]) def rebuildContainer(self): LAfterAnimation(self.removeContainer) LAfterAnimation(self.buildContainer) def pushWork(self, key, widget): if (widget): self.workStack.push(key, widget) self.rebuildContainer() def popWork(self, key): w = None if self.workStack.size() > 0: w = self.workStack.pop(key) self.rebuildContainer() return w def setWork(self, key, widget): self.pushWork(key, widget) def getWork(self, key): return self.workStack.peek(key) def processAndroidBack(self): ret = False # try to close currently open popup windows, one by one r = range(len(self.workStack.items)) rr = reversed(r) for i in rr: t = self.workStack.items[i] # print("stackkey: %s" % str(t[0])) # print("stackitem: %s" % str(t[1])) if t[0] == 'playground': pass else: if isinstance(t[1], LTopLevel): ret = t[1].processAndroidBack() if not ret: self.popWork(t[0]) ret = True if ret: break return ret # ============================================================================= class LApp(App): # Handling of android return key def key_input(self, window, key, scancode, codepoint, modifier): if key == 27: # Back key of Android. lapp = App.get_running_app() app = lapp.app if app is None: return False # delegate # redirect to mainwindow to close popups, tree nodes # and html pages. if (self.mainWindow.processAndroidBack()): return True # consumed # redirect to game to undo last step app.menubar.mUndo() return True # consumed else: return False # delegate def __init__(self, args): super(LApp, self).__init__() self.args = args self.title = "PySolFC" self.baseWindow = FloatLayout() def build(self): class MyLabel(Label, LBase): def __init__(self, **kw): super(MyLabel, self).__init__(**kw) with self.canvas.before: Color(0.05, 0.05, 0.05, 1) self.rect = Rectangle(pos=self.pos, size=self.size) self.bind(pos=self.update_rect) self.bind(size=self.update_rect) def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size # self.startLabel = MyLabel(text="PySolFC", color=[0.9,0.9,0.9,1]) # noqa # self.baseWindow.add_widget(self.startLabel) return self.baseWindow def app_start(self, *args): logging.info("LApp: app_start") logging.info('top = %s' % str(self.baseWindow)) self.mainWindow = LMainWindow() Cache.register('LAppCache', limit=10) Cache.append('LAppCache', 'baseWindow', self.baseWindow, timeout=0) Cache.append('LAppCache', 'mainWindow', self.mainWindow, timeout=0) Cache.append('LAppCache', 'mainApp', self, timeout=0) self.startCode = 0 Window.bind(on_keyboard=self.key_input) from pysollib.app import Application from pysollib.main import pysol_init self.app = app = Application() app.top = self.baseWindow self.startCode = pysol_init(app, self.args) if self.startCode > 0: logging.info("LApp: on_start fails") return self.mainloop = self.app.mainproc() # Einrichten logging.info("LApp: mainproc initialised sending start signal") self.mainloop.send(None) # Spielprozess starten logging.info("LApp: app_start processed, returned to kivy mainloop") self.baseWindow.add_widget(self.mainWindow,index=1) # noqa self.baseWindow.opacity = 0 anim = Animation(opacity=1,duration=0.7) # noqa Clock.schedule_once(lambda dt: anim.start(self.baseWindow),0.3) # noqa Clock.schedule_once(lambda dt: set_fullscreen(True),0.0) # noqa def on_start(self): logging.info("LApp: on_start") # Window.update_viewport() # There is still a black screen gap between android splash # and displayed app window. But seems to depend on the device # used. There are actual discussions running on that. Some # suggest its a SDL2 issue. debug = False if debug: # Investigation of the black screen gap on start. # For testing we introduce a button. The app will start # when the button is pressed. # It gets more and more clear that the black screen is # screen refresh problem. only arising, when in buildozer.spec # fullscreen = 1 is set. # Without fullscreen, Initialisation works perfectly and # the start button is displayed right after the splash has been # removed. # In fullscreen mode the button is still there but not # visible. It can be pressed to start the app. # When the (black) device is rotated the button is displayed # because this initiates a screen refresh. # In the SDLActivity there are interface functions available. # One of these allows to switch between normal and fullscreen. # If this function is used, then the complete behaviour of # fullscreen is much better. Fullscreen is stable # this way and does not switch back occasionally. # The result of that investigation is: # - App will now be built with fullscreen = 0 in buildozer.spec # - Fullscreen is switched on at runtime after the app has fully # started using the SDLActivity command. from kivy.uix.button import Button self.mybutton = Button(text='startbutton') def addbutton(dt): # noqa self.baseWindow.add_widget(self.mybutton) def delaystart(*args): # noqa self.baseWindow.remove_widget(self.mybutton) Clock.schedule_once(self.app_start, 0.5) self.mybutton.bind(on_press=delaystart) Clock.schedule_once(addbutton, 1.5) else: Clock.schedule_once(self.app_start, 0.0) # self.app_start(0) # Android: Request missing android permissions. requestStoragePerm() logging.info("LApp: on_start processed") # NOTE: The Kivy Eventloop starts after this call # to process input and events. (NOT EARLIER!). This is # also the point, where the android splash screen will be # removed. def on_stop(self): # Achtung wird u.U. 2 mal aufgerufen !!! logging.info("LApp: on_stop") if self.startCode > 0: return # lapp: erweiterte klasse dieser (mit pysolfc app members). lapp = App.get_running_app() lapp.app.menubar.mHoldAndQuit() def on_pause(self): logging.info("LApp: on_pause") # return True: wenn wir wirklich in pause gehen. Dann wird auch # resume aufgerufen falls die app wieder aktiviert wird. # return False: app wird gestoppt (on_stop wird aufgerufen) if self.startCode > 0: return False pauseSupport = True # True ist die bessere Variante. lapp = App.get_running_app() app = lapp.app if app is None: return logging.info("LApp: on_pause - pause on") # set pause if not app.game.pause: app.game.doPause() logging.info("LApp: on_pause - savegame") # save game try: app.game.gstats.holded = 1 app.game._saveGame(app.fn.holdgame) app.opt.game_holded = app.game.id app.opt.last_gameid = app.game.id except Exception: traceback.print_exc() pass # save options try: app.saveOptions() except Exception: traceback.print_exc() pass # save statistics try: app.saveStatistics() except Exception: traceback.print_exc() pass logging.info("LApp: on_pause - gamesaved") logging.info("LApp: on_pause, Window.size=%s" % str(Window.size)) return pauseSupport def on_resume(self): logging.info("LApp: on_resume") lapp = App.get_running_app() app = lapp.app if app is None: return AndroidScreenRotation.unlock(toaster=False) so = get_screen_ori() go = so # flake8: F841 nonsense! so = go logging.info("LApp: on_resume, Window.size=%s" % str(Window.size)) # ANM: # kivy.core.window.Window hat hier u.U. eine falsche dimension # und unterscheidet sich vom display (-> in get_screen_ori). # Eine korrektur der Parameter von Window kann hier wie skizziert # durchgeführt werden und führt auch zu den korrekten 'on_size' # Notifikationen. Allerdings wird später (nach diesem Aufruf) # eine weitere Notifikation erhalten, welche das Fenster u.U. # wieder falsch aufstellt. (woher kommt die und warum ist sie # oft falsch ?) # Gelegentlich beobachtet: Schwarzer Bilschirm nach resume. Und # das bleibt dann so auf ewig. Aber die app läuft stabil. Die # einzige Interaktion mit der App ist über die Android buttons # für hintrgrund und resume. Diese funktioneren gemäss logcat # einwandfrei. Daher versuchen wir ... um den graphik context # wieder zu aktivieren/auszurichten: # gemäss einer neueren Antwort auf kivy issue 3671 gehört auch # update-viewport zu den nützlichen massnahmen. Clock.schedule_once(lambda dt: Window.update_viewport(),0.0) # noqa Clock.schedule_once(lambda dt: self.mainWindow.rebuildContainer(), 0.5) Clock.schedule_once(lambda dt: set_fullscreen(True),1.0) # noqa # Pause modus abschalten nach resume: if app.game.pause: Clock.schedule_once(self.makeEndPauseCmd(app), 3.0) def makeEndPauseCmd(self, app): def endPauseCmd(dt): if app.game.pause: logging.info("LApp: on_resume - pause off") app.game.doPause() return endPauseCmd