mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
637 lines
20 KiB
Python
637 lines
20 KiB
Python
#!/usr/bin/env python
|
|
# -*- mode: python; coding: utf-8; -*-
|
|
# ---------------------------------------------------------------------------#
|
|
#
|
|
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
|
|
# Copyright (C) 2003 Mt. Hood Playing Card Co.
|
|
# Copyright (C) 2005-2009 Skomoroh
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# ---------------------------------------------------------------------------#
|
|
# Note:
|
|
# Many classes or some methods of classes are dead code resulting from the tk
|
|
# implementation. If executed it would throw exceptions.
|
|
#
|
|
# Kivy Implementation used: MfxScrolledCanvas, MfxDialog (partly)
|
|
|
|
from __future__ import division
|
|
|
|
import logging
|
|
|
|
from kivy.clock import Clock
|
|
from kivy.uix.anchorlayout import AnchorLayout
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
from kivy.uix.label import Label
|
|
|
|
from pysollib.kivy.LApp import LBoxLayout
|
|
from pysollib.kivy.LApp import LScrollView
|
|
from pysollib.kivy.LApp import LTopLevel
|
|
from pysollib.kivy.LImage import LImage
|
|
from pysollib.kivy.tkcanvas import MfxCanvas
|
|
from pysollib.kivy.tkutil import bind, unbind_destroy
|
|
from pysollib.kivy.tkutil import makeToplevel
|
|
from pysollib.mfxutil import KwStruct, kwdefault
|
|
from pysollib.mygettext import _
|
|
from pysollib.settings import WIN_SYSTEM
|
|
|
|
# ************************************************************************
|
|
# * abstract base class for the dialogs in this module
|
|
# ************************************************************************
|
|
|
|
|
|
class MfxDialog: # ex. _ToplevelDialog
|
|
img = {}
|
|
button_img = {}
|
|
|
|
def __init__(self, parent, title="", resizable=False, default=-1):
|
|
self.parent = parent
|
|
self.status = 0
|
|
self.button = default
|
|
self.timer = None
|
|
self.buttons = []
|
|
self.accel_keys = {}
|
|
self.top = makeToplevel(parent, title=title)
|
|
|
|
def wmDeleteWindow(self, *event):
|
|
self.status = 1
|
|
raise SystemExit
|
|
# return EVENT_HANDLED
|
|
|
|
def mCancel(self, *event):
|
|
self.status = 1
|
|
raise SystemExit
|
|
|
|
def mTimeout(self, *event):
|
|
self.status = 2
|
|
raise SystemExit
|
|
|
|
def mDone(self, button):
|
|
self.button = button
|
|
raise SystemExit
|
|
|
|
def altKeyEvent(self, event):
|
|
key = event.char
|
|
# key = unicode(key, 'utf-8')
|
|
key = key.lower()
|
|
button = self.accel_keys.get(key)
|
|
if button is not None:
|
|
self.mDone(button)
|
|
|
|
def initKw(self, kw):
|
|
kw = KwStruct(kw,
|
|
timeout=0, resizable=False,
|
|
text="", justify="center",
|
|
strings=(_("&OK"), ),
|
|
default=0,
|
|
width=0,
|
|
padx=20, pady=20,
|
|
bitmap=None, bitmap_side="left",
|
|
bitmap_padx=10, bitmap_pady=20,
|
|
image=None, image_side="left",
|
|
image_padx=10, image_pady=20,
|
|
)
|
|
# default to separator if more than one button
|
|
sep = len(kw.strings) > 1
|
|
kwdefault(kw.__dict__, separator=sep)
|
|
return kw
|
|
|
|
def createFrames(self, kw):
|
|
a = LBoxLayout(orientation="vertical")
|
|
b = LBoxLayout(orientation="vertical")
|
|
return a, b
|
|
|
|
|
|
# ************************************************************************
|
|
# Needed Labels.
|
|
|
|
class MfxSimpleEntry:
|
|
pass
|
|
|
|
|
|
class MfxToolTip:
|
|
pass
|
|
|
|
# ************************************************************************
|
|
# * replacement for the tk_dialog script
|
|
# ************************************************************************
|
|
# Kivy implementation helpers.
|
|
|
|
|
|
class FLabel(Label):
|
|
def __init__(self, **kw):
|
|
super(FLabel, self).__init__(**kw)
|
|
|
|
self.bind(size=self.onUpdate)
|
|
self.bind(pos=self.onUpdate)
|
|
self.bind(text=self.onUpdate)
|
|
|
|
def onUpdate(self, instance, size):
|
|
self.size_hint_y = None
|
|
self.text_size = self.width, None
|
|
self.texture_update()
|
|
self.height = self.texture_size[1]
|
|
|
|
|
|
class FText(LScrollView):
|
|
def __init__(self, **kw):
|
|
super(FText, self).__init__(**kw)
|
|
|
|
self.label = FLabel(**kw)
|
|
self.add_widget(self.label)
|
|
|
|
|
|
class MfxMessageDialog(MfxDialog):
|
|
def __init__(self, parent, title, **kw):
|
|
kw = self.initKw(kw)
|
|
MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
|
|
|
|
if (kw.image):
|
|
image = LImage(texture=kw.image.texture, size_hint=(1, 1))
|
|
self.top.add_widget(image)
|
|
|
|
label = FText(text=kw.text, halign='center')
|
|
self.top.add_widget(label)
|
|
|
|
# LB
|
|
# nicht automatisch ein neues spiel laden.
|
|
if (title == _("Game won")):
|
|
self.status = 1
|
|
# self.button = 0
|
|
if (title == _("Game finished")):
|
|
self.status = 1
|
|
# self.button =
|
|
|
|
# ************************************************************************
|
|
# *
|
|
# ************************************************************************
|
|
|
|
|
|
class MfxExceptionDialog(MfxMessageDialog):
|
|
def __init__(self, parent, ex, title=_("Error"), **kw):
|
|
kw = KwStruct(kw, bitmap="error")
|
|
text = kw.get("text", "")
|
|
if not text.endswith("\n"):
|
|
text = text + "\n"
|
|
text = text + "\n"
|
|
if isinstance(ex, EnvironmentError) and ex.filename is not None:
|
|
t = "[Errno %s] %s:\n%s" % (
|
|
ex.errno, ex.strerror, repr(ex.filename))
|
|
else:
|
|
t = str(ex)
|
|
kw.text = text + t
|
|
MfxMessageDialog.__init__(self, parent, title, **kw.getKw())
|
|
|
|
|
|
# ************************************************************************
|
|
# *
|
|
# ************************************************************************
|
|
|
|
class PysolAboutDialog(object):
|
|
|
|
# Die einzige Instanz.
|
|
AboutDialog = None
|
|
|
|
def onClick(self, event):
|
|
print('LTopLevel: onClick')
|
|
PysolAboutDialog.AboutDialog.parent.popWork('AboutDialog')
|
|
PysolAboutDialog.AboutDialog.running = False
|
|
|
|
def __init__(self, app, parent, title, **kw):
|
|
logging.info('PysolAboutDialog:')
|
|
super(PysolAboutDialog, self).__init__()
|
|
|
|
self._url = kw['url']
|
|
logging.info('PysolAboutDialog: txt=%s' % title)
|
|
|
|
text = kw['text']
|
|
text = text + '\n' + self._url
|
|
logging.info('PysolAboutDialog: txt=%s' % text)
|
|
|
|
text = text + '\n\n' + 'Adaptation to Kivy/Android\n' + \
|
|
' Copyright (C) (2016-23) LB'
|
|
|
|
self.parent = parent
|
|
self.app = app
|
|
self.window = None
|
|
self.running = False
|
|
self.status = 1 # -> von help.py so benötigt
|
|
self.button = 0 # -> von help.py so benötigt
|
|
|
|
# bestehenden Dialog rezyklieren.
|
|
|
|
logging.info('PysolAboutDialog: 1')
|
|
onlyone = PysolAboutDialog.AboutDialog
|
|
if (onlyone and onlyone.running):
|
|
return
|
|
if (onlyone):
|
|
onlyone.parent.pushWork('AboutDialog', onlyone.window)
|
|
onlyone.running = True
|
|
return
|
|
|
|
# neuen Dialog aufbauen.
|
|
|
|
window = LTopLevel(parent, title, size_hint=(1.0, 1.0))
|
|
window.titleline.bind(on_press=self.onClick)
|
|
self.parent.pushWork('AboutDialog', window)
|
|
self.window = window
|
|
self.running = True
|
|
PysolAboutDialog.AboutDialog = self
|
|
|
|
if kw['image']:
|
|
image = LImage(texture=kw['image'].texture)
|
|
image.size_hint = (1, 0.8)
|
|
al = AnchorLayout()
|
|
al.add_widget(image)
|
|
al.size_hint = (1, 0.3)
|
|
window.content.add_widget(al)
|
|
|
|
label = FText(text=text, halign='center', size_hint=(1, 1))
|
|
window.content.add_widget(label)
|
|
|
|
|
|
# ************************************************************************
|
|
# * a simple tooltip
|
|
# ************************************************************************
|
|
# ToolTip - not used in Kivy - would not run without adaptations.
|
|
|
|
|
|
'''
|
|
class MfxTooltip:
|
|
last_leave_time = 0
|
|
|
|
def __init__(self, widget):
|
|
# private vars
|
|
self.widget = widget
|
|
self.text = None
|
|
self.timer = None
|
|
self.cancel_timer = None
|
|
self.tooltip = None
|
|
self.label = None
|
|
self.bindings = []
|
|
self.bindings.append(self.widget.bind("<Enter>", self._enter))
|
|
self.bindings.append(self.widget.bind("<Leave>", self._leave))
|
|
self.bindings.append(self.widget.bind("<ButtonPress>", self._leave))
|
|
# user overrideable settings
|
|
self.timeout = 800 # milliseconds
|
|
self.cancel_timeout = 5000
|
|
self.leave_timeout = 400
|
|
self.relief = 'solid'
|
|
self.justify = 'left'
|
|
self.fg = "#000000"
|
|
self.bg = "#ffffe0"
|
|
self.xoffset = 0
|
|
self.yoffset = 4
|
|
|
|
def setText(self, text):
|
|
self.text = text
|
|
|
|
def _unbind(self):
|
|
if self.bindings and self.widget:
|
|
self.widget.unbind("<Enter>", self.bindings[0])
|
|
self.widget.unbind("<Leave>", self.bindings[1])
|
|
self.widget.unbind("<ButtonPress>", self.bindings[2])
|
|
self.bindings = []
|
|
|
|
def destroy(self):
|
|
self._unbind()
|
|
self._leave()
|
|
|
|
def _enter(self, *event):
|
|
after_cancel(self.timer)
|
|
after_cancel(self.cancel_timer)
|
|
self.cancel_timer = None
|
|
timediff = time.time() - MfxTooltip.last_leave_time
|
|
if timediff < self.leave_timeout / 1000.:
|
|
self._showTip()
|
|
else:
|
|
self.timer = after(self.widget, self.timeout, self._showTip)
|
|
|
|
def _leave(self, *event):
|
|
after_cancel(self.timer)
|
|
after_cancel(self.cancel_timer)
|
|
self.timer = self.cancel_timer = None
|
|
if self.tooltip:
|
|
self.label.destroy()
|
|
destruct(self.label)
|
|
self.label = None
|
|
self.tooltip.destroy()
|
|
destruct(self.tooltip)
|
|
self.tooltip = None
|
|
MfxTooltip.last_leave_time = time.time()
|
|
|
|
def _showTip(self):
|
|
self.timer = None
|
|
if self.tooltip or not self.text:
|
|
return
|
|
# if isinstance(self.widget, (Tkinter.Button, Tkinter.Checkbutton)):
|
|
# if self.widget["state"] == 'disabled':
|
|
# return
|
|
# x = self.widget.winfo_rootx()
|
|
x = self.widget.winfo_pointerx()
|
|
y = self.widget.winfo_rooty() + self.widget.winfo_height()
|
|
x += self.xoffset
|
|
y += self.yoffset
|
|
self.tooltip = Tkinter.Toplevel()
|
|
self.tooltip.wm_iconify()
|
|
self.tooltip.wm_overrideredirect(1)
|
|
self.tooltip.wm_protocol("WM_DELETE_WINDOW", self.destroy)
|
|
self.label = Tkinter.Label(self.tooltip, text=self.text,
|
|
relief=self.relief, justify=self.justify,
|
|
fg=self.fg, bg=self.bg, bd=1, takefocus=0)
|
|
self.label.pack(ipadx=1, ipady=1)
|
|
self.tooltip.wm_geometry("%+d%+d" % (x, y))
|
|
self.tooltip.wm_deiconify()
|
|
self.cancel_timer = after(
|
|
self.widget, self.cancel_timeout, self._leave)
|
|
# self.tooltip.tkraise()
|
|
'''
|
|
|
|
# ************************************************************************
|
|
# * A canvas widget with scrollbars and some useful bindings.
|
|
# ************************************************************************
|
|
# Kivy implementation of MfxScrolledCanvas.
|
|
|
|
|
|
class LScrollFrame(BoxLayout):
|
|
def __init__(self, **kw):
|
|
super(LScrollFrame, self).__init__(orientation="vertical", **kw)
|
|
|
|
|
|
class MfxScrolledCanvas(object):
|
|
def __init__(self, parent, hbar=True, vbar=True, propagate=False, **kw):
|
|
kwdefault(kw, highlightthickness=0, bd=1, relief='sunken')
|
|
self.parent = parent
|
|
|
|
# workarea = parent.getWork()
|
|
print('MfxScrolledCanvas: parent=%s' % (parent))
|
|
|
|
super(MfxScrolledCanvas, self).__init__()
|
|
self.createFrame(kw)
|
|
self.canvas = None
|
|
# do_scroll_x = None
|
|
# do_scroll_y = None
|
|
# self.hbar = None
|
|
# self.vbar = None
|
|
self.hbar_show = False
|
|
self.vbar_show = False
|
|
self.createCanvas(kw)
|
|
# self.frame.grid_rowconfigure(0, weight=1)
|
|
# self.frame.grid_columnconfigure(0, weight=1)
|
|
# self.frame.grid_propagate(propagate)
|
|
if hbar:
|
|
self.createHbar()
|
|
self.bindHbar()
|
|
if vbar:
|
|
self.createVbar()
|
|
self.bindVbar()
|
|
# self.canvas.focus_set()
|
|
|
|
def destroy(self):
|
|
logging.info('MfxRoot: destroy')
|
|
self.unbind_all()
|
|
self.canvas.destroy()
|
|
self.frame.destroy()
|
|
|
|
def pack(self, **kw):
|
|
pass
|
|
# self.frame.pack(**kw)
|
|
|
|
def grid(self, **kw):
|
|
pass
|
|
# self.frame.grid(**kw)
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
def setTile(self, app, i, scale_method, force=False):
|
|
logging.info('MfxRoot: setTitle app=%s' % app)
|
|
|
|
tile = app.tabletile_manager.get(i)
|
|
|
|
print('setTile: (tile) %s, index=%s' % (tile, i))
|
|
|
|
if tile is None or tile.error:
|
|
return False
|
|
|
|
# print i, tile
|
|
if i == 0:
|
|
assert tile.color
|
|
assert tile.filename is None
|
|
else:
|
|
assert tile.color is None
|
|
assert tile.filename
|
|
assert tile.basename
|
|
if not force:
|
|
if (i == app.tabletile_index and
|
|
tile.color == app.opt.colors['table']):
|
|
return False
|
|
#
|
|
print('setTile2: %s' % (tile.filename))
|
|
|
|
if not self.canvas.setTile(
|
|
tile.filename, tile.stretch, tile.save_aspect):
|
|
tile.error = True
|
|
return False
|
|
|
|
print(
|
|
'MfxScrolledCanvas: tile.color, app.top_bg %s, %s'
|
|
% (tile.color, app.top_bg))
|
|
if i == 0:
|
|
if force:
|
|
tile.color = app.opt.colors['table']
|
|
self.canvas.config(bg=tile.color)
|
|
else:
|
|
if type(app.top_bg) is str:
|
|
self.canvas.config(bg=app.top_bg)
|
|
|
|
self.canvas.setTextColor(app.opt.colors['text'])
|
|
return True
|
|
#
|
|
#
|
|
#
|
|
|
|
def deleteAllItems(self):
|
|
logging.info('MfxRoot: deleteAllItems')
|
|
# self.parent.getWork()
|
|
# self.parent.popWork()
|
|
# self.frame.clear_widgets()
|
|
self.canvas.clear_widgets()
|
|
|
|
def update_idletasks(self):
|
|
logging.info('MfxRoot: update_idletasks')
|
|
Clock.schedule_once(lambda x: self.canvas.canvas.ask_update)
|
|
|
|
def unbind_all(self):
|
|
unbind_destroy(self.hbar)
|
|
unbind_destroy(self.vbar)
|
|
unbind_destroy(self.canvas)
|
|
unbind_destroy(self.frame)
|
|
|
|
def createFrame(self, kw):
|
|
logging.info('MfxRoot: createFrame')
|
|
# width = kw.get("width")
|
|
# height = kw.get("height")
|
|
print('createFrame: kw=%s' % kw)
|
|
# self.frame = Tkinter.Frame(self.parent, width=width, height=height)
|
|
|
|
self.frame = LScrollFrame(size_hint=(1, 1))
|
|
|
|
print("createFrame: self.parent %s" % str(self.frame))
|
|
|
|
def createCanvas(self, kw):
|
|
logging.info('MfxRoot: createCanvas')
|
|
# bd = kw['bd']
|
|
kw['bd'] = 0
|
|
# relief = kw['relief']
|
|
del kw['relief']
|
|
# frame = Tkinter.Frame(self.frame, bd=bd, relief=relief)
|
|
# frame.grid(row=0, column=0, sticky="news")
|
|
'''
|
|
self.canvas = MfxCanvas(self.frame, **kw)
|
|
self.frame.add_widget(self.canvas)
|
|
self.parent.pushWork(self.frame)
|
|
'''
|
|
self.canvas = MfxCanvas(self.parent, **kw)
|
|
self.frame = self.canvas
|
|
self.parent.pushWork('playground', self.frame)
|
|
''
|
|
# self.canvas.pack(expand=True, fill='both')
|
|
|
|
def createHbar(self):
|
|
pass
|
|
'''
|
|
self.hbar = Tkinter.Scrollbar(self.frame, takefocus=0,
|
|
orient="horizontal")
|
|
self.canvas["xscrollcommand"] = self._setHbar
|
|
self.hbar["command"] = self.canvas.xview
|
|
self.hbar.grid(row=1, column=0, sticky="we")
|
|
self.hbar.grid_remove()
|
|
'''
|
|
|
|
def createVbar(self):
|
|
pass
|
|
'''
|
|
self.vbar = Tkinter.Scrollbar(self.frame, takefocus=0)
|
|
self.canvas["yscrollcommand"] = self._setVbar
|
|
self.vbar["command"] = self.canvas.yview
|
|
self.vbar.grid(row=0, column=1, sticky="ns")
|
|
self.vbar.grid_remove()
|
|
'''
|
|
|
|
def bindHbar(self, w=None):
|
|
if w is None:
|
|
w = self.canvas
|
|
bind(w, "<KeyPress-Left>", self.unit_left)
|
|
bind(w, "<KeyPress-Right>", self.unit_right)
|
|
|
|
def bindVbar(self, w=None):
|
|
if w is None:
|
|
w = self.canvas
|
|
bind(w, "<KeyPress-Prior>", self.page_up)
|
|
bind(w, "<KeyPress-Next>", self.page_down)
|
|
bind(w, "<KeyPress-Up>", self.unit_up)
|
|
bind(w, "<KeyPress-Down>", self.unit_down)
|
|
bind(w, "<KeyPress-Begin>", self.scroll_top)
|
|
bind(w, "<KeyPress-Home>", self.scroll_top)
|
|
bind(w, "<KeyPress-End>", self.scroll_bottom)
|
|
# mousewheel support
|
|
if WIN_SYSTEM == 'x11':
|
|
bind(w, '<4>', self.mouse_wheel_up)
|
|
bind(w, '<5>', self.mouse_wheel_down)
|
|
# don't work on Linux
|
|
# bind(w, '<MouseWheel>', self.mouse_wheel)
|
|
|
|
def mouse_wheel(self, *args):
|
|
pass
|
|
|
|
def _setHbar(self, first, last):
|
|
if self.canvas.busy:
|
|
return
|
|
sb = self.hbar
|
|
if float(first) <= 0 and float(last) >= 1:
|
|
sb.grid_remove()
|
|
self.hbar_show = False
|
|
else:
|
|
if self.canvas.winfo_ismapped():
|
|
sb.grid()
|
|
self.hbar_show = True
|
|
sb.set(first, last)
|
|
|
|
def _setVbar(self, first, last):
|
|
if self.canvas.busy:
|
|
return
|
|
sb = self.vbar
|
|
if float(first) <= 0 and float(last) >= 1:
|
|
sb.grid_remove()
|
|
self.vbar_show = False
|
|
else:
|
|
if self.canvas.winfo_ismapped():
|
|
sb.grid()
|
|
self.vbar_show = True
|
|
sb.set(first, last)
|
|
|
|
def _xview(self, *args):
|
|
if self.hbar_show:
|
|
self.canvas.xview(*args)
|
|
return 'break'
|
|
|
|
def _yview(self, *args):
|
|
if self.vbar_show:
|
|
self.canvas.yview(*args)
|
|
return 'break'
|
|
|
|
def page_up(self, *event):
|
|
return self._yview('scroll', -1, 'page')
|
|
|
|
def page_down(self, *event):
|
|
return self._yview('scroll', 1, 'page')
|
|
|
|
def unit_up(self, *event):
|
|
return self._yview('scroll', -1, 'unit')
|
|
|
|
def unit_down(self, *event):
|
|
return self._yview('scroll', 1, 'unit')
|
|
|
|
def mouse_wheel_up(self, *event):
|
|
return self._yview('scroll', -5, 'unit')
|
|
|
|
def mouse_wheel_down(self, *event):
|
|
return self._yview('scroll', 5, 'unit')
|
|
|
|
def page_left(self, *event):
|
|
return self._xview('scroll', -1, 'page')
|
|
|
|
def page_right(self, *event):
|
|
return self._xview('scroll', 1, 'page')
|
|
|
|
def unit_left(self, *event):
|
|
return self._xview('scroll', -1, 'unit')
|
|
|
|
def unit_right(self, *event):
|
|
return self._xview('scroll', 1, 'unit')
|
|
|
|
def scroll_top(self, *event):
|
|
return self._yview('moveto', 0)
|
|
|
|
def scroll_bottom(self, *event):
|
|
return self._yview('moveto', 1)
|
|
|
|
|
|
# ************************************************************************
|
|
# *
|
|
# ************************************************************************
|
|
# not used witch kivy. would not nun as it refers TkInter.
|