mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
Kivy/Android Version
- MfxCanvas implemented with new base class LImage - MfxCanvas background updates adapted - Screen rotation lock change: locks on card selection, i.e. when the user plays, unlocks on menu selection or pause. - animations callback implemented in MfxCanvasGroup (mainly for mahjongg games) in order to have correct placments with undo when animation is enabled - animation scheduling implemented on a tasking basis. New module LTaskQ added. - refactorings and clean ups
This commit is contained in:
parent
069c8e8a7b
commit
492acd6961
10 changed files with 295 additions and 360 deletions
|
@ -1,7 +1,9 @@
|
|||
Android specific:
|
||||
- Temporary screen orientation lock added. Long tap (3 seconds) to the
|
||||
playground locks screen rotation, pause unlocks.
|
||||
- Toolbar: configuration of displayed toolbar buttons added to the options menu.
|
||||
- Temporary screen orientation lock added. While playing a game screen
|
||||
rotation is locked with the first card move, unlocked when selecting
|
||||
menu or pause.
|
||||
- Toolbar: configuration of displayed toolbar buttons added to the options
|
||||
menu.
|
||||
- Toolbar: dynamic updates on Toolbar and Options settings.
|
||||
- Toolbar buttons for 'new deal' and 'restart' now display a
|
||||
toast. Tap to it to accept.
|
||||
|
|
|
@ -1452,16 +1452,14 @@ class Game(object):
|
|||
return
|
||||
|
||||
if TOOLKIT == 'kivy':
|
||||
if tkraise:
|
||||
for card in cards:
|
||||
card.tkraise()
|
||||
c0 = cards[0]
|
||||
dx, dy = (x - c0.x), (y - c0.y)
|
||||
base = float(self.app.opt.animations)
|
||||
duration = base*base/5.0/10.0
|
||||
duration = base*base/40.0 + 0.05
|
||||
for card in cards:
|
||||
card.animatedMove(dx, dy, duration)
|
||||
self.top.waitAnimation(swallow=True, pickup=True)
|
||||
# self.top.waitAnimation(swallow=True, pickup=True)
|
||||
# synchronise: ev. per option ?
|
||||
return
|
||||
|
||||
# init timer - need a high resolution for this to work
|
||||
|
|
|
@ -54,6 +54,7 @@ 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
|
||||
|
@ -113,38 +114,51 @@ class LPopCommander(LBase):
|
|||
# =============================================================================
|
||||
|
||||
|
||||
class LAnimationTask(LTask, LBase):
|
||||
def __init__(self, anim, spos, widget, delay):
|
||||
super(LAnimationTask, self).__init__(widget.card)
|
||||
self.anim = anim
|
||||
self.spos = spos
|
||||
self.widget = widget
|
||||
print(self.widget.card)
|
||||
self.delay = delay
|
||||
|
||||
def start(self):
|
||||
super(LAnimationTask, self).start()
|
||||
self.widget.pos = self.spos
|
||||
self.anim.bind(on_complete=self.stop)
|
||||
self.anim.start(self.widget)
|
||||
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class LAnimationMgr(object):
|
||||
def __init__(self, **kw):
|
||||
super(LAnimationMgr, self).__init__()
|
||||
self.animations = []
|
||||
self.widgets = {}
|
||||
self.callbacks = []
|
||||
self.taskQ = LTaskQ()
|
||||
|
||||
def checkRunning(self):
|
||||
return len(self.animations) > 0
|
||||
|
||||
def addEndCallback(self, cb):
|
||||
self.callbacks.append(cb)
|
||||
# print('len of callbacks', len(self.callbacks))
|
||||
|
||||
def animEnd(self, anim, widget):
|
||||
# print('LAnimationMgr: animEnd = %s.%s' % (anim, widget))
|
||||
|
||||
self.widgets[widget] = self.widgets[widget][1:]
|
||||
self.animations.remove(anim)
|
||||
if len(self.widgets[widget]) > 0:
|
||||
# start next animation on widget
|
||||
nanim = self.widgets[widget][0]
|
||||
self.animations.append(nanim)
|
||||
print('LAnimationMgr: animEnd, append = %s' % (nanim))
|
||||
nanim.start(widget)
|
||||
else:
|
||||
# no further animations for widget so stop
|
||||
del self.widgets[widget]
|
||||
if not self.checkRunning():
|
||||
# print('LAnimationMgr: animEnd -> do callbacks')
|
||||
for cb in self.callbacks:
|
||||
cb()
|
||||
print('LAnimationMgr: animEnd -> callbacks done')
|
||||
self.callbacks = []
|
||||
|
||||
# print('Clock.get_fps() ->', Clock.get_fps())
|
||||
|
||||
def makeAnimStart(self, anim, spos, widget):
|
||||
def animStart(dt):
|
||||
widget.pos = spos
|
||||
# print('LAnimationMgr: animStart = %s ... %s' % (anim, dt))
|
||||
anim.start(widget)
|
||||
return animStart
|
||||
|
||||
def checkRunning(self):
|
||||
return len(self.animations) > 0
|
||||
print('Clock.get_fps() ->', Clock.get_fps())
|
||||
|
||||
def create(self, spos, widget, **kw):
|
||||
x = 0.0
|
||||
|
@ -166,22 +180,11 @@ class LAnimationMgr(object):
|
|||
anim.bind(on_complete=kw['bindE'])
|
||||
if 'bindS' in kw:
|
||||
anim.bind(on_start=kw['bindS'])
|
||||
self.animations.append(anim)
|
||||
|
||||
offset = duration / 3.0
|
||||
# offset = duration*1.2
|
||||
timedelay = offset * len(self.animations)
|
||||
# print('offset = %s'% offset)
|
||||
# print('LAnimationMgr: timedelay = %s' % timedelay)
|
||||
|
||||
if widget in self.widgets:
|
||||
# append additional animation to widget
|
||||
self.widgets[widget].append(anim)
|
||||
else:
|
||||
# setup first animation for widget
|
||||
self.animations.append(anim)
|
||||
self.widgets[widget] = [anim]
|
||||
Clock.schedule_once(self.makeAnimStart(
|
||||
anim, spos, widget), timedelay)
|
||||
animTask = LAnimationTask(anim, spos, widget, offset)
|
||||
Clock.schedule_once(lambda dt: self.taskQ.taskInsert(animTask), 0.016)
|
||||
|
||||
|
||||
LAnimationManager = LAnimationMgr()
|
||||
|
@ -673,7 +676,8 @@ class LRectangle(Widget, LBase):
|
|||
# =============================================================================
|
||||
# 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.
|
||||
# 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):
|
||||
|
@ -719,29 +723,28 @@ class LImageItem(BoxLayout, LBase):
|
|||
|
||||
def on_touch_down(self, touch):
|
||||
|
||||
print('LCardImage: size = %s' % self.size)
|
||||
# print('LCardImage: size = %s' % self.size)
|
||||
if self.collide_point(*touch.pos):
|
||||
|
||||
for c in self.children:
|
||||
# print('child at %s' % c)
|
||||
if (c.on_touch_down(touch) and 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)
|
||||
return True
|
||||
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))
|
||||
|
@ -775,22 +778,19 @@ class LImageItem(BoxLayout, LBase):
|
|||
|
||||
if self.collide_point(*touch.pos):
|
||||
|
||||
for c in self.children:
|
||||
# print('child at %s' % c)
|
||||
|
||||
if (c.on_touch_up(touch) and 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.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))
|
||||
|
@ -1266,7 +1266,7 @@ class LMenuItem(ActionButton, LBase):
|
|||
pass
|
||||
|
||||
def setCommand(self, cmd):
|
||||
# print('LMenuItem: setCommand')
|
||||
print('LMenuItem: setCommand')
|
||||
self.bind(on_release=cmd)
|
||||
|
||||
# def __str__(self):
|
||||
|
@ -1575,7 +1575,6 @@ class LMainWindow(BoxLayout, LTkBase):
|
|||
|
||||
def on_touch_down(self, touch):
|
||||
ret = False
|
||||
|
||||
if super().on_touch_down(touch):
|
||||
return True
|
||||
|
||||
|
@ -1655,7 +1654,6 @@ class LMainWindow(BoxLayout, LTkBase):
|
|||
|
||||
def on_longPress(self, instance, timestamp):
|
||||
print('longPressed at {time}'.format(time=timestamp))
|
||||
AndroidScreenRotation.lock()
|
||||
|
||||
# Menubar:
|
||||
|
||||
|
@ -1889,7 +1887,7 @@ class LApp(App):
|
|||
if app is None:
|
||||
return
|
||||
|
||||
AndroidScreenRotation.unlock()
|
||||
AndroidScreenRotation.unlock(toaster=False)
|
||||
|
||||
so = get_screen_ori()
|
||||
go = so # flake8: F841 nonsense!
|
||||
|
|
|
@ -31,7 +31,7 @@ class LImage(Widget, LBase):
|
|||
COVER = 2
|
||||
SCALE_DOWN = 3
|
||||
TILING = 4
|
||||
fit_mode = StringProperty("fill")
|
||||
fit_mode = StringProperty("contain")
|
||||
texture = ObjectProperty(None, allownone=True)
|
||||
|
||||
def make_scale_down(self, s, p):
|
||||
|
@ -63,12 +63,9 @@ class LImage(Widget, LBase):
|
|||
def make_cover(self, s, p):
|
||||
aspect = self.texture.size[0]/self.texture.size[1]
|
||||
waspect = self.size[0]/self.size[1]
|
||||
print ('aspect: ', aspect) # noqa
|
||||
print ('waspect: ', waspect) # noqa
|
||||
|
||||
# 'clamp_to_edge','repeat','mirrored_repeat'
|
||||
self.texture.wrap = 'repeat'
|
||||
print ('wrap: ',self.texture.wrap) # noqa
|
||||
|
||||
# set rect size/pos to window
|
||||
self.rect.size = s
|
||||
|
@ -172,7 +169,8 @@ class LImage(Widget, LBase):
|
|||
self.tex_coord_update(self.texture)
|
||||
|
||||
# initial size is the natural size of the image.
|
||||
self.size = self.texture.size
|
||||
if self.texture is not None:
|
||||
self.size = self.texture.size
|
||||
|
||||
def tex_coord_update(self, texture):
|
||||
if hasattr(self, "rect"):
|
||||
|
@ -202,10 +200,12 @@ class LImage(Widget, LBase):
|
|||
self.make_format(self.size, p)
|
||||
|
||||
def on_fit_mode(self, a, m):
|
||||
print('on_fit_mode', m)
|
||||
self.fit_num_update(self.fit_mode)
|
||||
self.make_format(self.size, self.pos)
|
||||
|
||||
def on_texture(self, a, texture):
|
||||
print('on_texture', texture)
|
||||
self.tex_coord_update(self.texture)
|
||||
self.make_format(self.size, self.pos)
|
||||
|
||||
|
@ -221,24 +221,4 @@ class LImage(Widget, LBase):
|
|||
def subsample(self, r):
|
||||
return LImage(texture=self.texture)
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
# print('LImage: touch_down on %s' % str(touch.pos))
|
||||
if self.collide_point(*touch.pos):
|
||||
if (self.source is not None):
|
||||
print('LImage match %s' % self.source)
|
||||
else:
|
||||
print('LImage match with texture')
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
# print('LImage: touch_up on %s' % str(touch.pos))
|
||||
if self.collide_point(*touch.pos):
|
||||
if (self.source is not None):
|
||||
print('LImage match %s' % self.source)
|
||||
else:
|
||||
print('LImage match with texture')
|
||||
return True
|
||||
return False
|
||||
|
||||
# =============================================================================
|
||||
|
|
112
pysollib/kivy/LTask.py
Normal file
112
pysollib/kivy/LTask.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
# -*- mode: python; coding: utf-8; -*-
|
||||
# ---------------------------------------------------------------------------##
|
||||
# Copyright 2016, 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from kivy._event import EventDispatcher
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import BooleanProperty, ListProperty
|
||||
|
||||
|
||||
class LTask(EventDispatcher):
|
||||
done = BooleanProperty(False)
|
||||
|
||||
def __init__(self, name):
|
||||
super(LTask, self).__init__()
|
||||
self.done = False
|
||||
self.name = name
|
||||
self.delay = 0.01
|
||||
|
||||
def start(self):
|
||||
# print('start of ',self.name)
|
||||
self.done = False
|
||||
|
||||
def stop(self, *args):
|
||||
# print('stop of ',self.name)
|
||||
self.done = True
|
||||
|
||||
def on_done(self, instance, value):
|
||||
# print ('on_done',instance,value)
|
||||
pass
|
||||
|
||||
|
||||
class LTaskQ(EventDispatcher):
|
||||
waitQ = ListProperty([])
|
||||
runQ = ListProperty([])
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LTaskQ, self).__init__(**kwargs)
|
||||
self.waitQ = []
|
||||
self.waitQlen = 0
|
||||
self.runQ = []
|
||||
self.runQlen = 0
|
||||
self.runQmax = 52
|
||||
self.runSeq = 1.0/10.0 # default delay.
|
||||
|
||||
def scheduleNext(self, dt):
|
||||
if (self.waitQlen > 0):
|
||||
# run as long as there is enough place in the runQ
|
||||
# do not run more than one job with the same name
|
||||
# at the same time.
|
||||
delayed = False
|
||||
if (self.runQlen < self.runQmax):
|
||||
names = [ t.name for t in self.runQ ] # noqa
|
||||
if self.waitQ[0].name not in names:
|
||||
self.runQ.append(self.waitQ[0])
|
||||
del self.waitQ[0]
|
||||
else:
|
||||
delayed = True
|
||||
|
||||
delay = self.runSeq
|
||||
if self.waitQlen > 0 and not delayed:
|
||||
delay = self.waitQ[0].delay
|
||||
|
||||
if (self.waitQlen > 0 or self.runQlen > 0):
|
||||
Clock.schedule_once(self.scheduleNext, delay)
|
||||
|
||||
def on_runQ(self, instance, value):
|
||||
lastlen = self.runQlen
|
||||
self.runQlen = len(self.runQ)
|
||||
|
||||
if (self.runQlen > lastlen):
|
||||
self.runQ[-1].bind(done=self.taskStopped)
|
||||
self.runQ[-1].start()
|
||||
|
||||
def on_waitQ(self, instance, value):
|
||||
self.waitQlen = len(self.waitQ)
|
||||
if (self.waitQlen > 0 and self.runQlen == 0):
|
||||
self.scheduleNext(0)
|
||||
|
||||
def taskQsAreEmpty(self):
|
||||
return (self.waitQlen == 0 and self.runQlen == 0)
|
||||
|
||||
def taskInsert(self, task):
|
||||
# print('taskInsert called', task)
|
||||
self.waitQ.append(task)
|
||||
|
||||
def taskStopped(self, instance, value):
|
||||
if (value is True):
|
||||
idx = 0
|
||||
while (idx < self.runQlen):
|
||||
if (self.runQ[idx] is instance):
|
||||
break
|
||||
idx += 1
|
||||
|
||||
if (idx < self.runQlen):
|
||||
# print('taskStopped', instance, 'index', idx)
|
||||
del self.runQ[idx]
|
||||
else:
|
||||
# print('taskStopped: index out of range!')
|
||||
pass
|
|
@ -20,20 +20,22 @@ class AndroidRot(object):
|
|||
def isLocked(self):
|
||||
return self.locked
|
||||
|
||||
def lock(self):
|
||||
def lock(self, toaster=True):
|
||||
if jnius is not None:
|
||||
if not self.locked:
|
||||
self.currentActivity.setRequestedOrientation(
|
||||
self.ActivityInfo.SCREEN_ORIENTATION_LOCKED)
|
||||
AndroidToaster.toastShort(_("screen rotation disabled"))
|
||||
if toaster:
|
||||
AndroidToaster.toastShort(_("screen rotation disabled"))
|
||||
self.locked = True
|
||||
|
||||
def unlock(self):
|
||||
def unlock(self, toaster=True):
|
||||
if jnius is not None:
|
||||
if self.locked:
|
||||
self.currentActivity.setRequestedOrientation(
|
||||
self.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR)
|
||||
AndroidToaster.toastShort(_("screen rotation enabled"))
|
||||
if toaster:
|
||||
AndroidToaster.toastShort(_("screen rotation enabled"))
|
||||
self.locked = False
|
||||
|
||||
def toggle(self):
|
||||
|
|
|
@ -24,23 +24,17 @@
|
|||
from pysollib.acard import AbstractCard
|
||||
from pysollib.kivy.LApp import LImageItem
|
||||
from pysollib.kivy.LImage import LImage
|
||||
from pysollib.kivy.tkcanvas import MfxCanvasGroup, MfxCanvasImage
|
||||
from pysollib.kivy.tkcanvas import MfxCanvasImage
|
||||
|
||||
|
||||
class _HideableCard(AbstractCard):
|
||||
def hide(self, stack):
|
||||
if stack is self.hide_stack:
|
||||
return
|
||||
self.item.config(state="hidden")
|
||||
self.hide_stack = stack
|
||||
# print "hide:", self.id, self.item.coords()
|
||||
|
||||
def unhide(self):
|
||||
if self.hide_stack is None:
|
||||
return 0
|
||||
# print "unhide:", self.id, self.item.coords()
|
||||
self.item.config(state="normal")
|
||||
self.hide_stack = None
|
||||
return 1
|
||||
|
||||
# moveBy aus Basisklasse überschreiben.
|
||||
|
@ -76,9 +70,6 @@ class _OneImageCard(_HideableCard):
|
|||
|
||||
self._face_image = LImage(texture=fimage.texture)
|
||||
self._back_image = LImage(texture=bimage.texture)
|
||||
# self._face_image = Image(source=fimage.source)
|
||||
# self._back_image = Image(source=bimage.source)
|
||||
self._shade_image = game.getCardShadeImage()
|
||||
|
||||
aimage = LImageItem(
|
||||
pos=(x, -y), size=self._face_image.size, game=game, card=self)
|
||||
|
@ -87,7 +78,6 @@ class _OneImageCard(_HideableCard):
|
|||
|
||||
self.item = MfxCanvasImage(
|
||||
game.canvas, self.x, self.y, image=aimage, anchor="nw")
|
||||
self.shade_item = None
|
||||
|
||||
# print ('card: face = %s xy=%s/%s' % (self._face_image.source, x, y))
|
||||
# print ('card: back = %s xy=%s/%s' % (self._back_image.source, x, y))
|
||||
|
@ -117,151 +107,22 @@ class _OneImageCard(_HideableCard):
|
|||
if not self.face_up:
|
||||
self._setImage(image=self._back_image)
|
||||
|
||||
'''
|
||||
def setSelected(self, s, group=None):
|
||||
print('card: setselected(%s, %s)' % (s, group))
|
||||
# wird nicht bedient.
|
||||
# NOTE:
|
||||
# This is one of the zombie methods (nowhere impelemeted: close,
|
||||
# unclose, and somewhat dummy implemented ones: hide, unhide)
|
||||
# of the base class AbstractCard.
|
||||
# (-> some clean ups for clarity would be nice)
|
||||
pass
|
||||
'''
|
||||
|
||||
def animatedMove(self, dx, dy, duration=0.2):
|
||||
self.item.animatedMove(dx, dy, duration)
|
||||
|
||||
# ************************************************************************
|
||||
# * New idea since 3.00
|
||||
# *
|
||||
# * Hide a card by configuring the canvas image to None.
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
class _OneImageCardWithHideByConfig(_OneImageCard):
|
||||
def hide(self, stack):
|
||||
if stack is self.hide_stack:
|
||||
return
|
||||
self._setImage(image=None)
|
||||
self.hide_stack = stack
|
||||
|
||||
def unhide(self):
|
||||
if self.hide_stack is None:
|
||||
return 0
|
||||
if self.face_up:
|
||||
self._setImage(image=self._face_image)
|
||||
else:
|
||||
self._setImage(image=self._back_image)
|
||||
self.hide_stack = None
|
||||
return 1
|
||||
|
||||
#
|
||||
# much like in _OneImageCard
|
||||
#
|
||||
|
||||
def showFace(self, unhide=1):
|
||||
if not self.face_up:
|
||||
if unhide:
|
||||
self._setImage(image=self._face_image)
|
||||
self.item.tkraise()
|
||||
self.face_up = 1
|
||||
|
||||
def showBack(self, unhide=1):
|
||||
if self.face_up:
|
||||
if unhide:
|
||||
self._setImage(image=self._back_image)
|
||||
self.item.tkraise()
|
||||
self.face_up = 0
|
||||
|
||||
def updateCardBackground(self, image):
|
||||
self._back_image = image
|
||||
if not self.face_up and not self.hide_stack:
|
||||
self._setImage(image=image)
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Old implemetation prior to 2.10
|
||||
# *
|
||||
# * The card consists of two CanvasImages. To show the card face up,
|
||||
# * the face item is placed in front of the back. To show it face
|
||||
# * down, this is reversed.
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
class _TwoImageCard(_HideableCard):
|
||||
# Private instance variables:
|
||||
# __face, __back -- the canvas items making up the card
|
||||
def __init__(self, id, deck, suit, rank, game, x=0, y=0):
|
||||
_HideableCard.__init__(self, id, deck, suit, rank, game, x=x, y=y)
|
||||
self.item = MfxCanvasGroup(game.canvas)
|
||||
self.__face = MfxCanvasImage(
|
||||
game.canvas, self.x, self.y,
|
||||
image=game.getCardFaceImage(deck, suit, rank), anchor="nw")
|
||||
self.__back = MfxCanvasImage(
|
||||
game.canvas, self.x, self.y,
|
||||
image=game.getCardBackImage(deck, suit, rank), anchor="nw")
|
||||
self.__face.addtag(self.item)
|
||||
self.__back.addtag(self.item)
|
||||
|
||||
def showFace(self, unhide=1):
|
||||
if not self.face_up:
|
||||
self.__face.tkraise()
|
||||
self.tkraise(unhide)
|
||||
self.face_up = 1
|
||||
|
||||
def showBack(self, unhide=1):
|
||||
if self.face_up:
|
||||
self.__back.tkraise()
|
||||
self.tkraise(unhide)
|
||||
self.face_up = 0
|
||||
|
||||
def updateCardBackground(self, image):
|
||||
self.__back.config(image=image)
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * New idea since 2.90
|
||||
# *
|
||||
# * The card consists of two CanvasImages. Instead of raising
|
||||
# * one image above the other we move the inactive image out
|
||||
# * of the visible canvas.
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
class _TwoImageCardWithHideItem(_HideableCard):
|
||||
# Private instance variables:
|
||||
# __face, __back -- the canvas items making up the card
|
||||
def __init__(self, id, deck, suit, rank, game, x=0, y=0):
|
||||
_HideableCard.__init__(self, id, deck, suit, rank, game, x=x, y=y)
|
||||
self.item = MfxCanvasGroup(game.canvas)
|
||||
self.__face = MfxCanvasImage(
|
||||
game.canvas, self.x, self.y + 11000,
|
||||
image=game.getCardFaceImage(deck, suit, rank), anchor="nw")
|
||||
self.__back = MfxCanvasImage(
|
||||
game.canvas, self.x, self.y,
|
||||
image=game.getCardBackImage(deck, suit, rank), anchor="nw")
|
||||
self.__face.addtag(self.item)
|
||||
self.__back.addtag(self.item)
|
||||
|
||||
def showFace(self, unhide=1):
|
||||
if not self.face_up:
|
||||
self.__back.move(0, 10000)
|
||||
# self.__face.tkraise()
|
||||
self.__face.move(0, -11000)
|
||||
self.tkraise(unhide)
|
||||
self.face_up = 1
|
||||
|
||||
def showBack(self, unhide=1):
|
||||
if self.face_up:
|
||||
self.__face.move(0, 11000)
|
||||
# self.__back.tkraise()
|
||||
self.__back.move(0, -10000)
|
||||
self.tkraise(unhide)
|
||||
self.face_up = 0
|
||||
|
||||
def updateCardBackground(self, image):
|
||||
self.__back.config(image=image)
|
||||
|
||||
|
||||
# choose the implementation
|
||||
# Card = _TwoImageCardWithHideItem
|
||||
# Card = _TwoImageCard
|
||||
# Card = _OneImageCardWithHideByConfig
|
||||
Card = _OneImageCard
|
||||
|
||||
|
||||
'''end of file'''
|
||||
|
|
|
@ -176,6 +176,9 @@ class MainMenuDialog(LMenuDialog):
|
|||
super(MainMenuDialog, self).__init__(
|
||||
menubar, parent, title, app, **kw)
|
||||
|
||||
print('MainMenuDialog starting')
|
||||
AndroidScreenRotation.unlock()
|
||||
|
||||
def buildTree(self, tv, node):
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
|
@ -222,6 +225,7 @@ class FileMenuDialog(LMenuDialog):
|
|||
|
||||
def make_game_command(self, key, command):
|
||||
def game_command():
|
||||
self.closeWindow(0)
|
||||
command(key)
|
||||
return game_command
|
||||
|
||||
|
@ -2086,7 +2090,7 @@ class PysolMenubarTk:
|
|||
return
|
||||
self.game.doPause()
|
||||
self.tkopt.pause.set(self.game.pause)
|
||||
AndroidScreenRotation.unlock()
|
||||
AndroidScreenRotation.unlock(toaster=False)
|
||||
|
||||
def mOptLanguage(self, *args):
|
||||
if self._cancelDrag(break_pause=False):
|
||||
|
|
|
@ -26,12 +26,11 @@ from __future__ import division
|
|||
|
||||
import logging
|
||||
import math
|
||||
# import inspect
|
||||
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.graphics import Color
|
||||
from kivy.graphics import Rectangle
|
||||
from kivy.uix.anchorlayout import AnchorLayout
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
from pysollib.kivy.LApp import LAnimationManager
|
||||
from pysollib.kivy.LApp import LColorToKivy
|
||||
|
@ -39,7 +38,6 @@ from pysollib.kivy.LApp import LImageItem
|
|||
from pysollib.kivy.LApp import LLine
|
||||
from pysollib.kivy.LApp import LRectangle
|
||||
from pysollib.kivy.LApp import LText
|
||||
from pysollib.kivy.LBase import LBase
|
||||
from pysollib.kivy.LImage import LImage
|
||||
|
||||
# ************************************************************************
|
||||
|
@ -147,12 +145,26 @@ class MfxCanvasGroup():
|
|||
# damit is 0 das unterste image und -1 das oberste.
|
||||
return ilst
|
||||
|
||||
def makeDeferredRaise(self, pos):
|
||||
def animCallback():
|
||||
self.tkraise(position=pos)
|
||||
return animCallback
|
||||
|
||||
def tkraise(self, position=None):
|
||||
# print(self, ' tkraise(', position, ')')
|
||||
# Das wird bei Mahjongg extensiv aufgerufen, wenn der Move
|
||||
# abeschlossen ist. Wird aber auch bei andern games benutzt.
|
||||
# print('stack[1] = ',inspect.stack()[1].frame)
|
||||
# print('stack[2] = ',inspect.stack()[2].frame)
|
||||
|
||||
if LAnimationManager.checkRunning():
|
||||
LAnimationManager.addEndCallback(
|
||||
self.makeDeferredRaise(position))
|
||||
return
|
||||
|
||||
imgs = self._imglist(self)
|
||||
# print('number of imaages:', len(imgs))
|
||||
|
||||
if position is not None:
|
||||
# In self.canvas suchen: das oberste image, welches zu position
|
||||
# gehört und liste der images oberhalb einfüllen u.a.a.O weglassen.
|
||||
|
@ -178,9 +190,21 @@ class MfxCanvasGroup():
|
|||
for w in ws:
|
||||
self.canvas.add_widget(w)
|
||||
|
||||
def makeDeferredLower(self, pos):
|
||||
def animCallback():
|
||||
self.lower(position=pos)
|
||||
return animCallback
|
||||
|
||||
def lower(self, position=None):
|
||||
# print(self, ' lower(', position, ')')
|
||||
# dasselbe wi tkraise aber vorher statt nachher einfügen.
|
||||
# print('stack[1] = ',inspect.stack()[1].frame)
|
||||
# print('stack[2] = ',inspect.stack()[2].frame)
|
||||
|
||||
if LAnimationManager.checkRunning():
|
||||
LAnimationManager.addEndCallback(
|
||||
self.makeDeferredLower(position))
|
||||
return
|
||||
|
||||
imgs = self._imglist(self)
|
||||
if position is not None:
|
||||
|
@ -221,10 +245,8 @@ class MfxCanvasGroup():
|
|||
|
||||
|
||||
def cardmagnif(canvas, size):
|
||||
|
||||
def pyth(s):
|
||||
return math.sqrt(s[0]*s[0]+s[1]*s[1])
|
||||
|
||||
cs = canvas.wmain.app.images.getSize()
|
||||
csl = pyth(cs)
|
||||
sl = pyth(size)
|
||||
|
@ -258,7 +280,10 @@ class MfxCanvasImage(object):
|
|||
|
||||
super(MfxCanvasImage, self).__init__()
|
||||
self.canvas = canvas
|
||||
|
||||
# animation support:
|
||||
self.animation = None
|
||||
self.deferred_raises = []
|
||||
|
||||
ed = kwargs['image']
|
||||
size = ed.size
|
||||
|
@ -301,15 +326,27 @@ class MfxCanvasImage(object):
|
|||
def config(self, **kw):
|
||||
pass
|
||||
|
||||
def makeDeferredRaise(self, pos):
|
||||
def animCallback():
|
||||
self.canvas.tag_raise(self.image, pos)
|
||||
return animCallback
|
||||
|
||||
def tkraise(self, aboveThis=None):
|
||||
# print(self, ': tkraise, above =', aboveThis)
|
||||
|
||||
abitm = None
|
||||
if aboveThis:
|
||||
if isinstance(aboveThis, MfxCanvasImage):
|
||||
abitm = aboveThis.widget
|
||||
if isinstance(aboveThis, LImageItem):
|
||||
abitm = aboveThis
|
||||
abitm = aboveThis.widget
|
||||
|
||||
# print('stack[1] = ', inspect.stack()[1].frame)
|
||||
# print('stack[2] = ', inspect.stack()[2].frame)
|
||||
# print('stack[3] = ', inspect.stack()[3].frame)
|
||||
|
||||
if self.animation:
|
||||
print('defer tkraise to animation', abitm)
|
||||
self.deferred_raises.append(self.makeDeferredRaise(abitm))
|
||||
return
|
||||
|
||||
# print('direct tkraise', abitm)
|
||||
self.canvas.tag_raise(self.image, abitm)
|
||||
|
||||
def addtag(self, tag):
|
||||
|
@ -340,21 +377,30 @@ class MfxCanvasImage(object):
|
|||
|
||||
def makeAnimStart(self):
|
||||
def animStart(anim, widget):
|
||||
# print('MfxCanvasImage: animStart %s' % self)
|
||||
# nothing to do hiere
|
||||
pass
|
||||
print('MfxCanvasImage: animStart %s' % self)
|
||||
# karte nach vorne nehmen - vorgänger karte merken
|
||||
# für restore.
|
||||
# self.canvas.tag_raise(self.image)
|
||||
|
||||
for cb in self.deferred_raises:
|
||||
print('do deferred tkraise:')
|
||||
cb()
|
||||
self.deferred_raises = []
|
||||
|
||||
return animStart
|
||||
|
||||
def makeAnimEnd(self, dpos, dsize):
|
||||
def animEnd(anim, widget):
|
||||
# print('MfxCanvasImage: animEnd %s' % self)
|
||||
print('MfxCanvasImage: animEnd %s' % self)
|
||||
# karte wieder vor die vorgängerkarte setzen.
|
||||
self.animation = False
|
||||
self.deferred_raises = []
|
||||
image = self.image
|
||||
image.pos, image.size = self.canvas.CoreToKivy(dpos, dsize)
|
||||
return animEnd
|
||||
|
||||
def animatedMove(self, dx, dy, duration=0.2):
|
||||
# print ('MfxCanvasImage: animatedMove %s, %s' % (dx, dy))
|
||||
print('MfxCanvasImage: animatedMove %s, %s' % (dx, dy))
|
||||
|
||||
image = self.image
|
||||
dsize = image.coreSize
|
||||
|
@ -512,13 +558,13 @@ class MfxCanvasText(object):
|
|||
# ************************************************************************
|
||||
|
||||
|
||||
class MfxCanvas(Widget, LBase):
|
||||
class MfxCanvas(LImage):
|
||||
|
||||
def __str__(self):
|
||||
return f'<MfxCanvas @ {hex(id(self))}>'
|
||||
|
||||
def __init__(self, wmain, *args, **kw):
|
||||
super(MfxCanvas, self).__init__()
|
||||
super(MfxCanvas, self).__init__(background=True)
|
||||
|
||||
# print('MfxCanvas: __init__()')
|
||||
# self.tags = {} # bei basisklasse widget (ev. nur vorläufig)
|
||||
|
@ -646,91 +692,23 @@ class MfxCanvas(Widget, LBase):
|
|||
|
||||
# Hintergrund update.
|
||||
|
||||
self.canvas.before.clear()
|
||||
kc = LColorToKivy(self._bg_color)
|
||||
texture = None
|
||||
if self._bg_img:
|
||||
texture = self._bg_img.texture
|
||||
|
||||
# Color only: Nur eine Hintergrundfarbe wird installiert.
|
||||
self.texture = texture
|
||||
if texture is None:
|
||||
kc = LColorToKivy(self._bg_color)
|
||||
self.canvas.before.add(
|
||||
Color(kc[0], kc[1], kc[2], kc[3]))
|
||||
self.canvas.before.add(
|
||||
Rectangle(pos=self.pos, size=self.size))
|
||||
return
|
||||
|
||||
# Image: Das Bild wird im Fenster expandiert.
|
||||
if self._stretch_bg_image:
|
||||
if self._save_aspect_bg_image == 0:
|
||||
self.canvas.before.add(
|
||||
Rectangle(texture=texture, pos=self.pos, size=self.size))
|
||||
else:
|
||||
# save aspect
|
||||
aspect = texture.size[0]/texture.size[1]
|
||||
waspect = self.size[0]/self.size[1]
|
||||
print ('aspect: ', aspect) # noqa
|
||||
print ('waspect: ', waspect) # noqa
|
||||
|
||||
# 'clamp_to_edge','repeat','mirrored_repeat'
|
||||
texture.wrap = 'repeat'
|
||||
print ('wrap: ',texture.wrap) # noqa
|
||||
|
||||
# add new rectangle to canvas.
|
||||
r = Rectangle(texture=texture, pos=self.pos, size=self.size)
|
||||
self.canvas.before.add(r)
|
||||
|
||||
# evaluate original texture coords.
|
||||
uu = r.tex_coords[0]
|
||||
vv = r.tex_coords[1]
|
||||
ww = r.tex_coords[2] - uu
|
||||
hh = r.tex_coords[5] - vv
|
||||
|
||||
# in order to center the image in the window
|
||||
# modify texture coords
|
||||
if waspect < aspect:
|
||||
w = ww/aspect*waspect # noqa
|
||||
h = hh
|
||||
u = 0.5 - w/2.0 # noqa
|
||||
v = vv
|
||||
else:
|
||||
w = ww
|
||||
h = hh*aspect/waspect # noqa
|
||||
u = uu
|
||||
v = 0.5 - h/2.0 # noqa
|
||||
|
||||
# and update them.
|
||||
tc = ( u, v, u + w, v, u + w, v + h, u, v + h ) # noqa
|
||||
r.tex_coords = tc
|
||||
|
||||
print ('tex_coords (orig):',uu,vv,ww,hh) # noqa
|
||||
print ('tex_coords (mod): ',u,v,w,h) # noqa
|
||||
return
|
||||
|
||||
self.setColor(kc)
|
||||
else:
|
||||
# Tiles placement, reference point is bottom left.
|
||||
|
||||
texture.wrap = 'repeat'
|
||||
r = Rectangle(texture=texture, pos=self.pos, size=self.size)
|
||||
self.canvas.before.add(r)
|
||||
|
||||
# stsize = (texture.size[0] * self.scale,
|
||||
# texture.size[1] * self.scale)
|
||||
stsize = texture.size
|
||||
stepsy = self.size[1] / stsize[1]
|
||||
stepsx = self.size[0] / stsize[0]
|
||||
|
||||
u = r.tex_coords[0]
|
||||
v = r.tex_coords[1]
|
||||
ww = r.tex_coords[2] - u
|
||||
hh = r.tex_coords[5] - v
|
||||
w = ww * stepsx
|
||||
h = hh * stepsy
|
||||
|
||||
# move reference point to top left:
|
||||
v = stepsy - math.floor(stepsy)
|
||||
|
||||
r.tex_coords = ( u, v, u + w, v, u + w, v + h, u, v + h ) # noqa
|
||||
self.setColor([1,1,1,1]) # noqa
|
||||
if self._stretch_bg_image:
|
||||
if self._save_aspect_bg_image == 0:
|
||||
self.fit_mode = "fill"
|
||||
else:
|
||||
self.fit_mode = "cover"
|
||||
else:
|
||||
self.fit_mode = "tiling"
|
||||
|
||||
# Funktionen, welche vom Core aufgerufen werden.
|
||||
|
||||
|
|
|
@ -440,7 +440,7 @@ class MfxScrolledCanvas(object):
|
|||
tile.color == app.opt.colors['table']):
|
||||
return False
|
||||
#
|
||||
print('setTile2: %s' % (tile.filename))
|
||||
print('setTile2: %s, %s' % (tile.filename, tile.color))
|
||||
|
||||
if not self.canvas.setTile(
|
||||
tile.filename, tile.stretch, tile.save_aspect):
|
||||
|
|
Loading…
Add table
Reference in a new issue