1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00

* scalable cards (req: PIL >= 1.1.7)

git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@263 efabe8c0-fbe8-4139-b769-b5e6d273206e
This commit is contained in:
skomoroh 2011-03-06 06:33:38 +00:00
parent fd50923635
commit 24c0b00c0d
36 changed files with 857 additions and 304 deletions

View file

@ -22,3 +22,10 @@ Matthew Hohlfeld <hohlfeld@cs.ucsd.edu>
Nicola Larosa
John Stoneham <obijohn99@aol.com>
David Svoboda <svoboda@users.sourceforge.net>
Translations
============
Holger Schäkel <Holger@Schaekel-row.de> (de)
Jerzy Trzeciak <artusek@wp.pl> (pl)

View file

@ -31,6 +31,7 @@ from mfxutil import destruct, Struct
from mfxutil import pickle, unpickle, UnpicklingError
from mfxutil import getusername, getprefdir
from mfxutil import latin1_to_ascii, print_err
from mfxutil import USE_PIL
from util import CARDSET, IMAGE_EXTENSIONS
from settings import PACKAGE, VERSION_TUPLE, WIN_SYSTEM
from resource import CSI, CardsetConfig, Cardset, CardsetManager
@ -536,11 +537,10 @@ class Application:
pass
self.wm_save_state()
# save game geometry
geom = (self.canvas.winfo_width(), self.canvas.winfo_height())
if self.opt.save_games_geometry and not self.opt.wm_maximized:
w = self.canvas.winfo_width()
h = self.canvas.winfo_height()
geom = (w, h)
self.opt.games_geometry[self.game.id] = geom
self.opt.game_geometry = geom
self.freeGame()
#
if self.nextgame.id <= 0:
@ -1020,8 +1020,18 @@ Please select a %s type %s.
if d.status != 0 or d.button != 1:
return None
cs = self.cardset_manager.get(d.key)
if cs is None or d.key == key:
changed = (self.opt.scale_x,
self.opt.scale_y,
self.opt.auto_scale,
self.opt.preserve_aspect_ratio) != d.scale_values
if cs is None:
return None
if d.key == key and not changed:
return None
(self.opt.scale_x,
self.opt.scale_y,
self.opt.auto_scale,
self.opt.preserve_aspect_ratio) = d.scale_values
return cs

View file

