From 6a60c19bbd7c64012b50eff0024f05abf1fee1e3 Mon Sep 17 00:00:00 2001 From: skomoroh Date: Mon, 7 Mar 2011 09:23:26 +0000 Subject: [PATCH] * scalable cards: +tk git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@273 efabe8c0-fbe8-4139-b769-b5e6d273206e --- pysollib/mfxutil.py | 2 +- pysollib/stack.py | 7 ++- pysollib/tk/card.py | 11 ++++ pysollib/tk/menubar.py | 94 +++++++++++++++++++++++++++++++--- pysollib/tk/selectcardset.py | 98 ++++++++++++++++++++++++++++++++++-- pysollib/tk/tkcanvas.py | 28 ++++++++--- pysollib/tk/tkutil.py | 37 ++++++++++---- 7 files changed, 247 insertions(+), 30 deletions(-) diff --git a/pysollib/mfxutil.py b/pysollib/mfxutil.py index bfd4dfce..d0c00900 100644 --- a/pysollib/mfxutil.py +++ b/pysollib/mfxutil.py @@ -55,7 +55,7 @@ if TOOLKIT == 'tk': import PpmImagePlugin Image._initialized = 2 USE_PIL = False -if TOOLKIT == 'tk' and USE_TILE and Image and Image.VERSION >= '1.1.7': +if TOOLKIT == 'tk' and Image and Image.VERSION >= '1.1.7': USE_PIL = True # debug diff --git a/pysollib/stack.py b/pysollib/stack.py index 7ceefe06..c3e01700 100644 --- a/pysollib/stack.py +++ b/pysollib/stack.py @@ -925,13 +925,18 @@ class Stack: img = self.getBottomImage() self.images.bottom['image'] = img self.images.bottom.moveTo(x, y) + if self.items.bottom: + c = self.items.bottom.coords() + c = ((int(round(c[0]*xf)), int(round(c[1]*yf))), + (int(round(c[2]*xf)), int(round(c[3]*yf)))) + self.items.bottom.coords(c) if self.items.shade_item: c = self.cards[-1] img = self.game.app.images.getHighlightedCard(c.deck, c.suit, c.rank) if img: self.items.shade_item['image'] = img self.items.shade_item.moveTo(x, y) - # + # move the items def move(item): ix, iy = item.init_coord x = int(round(ix*xf)) diff --git a/pysollib/tk/card.py b/pysollib/tk/card.py index 70bd2a3d..7d135761 100644 --- a/pysollib/tk/card.py +++ b/pysollib/tk/card.py @@ -107,6 +107,17 @@ class _OneImageCard(_HideableCard): item = self.item item.canvas.tk.call(item.canvas._w, "move", item.id, dx, dy) + # for resize + def update(self, id, deck, suit, rank, game): + self._face_image = game.getCardFaceImage(deck, suit, rank) + self._back_image = game.getCardBackImage(deck, suit, rank) + self._shade_image = game.getCardShadeImage() + if self.face_up: + img = self._face_image + else: + img = self._back_image + self.item.config(image=img) + self._active_image = img # ************************************************************************ diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 76f2ddc9..d232b7bf 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -30,7 +30,7 @@ import Tkinter, tkFileDialog # PySol imports from pysollib.mfxutil import Struct, kwdefault -from pysollib.mfxutil import Image +from pysollib.mfxutil import Image, USE_PIL from pysollib.util import CARDSET from pysollib.settings import TITLE, WIN_SYSTEM from pysollib.settings import TOP_TITLE @@ -209,6 +209,7 @@ class PysolMenubarTk: mahjongg_show_removed = Tkinter.BooleanVar(), shisen_show_hint = Tkinter.BooleanVar(), sound = Tkinter.BooleanVar(), + auto_scale = Tkinter.BooleanVar(), cardback = Tkinter.IntVar(), tabletile = Tkinter.IntVar(), animations = Tkinter.IntVar(), @@ -258,6 +259,7 @@ class PysolMenubarTk: tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed) tkopt.shisen_show_hint.set(opt.shisen_show_hint) tkopt.sound.set(opt.sound) + tkopt.auto_scale.set(opt.auto_scale) tkopt.cardback.set(self.app.cardset.backindex) tkopt.tabletile.set(self.app.tabletile_index) tkopt.animations.set(opt.animations) @@ -453,6 +455,11 @@ class PysolMenubarTk: else: menu.add_checkbutton(label=label, variable=self.tkopt.sound, command=self.mOptSoundDialog) # cardsets + if USE_PIL: + submenu = MfxMenu(menu, label=n_("Card si&ze")) + submenu.add_command(label=n_("&Increase the card size"), command=self.mIncreaseCardset, accelerator=m+"+") + submenu.add_command(label=n_("&Decrease the card size"), command=self.mDecreaseCardset, accelerator=m+"-") + submenu.add_checkbutton(label=n_("&Auto scaling"), variable=self.tkopt.auto_scale, command=self.mOptAutoScale, accelerator=m+'0') #manager = self.app.cardset_manager #n = manager.len() menu.add_command(label=n_("Cards&et..."), command=self.mSelectCardsetDialog, accelerator=m+"E") @@ -493,7 +500,7 @@ class PysolMenubarTk: submenu.add_checkbutton(label=n_("Show &statusbar"), variable=self.tkopt.statusbar, command=self.mOptStatusbar) submenu.add_checkbutton(label=n_("Show &number of cards"), variable=self.tkopt.num_cards, command=self.mOptNumCards) submenu.add_checkbutton(label=n_("Show &help bar"), variable=self.tkopt.helpbar, command=self.mOptHelpbar) - menu.add_checkbutton(label=n_("Save games &geometry"), variable=self.tkopt.save_games_geometry, command=self.mOptSaveGamesGeometry) + #menu.add_checkbutton(label=n_("Save games &geometry"), variable=self.tkopt.save_games_geometry, command=self.mOptSaveGamesGeometry) menu.add_checkbutton(label=n_("&Demo logo"), variable=self.tkopt.demo_logo, command=self.mOptDemoLogo) menu.add_checkbutton(label=n_("Startup splash sc&reen"), variable=self.tkopt.splashscreen, command=self.mOptSplashscreen) ### menu.add_separator() @@ -542,6 +549,11 @@ class PysolMenubarTk: self._bindKey("", "F3", self.mFindCard) self._bindKey(ctrl, "d", self.mDemo) self._bindKey(ctrl, "e", self.mSelectCardsetDialog) + if USE_PIL: + self._bindKey(ctrl, "plus", self.mIncreaseCardset) + self._bindKey(ctrl, "equal", self.mIncreaseCardset) + self._bindKey(ctrl, "minus", self.mDecreaseCardset) + self._bindKey(ctrl, "0", self.mOptAutoScale) self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented self._bindKey(ctrl, "i", self.mOptChangeTableTile) # undocumented self._bindKey(ctrl, "p", self.mOptPlayerOptions) # undocumented @@ -1135,6 +1147,56 @@ class PysolMenubarTk: self.app.opt.shisen_show_hint = self.tkopt.shisen_show_hint.get() ##self.game.updateMenus() + def _updateCardSize(self): + geom = (self.app.canvas.winfo_width(), + self.app.canvas.winfo_height()) + self.app.opt.game_geometry = geom + self.app.game.resizeGame() + if self.app.opt.auto_scale: + w, h = self.app.opt.game_geometry + self.app.canvas.setInitialSize(w, h, scrollregion=False) + else: + w = int(round(self.app.game.width * self.app.opt.scale_x)) + h = int(round(self.app.game.height * self.app.opt.scale_y)) + self.app.canvas.setInitialSize(w, h) + self.app.top.wm_geometry("") # cancel user-specified geometry + ##self.app.top.update_idletasks() + + def mIncreaseCardset(self, *event): + if self._cancelDrag(break_pause=True): return + if self.app.opt.scale_x < 4: + self.app.opt.scale_x += 0.1 + else: + return + if self.app.opt.scale_y < 4: + self.app.opt.scale_y += 0.1 + else: + return + self.app.opt.auto_scale = False + self.tkopt.auto_scale.set(False) + self._updateCardSize() + + def mDecreaseCardset(self, *event): + if self._cancelDrag(break_pause=True): return + if self.app.opt.scale_x > 0.5: + self.app.opt.scale_x -= 0.1 + else: + return + if self.app.opt.scale_y > 0.5: + self.app.opt.scale_y -= 0.1 + else: + return + self.app.opt.auto_scale = False + self.tkopt.auto_scale.set(False) + self._updateCardSize() + + def mOptAutoScale(self, *event): + if self._cancelDrag(break_pause=True): return + auto_scale = not self.app.opt.auto_scale + self.app.opt.auto_scale = auto_scale + self.tkopt.auto_scale.set(auto_scale) + self._updateCardSize() + def mSelectCardsetDialog(self, *event): if self._cancelDrag(break_pause=False): return ##strings, default = ("&OK", "&Load", "&Cancel"), 0 @@ -1147,14 +1209,30 @@ class PysolMenubarTk: app=self.app, manager=self.app.cardset_manager, key=key, strings=strings, default=default) cs = self.app.cardset_manager.get(d.key) - if cs is None or d.key == self.app.cardset.index: + if d.status != 0 or d.button != 1 or cs is None: return - if d.status == 0 and d.button in (0, 1) and d.key >= 0: + if USE_PIL: + changed = (self.app.opt.scale_x, + self.app.opt.scale_y, + self.app.opt.auto_scale, + self.app.opt.preserve_aspect_ratio) != d.scale_values + else: + changed = False + if d.key == self.app.cardset.index and not changed: + return + if d.key >= 0: self.app.nextgame.cardset = cs - if d.button == 1: - self._cancelDrag() - self.game.endGame(bookmark=1) - self.game.quitGame(bookmark=1) + if USE_PIL: + (self.app.opt.scale_x, + self.app.opt.scale_y, + self.app.opt.auto_scale, + self.app.opt.preserve_aspect_ratio) = d.scale_values + if not self.app.opt.auto_scale: + self.app.images.resize(self.app.opt.scale_x, + self.app.opt.scale_y) + self._cancelDrag() + self.game.endGame(bookmark=1) + self.game.quitGame(bookmark=1) def _mOptCardback(self, index): if self._cancelDrag(break_pause=False): return diff --git a/pysollib/tk/selectcardset.py b/pysollib/tk/selectcardset.py index cdc14e55..1b42c296 100644 --- a/pysollib/tk/selectcardset.py +++ b/pysollib/tk/selectcardset.py @@ -28,7 +28,7 @@ import os import Tkinter # PySol imports -from pysollib.mfxutil import KwStruct +from pysollib.mfxutil import KwStruct, USE_PIL from pysollib.util import CARDSET from pysollib.resource import CSI @@ -178,6 +178,7 @@ class SelectCardsetDialogWithPreview(MfxDialog): key = manager.getSelected() self.manager = manager self.key = key + self.app = app #padx, pady = kw.padx, kw.pady padx, pady = 5, 5 if self.TreeDataHolder_Class.data is None: @@ -185,7 +186,7 @@ class SelectCardsetDialogWithPreview(MfxDialog): # self.top.wm_minsize(400, 200) if self.top.winfo_screenwidth() >= 800: - w1, w2 = 216, 400 + w1, w2 = 240, 400 else: w1, w2 = 200, 300 paned_window = Tkinter.PanedWindow(top_frame) @@ -199,7 +200,56 @@ class SelectCardsetDialogWithPreview(MfxDialog): self.tree = self.Tree_Class(self, left_frame, key=key, default=kw.default, font=font, width=w1) - self.tree.frame.pack(fill='both', expand=True, padx=padx, pady=pady) + self.tree.frame.grid(row=0, column=0, sticky='nsew', + padx=padx, pady=pady) + if USE_PIL: + # + var = Tkinter.DoubleVar() + var.set(app.opt.scale_x) + self.scale_x = Tkinter.Scale( + left_frame, label=_('Scale X:'), + from_=0.5, to=4.0, resolution=0.1, + orient='horizontal', variable=var, + #value=app.opt.scale_x, + command=self._updateScale) + self.scale_x.grid(row=1, column=0, sticky='ew', padx=padx, pady=pady) + # + var = Tkinter.DoubleVar() + var.set(app.opt.scale_y) + self.scale_y = Tkinter.Scale( + left_frame, label=_('Scale Y:'), + from_=0.5, to=4.0, resolution=0.1, + orient='horizontal', variable=var, + #value=app.opt.scale_y, + command=self._updateScale) + self.scale_y.grid(row=2, column=0, sticky='ew', padx=padx, pady=pady) + # + self.auto_scale = Tkinter.BooleanVar() + self.auto_scale.set(app.opt.auto_scale) + check = Tkinter.Checkbutton( + left_frame, text=_('Auto scaling'), + variable=self.auto_scale, + takefocus=False, + command=self._updateAutoScale + ) + check.grid(row=3, column=0, columnspan=2, sticky='w', + padx=padx, pady=pady) + # + self.preserve_aspect = Tkinter.BooleanVar() + self.preserve_aspect.set(app.opt.preserve_aspect_ratio) + self.aspect_check = Tkinter.Checkbutton( + left_frame, text=_('Preserve aspect ratio'), + variable=self.preserve_aspect, + takefocus=False, + #command=self._updateScale + ) + self.aspect_check.grid(row=4, column=0, sticky='w', + padx=padx, pady=pady) + self._updateAutoScale() + # + left_frame.rowconfigure(0, weight=1) + left_frame.columnconfigure(0, weight=1) + # self.preview = MfxScrolledCanvas(right_frame, width=w2) self.preview.setTile(app, app.tabletile_index, force=True) self.preview.pack(fill='both', expand=True, padx=padx, pady=pady) @@ -207,6 +257,7 @@ class SelectCardsetDialogWithPreview(MfxDialog): # create a preview of the current state self.preview_key = -1 self.preview_images = [] + self.scale_images = [] self.updatePreview(key) # focus = self.createButtons(bottom_frame, kw) @@ -234,6 +285,20 @@ class SelectCardsetDialogWithPreview(MfxDialog): if button in (0, 1): # Ok/Load self.key = self.tree.selection_key self.tree.n_expansions = 1 # save xyview in any case + if USE_PIL: + auto_scale = bool(self.auto_scale.get()) + if button == 1: + self.app.menubar.tkopt.auto_scale.set(auto_scale) + if auto_scale: + self.scale_values = (self.app.opt.scale_x, + self.app.opt.scale_y, + auto_scale, + bool(self.preserve_aspect.get())) + else: + self.scale_values = (self.scale_x.get(), + self.scale_y.get(), + auto_scale, + self.app.opt.preserve_aspect_ratio) if button in (3, 4): cs = self.manager.get(self.tree.selection_key) if not cs: @@ -244,9 +309,24 @@ class SelectCardsetDialogWithPreview(MfxDialog): return MfxDialog.mDone(self, button) - def updatePreview(self, key): + def _updateAutoScale(self, v=None): + if self.auto_scale.get(): + self.aspect_check.config(state='normal') + self.scale_x.config(state='disabled') + self.scale_y.config(state='disabled') + else: + self.aspect_check.config(state='disabled') + self.scale_x.config(state='normal') + self.scale_y.config(state='normal') + + def _updateScale(self, v): + self.updatePreview() + + def updatePreview(self, key=None): if key == self.preview_key: return + if key is None: + key = self.key canvas = self.preview.canvas canvas.deleteAllItems() self.preview_images = [] @@ -265,7 +345,16 @@ class SelectCardsetDialogWithPreview(MfxDialog): self.preview_images = [] return i, x, y, sx, sy, dx, dy = 0, 10, 10, 0, 0, cs.CARDW + 10, cs.CARDH + 10 + if USE_PIL: + xf = self.scale_x.get() + yf = self.scale_y.get() + dx = int(dx*xf) + dy = int(dy*yf) + self.scale_images = [] for image in self.preview_images: + if USE_PIL: + image = image.resize(xf, yf) + self.scale_images.append(image) MfxCanvasImage(canvas, x, y, anchor="nw", image=image) sx, sy = max(x, sx), max(y, sy) i = i + 1 @@ -278,6 +367,7 @@ class SelectCardsetDialogWithPreview(MfxDialog): canvas.event_generate('') # update bg image #canvas.config(xscrollincrement=dx, yscrollincrement=dy) self.preview_key = key + self.key = key class SelectCardsetByTypeDialogWithPreview(SelectCardsetDialogWithPreview): diff --git a/pysollib/tk/tkcanvas.py b/pysollib/tk/tkcanvas.py index fc195a06..9ce6e599 100644 --- a/pysollib/tk/tkcanvas.py +++ b/pysollib/tk/tkcanvas.py @@ -57,14 +57,15 @@ class MfxCanvasGroup(Canvas.Group): return self.canvas.tk.splitlist(self._do("gettags")) class MfxCanvasImage(Canvas.ImageItem): - def __init__(self, canvas, *args, **kwargs): + def __init__(self, canvas, x, y, **kwargs): + self.init_coord = x, y group = None if 'group' in kwargs: group = kwargs['group'] del kwargs['group'] if 'image' in kwargs: self._image = kwargs['image'] - Canvas.ImageItem.__init__(self, canvas, *args, **kwargs) + Canvas.ImageItem.__init__(self, canvas, x, y, **kwargs) if group: self.addtag(group) def moveTo(self, x, y): @@ -89,6 +90,8 @@ class MfxCanvasRectangle(Canvas.Rectangle): class MfxCanvasText(Canvas.CanvasText): def __init__(self, canvas, x, y, preview=-1, **kwargs): + self.init_coord = x, y + self.x, self.y = x, y if preview < 0: preview = canvas.preview if preview > 1: @@ -104,6 +107,10 @@ class MfxCanvasText(Canvas.CanvasText): canvas._text_items.append(self) if group: self.addtag(group) + def moveTo(self, x, y): + dx, dy = x - self.x, y - self.y + self.x, self.y = x, y + self.move(dx, dy) # ************************************************************************ @@ -228,17 +235,24 @@ class MfxCanvas(Tkinter.Canvas): # # - def setInitialSize(self, width, height): - ##print 'setInitialSize:', width, height + def setInitialSize(self, width, height, margins=True, scrollregion=True): + #print 'Canvas.setInitialSize:', width, height, scrollregion if self.preview: self.config(width=width, height=height, scrollregion=(0, 0, width, height)) else: # add margins - ##dx, dy = 40, 40 dx, dy = self.xmargin, self.ymargin - self.config(width=dx+width+dx, height=dy+height+dy, - scrollregion=(-dx, -dy, width+dx, height+dy)) + if margins: + w, h = dx+width+dx, dy+height+dy + else: + w, h = width, height + self.config(width=w, height=h) + if scrollregion: + self.config(scrollregion=(-dx, -dy, width+dx, height+dy)) + else: + # no scrolls + self.config(scrollregion=(-dx, -dy, dx, dy)) # diff --git a/pysollib/tk/tkutil.py b/pysollib/tk/tkutil.py index 5271ecb5..49c2e940 100644 --- a/pysollib/tk/tkutil.py +++ b/pysollib/tk/tkutil.py @@ -41,6 +41,7 @@ __all__ = ['wm_withdraw', 'shadowImage', 'markImage', 'createBottom', + 'resizeBottom', 'get_text_width', ] @@ -248,11 +249,15 @@ def after_cancel(t): if Image: class PIL_Image(ImageTk.PhotoImage): - def __init__(self, file=None, image=None): + def __init__(self, file=None, image=None, pil_image_orig=None): if file: image = Image.open(file).convert('RGBA') ImageTk.PhotoImage.__init__(self, image) self._pil_image = image + if pil_image_orig: + self._pil_image_orig = pil_image_orig + else: + self._pil_image_orig = image def subsample(self, r): im = self._pil_image w, h = im.size @@ -260,6 +265,11 @@ if Image: im = im.resize((w, h)) im = PIL_Image(image=im) return im + def resize(self, xf, yf): + w, h = self._pil_image_orig.size + w0, h0 = int(w*xf), int(h*yf) + im = self._pil_image_orig.resize((w0,h0), Image.ANTIALIAS) + return PIL_Image(image=im, pil_image_orig=self._pil_image_orig) def makeImage(file=None, data=None, dither=None, alpha=None): @@ -360,14 +370,11 @@ def markImage(image): out = Image.composite(tmp, image, image) return out -def createBottom(image, color='white', backfile=None): - if not hasattr(image, '_pil_image'): - return None - im = image._pil_image +def _createBottomImage(image, color='white', backfile=None): th = 1 # thickness - sh = Image.new('RGBA', im.size, color) - out = Image.composite(sh, im, im) - w, h = im.size + sh = Image.new('RGBA', image.size, color) + out = Image.composite(sh, image, image) + w, h = image.size size = (w-th*2, h-th*2) tmp = Image.new('RGBA', size, color) tmp.putalpha(60) @@ -376,15 +383,27 @@ def createBottom(image, color='white', backfile=None): if backfile: back = Image.open(backfile).convert('RGBA') w0, h0 = back.size - w1, h1 = im.size + w1, h1 = w, h a = min(float(w1)/w0, float(h1)/h0) a = a*0.9 w0, h0 = int(w0*a), int(h0*a) back = back.resize((w0,h0), Image.ANTIALIAS) x, y = (w1 - w0) / 2, (h1 - h0) / 2 out.paste(back, (x,y), back) + return out + +def createBottom(maskimage, color='white', backfile=None): + if not hasattr(maskimage, '_pil_image'): + return None + maskimage = maskimage._pil_image + out = _createBottomImage(maskimage, color, backfile) return PIL_Image(image=out) +def resizeBottom(image, maskimage, color='white', backfile=None): + maskimage = maskimage._pil_image + out = _createBottomImage(maskimage, color, backfile) + image['image'] = out + # ************************************************************************ # * font utils