From c5b22563a871a8505f5f6d19b85a3d44e881ebf8 Mon Sep 17 00:00:00 2001 From: lufebe16 Date: Tue, 12 Dec 2023 10:46:55 +0100 Subject: [PATCH] Kivy/Android - switching of redeal images (redeal/stopped) corrected. - new wrapper class for options management. Refactorings. - redeal, pause and demo logo style selections updated. --- pysollib/game/__init__.py | 13 ++++-- pysollib/kivy/LApp.py | 8 ++++ pysollib/kivy/LObjWrap.py | 69 ++++++++++++++++++++++++++++ pysollib/kivy/menubar.py | 97 +++++++++++++++++++-------------------- pysollib/kivy/tkcanvas.py | 23 +++++++++- pysollib/stack.py | 8 ++++ 6 files changed, 162 insertions(+), 56 deletions(-) create mode 100644 pysollib/kivy/LObjWrap.py diff --git a/pysollib/game/__init__.py b/pysollib/game/__init__.py index 6cfe30dc..9fa92a58 100644 --- a/pysollib/game/__init__.py +++ b/pysollib/game/__init__.py @@ -1779,6 +1779,8 @@ class Game(object): return if TOOLKIT == 'gtk': return + if TOOLKIT == 'kivy': + return if not Image: return self.canvas.hideAllItems() @@ -1801,6 +1803,8 @@ class Game(object): return def redealAnimation(self): + if TOOLKIT == 'kivy': + return if self.preview: return if not self.app.opt.animations or not self.app.opt.redeal_animation: @@ -3428,6 +3432,11 @@ class Game(object): d = time.time() - self.stats.update_time + self.stats.elapsed_time self.updateStatus(time=format_time(d)) + def displayPauseImage(self): + n = self.random.initial_seed % len(self.app.gimages.pause) + self.pause_logo = self.app.gimages.pause[int(n)] + self.canvas.setTopImage(self.pause_logo) + def doPause(self): if self.finished: return @@ -3439,9 +3448,7 @@ class Game(object): if self.pause: # self.updateTime() self.canvas.hideAllItems() - n = self.random.initial_seed % len(self.app.gimages.pause) - self.pause_logo = self.app.gimages.pause[int(n)] - self.canvas.setTopImage(self.pause_logo) + self.displayPauseImage() else: self.stats.update_time = time.time() self.updatePlayTime() diff --git a/pysollib/kivy/LApp.py b/pysollib/kivy/LApp.py index 1a6a9c02..e3d3a3be 100644 --- a/pysollib/kivy/LApp.py +++ b/pysollib/kivy/LApp.py @@ -698,12 +698,17 @@ class LImageItem(BoxLayout, LBase): 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. @@ -711,6 +716,9 @@ class LImageItem(BoxLayout, LBase): 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) diff --git a/pysollib/kivy/LObjWrap.py b/pysollib/kivy/LObjWrap.py new file mode 100644 index 00000000..d4250706 --- /dev/null +++ b/pysollib/kivy/LObjWrap.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +# -*- mode: python; coding: utf-8; -*- +# ============================================================================= +# Copyright (C) 2017-2023 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. +# Flake8: noqa +# +# ============================================================================= + +import logging + +from kivy.event import EventDispatcher +from kivy.properties import BooleanProperty +from kivy.properties import NumericProperty +from kivy.properties import ListProperty +from kivy.properties import StringProperty + +# ============================================================================= +# Joins kivy properties to object members of referenced class. +# Usage: +# - use derived classes LBoolWrap etc. according to type. +# - write: obj.value = +# - read: = obj.value +# - If you need additional functionality on change add a callback function +# as 'command'. It will be called whenever the value changes. + +class LObjWrap(EventDispatcher): + def __init__(self,obj,ref,command=None): + self.obj = obj + self.ref = ref + self.value = getattr(self.obj,self.ref) + # logging.info("LObjWrap: setup for %s" % (self.ref)) + self.bind(value=self.on_value) + if command is not None: + self.bind(value=command) + + def on_value(self,inst,val): + logging.info("LObjWrap: %s = %s" % (self.ref,val)) + setattr(self.obj,self.ref,val) + +class LBoolWrap(LObjWrap): + value = BooleanProperty(False) + + def __init__(self,obj,ref,command=None): + super(LBoolWrap,self).__init__(obj,ref,command) + +class LNumWrap(LObjWrap): + value = NumericProperty(0) + + def __init__(self,obj,ref,command=None): + super(LNumWrap,self).__init__(obj,ref,command) + +class LStringWrap(LObjWrap): + value = StringProperty('') + + def __init__(self,obj,ref,command=None): + super(LStringWrap,self).__init__(obj,ref,command) + +class LListWrap(LObjWrap): + value = ListProperty([]) + + def __init__(self,obj,ref,command=None): + super(LListWrap,self).__init__(obj,ref,command) + +# ============================================================================= diff --git a/pysollib/kivy/menubar.py b/pysollib/kivy/menubar.py index e99e2b94..5aec8957 100644 --- a/pysollib/kivy/menubar.py +++ b/pysollib/kivy/menubar.py @@ -40,6 +40,9 @@ from pysollib.kivy.LApp import LScrollView from pysollib.kivy.LApp import LTopLevel from pysollib.kivy.LApp import LTreeNode from pysollib.kivy.LApp import LTreeRoot +from pysollib.kivy.LObjWrap import LBoolWrap +from pysollib.kivy.LObjWrap import LNumWrap +from pysollib.kivy.LObjWrap import LStringWrap from pysollib.kivy.androidrot import AndroidScreenRotation from pysollib.kivy.findcarddialog import destroy_find_card_dialog from pysollib.kivy.fullpicturedialog import destroy_full_picture_dialog @@ -119,16 +122,18 @@ class LMenuBase(object): self.closeWindow(0) return auto_close_command - def make_auto_command(self, variable, command): + def make_toggle_command(self, variable, command): def auto_command(): - variable.set(not variable.get()) - command() + variable.value = not variable.value + if command is not None: + command() return auto_command def make_val_command(self, variable, value, command): def val_command(): variable.value = value - command() + if command is not None: + command() return val_command def make_vars_command(self, command, key): @@ -149,7 +154,7 @@ class LMenuBase(object): return _command def addCheckNode(self, tv, rg, title, auto_var, auto_com): - command = self.make_auto_command(auto_var, auto_com) + command = self.make_toggle_command(auto_var, auto_com) rg1 = tv.add_node( LTreeNode(text=title, command=command, variable=auto_var), rg) return rg1 @@ -278,7 +283,7 @@ class MainMenuDialog(LMenuDialog): menubar, parent, title, app, **kw) print('MainMenuDialog starting') - AndroidScreenRotation.unlock() + AndroidScreenRotation.unlock(toaster=False) def buildTree(self, tv, node): rg = tv.add_node( @@ -1134,17 +1139,23 @@ class LOptionsMenuGenerator(LTreeGenerator): self.menubar.tkopt.animations, 5, self.menubar.mOptAnimations) - # submenu.add_separator() - + # NOTE: All the following animation features only work on the + # desktop and only if pillow is installed. So its useless to + # present them here. + ''' self.addCheckNode(tv, rg, _('Redeal animation'), self.menubar.tkopt.redeal_animation, self.menubar.mRedealAnimation) - self.addCheckNode(tv, rg, _('Winning animation'), self.menubar.tkopt.win_animation, self.menubar.mWinAnimation) + self.addCheckNode(tv, rg, + _('Flip animation'), + self.menubar.tkopt.flip_animation, + None) + ''' yield # ------------------------------------------- @@ -1472,6 +1483,7 @@ class PysolMenubarTk: self.progress.update(step=1) def _createTkOpt(self): + opt = self.app.opt # structure to convert menu-options to Toolkit variables self.tkopt = Struct( gameid=IntVar(), @@ -1501,9 +1513,10 @@ class PysolMenubarTk: sound_music_volume=IntVar(), cardback=IntVar(), tabletile=IntVar(), - animations=IntVar(), - redeal_animation=BooleanVar(), - win_animation=BooleanVar(), + animations=LNumWrap(opt, "animations"), + redeal_animation=LBoolWrap(opt, "redeal_animation"), + win_animation=LBoolWrap(opt, "win_animation"), + flip_animation=LBoolWrap(opt, "flip_animation"), shadow=BooleanVar(), shade=BooleanVar(), shade_filled_stacks=BooleanVar(), @@ -1518,10 +1531,10 @@ class PysolMenubarTk: helpbar=BooleanVar(), save_games_geometry=BooleanVar(), splashscreen=BooleanVar(), - demo_logo=BooleanVar(), - demo_logo_style=StringVar(), - pause_text_style=StringVar(), - redeal_icon_style=StringVar(), + demo_logo=LBoolWrap(opt, "demo_logo"), + demo_logo_style=LStringWrap(opt, "demo_logo_style"), + pause_text_style=LStringWrap(opt, "pause_text_style"), + redeal_icon_style=LStringWrap(opt, "redeal_icon_style"), mouse_type=StringVar(), mouse_undo=BooleanVar(), negative_bottom=BooleanVar(), @@ -1570,9 +1583,6 @@ class PysolMenubarTk: tkopt.sound_music_volume.set(opt.sound_music_volume) tkopt.cardback.set(self.app.cardset.backindex) tkopt.tabletile.set(self.app.tabletile_index) - tkopt.animations.set(opt.animations) - tkopt.redeal_animation.set(opt.redeal_animation) - tkopt.win_animation.set(opt.win_animation) tkopt.shadow.set(opt.shadow) tkopt.shade.set(opt.shade) tkopt.toolbar.set(opt.toolbar) @@ -1583,10 +1593,6 @@ class PysolMenubarTk: tkopt.toolbar_relief.set(opt.toolbar_relief) tkopt.statusbar.set(opt.statusbar) tkopt.save_games_geometry.set(opt.save_games_geometry) - tkopt.demo_logo.set(opt.demo_logo) - tkopt.demo_logo_style.set(opt.demo_logo_style) - tkopt.pause_text_style.set(opt.pause_text_style) - tkopt.redeal_icon_style.set(opt.redeal_icon_style) tkopt.splashscreen.set(opt.splashscreen) tkopt.mouse_type.set(opt.mouse_type) tkopt.mouse_undo.set(opt.mouse_undo) @@ -2334,17 +2340,14 @@ the next time you restart the %(app)s""") % {'app': TITLE}) def mOptAnimations(self, *args): if self._cancelDrag(break_pause=False): return - self.app.opt.animations = self.tkopt.animations.value def mRedealAnimation(self, *args): if self._cancelDrag(break_pause=False): return - self.app.opt.redeal_animation = self.tkopt.redeal_animation.value def mWinAnimation(self, *args): if self._cancelDrag(break_pause=False): return - self.app.opt.win_animation = self.tkopt.win_animation.value def mWinDialog(self, *args): if self._cancelDrag(break_pause=False): @@ -2499,13 +2502,13 @@ the next time you restart the %(app)s""") % {'app': TITLE}) self.toolbarConfig(w, self.tkopt.toolbar_vars[w].get()) def mOptDemoLogoStyle(self, *event): - self.setDemoLogoStyle(self.tkopt.demo_logo_style.get()) + self.setDemoLogoStyle() def mOptPauseTextStyle(self, *event): - self.setPauseTextStyle(self.tkopt.pause_text_style.get()) + self.setPauseTextStyle() def mOptRedealIconStyle(self, *event): - self.setRedealIconStyle(self.tkopt.redeal_icon_style.get()) + self.setRedealIconStyle() def mOptStatusbar(self, *event): if self._cancelDrag(break_pause=False): @@ -2542,7 +2545,6 @@ the next time you restart the %(app)s""") % {'app': TITLE}) def mOptDemoLogo(self, *event): if self._cancelDrag(break_pause=False): return - self.app.opt.demo_logo = self.tkopt.demo_logo.get() def mOptSplashscreen(self, *event): if self._cancelDrag(break_pause=False): @@ -2630,20 +2632,15 @@ the next time you restart the %(app)s""") % {'app': TITLE}) # other graphics # - def setDemoLogoStyle(self, style): + def setDemoLogoStyle(self, style=None): if self._cancelDrag(break_pause=False): return - if style == "none": - self.app.opt.demo_logo = False + if self.tkopt.demo_logo_style.value == "none": + self.tkopt.demo_logo.value = False else: - self.app.opt.demo_logo = True - self.app.opt.demo_logo_style = style - self.tkopt.demo_logo_style.set(style) # update radiobutton + self.tkopt.demo_logo.value = True self.app.loadImages2() self.app.loadImages4() - self.app.updateCardset() - self.game.endGame(bookmark=1) - self.game.quitGame(bookmark=1) def setDialogIconStyle(self, style): if self._cancelDrag(break_pause=False): @@ -2653,27 +2650,25 @@ the next time you restart the %(app)s""") % {'app': TITLE}) self.app.loadImages1() self.app.loadImages4() - def setPauseTextStyle(self, style): + def setPauseTextStyle(self, style=None): if self._cancelDrag(break_pause=False): return - self.app.opt.pause_text_style = style - self.tkopt.pause_text_style.set(style) # update radiobutton self.app.loadImages2() self.app.loadImages4() - self.app.updateCardset() - self.game.endGame(bookmark=1) - self.game.quitGame(bookmark=1) + if self.tkopt.pause.value: + self.app.game.displayPauseImage() - def setRedealIconStyle(self, style): + def setRedealIconStyle(self, style=None): if self._cancelDrag(break_pause=False): return - self.app.opt.redeal_icon_style = style - self.tkopt.redeal_icon_style.set(style) # update radiobutton self.app.loadImages2() self.app.loadImages4() - self.app.updateCardset() - self.game.endGame(bookmark=1) - self.game.quitGame(bookmark=1) + try: + images = self.app.game.canvas.findImagesByType("redeal_image") + for i in images: + i.group.stack.updateRedealImage() + except: # noqa + pass # # stacks descriptions diff --git a/pysollib/kivy/tkcanvas.py b/pysollib/kivy/tkcanvas.py index 57c35063..da6cb073 100644 --- a/pysollib/kivy/tkcanvas.py +++ b/pysollib/kivy/tkcanvas.py @@ -277,6 +277,7 @@ class MfxCanvasImage(object): super(MfxCanvasImage, self).__init__() self.canvas = canvas + self.redeal = False # animation mode support: self.animation = 0 @@ -291,12 +292,15 @@ class MfxCanvasImage(object): else: image = LImage(texture=ed.texture) if self.hint == "redeal_image": - cm = cardmagnif(canvas, size)/3.0 + cm = cardmagnif(canvas, size)/1.9 image.size = [cm*ed.getWidth(), cm*ed.getHeight()] + self.redeal = True + aimage = LImageItem( + size=ed.size, group=group, image_type=self.hint) else: image.size = [ed.getWidth(), ed.getHeight()] + aimage = LImageItem(size=ed.size, group=group) - aimage = LImageItem(size=ed.size, group=group) aimage.add_widget(image) aimage.size = image.size size = image.size @@ -322,6 +326,13 @@ class MfxCanvasImage(object): return f'' def config(self, **kw): + # print('MfxCanvasImage conifg:',kw) + if "image" in kw: + # print('is redeal image:',self.redeal) + if self.redeal: + image = self.image.children[0] + image.texture = kw["image"].texture + # print('redeal texture:',image.texture) pass def makeDeferredRaise(self, pos): @@ -819,6 +830,14 @@ class MfxCanvas(LImage): print('MfxCanvas: findCard no cardid') return -1 + def findImagesByType(self, image_type): + images = [] + for c in self.children: + if type(c) is LImageItem: + if c.get_image_type() == image_type: + images.append(c) + return images + def setTextColor(self, color): # print('MfxCanvas: setTextColor') # color is ignored: it sets a predefined (option settable) diff --git a/pysollib/stack.py b/pysollib/stack.py index 855eeb42..a0123ac9 100644 --- a/pysollib/stack.py +++ b/pysollib/stack.py @@ -1857,6 +1857,14 @@ class TalonStack(Stack, for stack in self.game.allstacks: stack.updateText() + def updateRedealImage(self): + deal = self.canDealCards() != 0 + if self.images.redeal is not None: + img = (self.getRedealImages())[deal] + if img is not None and img is not self.images.redeal_img: + self.images.redeal.config(image=img) + self.images.redeal_img = img + def updateText(self, update_rounds=1, update_redeal=1): # assertView(self) Stack.updateText(self)