@ -31,7 +31,7 @@ from cStringIO import StringIO
# PySol imports
from mfxutil import Pickler, Unpickler, UnpicklingError
from mfxutil import Image, ImageTk
from mfxutil import Image, ImageTk, USE_PIL
from mfxutil import destruct, Struct, SubclassResponsibility
from mfxutil import uclock, usleep
from mfxutil import format_time, print_err
@ -135,12 +135,15 @@ class Game:
remaining = [], # list of stacks in no region
#
data = [], # raw data
init_info = [], # init info (at the start)
)
self.init_size = (0,0)
self.event_handled = False # if click event handled by Stack (???)
self.reset()
# main constructor
def create(self, app):
#print 'Game.create'
old_busy = self.busy
self.__createCommon(app)
self.setCursor(cursor=CURSOR_WATCH)
@ -186,13 +189,46 @@ class Game:
##self.top.bind('<3>', self.top._sleepEvent)
# update display properties
self.canvas.busy = True
self.canvas.setInitialSize(self.width, self.height)
if self.app.opt.save_games_geometry and \
# geometry
if not USE_PIL and self.app.opt.save_games_geometry and \
self.id in self.app.opt.games_geometry:
# restore game geometry
w, h = self.app.opt.games_geometry[self.id]
self.canvas.config(width=w, height=h)
self.top.wm_geometry("") # cancel user-specified geometry
if USE_PIL:
if self.app.opt.auto_scale:
w, h = self.app.opt.game_geometry
self.canvas.setInitialSize(w, h, margins=False,
scrollregion=False)
## self.canvas.config(width=w, height=h)
## dx, dy = self.canvas.xmargin, self.canvas.ymargin
## self.canvas.config(scrollregion=(-dx, -dy, dx, dy))
else:
w = int(round(self.width * self.app.opt.scale_x))
h = int(round(self.height * self.app.opt.scale_y))
self.canvas.setInitialSize(w, h)
self.top.wm_geometry("") # cancel user-specified geometry
# preserve texts positions
for t in ('info', 'help', 'misc', 'score', 'base_rank'):
item = getattr(self.texts, t)
if item:
coords = self.canvas.coords(item)
setattr(self.init_texts, t, coords)
#
for item in self.texts.list:
coords = self.canvas.coords(item)
self.init_texts.list.append(coords)
# resize
self.resizeGame()
# fix coords of cards (see self.createCards)
x, y = self.s.talon.x, self.s.talon.y
for c in self.cards:
c.moveTo(x, y)
else:
# no PIL
self.canvas.setInitialSize(self.width, self.height)
self.top.wm_geometry("") # cancel user-specified geometry
# done; update view
self.top.update_idletasks()
self.canvas.busy = False
if DEBUG >= 4:
@ -200,12 +236,11 @@ class Game:
width=2, fill=None, outline='green')
#
self.stats.update_time = time.time()
self.busy = old_busy
self.showHelp() # just in case
hint_class = self.getHintClass()
if hint_class is not None:
self.Stuck_Class = hint_class(self, 0)
##self.reallocateStacks()
self.busy = old_busy
def _checkGame(self):
@ -262,7 +297,8 @@ class Game:
bind(self.canvas, "<2>", self.dropHandler)
bind(self.canvas, "<3>", self.redoHandler)
bind(self.canvas, '<Unmap>', self._unmapHandler)
bind(self.canvas, '<Configure>', self.configureHandler, add=True)
bind(self.canvas, '<Configure>', self._configureHandler, add=True)
def __createCommon(self, app):
self.busy = 1
@ -293,6 +329,16 @@ class Game:
misc = None, #
score = None, # for displaying the score
base_rank = None, # for displaying the base_rank
list = [], # list of texts (if we use many text)
)
# initial position of the texts
self.init_texts = Struct(
info = None, # misc info text
help = None, # a static help text
misc = None, #
score = None, # for displaying the score
base_rank = None, # for displaying the base_rank
list = [],
)
def createPreview(self, app):
@ -409,6 +455,8 @@ class Game:
# this is called from within createGame()
def setSize(self, w, h):
self.width, self.height = int(round(w)), int(round(h))
dx, dy = self.canvas.xmargin, self.canvas.ymargin
self.init_size = self.width+2*dx, self.height+2*dy
def setCursor(self, cursor):
if self.canvas:
@ -424,6 +472,7 @@ class Game:
# start a new name
def newGame(self, random=None, restart=0, autoplay=1):
#print 'Game.newGame'
self.finished = False
old_busy, self.busy = self.busy, 1
self.setCursor(cursor=CURSOR_WATCH)
@ -538,8 +587,8 @@ class Game:
self.stats.update_time = time.time()
self.busy = old_busy
#
##self.configureHandler() # reallocateCards
after(self.top, 200, self.configureHandler) # wait for canvas is mapped
##self._configureHandler() # reallocateCards
after(self.top, 200, self._configureHandler) # wait for canvas is mapped
#
if TOOLKIT == 'gtk':
## FIXME
@ -561,6 +610,7 @@ class Game:
self.busy = old_busy
def resetGame(self):
#print 'Game.resetGame'
self.hints.list = None
self.s.talon.removeAllCards()
for stack in self.allstacks:
@ -638,22 +688,73 @@ class Game:
self.endGame(restart=1)
self.newGame(restart=1, random=self.random)
def reallocateStacks(self):
w0, h0 = self.width, self.height
iw = int(self.canvas.cget('width'))
ih = int(self.canvas.cget('height'))
vw = self.canvas.winfo_width()
vh = self.canvas.winfo_height()
if vw <= iw or vh <= ih:
def resizeImages(self):
# resizing images and cards
if self.app.opt.auto_scale:
if self.canvas.winfo_ismapped():
# apparent size of canvas
vw = self.canvas.winfo_width()
vh = self.canvas.winfo_height()
else:
# we have no a real size of canvas (winfo_width / winfo_reqwidth)
# so we use a saved size
vw, vh = self.app.opt.game_geometry
if not vw:
# first run of the game
return 1, 1
iw, ih = self.init_size # requested size of canvas (createGame -> setSize)
# calculate factor of resizing
xf = float(vw)/iw
yf = float(vh)/ih
if self.app.opt.preserve_aspect_ratio:
xf = yf = min(xf, yf)
else:
xf, yf = self.app.opt.scale_x, self.app.opt.scale_y
# images
self.app.images.resize(xf, yf)
# cards
for card in self.cards:
card.update(card.id, card.deck, card.suit, card.rank, self)
return xf, yf
def resizeGame(self):
#if self.busy:
# return
if not USE_PIL:
return
xf = float(vw)/iw
yf = float(vh)/ih
self.deleteStackDesc()
xf, yf = self.resizeImages()
#print 'resizeGame', xf, yf
# stacks
for stack in self.allstacks:
x0, y0 = stack.init_coord
x, y = int(x0*xf), int(y0*yf)
if x == stack.x and y == stack.y:
continue
stack.moveTo(x, y)
x, y = int(round(x0*xf)), int(round(y0*yf))
#if x == stack.x and y == stack.y:
# continue
stack.resize(xf, yf)
stack.updatePositions()
# regions
info = []
for stacks, rect in self.regions.init_info:
rect = (int(round(rect[0]*xf)), int(round(rect[1]*yf)),
int(round(rect[2]*xf)), int(round(rect[3]*yf)))
info.append((stacks, rect))
self.regions.info = tuple(info)
# texts
for t in ('info', 'help', 'misc', 'score', 'base_rank'):
init_coord = getattr(self.init_texts, t)
if init_coord:
item = getattr(self.texts, t)
x, y = int(round(init_coord[0]*xf)), int(round(init_coord[1]*yf))
self.canvas.coords(item, x, y)
for i in range(len(self.texts.list)):
init_coord = self.init_texts.list[i]
item = self.texts.list[i]
x, y = int(round(init_coord[0]*xf)), int(round(init_coord[1]*yf))
self.canvas.coords(item, x, y)
#
#self.canvas.update()
#self.canvas.update_idletasks()
def createRandom(self, random):
if random is None:
@ -985,11 +1086,27 @@ class Game:
if self.app and not self.pause:
self.app.menubar.mPause()
def configureHandler(self, event=None):
_resizeHandlerID = None
def _resizeHandler(self):
#print '_resizeHandler'
self._resizeHandlerID = None
self.resizeGame()
def _configureHandler(self, event=None):
#print 'configureHandler'
if not USE_PIL:
return
if not self.app:
return
if not self.canvas:
return
for stack in self.allstacks:
stack.updatePositions()
if not self.app.opt.auto_scale:
return
if self.preview:
return
if self._resizeHandlerID:
self.canvas.after_cancel(self._resizeHandlerID)
self._resizeHandlerID = self.canvas.after(250, self._resizeHandler)
#
# sound support
@ -1504,13 +1621,20 @@ class Game:
assert len(stacks) > 0
assert len(rect) == 4 and rect[0] < rect[2] and rect[1] < rect[3]
if DEBUG >= 2:
MfxCanvasRectangle(self.canvas, rect[0], rect[1], rect[2], rect[3],
xf, yf = self.app.images._xfactor, self.app.images._yfactor
MfxCanvasRectangle(self.canvas,
xf*rect[0], yf*rect[1], xf*rect[2], yf*rect[3],
width=2, fill=None, outline='red')
for s in stacks:
assert s and s in self.allstacks
# verify that the stack lies within the rectangle
x, y, r = s.x, s.y, rect
##print x, y, r
r = rect
if USE_PIL:
x, y = s.init_coord
#print 'setRegion:', x, y, r
else:
x, y = s.x, s.y
#print 'setRegion:', x, y, r
assert r[0] <= x <= r[2] and r[1] <= y <= r[3]
# verify that the stack is not already in another region
# with the same priority
@ -1538,7 +1662,7 @@ class Game:
while stack in remaining:
remaining.remove(stack)
self.regions.remaining = tuple(remaining)
##print self.regions.info
self.regions.init_info = self.regions.info
def getInvisibleCoords(self):
# for InvisibleStack, etc
@ -1917,6 +2041,7 @@ Congratulations, you did it !
if self.pause:
return 0
self.stopWinAnimation()
cw, ch = self.app.images.getSize()
items = []
for s, c1, c2, color in info:
assert c1 in s.cards and c2 in s.cards
@ -1943,33 +2068,33 @@ Congratulations, you did it !
x2, y2 = s.getPositionFor(c2)
if sx0 != 0 and sy0 == 0:
# horizontal stack
y2 = y2 + self.app.images.CARDH
y2 += ch
if c2 is s.cards[-1]: # top card
x2 = x2 + self.app.images.CARDW
x2 += cw
else:
if sx0 > 0:
# left to right
x2 = x2 + sx0
x2 += sx0
else:
# right to left
x1 = x1 + self.app.images.CARDW
x2 = x2 + self.app.images.CARDW + sx0
x1 += cw
x2 += cw + sx0
elif sx0 == 0 and sy0 != 0:
# vertical stack
x2 = x2 + self.app.images.CARDW
x2 += cw
if c2 is s.cards[-1]: # top card
y2 = y2 + self.app.images.CARDH
y2 += ch
else:
if sy0 > 0:
# up to down
y2 = y2 + sy0
else:
# down to up
y1 = y1 + self.app.images.CARDH
y2 = y2 + self.app.images.CARDH + sy0
y1 += ch
y2 += ch + sy0
else:
x2 = x2 + self.app.images.CARDW
y2 = y2 + self.app.images.CARDH
x2 += cw
y2 += ch
tkraise = True
##print c1, c2, x1, y1, x2, y2
x1, x2 = x1-delta[0], x2+delta[1]
@ -2237,19 +2362,21 @@ Congratulations, you did it !
images = self.app.images
x1, y1 = from_stack.getPositionFor(from_stack.cards[-ncards])
x2, y2 = to_stack.getPositionFor(to_stack.getCard())
x1, y1 = x1 + images.CARD_DX, y1 + images.CARD_DY
x2, y2 = x2 + images.CARD_DX, y2 + images.CARD_DY
cw, ch = images.getSize()
dx, dy = images.getDelta()
x1, y1 = x1 + dx, y1 + dy
x2, y2 = x2 + dx, y2 + dy
if ncards == 1:
x1 = x1 + images.CARDW / 2
y1 = y1 + images.CARDH / 2
x1 += cw / 2
y1 += ch / 2
elif from_stack.CARD_XOFFSET[0]:
x1 = x1 + from_stack.CARD_XOFFSET[0] / 2
y1 = y1 + images.CARDH / 2
x1 += from_stack.CARD_XOFFSET[0] / 2
y1 += ch / 2
else:
x1 = x1 + images.CARDW / 2
y1 = y1 + from_stack.CARD_YOFFSET[0] / 2
x2 = x2 + images.CARDW / 2
y2 = y2 + images.CARDH / 2
x1 += cw / 2
y1 += from_stack.CARD_YOFFSET[0] / 2
x2 += cw / 2
y2 += ch / 2
# draw the hint
arrow = MfxCanvasLine(self.canvas, x1, y1, x2, y2, width=7,
fill=self.app.opt.colors['hintarrow'],

View file

@ -32,7 +32,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
from numerica import Numerica_Hint

View file

@ -34,7 +34,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -33,7 +33,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
from fortythieves import FortyThieves_Hint
from spider import Spider_Hint

View file

@ -32,7 +32,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
from gypsy import DieRussische_Foundation

View file

@ -34,7 +34,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -35,7 +35,6 @@ from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import KlondikeType_Hint, YukonType_Hint
from pysollib.pysoltk import MfxCanvasText
from spider import Spider_SS_Foundation, Spider_RowStack, Spider_Hint

View file

@ -35,7 +35,6 @@ from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import KlondikeType_Hint
from pysollib.pysoltk import MfxCanvasText
from spider import Spider_RowStack, Spider_SS_Foundation, Spider_Hint

View file

@ -32,7 +32,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -276,7 +276,7 @@ class Mahjongg_RowStack(OpenStack):
drag.shade_img.delete()
#game.canvas.delete(drag.shade_img)
drag.shade_img = None
img = game.app.images.getShadowCard(card.deck, card.suit, card.rank)
img = game.app.images.getHighlightedCard(card.deck, card.suit, card.rank)
if img is None:
return 1
img = MfxCanvasImage(game.canvas, self.x, self.y, image=img,
@ -896,7 +896,7 @@ a solvable configuration.'''),
assert c1 in s.cards
tkraise = False
x, y = s.x, s.y
img = self.app.images.getHighlightCard(c1.deck, c1.suit, c1.rank)
img = self.app.images.getHighlightedCard(c1.deck, c1.suit, c1.rank, 'black')
if img is None:
continue
img = MfxCanvasImage(self.canvas, x, y, image=img,

View file

@ -123,7 +123,7 @@ r(5078, "Squaring", layout="0caaacaceaciaakacmacqaasacuacyaaAacCaaacaecaicdkcamc
r(5079, "Stairs", layout="0aoaaebaybeacdccagcaicakcbmccocbqcascaucawcdAceCcaedayddaeaieaoeauedCebefbyfaagaigaogaugaCgbchcehbghakhbmhbqhashbwhcyhbAhaaiaiiaoiauiaCibejbyjdakaikaokaukdCkaelayleamdcmagmaimakmbmmcombqmasmaumawmdAmeCmaenaynaoohechychofhahkohhChhojhemhym")
r(5080, "Star Ship", layout="0eoaaabdmbdqbaCbaccckccscaAcaadbidbudaCdbceagecoeawebAeaafaefamfaqfayfaCfecgaggaigbkgdogbsgaugawgeAgaahaehamhaqhayhaChbciagicoiawibAiaajbijbujaCjackckkcskaAkaaldmldqlaCleomhachCchaehCehaghegimgiqghyghCghaihCihakhCkoadoCdoafoCfoahoChoajoCjvaevCevagvCgvaivCiCafCCfCahCCh")
#
r(5081, "Step Pyramid", layout="0aaaacaaeaagaaiaakaamaaoaaqaaacaccaecagcaicakcamcaocaqcaaeaceaoeaqeaagacgaogaqgaaiaciaoiaqiaakackaekagkaikakkamkaokaqkaamacmaemagmaimakmammaomaqmhbbhdbhfbhhbhjbhlbhnbhpbhbdhddhfdhhdhjdhldhndhpdhbfhdfhnfhpfhbhhdhhnhhphhbjhdjhfjhhjhjjhljhnjhpjhblhdlhflhhlhjlhllhnlhplpccoecogcoicokcomcpococepeepgepiepkepmeooeocgpegpmgoogocipeipgipiipkipmiooipckoekogkoikokkomkpokCffChfCjfClfCfhChhCjhClh")
r(5081, "Steps Pyramid", layout="0aaaacaaeaagaaiaakaamaaoaaqaaacaccaecagcaicakcamcaocaqcaaeaceaoeaqeaagacgaogaqgaaiaciaoiaqiaakackaekagkaikakkamkaokaqkaamacmaemagmaimakmammaomaqmhbbhdbhfbhhbhjbhlbhnbhpbhbdhddhfdhhdhjdhldhndhpdhbfhdfhnfhpfhbhhdhhnhhphhbjhdjhfjhhjhjjhljhnjhpjhblhdlhflhhlhjlhllhnlhplpccoecogcoicokcomcpococepeepgepiepkepmeooeocgpegpmgoogocipeipgipiipkipmiooipckoekogkoikokkomkpokCffChfCjfClfCfhChhCjhClh")
r(5082, "Stonehenge", layout="0cdachackacoacracvacyacCacaccFcajeaneareavecagcFgddhdhhdlhdphdthdxhdBhcajcFjajkankarkavkcancFncdpchpckpcopcrpcvpcypcCpveavgavlavnavsavuavzavBavadvFdvafvFfvakvFkvamvFmvepvgpvlpvnpvspvupvzpvBpCehCghCihCkhCmhCohCqhCshCuhCwhCyhCAh")
r(5083, "SunMoon", layout="0dgaciabkaamabyadebbrbbBbdccbvccaddcecheckecnebDecafbtfbAfdcgdjgdlgbxgcahchhcnhdcidjidlibribDicajbvjdckchkckkcnkbAkcalbsldcmbxmdenbBndgociobkoamobuovaevagvaivakCkh")
r(5084, "Temple", layout="0baaacaaeaalaanaapaaraataaAaaCabEaaacaccalcbncbpcbrcatcaCcaEcajdavdaaeblebnebpebrebteaEeaffahfajfavfaxfazfblgbngbpgbrgbtgadhafhahhajhavhaxhazhaBhblibnibpibribtiafjahjajjavjaxjazjaakblkbnkbpkbrkbtkaEkajlavlaamacmalmbnmbpmbrmatmaCmaEmbaoacoaeoaloanoapoaroatoaAoaCobEohhghjghvghxghhihjihvihxiooeoqeokgomgoogoqgosgougokiomiooioqiosiouiookoqkvpgvpi")

View file

@ -250,33 +250,31 @@ class Shisen_RowStack(Mahjongg_RowStack):
def drawArrow(self, other_stack, sleep):
game = self.game
images = game.app.images
cs = game.app.cardset
path = self.acceptsCards(other_stack, [other_stack.cards[-1]])
#print path
x0, y0 = game.XMARGIN, game.YMARGIN
#print x0, y0
images = game.app.images
cs = game.app.cardset
cardw, cardh = images.CARDW, images.CARDH
if cs.version >= 6:
cardw = images.CARDW-cs.SHADOW_XOFFSET
cardh = images.CARDH-cs.SHADOW_YOFFSET
else:
cardw = images.CARDW
cardh = images.CARDH
cardw -= cs.SHADOW_XOFFSET
cardh -= cs.SHADOW_YOFFSET
coords = []
dx, dy = game._delta_x, game._delta_y
xf, yf = images._xfactor, images._yfactor
for x, y in path:
if x == 0:
coords.append(6)
elif x == game.L[0]+1:
coords.append(x0+cardw*(x-1)+10+dx)
coords.append(int(round(xf * (x0+cardw*(x-1)+10+dx))))
else:
coords.append(x0+cardw/2+cardw*(x-1)+dx)
coords.append(int(round(xf * (x0+cardw/2+cardw*(x-1)+dx))))
if y == 0:
coords.append(6)
elif y == game.L[1]+1:
coords.append(y0+cardh*(y-1)+6)
coords.append(int(round(yf * (y0+cardh*(y-1)+6))))
else:
coords.append(y0+cardh/2+cardh*(y-1))
coords.append(int(round(yf * (y0+cardh/2+cardh*(y-1)))))
#print coords
##s1 = min(cardw/2, cardh/2, 30)
##w = min(s1/3, 7)

View file

@ -33,7 +33,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -64,7 +64,7 @@ class PushPin_Talon(DealRowTalonStack):
if not r.cards:
return self.dealRowAvail(rows=[r], sound=sound)
return self.dealRowAvail(rows=[self.game.s.rows[0]], sound=sound)
getBottomImage = Stack._getBlankBottomImage
getBottomImage = Stack._getNoneBottomImage
class PushPin_RowStack(ReserveStack):

View file

@ -33,7 +33,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
from unionsquare import UnionSquare_Foundation

View file

@ -81,6 +81,7 @@ Straight
Three of a Kind
Two Pair
One Pair'''))
self.texts.list.append(t)
bb = t.bbox()
x = bb[1][0] + 16
h = bb[1][1] - bb[0][1]
@ -91,6 +92,7 @@ One Pair'''))
t = MfxCanvasText(self.canvas, x, y, anchor="nw",
font=self.app.getFont("canvas_default"),
text="100\n75\n50\n25\n20\n15\n10\n5\n2")
self.texts.list.append(t)
x = t.bbox()[1][0] + 16
self.texts.misc = MfxCanvasText(self.canvas, x, y, anchor="nw",
font=self.app.getFont("canvas_default"),
@ -113,18 +115,17 @@ One Pair'''))
# create texts 2)
if self.preview <= 1:
self.texts.addattr(hands=[])
for i in (4, 9, 14, 19, 24):
tx, ty, ta, tf = l.getTextAttr(s.rows[i], anchor="e")
t = MfxCanvasText(self.canvas, tx+8, ty,
anchor=ta,
font=self.app.getFont("canvas_default"))
self.texts.hands.append(t)
self.texts.list.append(t)
for i in range(20, 25):
tx, ty, ta, tf = l.getTextAttr(s.rows[i], anchor="ss")
t = MfxCanvasText(self.canvas, tx, ty, anchor=ta,
font=self.app.getFont("canvas_default"))
self.texts.hands.append(t)
self.texts.list.append(t)
self.texts.score = MfxCanvasText(self.canvas, l.XM, 5*l.YS, anchor="sw",
font=self.app.getFont("canvas_large"))
@ -171,7 +172,7 @@ One Pair'''))
type, value = self.getHandScore(self.poker_hands[i])
if 0 <= type <= 8:
count[type] = count[type] + 1
self.texts.hands[i].config(text=str(value))
self.texts.list[i+2].config(text=str(value))
score = score + value
t = '\n'.join(map(str, count))
self.texts.misc.config(text=t)

View file

@ -34,7 +34,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -34,7 +34,7 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************
# * Sultan

View file

@ -34,7 +34,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -64,10 +64,12 @@ class FlowerClock(AbstractFlowerGame):
for i in range(12):
x0 = x + xoffset[i] * l.XS
y0 = y + yoffset[i] * l.YS
s.foundations.append(FlowerClock_Foundation(x0, y0, self, ANY_SUIT, base_rank=3))
stack = FlowerClock_Foundation(x0, y0, self, ANY_SUIT, base_rank=3)
s.foundations.append(stack)
t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS,
anchor="center", font=font,
text=self.SUITS[i])
stack.texts.misc = t
# Create row stacks
for j in range(2):
@ -311,15 +313,15 @@ class Pagoda(AbstractFlowerGame):
self.setSize(l.XM + l.XS * 11, l.YS * 6)
# Create foundations
self.foundation_texts = []
id = 0
for j in range(4):
x, y = l.XM + l.XS * 8, l.YM * 3 + l.YS * j * 1.5
for i in range(3):
s.foundations.append(Pagoda_Foundation(x, y, self, id))
stack = Pagoda_Foundation(x, y, self, id)
s.foundations.append(stack)
t = MfxCanvasText(self.canvas, x + l.CW / 2, y - 12,
anchor="center", font=font)
self.foundation_texts.append(t)
stack.texts.misc = t
x = x + l.XS
id = id + 1
@ -370,7 +372,7 @@ class Pagoda(AbstractFlowerGame):
text = _("Rising")
else:
text = _("Setting")
self.foundation_texts[i].config(text=text)
s.texts.misc.config(text=text)
#
# Game over rides
@ -468,13 +470,14 @@ class GreatWall(AbstractFlowerGame):
self.setSize(w, h)
# Create foundations
self.foundation_texts = []
x, y = (l.XM, l.XM, w - l.XS, w - l.XS), (l.YM, h / 2, l.YM, h / 2)
for i in range(4):
s.foundations.append(GreatWall_FoundationStack(x[i], y[i] + l.YM, self, -1, base_rank=i))
self.foundation_texts.append(MfxCanvasText(self.canvas,
x[i] + l.CW / 2, y[i],
anchor="center", font=font))
stack = GreatWall_FoundationStack(x[i], y[i] + l.YM, self, -1,
base_rank=i)
s.foundations.append(stack)
stack.texts.misc = MfxCanvasText(self.canvas,
x[i] + l.CW / 2, y[i],
anchor="center", font=font)
# Create row stacks
x = l.XM + l.XS * 1.5
@ -501,14 +504,15 @@ class GreatWall(AbstractFlowerGame):
if self.preview > 1:
return
for i in range(4):
l = len(self.s.foundations[i].cards) / 12
stack = self.s.foundations[i]
l = len(stack.cards) / 12
if l == 0:
t = ""
text = ""
elif l == 4:
t = _("Filled")
text = _("Filled")
else:
t = str(l) + (_("st"), _("nd"), _("rd"), _("th"))[l - 1] + _(" Deck")
self.foundation_texts[i].config(text=str(t))
text = str(l) + (_("st"), _("nd"), _("rd"), _("th"))[l - 1] + _(" Deck")
stack.texts.misc.config(text=text)
#
# Game over rides
@ -568,11 +572,13 @@ class FourWinds(AbstractFlowerGame):
for i in range(4):
x0 = x + (xoffset[i] * l.XS)
y0 = y + (yoffset[i] * l.YS)
s.foundations.append(FourWinds_Foundation(x0, y0, self, -1,
max_cards=12, max_accept=1, base_rank=i))
stack = FourWinds_Foundation(x0, y0, self, -1,
max_cards=12, max_accept=1, base_rank=i)
s.foundations.append(stack)
t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS + 5,
anchor="center", font=font,
text=TEXTS[i])
stack.texts.misc = t
# Create rows
xoffset = (1.25, 3.75, 3.75, 1.25)
@ -580,11 +586,14 @@ class FourWinds(AbstractFlowerGame):
for i in range(4):
x0 = x + (xoffset[i] * l.XS)
y0 = y + (yoffset[i] * l.YS)
s.rows.append(FourWinds_RowStack(x0, y0, self, yoffset=10,
max_cards=3, max_accept=1, base_rank=ANY_RANK))
stack = FourWinds_RowStack(x0, y0, self, yoffset=10,
max_cards=3, max_accept=1,
base_rank=ANY_RANK)
s.rows.append(stack)
t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS + 5,
anchor="center", font=font,
text=TEXTS[i+4])
stack.texts.misc = t
self.setRegion(s.rows, (x + l.XS, y + l.YS * 0.65, x + l.XS * 4 + 5, y + l.YS * 3 + 5))
# Create talon

View file

@ -297,10 +297,12 @@ class BitsNBytes(Game):
for j in range(4):
x = l.XM * 4 + l.XS * 7
for i in range(4):
s.rows.append(Bits_RowStack(x, y, self, max_cards=1,
max_accept=1, base_suit=j, max_move=0))
self.bit_texts.append(MfxCanvasText(self.canvas, x + l.CW / 2 , y + l.CH / 2,
anchor="center", font=font))
stack = Bits_RowStack(x, y, self, max_cards=1, max_accept=1,
base_suit=j, max_move=0)
s.rows.append(stack)
stack.texts.misc = MfxCanvasText(self.canvas,
x + l.CW / 2 , y + l.CH / 2,
anchor="center", font=font)
x = x - l.XS
y = y + l.YS
@ -347,7 +349,8 @@ class BitsNBytes(Game):
break
s = self.s.foundations[j].cards[-1].rank + 1
for i in range(4):
self.bit_texts[i + j * 4].config(text = str(s % 2))
stack = self.s.rows[i + j * 4]
stack.texts.misc.config(text = str(s % 2))
s = int(s / 2)
def _shuffleHook(self, cards):

View file

@ -34,8 +34,6 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
# ************************************************************************

View file

@ -29,10 +29,10 @@ import traceback
# PySol imports
from resource import CSI
from settings import TOOLKIT
from mfxutil import Image, ImageTk
from mfxutil import Image, ImageTk, USE_PIL
# Toolkit imports
from pysoltk import loadImage, copyImage, createImage, shadowImage, createBottom
from pysoltk import loadImage, copyImage, createImage, shadowImage, createBottom, resizeBottom
# ************************************************************************
@ -54,37 +54,26 @@ class Images:
self.d = dataloader
self.cs = cs
self.reduced = r
self._xfactor = 1.0
self._yfactor = 1.0
if cs is None:
return
self.use_pil = False
if TOOLKIT == 'tk' and Image and Image.VERSION >= '1.1.7':
self.use_pil = True
# copy from cardset
self.CARDW, self.CARDH, self.CARDD = cs.CARDW/r, cs.CARDH/r, cs.CARDD/r
self.CARD_XOFFSET = cs.CARD_XOFFSET
self.CARD_YOFFSET = cs.CARD_YOFFSET
if r > 1:
self.CARD_XOFFSET = max(10, cs.CARD_XOFFSET)/r
self.CARD_YOFFSET = max(10, cs.CARD_YOFFSET)/r
self.SHADOW_XOFFSET, self.SHADOW_YOFFSET = cs.SHADOW_XOFFSET/r, cs.SHADOW_YOFFSET/r
self.CARD_DX, self.CARD_DY = cs.CARD_DX/r, cs.CARD_DY/r
# other
self._shade_index = 0
self._setSize()
self._card = []
self._back = []
self._bottom = []
self._bottom_negative = []
self._bottom_positive = []
self._blank_bottom = None
self._letter = []
self._bottom = [] # bottom of stack (link to _bottom_negative/_bottom_positive)
self._bottom_negative = [] # negative bottom of stack (white)
self._bottom_positive = [] # positive bottom of stack (black)
self._blank_bottom = None # blank (transparent) bottom of stack
self._letter = [] # images of letter
self._letter_negative = []
self._letter_positive = []
self._shadow = []
self._xshadow = []
self._shade = []
self._shadow_cards = {} # key: (suit, rank)
self._shadow = [] # vertical shadow of card (used when we drag a card)
self._xshadow = [] # horizontal shadow of card
self._pil_shadow = {} # key: (width, height)
self._pil_shadow_image = None
self._highlight = [] # highlight of card (tip)
self._highlight_index = 0 #
self._highlighted_images = {} # key: (suit, rank)
def destruct(self):
pass
@ -115,9 +104,13 @@ class Images:
imagedir = self.d.findDir(cs_type, d)
except:
pass
if not self.use_pil or imagedir is None:
if not USE_PIL or imagedir is None:
# load image
return self.__loadCard(filename+self.cs.ext, check_w, check_h)
img = self.__loadCard(filename+self.cs.ext, check_w, check_h)
if USE_PIL:
# we have no bottom images (data/images/cards/bottoms/<cs_type>)
img = img.resize(self._xfactor, self._yfactor)
return img
# create image
d = os.path.join('images', 'cards', 'bottoms', cs_type)
try:
@ -134,9 +127,10 @@ class Images:
self._back.append(ImagesCardback(len(self._back), name, im1, im2))
def _createMissingImages(self):
cw, ch = self.getSize()
# back
if not self._back:
im = createImage(self.CARDW, self.CARDH, fill="#a0a0a0", outline="#000000")
im = createImage(cw, ch, fill="#a0a0a0", outline="#000000")
name = ""
self.__addBack(im, name)
self.cs.backnames = tuple(self.cs.backnames) + (name,)
@ -145,21 +139,21 @@ class Images:
neg_bottom = None
while len(self._bottom_positive) < 7:
if bottom is None:
bottom = createImage(self.CARDW, self.CARDH, fill=None, outline="#000000")
bottom = createImage(cw, ch, fill=None, outline="#000000")
self._bottom_positive.append(bottom)
while len(self._bottom_negative) < 7:
if neg_bottom is None:
neg_bottom = createImage(self.CARDW, self.CARDH, fill=None, outline="#ffffff")
neg_bottom = createImage(cw, ch, fill=None, outline="#ffffff")
self._bottom_negative.append(neg_bottom)
while len(self._letter_positive) < 4:
if bottom is None:
bottom = createImage(self.CARDW, self.CARDH, fill=None, outline="#000000")
bottom = createImage(cw, ch, fill=None, outline="#000000")
self._letter_positive.append(bottom)
while len(self._letter_negative) < 4:
if neg_bottom is None:
neg_bottom = createImage(self.CARDW, self.CARDH, fill=None, outline="#ffffff")
neg_bottom = createImage(cw, ch, fill=None, outline="#ffffff")
self._letter_negative.append(neg_bottom)
self._blank_bottom = createImage(self.CARDW, self.CARDH, fill=None, outline=None)
self._blank_bottom = createImage(cw, ch, fill=None, outline=None)
def load(self, app, progress=None):
ext = self.cs.ext[1:]
@ -199,7 +193,7 @@ class Images:
self._letter_negative.append(self.__loadBottom(name, color='white'))
if progress: progress.update(step=pstep)
# shadow
if not self.use_pil:
if not USE_PIL:
for i in range(self.cs.nshadows):
name = "shadow%02d.%s" % (i, ext)
im = self.__loadCard(name, check_w=0, check_h=0)
@ -210,10 +204,10 @@ class Images:
self._xshadow.append(im)
if progress: progress.update(step=pstep)
# shade
if self.use_pil:
self._shade.append(self._getShadow(self._card[0], None, '#3896f8'))
if USE_PIL:
self._highlight.append(self._getHighlight(self._card[0], None, '#3896f8'))
else:
self._shade.append(self.__loadCard("shade." + ext))
self._highlight.append(self.__loadCard("shade." + ext))
if progress: progress.update(step=pstep)
# create missing
self._createMissingImages()
@ -278,8 +272,9 @@ class Images:
x1, y1 = stack.getPositionFor(cards[-1])
x0, x1 = min(x1, x0), max(x1, x0)
y0, y1 = min(y1, y0), max(y1, y0)
x1 += self.CARDW
y1 += self.CARDH
cw, ch = self.getSize()
x1 += cw
y1 += ch
w, h = x1-x0, y1-y0
if (w,h) in self._pil_shadow:
return self._pil_shadow[(w,h)]
@ -298,43 +293,43 @@ class Images:
mask = mask.crop((sx,sy,w,h))
tmp = Image.new('RGBA', (w-sx,h-sy))
shadow.paste(tmp, (0,0), mask)
#
shadow = ImageTk.PhotoImage(shadow)
self._pil_shadow[(w,h)] = shadow
return shadow
def getShade(self):
return self._shade[self._shade_index]
# highlight
return self._highlight[self._highlight_index]
def _getShadow(self, image, card, color='#3896f8', factor=0.3):
if self.use_pil:
# use alpha image; one for each color
if color in self._shadow_cards:
shade = self._shadow_cards[color]
def _getHighlight(self, image, card, color='#3896f8', factor=0.3):
if USE_PIL:
# use semitransparent image; one for each color (PIL >= 1.1.7)
if color in self._highlighted_images:
shade = self._highlighted_images[color]
else:
shade = shadowImage(image, color, factor)
self._shadow_cards[color] = shade
self._highlighted_images[color] = shade
else:
if card in self._shadow_cards:
shade = self._shadow_cards[card]
# use alpha blending (PIL <= 1.1.6)
if card in self._highlighted_images:
shade = self._highlighted_images[card]
else:
shade = shadowImage(image, color, factor)
self._shadow_cards[card] = shade
self._highlighted_images[card] = shade
if not shade:
# we have not PIL
return self.getShade()
return shade
def getShadowCard(self, deck, suit, rank):
def getHighlightedCard(self, deck, suit, rank, color=None):
image = self.getFace(deck, suit, rank)
return self._getShadow(image, (suit, rank))
if color:
return self._getHighlight(image, (suit, rank, color), color)
return self._getHighlight(image, (suit, rank))
def getHighlightCard(self, deck, suit, rank):
image = self.getFace(deck, suit, rank)
return self._getShadow(image, (suit, rank, 'black'), 'black')
def getShadowBack(self):
def getHighlightedBack(self):
image = self.getBack()
return self._getShadow(image, 'back')
return self._getHighlight(image, 'back')
def getCardbacks(self):
return self._back
@ -347,6 +342,83 @@ class Images:
self._bottom = self._bottom_positive
self._letter = self._letter_positive
def _setSize(self, xf=1, yf=1):
#print 'Images._setSize', xf, yf
self._xfactor = xf
self._yfactor = yf
cs = self.cs
if cs is None:
return
r = self.reduced
xf = float(xf)/r
yf = float(yf)/r
# copy from cardset
self.CARDW, self.CARDH = int(cs.CARDW*xf), int(cs.CARDH*yf)
if r > 1:
self.CARD_XOFFSET = max(10/r, int(cs.CARD_XOFFSET*xf))
self.CARD_YOFFSET = max(10/r, int(cs.CARD_YOFFSET*yf))
else:
self.CARD_XOFFSET = int(cs.CARD_XOFFSET*xf)
self.CARD_YOFFSET = int(cs.CARD_YOFFSET*yf)
self.SHADOW_XOFFSET = int(cs.SHADOW_XOFFSET*xf)
self.SHADOW_YOFFSET = int(cs.SHADOW_YOFFSET*yf)
self.CARD_DX, self.CARD_DY = int(cs.CARD_DX*xf), int(cs.CARD_DY*yf)
def getSize(self):
return (int(self.CARDW * self._xfactor),
int(self.CARDH * self._yfactor))
def getOffsets(self):
return (int(self.CARD_XOFFSET * self._xfactor),
int(self.CARD_YOFFSET * self._yfactor))
def getDelta(self):
return (int(self.CARD_DX * self._xfactor),
int(self.CARD_DY * self._yfactor))
def resize(self, xf, yf):
#print 'Images.resize:', xf, yf, self._card[0].width()
if self._xfactor == xf and self._yfactor == yf:
#print 'no resize'
return
self._xfactor = xf
self._yfactor = yf
# cards
cards = []
for c in self._card:
c = c.resize(xf, yf)
cards.append(c)
self._card = cards
# back
for b in self._back:
b.image = b.image.resize(xf, yf)
# stack bottom image
neg = self._bottom is self._bottom_negative
self._bottom_negative = []
self._bottom_positive = []
for i in range(self.cs.nbottoms):
name = "bottom%02d" % (i + 1)
self._bottom_positive.append(self.__loadBottom(name, color='black'))
name = "bottom%02d-n" % (i + 1)
self._bottom_negative.append(self.__loadBottom(name, color='white'))
# letters
self._letter_positive = []
self._letter_negative = []
for rank in range(self.cs.nletters):
name = "l%02d" % (rank + 1)
self._letter_positive.append(self.__loadBottom(name, color='black'))
name = "l%02d-n" % (rank + 1)
self._letter_negative.append(self.__loadBottom(name, color='white'))
self._createMissingImages()
self.setNegative(neg)
#
self._highlighted_images = {}
self._highlight = []
self._highlight.append(self._getHighlight(self._card[0], None, '#3896f8'))
self._pil_shadow = {}
def reset(self):
print 'Image.reset'
self.resize(1, 1)
# ************************************************************************
# *
@ -371,9 +443,9 @@ class SubsampledImages(Images):
self._back.append(ImagesCardback(len(self._back), _back.name, im, im))
#
CW, CH = self.CARDW, self.CARDH
for im in images._shade:
##self._shade.append(None)
self._shade.append(copyImage(im, 0, 0, CW, CH))
for im in images._highlight:
##self._highlight.append(None)
self._highlight.append(copyImage(im, 0, 0, CW, CH))
def getShadow(self, ncards):
return None

View file

@ -89,10 +89,8 @@ class Layout:
self.CH = images.CARDH
self.XOFFSET = images.CARD_XOFFSET
self.YOFFSET = images.CARD_YOFFSET
self.XM = layout_x_margin # XMARGIN
self.YM = layout_y_margin # YMARGIN
self.XM = layout_x_margin # XMARGIN
self.YM = layout_y_margin # YMARGIN
if card_x_space is None:
self.XS = self.CW + layout_card_x_space # XSPACE
@ -914,8 +912,9 @@ class Layout:
w = XM * 2 + toprows * XS
# set size so that at least 2/3 of a card is visible with 12 cards
h = CH * 2 / 3 + (playcards - 1) * self.YOFFSET
h = max(h, 2 * YS)
h1 = CH * 2 / 3 + (playcards - 1) * self.YOFFSET
h2 = (3 + reserves / decks) * YS
h = max(h1, h2)
# create foundations
x, y = w - XS * decks, YM

View file

@ -36,7 +36,7 @@ try:
except:
thread = None
from settings import PACKAGE, TOOLKIT
from settings import PACKAGE, TOOLKIT, USE_TILE
Image = ImageTk = ImageOps = None
if TOOLKIT == 'tk':
@ -54,7 +54,13 @@ if TOOLKIT == 'tk':
import BmpImagePlugin
import PpmImagePlugin
Image._initialized = 2
USE_PIL = False
if TOOLKIT == 'tk' and USE_TILE and Image and Image.VERSION >= '1.1.7':
USE_PIL = True
# debug
#Image = None
#USE_PIL = False
# ************************************************************************
# * exceptions

View file

@ -95,6 +95,7 @@ randomize_place = boolean
save_cardsets = boolean
dragcursor = boolean
save_games_geometry = boolean
game_geometry = int_list(min=2, max=2)
sound = boolean
sound_mode = integer(0, 1)
sound_sample_volume = integer(0, 128)
@ -166,7 +167,11 @@ highlight_piles = float(0.2, 9.9)
7 = string_list(min=2, max=2)
8 = string_list(min=2, max=2)
9 = string_list(min=2, max=2)
scale_cards = boolean
scale_x = float
scale_y = float
auto_scale = boolean
preserve_aspect_ratio = boolean
'''.splitlines()
@ -237,7 +242,6 @@ class Options:
#('favorite_gameid', 'list'),
]
def __init__(self):
self._config = None # configobj.ConfigObj instance
self._config_encoding = 'utf-8'
@ -367,10 +371,17 @@ class Options:
self.wm_maximized = 0
self.save_games_geometry = False
self.games_geometry = {} # saved games geometry (gameid: (width, height))
self.game_geometry = (0, 0) # game geometry before exit
#
self.randomize_place = False
self.save_cardsets = True
self.dragcursor = True
#
self.scale_cards = False
self.scale_x = 1.0
self.scale_y = 1.0
self.auto_scale = False
self.preserve_aspect_ratio = True
def setDefaults(self, top=None):
WIN_SYSTEM = settings.WIN_SYSTEM
@ -477,11 +488,15 @@ class Options:
# cardsets
for key, val in self.cardset.items():
config['cardsets'][str(key)] = val
for key in ('scale_cards', 'scale_x', 'scale_y',
'auto_scale', 'preserve_aspect_ratio'):
config['cardsets'][key] = getattr(self, key)
# games_geometry
config['games_geometry'].clear()
for key, val in self.games_geometry.items():
config['games_geometry'][str(key)] = val
config['general']['game_geometry'] = self.game_geometry
config.write()
##config.write(sys.stdout); print
@ -628,6 +643,14 @@ class Options:
self.cardset[int(key)] = val
except:
traceback.print_exc()
for key, t in (('scale_cards', 'bool'),
('scale_x', 'float'),
('scale_y', 'float'),
('auto_scale', 'bool'),
('preserve_aspect_ratio', 'bool')):
val = self._getOption('cardsets', key, t)
if val is not None:
setattr(self, key, val)
# games_geometry
for key, val in config['games_geometry'].items():
@ -637,5 +660,11 @@ class Options:
self.games_geometry[int(key)] = val
except:
traceback.print_exc()
game_geometry = self._getOption('general', 'game_geometry', 'list')
if game_geometry is not None:
try:
self.game_geometry = tuple(int(i) for i in game_geometry)
except:
traceback.print_exc()

View file

@ -30,8 +30,8 @@ PACKAGE = 'PySolFC'
TITLE = 'PySol'
PACKAGE_URL = 'http://pysolfc.sourceforge.net/'
VERSION = '2.0.1'
VERSION_TUPLE = (2,0,1)
VERSION = '3.0'
VERSION_TUPLE = (3,0,0)
# Tk windowing system (auto set up in init.py)
WIN_SYSTEM = 'x11' # win32, x11, aqua, classic

View file

@ -97,7 +97,7 @@ import types
# PySol imports
from mfxutil import Struct, kwdefault, SubclassResponsibility
from mfxutil import Image, ImageTk
from mfxutil import Image, ImageTk, USE_PIL
from util import ACE, KING
from util import ANY_SUIT, ANY_COLOR, ANY_RANK, NO_RANK
from pysoltk import EVENT_HANDLED, EVENT_PROPAGATE
@ -238,7 +238,6 @@ class Stack:
mapkey = (x, y)
###assert not game.stackmap.has_key(mapkey) ## can happen in PyJonngg
game.stackmap[mapkey] = id
self.init_coord = (x, y)
#
# setup our pseudo MVC scheme
@ -282,15 +281,16 @@ class Stack:
#
# view
#
self.init_coord = (x, y)
view.x = x
view.y = y
view.canvas = game.canvas
view.CARD_XOFFSET = 0
view.CARD_YOFFSET = 0
view.INIT_CARD_OFFSETS = (0,0)
view.INIT_CARD_YOFFSET = 0 # for reallocateCards
view.group = MfxCanvasGroup(view.canvas)
view.shrink_face_down = 1
##view.group.move(view.x, view.y)
# image items
view.images = Struct(
bottom = None, # canvas item
@ -366,6 +366,11 @@ class Stack:
self.CARD_YOFFSET = (oy,)
else:
self.CARD_YOFFSET = tuple([int(round(y)) for y in oy])
# preserve offsets
self.INIT_CARD_OFFSETS = (self.CARD_XOFFSET, self.CARD_YOFFSET) # for resize()
self.INIT_CARD_YOFFSET = self.CARD_YOFFSET # for reallocateCards
if self.can_hide_cards < 0:
self.can_hide_cards = self.is_visible
if self.cap.max_cards < 3:
@ -398,7 +403,7 @@ class Stack:
# bottom image
if self.is_visible:
self.prepareBottom()
self.INIT_CARD_YOFFSET = self.CARD_YOFFSET # for reallocateCards
# stack bottom image
def prepareBottom(self):
@ -520,26 +525,6 @@ class Stack:
x, y = self.getPositionFor(card)
card.moveTo(x, y)
# move stack to new coords
def moveTo(self, x, y):
dx, dy = x-self.x, y-self.y
self.x, self.y = x, y
for c in self.cards:
x, y = self.getPositionFor(c)
c.moveTo(x, y)
if self.images.bottom:
self.images.bottom.move(dx, dy)
if self.images.redeal:
self.images.redeal.move(dx, dy)
if self.texts.ncards:
self.texts.ncards.move(dx, dy)
if self.texts.rounds:
self.texts.rounds.move(dx, dy)
if self.texts.redeal:
self.texts.redeal.move(dx, dy)
if self.texts.misc:
self.texts.misc.move(dx, dy)
# find card
def _findCard(self, event):
model, view = self, self
@ -553,11 +538,12 @@ class Stack:
model, view = self, self
if cards is None: cards = model.cards
images = self.game.app.images
cw, ch = images.getSize()
index = -1
for i in range(len(cards)):
c = cards[i]
r = (c.x, c.y, c.x + images.CARDW, c.y + images.CARDH)
if x >= r[0] and x < r[2] and y >= r[1] and y < r[3]:
r = (c.x, c.y, c.x + cw, c.y + ch)
if r[0] <= x < r[2] and r[1] <= y < r[3]:
index = i
return index
@ -667,6 +653,10 @@ class Stack:
def resetGame(self):
# Called when starting a new game.
self.CARD_YOFFSET = self.INIT_CARD_YOFFSET
self.items.shade_item = None
self.images.shade_img = None
#self.items.bottom = None
#self.images.bottom = None
def __repr__(self):
# Return a string for debug print statements.
@ -874,7 +864,7 @@ class Stack:
if not self.canvas.winfo_ismapped():
return False
yoffset = self.CARD_YOFFSET[0]
cardh = self.game.app.images.CARDH / 2 # 1/2 of a card is visible
cardh = self.game.app.images.getSize()[0] / 2 # 1/2 of a card is visible
num_face_up = len([c for c in self.cards if c.face_up])
num_face_down = len(self.cards) - num_face_up
stack_height = int(self.y +
@ -882,13 +872,19 @@ class Stack:
num_face_up * yoffset +
cardh)
visible_height = self.canvas.winfo_height()
game_height = self.game.height + 2*self.canvas.ymargin
if USE_PIL and self.game.app.opt.auto_scale:
# use visible_height only
game_height = 0
else:
game_height = self.game.height + 2*self.canvas.ymargin
height = max(visible_height, game_height)
#print 'reallocateCards:', stack_height, height, visible_height, game_height
if stack_height > height:
# compact stack
n = num_face_down / self.shrink_face_down + num_face_up
dy = float(height - self.y - cardh) / n
if dy < yoffset:
#print 'compact:', dy
self.CARD_YOFFSET = (dy,)
return True
elif stack_height < height:
@ -898,12 +894,62 @@ class Stack:
n = num_face_down / self.shrink_face_down + num_face_up
dy = float(height - self.y - cardh) / n
dy = min(dy, self.INIT_CARD_YOFFSET[0])
#print 'expande:', dy
self.CARD_YOFFSET = (dy,)
return True
return False
def resize(self, xf, yf):
print 'resize', self
# resize and move stack
# xf, yf - a multiplicative factor (from the original values)
#print 'Stack.resize:', self, self.is_visible, xf, yf
x0, y0 = self.init_coord
x, y = int(round(x0*xf)), int(round(y0*yf))
self.x, self.y = x, y
# offsets
xoffset = tuple(int(round(i*xf)) for i in self.INIT_CARD_OFFSETS[0])
yoffset = tuple(int(round(i*yf)) for i in self.INIT_CARD_OFFSETS[1])
self.CARD_XOFFSET = xoffset
self.CARD_YOFFSET = yoffset
self.INIT_CARD_YOFFSET = yoffset
#print '* resize offset:', self.INIT_CARD_XOFFSET,
# move cards
for c in self.cards:
cx, cy = self.getPositionFor(c)
c.moveTo(cx, cy)
# ---
if not self.is_visible:
return
# bottom and shade
if self.images.bottom:
img = self.getBottomImage()
self.images.bottom['image'] = img
self.images.bottom.moveTo(x, y)
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)
#
def move(item):
ix, iy = item.init_coord
x = int(round(ix*xf))
y = int(round(iy*yf))
item.moveTo(x, y)
# images
if self.images.redeal:
move(self.images.redeal)
# texts
if self.texts.ncards:
move(self.texts.ncards)
if self.texts.rounds:
move(self.texts.rounds)
if self.texts.redeal:
move(self.texts.redeal)
if self.texts.misc:
move(self.texts.misc)
def basicShallHighlightSameRank(self, card):
# by default all open stacks are available for highlighting
@ -1003,14 +1049,14 @@ class Stack:
return 0
##print self.cards[i]
self.cards[i].item.tkraise()
self.game.canvas.update_idletasks()
self.canvas.update_idletasks()
self.game.sleep(self.game.app.opt.timeouts['raise_card'])
if TOOLKIT == 'tk':
self.cards[i].item.lower(self.cards[i+1].item)
elif TOOLKIT == 'gtk':
for c in self.cards[i+1:]:
c.tkraise()
self.game.canvas.update_idletasks()
self.canvas.update_idletasks()
return 1
def controlmiddleclickHandler(self, event):
@ -1026,7 +1072,7 @@ class Stack:
if not face_up:
self.cards[i].showFace()
self.cards[i].item.tkraise()
self.game.canvas.update_idletasks()
self.canvas.update_idletasks()
self.game.sleep(self.game.app.opt.timeouts['raise_card'])
if not face_up:
self.cards[i].showBack()
@ -1036,7 +1082,7 @@ class Stack:
elif TOOLKIT == 'gtk':
for c in self.cards[i+1:]:
c.tkraise()
self.game.canvas.update_idletasks()
self.canvas.update_idletasks()
return 1
def rightclickHandler(self, event):
@ -1074,8 +1120,7 @@ class Stack:
x0, y0 = drag.stack.getPositionFor(c)
x1, y1 = c.x, c.y
dx, dy = abs(x0-x1), abs(y0-y1)
w = self.game.app.images.CARDW
h = self.game.app.images.CARDH
w, h = self.game.app.images.getSize()
if dx > 2*w or dy > 2*h:
self.game.animatedMoveTo(drag.stack, drag.stack,
drag.cards, x0, y0, frames=-1)
@ -1185,7 +1230,7 @@ class Stack:
if self.game.app.opt.mouse_type == 'point-n-click':
if self.acceptsCards(self.game.drag.stack,
self.game.drag.cards):
self.game.canvas.config(cursor=CURSOR_DOWN_ARROW)
self.canvas.config(cursor=CURSOR_DOWN_ARROW)
self.current_cursor = CURSOR_DOWN_ARROW
self.cursor_changed = True
else:
@ -1203,13 +1248,13 @@ class Stack:
if self.game.app.opt.mouse_type == 'drag-n-drop':
return EVENT_HANDLED
if self.cursor_changed:
self.game.canvas.config(cursor='')
self.canvas.config(cursor='')
self.current_cursor = ''
self.cursor_changed = False
drag_stack = self.game.drag.stack
if self is drag_stack:
x, y = event.x, event.y
w, h = self.game.canvas.winfo_width(), self.game.canvas.winfo_height()
w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
if x < 0 or y < 0 or x >= w or y >= h:
# cancel drag if mouse leave canvas
drag_stack.cancelDrag(event)
@ -1261,10 +1306,11 @@ class Stack:
##sx, sy = 0, 0
sx, sy = -images.SHADOW_XOFFSET, -images.SHADOW_YOFFSET
dx, dy = 0, 0
cw, ch = images.getSize()
if game.app.opt.mouse_type == 'sticky-mouse':
# return cards under mouse
dx = event.x - (x_offset+images.CARDW+sx) - game.canvas.xmargin
dy = event.y - (y_offset+images.CARDH+sy) - game.canvas.ymargin
dx = event.x - (x_offset+cw+sx) - game.canvas.xmargin
dy = event.y - (y_offset+ch+sy) - game.canvas.ymargin
if dx < 0: dx = 0
if dy < 0: dy = 0
for s in drag.shadows:
@ -1316,13 +1362,14 @@ class Stack:
images = self.game.app.images
cx, cy = cards[0].x, cards[0].y
ddx, ddy = cx-cards[-1].x, cy-cards[-1].y
if TOOLKIT == 'tk' and Image and Image.VERSION >= '1.1.7': # use PIL
cw, ch = images.getSize()
if USE_PIL:
c0 = cards[-1]
if self.CARD_XOFFSET[0] < 0: c0 = cards[0]
if self.CARD_YOFFSET[0] < 0: c0 = cards[0]
img = images.getShadowPIL(self, cards)
cx, cy = c0.x + images.CARDW + dx, c0.y + images.CARDH + dy
s = MfxCanvasImage(self.game.canvas, cx, cy,
cx, cy = c0.x + cw + dx, c0.y + ch + dy
s = MfxCanvasImage(self.canvas, cx, cy,
image=img, anchor=ANCHOR_SE)
s.lower(c0.item)
return (s,)
@ -1346,10 +1393,10 @@ class Stack:
else:
return ()
if img0 and img1:
cx, cy = c0.x + images.CARDW + dx, c0.y + images.CARDH + dy
s1 = MfxCanvasImage(self.game.canvas, cx, cy - img0.height(),
cx, cy = c0.x + cw + dx, c0.y + ch + dy
s1 = MfxCanvasImage(self.canvas, cx, cy - img0.height(),
image=img1, anchor=ANCHOR_SE)
s2 = MfxCanvasImage(self.game.canvas, cx, cy,
s2 = MfxCanvasImage(self.canvas, cx, cy,
image=img0, anchor=ANCHOR_SE)
if TOOLKIT == 'tk':
s1.lower(c0.item)
@ -1412,9 +1459,9 @@ class Stack:
if sstack.cards:
card = sstack.cards[-1]
if card.face_up:
img = images.getShadowCard(card.deck, card.suit, card.rank)
img = images.getHighlightedCard(card.deck, card.suit, card.rank)
else:
img = images.getShadowBack()
img = images.getHighlightedBack()
else:
img = images.getShade()
if not img:
@ -1440,11 +1487,11 @@ class Stack:
## self.CARD_YOFFSET != (0,)):
## return
card = self.cards[-1]
img = self.game.app.images.getShadowCard(card.deck, card.suit, card.rank)
img = self.game.app.images.getHighlightedCard(card.deck, card.suit, card.rank)
if img is None:
return
#self.game.canvas.update_idletasks()
item = MfxCanvasImage(self.game.canvas, card.x, card.y,
#self.canvas.update_idletasks()
item = MfxCanvasImage(self.canvas, card.x, card.y,
image=img, anchor=ANCHOR_NW, group=self.group)
#item.tkraise()
self.items.shade_item = item
@ -1463,8 +1510,9 @@ class Stack:
x1, y1 = self.getPositionFor(cards[-1])
x0, x1 = min(x1, x0), max(x1, x0)
y0, y1 = min(y1, y0), max(y1, y0)
x1 = x1 + self.game.app.images.CARDW
y1 = y1 + self.game.app.images.CARDH
cw, ch = self.game.app.images.getSize()
x1 += cw
y1 += ch
xx0, yy0 = x0, y0
w, h = x1-x0, y1-y0
#
@ -1491,7 +1539,7 @@ class Stack:
#
shade = markImage(shade)
tkshade = ImageTk.PhotoImage(shade)
im = MfxCanvasImage(self.game.canvas, xx0, yy0,
im = MfxCanvasImage(self.canvas, xx0, yy0,
image=tkshade, anchor=ANCHOR_NW,
group=self.group)
drag.shadows.append(im)
@ -1512,7 +1560,7 @@ class Stack:
# finish a drag operation
def finishDrag(self, event=None):
if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='')
self.canvas.config(cursor='')
drag = self.game.drag.copy()
if self.game.app.opt.mouse_type == 'point-n-click':
drag.stack._stopDrag()
@ -1528,7 +1576,7 @@ class Stack:
# cancel a drag operation
def cancelDrag(self, event=None):
if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='')
self.canvas.config(cursor='')
drag = self.game.drag.copy()
if self.game.app.opt.mouse_type == 'point-n-click':
drag.stack._stopDrag()
@ -1726,6 +1774,11 @@ class TalonStack(Stack,
Stack.__init__(self, x, y, game, cap=cap)
self.max_rounds = max_rounds
self.num_deal = num_deal
self.init_redeal = Struct(
top_bottom = None,
img_coord = None,
txt_coord = None,
)
self.resetGame()
def resetGame(self):
@ -1787,22 +1840,34 @@ class TalonStack(Stack,
self.texts.redeal.config(text=t)
self.texts.redeal_str = t
def prepareView(self):
Stack.prepareView(self)
def _addRedealImage(self):
# add or remove the redeal image/text
if not self.is_visible or self.images.bottom is None:
return
if self.images.redeal is not None or self.texts.redeal is not None:
return
if self.game.preview > 1:
return
images = self.game.app.images
cx, cy, ca = self.x + images.CARDW/2, self.y + images.CARDH/2, "center"
if images.CARDW >= 54 and images.CARDH >= 54:
cw, ch = images.getSize()
cx, cy = self.init_redeal.img_coord
ca = 'center'
tx, ty = self.init_redeal.txt_coord
if self.images.redeal:
self.canvas.delete(self.images.redeal)
self.images.redeal = None
self.images.redeal_img = None
if self.texts.redeal:
self.canvas.delete(self.texts.redeal)
self.texts.redeal = None
self.texts.redeal_str = ''
self.top_bottom = self.init_redeal.top_bottom
if cw >= 60 and ch >= 60:
# add a redeal image above the bottom image
img = (self.getRedealImages())[self.max_rounds != 1]
if img is not None:
self.images.redeal_img = img
self.images.redeal = MfxCanvasImage(self.game.canvas,
self.images.redeal = MfxCanvasImage(self.canvas,
cx, cy, image=img,
anchor="center",
group=self.group)
@ -1812,19 +1877,21 @@ class TalonStack(Stack,
### FIXME
pass
self.top_bottom = self.images.redeal
if images.CARDH >= 90:
cy, ca = self.y + images.CARDH - 4, "s"
if ch >= 90:
cy, ca = ty, "s"
else:
ca = None
font = self.game.app.getFont("canvas_default")
text_width = get_text_width(_('Redeal'), font=font,
root=self.game.canvas)
if images.CARDW >= text_width+4 and ca:
# add a redeal text above the bottom image
root=self.canvas)
if cw >= text_width+4 and ca:
# add a redeal text below the bottom image
if self.max_rounds != 1:
# FIXME: sometimes canvas do not show the text
#print 'add txt', cx, cy
self.texts.redeal_str = ""
images = self.game.app.images
self.texts.redeal = MfxCanvasText(self.game.canvas, cx, cy,
self.texts.redeal = MfxCanvasText(self.canvas, cx, cy,
anchor=ca, font=font,
group=self.group)
if TOOLKIT == 'tk':
@ -1834,6 +1901,22 @@ class TalonStack(Stack,
pass
self.top_bottom = self.texts.redeal
def prepareView(self):
Stack.prepareView(self)
if 0:
if not self.is_visible or self.images.bottom is None:
return
if self.images.redeal is not None or self.texts.redeal is not None:
return
if self.game.preview > 1:
return
images = self.game.app.images
self.init_redeal.top_bottom = self.top_bottom
cx, cy, ca = self.x + images.CARDW/2, self.y + images.CARDH/2, "center"
ty = self.y + images.CARDH - 4
self.init_redeal.img_coord = cx, cy
self.init_redeal.txt_coord = cx, ty
getBottomImage = Stack._getTalonBottomImage
def getRedealImages(self):
@ -1853,6 +1936,10 @@ class TalonStack(Stack,
#def getBaseCard(self):
# return self._getBaseCard()
def resize(self, xf, yf):
self._addRedealImage()
Stack.resize(self, xf, yf)
# A single click deals one card to each of the RowStacks.
class DealRowTalonStack(TalonStack):

View file

@ -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
# ************************************************************************

View file

@ -31,7 +31,7 @@ import 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 SELECT_GAME_MENU
@ -210,6 +210,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(),
@ -260,6 +261,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)
@ -448,6 +450,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")
@ -489,7 +496,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()
@ -538,6 +545,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
@ -1134,6 +1146,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
t = CARDSET
@ -1141,14 +1203,30 @@ class PysolMenubarTk:
d = SelectCardsetDialogWithPreview(self.top, title=_("Select ")+t,
app=self.app, manager=self.app.cardset_manager, key=key)
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 != 0 or cs is None:
return
if d.status == 0 and d.button == 0 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 == 0:
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
@ -1323,7 +1401,7 @@ class PysolMenubarTk:
self.game.showStackDesc()
#
# Tlie
# Tile (ttk)
#
def mOptTheme(self, *event):

View file

@ -29,13 +29,13 @@ import Tkinter
import ttk
# PySol imports
from pysollib.mfxutil import KwStruct
from pysollib.mfxutil import KwStruct, USE_PIL
from pysollib.util import CARDSET
from pysollib.resource import CSI
# Toolkit imports
from tkutil import loadImage
from tkwidget import MfxDialog, MfxScrolledCanvas
from tkwidget import MfxDialog, MfxScrolledCanvas, PysolScale
from tkcanvas import MfxCanvasImage
from selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
from selecttree import SelectDialogTreeData, SelectDialogTreeCanvas
@ -179,6 +179,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:
@ -186,7 +187,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 = ttk.PanedWindow(top_frame, orient='horizontal')
@ -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 = PysolScale(
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 = PysolScale(
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 = ttk.Checkbutton(
left_frame, text=_('Auto scaling'),
variable=self.auto_scale,
takefocus=False,
command=self._updateAutoScale
)
check.grid(row=3, column=0, columnspan=2, sticky='ew',
padx=padx, pady=pady)
#
self.preserve_aspect = Tkinter.BooleanVar()
self.preserve_aspect.set(app.opt.preserve_aspect_ratio)
self.aspect_check = ttk.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='ew',
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)
@ -233,6 +284,20 @@ class SelectCardsetDialogWithPreview(MfxDialog):
if button in (0, 1): # Load/Cancel
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 == 10: # Info
cs = self.manager.get(self.tree.selection_key)
if not cs:
@ -243,9 +308,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.state('disabled')
self.scale_y.state('disabled')
else:
self.aspect_check.config(state='disabled')
self.scale_x.state('!disabled')
self.scale_y.state('!disabled')
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 = []
@ -264,7 +344,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
@ -272,13 +361,12 @@ class SelectCardsetDialogWithPreview(MfxDialog):
x, y = 10, y + dy
else:
x = x + dx
## canvas.config(scrollregion=(0, 0, sx+dx, sy+dy))
## canvas.config(width=sx+dx, height=sy+dy)
canvas.config(scrollregion=(0, 0, sx+dx, sy+dy),
width=sx+dx, height=sy+dy)
#canvas.config(xscrollincrement=dx, yscrollincrement=dy)
canvas.event_generate('<Configure>') # update bg image
self.preview_key = key
self.key = key
class SelectCardsetByTypeDialogWithPreview(SelectCardsetDialogWithPreview):

View file

@ -58,14 +58,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):
@ -90,6 +91,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:
@ -105,6 +108,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)
# ************************************************************************
@ -229,17 +236,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))
#

View file

@ -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

View file

@ -667,7 +667,7 @@ class StackDesc:
font = game.app.getFont('canvas_small')
##print self.app.cardset.CARDW, self.app.images.CARDW
cardw = game.app.images.CARDW
cardw = game.app.images.getSize()[0]
x, y = stack.x+cardw/2, stack.y
text = stack.getHelp()+'\n'+stack.getBaseCard()
text = text.strip()
@ -715,30 +715,26 @@ class MyPysolScale:
kw['from_'] = kw['from_']/self.resolution
if 'to' in kw:
kw['to'] = kw['to']/self.resolution
if 'command' in kw:
self.command = kw['command']
else:
self.command = None
if 'variable' in kw:
self.variable = kw['variable']
del kw['variable']
else:
self.variable = None
value = None
if 'value' in kw:
value = kw['value']
del kw['value']
if self.variable:
self.variable.set(value)
else:
value = None
if self.variable:
value = self.variable.get()
if self.variable:
self.variable.trace('w', self._trace_var)
elif self.variable:
value = self.variable.get()
self.value = value
self.command = command = None
if 'command' in kw:
command = kw['command']
kw['command'] = self._scale_command
if 'label' in kw:
self.label_text = kw['label']
width = len(self.label_text)+4
#width = None
del kw['label']
else:
self.label_text = None
@ -753,13 +749,16 @@ class MyPysolScale:
self.scale = ttk.Scale(self.frame, **kw)
self.scale.pack(side=side, expand=True, fill='both', pady=4)
if self.variable:
self.variable.trace('w', self._trace_var)
if value is not None:
if self.variable:
self.variable.set(self._round(value))
self._set_text(self._round(value))
if self.variable:
self.variable.set(value)
self.command = command
def _round(self, value):
return int(float(value)/self.resolution)*self.resolution
return int(round(float(value)/self.resolution))*self.resolution
def _trace_var(self, *args):
self.scale.set(float(self.variable.get())/self.resolution)
@ -775,8 +774,9 @@ class MyPysolScale:
v = self._round(float(value)*self.resolution)
self._set_text(v)
self.variable.set(v)
if self.command:
if value != self.value and self.command:
self.command(value)
self.value = value
def pack(self, **kw):
self.frame.pack(**kw)
@ -787,6 +787,15 @@ class MyPysolScale:
self.scale.configure(**kw)
config = configure
def state(self, v):
self.scale.state(statespec=(v,))
self.label.state(statespec=(v,))
def get(self):
return self.variable.get()
def set(self, v):
self.variable.set(v)
class TkinterScale(Tkinter.Scale):
def __init__(self, parent, **kw):