1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-22 03:04:09 -04:00

Compare commits

...

7 commits

Author SHA1 Message Date
Shlomi Fish
7da41a93be Allow to swap the mouse keys using the ini file.
See: https://sourceforge.net/p/pysolfc/discussion/503708/thread/5d77434ff8/

" PySolFC / Discussion / Open Discussion: Any way to remap mouse buttons? "

We may not have covered all the affected calls to bind().
2020-10-19 18:18:13 +03:00
Shlomi Fish
934db8da3b cleanup the last commit
Merge an identical if/else and indent comments
2020-10-19 15:00:24 +03:00
cardset
c6811cca29 Info/Settings Sliders Moved
Moved Offset Sliders to the same place as Scale Sliders. I think that makes more sense in operation and overview. It also avoids that wrong values could be saved
2020-10-19 09:14:43 +02:00
Juhani Numminen
91f785c574 Fix list of packages to install with apt-get
libperl-dev is not necessary, while the old python-glade2, python-gnome2 and python-gtk2
libraries are no longer installable in Ubuntu 20.04 LTS or Debian 10.

Update for this section requested in sf.net ticket #39; https://sourceforge.net/p/pysolfc/bugs/39/
2020-10-15 15:56:27 +03:00
Juhani Numminen
bb2769a63a Change comment which had non-runnable command in it
This was brought up on a mailing list
https://groups.google.com/d/msg/foss-card-games/uJLry-w498E/HmwiTxUkDQAJ
2020-10-15 15:32:35 +03:00
Juhani Numminen
1c79d5437e Refactor initCardsets
Make variable names descriptive.

Don't check for duplicate or empty strings in result of getSearchDirs() because
that is not necessary. Document those properties in a comment in resource.py.

Wrap try...except more closely around expressions where we want to catch.
2020-10-15 12:30:36 +03:00
Juhani Numminen
24ccdf634b macOS: use "PySolFC Help" menuitem provided by tk
The details are explained in this tutorial
https://tkdocs.com/tutorial/menus.html

For this to work, init() must destroy the tkinter.Tk instance it creates
so that was uncommented.
2020-10-14 14:59:38 +03:00
19 changed files with 264 additions and 141 deletions

View file

@ -63,7 +63,7 @@ sudo urpmi git make pygtk2 pygtk2.0-libglade gnome-python-canvas tkinter
On Debian / Ubuntu / etc. you can do:
```
sudo apt-get install -y cpanminus libperl-dev make perl python-glade2 python-gnome2 python-gnome2-dev python-gtk2 python-setuptools python-tk
sudo apt-get install cpanminus make perl python3-setuptools python3-tk
```
#### Step 2 - build PySol.

View file

@ -1075,60 +1075,52 @@ Please select a %(correct_type)s type cardset.
# set offsets from options.cfg
if cs.ident in self.opt.offsets:
cs.CARD_XOFFSET, cs.CARD_YOFFSET = self.opt.offsets[cs.ident]
if cs.CARDD > self.top.winfo_screendepth():
return None
return cs
def initCardsets(self):
"""Load all valid cardset config.txt files and ignore invalid ones.
"""
screendepth = self.top.winfo_screendepth()
manager = self.cardset_manager
# find all available cardsets
dirs = manager.getSearchDirs(self, ("cardsets", ""), "PYSOL_CARDSETS")
if DEBUG:
dirs += manager.getSearchDirs(self, "cardsets-*")
# print dirs
found, t = [], {}
fnames = {} # (to check for duplicates)
found = []
found_names = [] # (to check for duplicates)
for dirname in dirs:
dirname = dirname.strip()
try:
names = []
if dirname and os.path.isdir(dirname) and dirname not in t:
t[dirname] = 1
names = os.listdir(dirname)
names.sort()
for name in names:
if not name.startswith('cardset-'):
continue
d = os.path.join(dirname, name)
if not os.path.isdir(d):
continue
f = os.path.join(d, "config.txt")
if os.path.isfile(f):
try:
cs = self._readCardsetConfig(d, f)
if cs:
# from pprint import pprint
# print cs.name
# pprint(cs.__dict__)
back = cs.backnames[cs.backindex]
f1 = os.path.join(d, back)
f2 = os.path.join(d, "shade" + cs.ext)
if (cs.ext in IMAGE_EXTENSIONS and
cs.name not in fnames and
os.path.isfile(f1) and
os.path.isfile(f2)):
found.append(cs)
# print '+', cs.name
fnames[cs.name] = 1
else:
print_err('failed to parse cardset file: %s'
% f)
except Exception:
# traceback.print_exc()
pass
subdirs = [os.path.join(dirname, subdir)
for subdir in os.listdir(dirname)
if subdir.startswith('cardset-')]
except EnvironmentError:
pass
traceback.print_exc()
continue
subdirs.sort()
for d in subdirs:
config_txt_path = os.path.join(d, "config.txt")
if not os.path.isfile(config_txt_path):
continue
try:
cs = self._readCardsetConfig(d, config_txt_path)
except Exception:
traceback.print_exc()
cs = None
if not cs:
print_err('failed to parse cardset file: %s'
% config_txt_path)
continue
back = cs.backnames[cs.backindex]
back_im_path = os.path.join(d, back)
shade_im_path = os.path.join(d, "shade" + cs.ext)
if (cs.name not in found_names and
cs.ext in IMAGE_EXTENSIONS and
cs.CARDD <= screendepth and
os.path.isfile(back_im_path) and
os.path.isfile(shade_im_path)):
found.append(cs)
found_names.append(cs.name)
# register cardsets
for obj in found:
if not manager.getByName(obj.name):

View file

@ -671,12 +671,19 @@ class Game(object):
print_err('max_rounds <= 1, but talon.texts.rounds is not None: '
'%s' % class_name, 2)
def _calcMouseBind(self, binding_format):
"""docstring for _calcMouseBind"""
return self.app.opt.calcCustomMouseButtonsBinding(binding_format)
def initBindings(self):
# note: a Game is only allowed to bind self.canvas and not to self.top
# bind(self.canvas, "<Double-1>", self.undoHandler)
bind(self.canvas, "<1>", self.undoHandler)
bind(self.canvas, "<2>", self.dropHandler)
bind(self.canvas, "<3>", self.redoHandler)
bind(self.canvas,
self._calcMouseBind("<{mouse_button1}>"), self.undoHandler)
bind(self.canvas,
self._calcMouseBind("<{mouse_button2}>"), self.dropHandler)
bind(self.canvas,
self._calcMouseBind("<{mouse_button3}>"), self.redoHandler)
bind(self.canvas, '<Unmap>', self._unmapHandler)
bind(self.canvas, '<Configure>', self._configureHandler, add=True)

View file

@ -222,6 +222,9 @@ class Mahjongg_RowStack(OpenStack):
for s in self.game.s.rows[self.id+1:]:
s.group.tkraise()
def _calcMouseBind(self, binding_format):
return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format)
# In Mahjongg games type there are a lot of stacks, so we optimize
# and don't create bindings that are not used anyway.
def initBindings(self):
@ -231,9 +234,21 @@ class Mahjongg_RowStack(OpenStack):
# bind(group, "<3>", self._Stack__controlclickEventHandler)
# bind(group, "<Control-1>", self._Stack__controlclickEventHandler)
#
bind(group, "<1>", self.__clickEventHandler)
bind(group, "<3>", self.__controlclickEventHandler)
bind(group, "<Control-1>", self.__controlclickEventHandler)
bind(
group,
self._calcMouseBind("<{mouse_button1}>"),
self.__clickEventHandler
)
bind(
group,
self._calcMouseBind("<{mouse_button3}>"),
self.__controlclickEventHandler
)
bind(
group,
self._calcMouseBind("<Control-{mouse_button1}>"),
self.__controlclickEventHandler
)
# bind(group, "<Enter>", self._Stack__enterEventHandler)
# bind(group, "<Leave>", self._Stack__leaveEventHandler)

View file

@ -60,9 +60,20 @@ class Matrix_RowStack(OpenStack):
# the tile (from Tk's stacking view)
return len(self.cards) - 1
def _calcMouseBind(self, binding_format):
return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format)
def initBindings(self):
bind(self.group, "<1>", self._Stack__clickEventHandler)
bind(self.group, "<Control-1>", self._Stack__controlclickEventHandler)
bind(
self.group,
self._calcMouseBind("<{mouse_button1}>"),
self._Stack__clickEventHandler
)
bind(
self.group,
self._calcMouseBind("<Control-{mouse_button1}>"),
self._Stack__controlclickEventHandler,
)
def getBottomImage(self):
return self.game.app.images.getBlankBottom()

View file

@ -117,7 +117,7 @@ def init():
else:
pysollib.settings.USE_TILE = True
# "can't invoke event <<ThemeChanged>>: application has been destroyed"
# root.destroy()
root.destroy()
tkinter._default_root = None
# check FreeCell-Solver

View file

@ -43,6 +43,17 @@ import validate
# * Options
# ************************************************************************
_global_settings = {}
def calcCustomMouseButtonsBinding(binding_format):
assert _global_settings['mouse_button1']
return binding_format.format(
mouse_button1=_global_settings['mouse_button1'],
mouse_button2=_global_settings['mouse_button2'],
mouse_button3=_global_settings['mouse_button3'],
)
configspec = '''
[general]
@ -253,6 +264,9 @@ class Options:
('solver_max_iterations', 'int'),
('solver_iterations_output_step', 'int'),
('solver_preset', 'string'),
('mouse_button1', 'int'),
('mouse_button2', 'int'),
('mouse_button3', 'int'),
# ('toolbar_vars', 'list'),
# ('recent_gameid', 'list'),
# ('favorite_gameid', 'list'),
@ -320,6 +334,9 @@ class Options:
self.num_cards = False
self.helpbar = False
self.splashscreen = True
self.mouse_button1 = 1
self.mouse_button2 = 2
self.mouse_button3 = 3
self.mouse_type = 'drag-n-drop' # or 'sticky-mouse' or 'point-n-click'
self.mouse_undo = False # use mouse for undo/redo
self.negative_bottom = True
@ -751,3 +768,20 @@ class Options:
self.offsets[key] = val
except Exception:
traceback.print_exc()
# mouse buttons swap
def _positive(button):
return max([button, 1])
_global_settings['mouse_button1'] = _positive(self.mouse_button1)
_global_settings['mouse_button2'] = _positive(self.mouse_button2)
_global_settings['mouse_button3'] = _positive(self.mouse_button3)
def calcCustomMouseButtonsBinding(self, binding_format):
"""docstring for calcCustomMouseButtonsBinding"""
def _positive(button):
return max([button, 1])
return binding_format.format(
mouse_button1=_positive(self.mouse_button1),
mouse_button2=_positive(self.mouse_button2),
mouse_button3=_positive(self.mouse_button3),
)

View file

@ -123,6 +123,8 @@ class ResourceManager:
pass
def getSearchDirs(self, app, search, env=None):
"""Get a list of normalized directory paths. The returned list has no
duplicates."""
if isinstance(search, str):
search = (search,)
result = []

View file

@ -269,22 +269,35 @@ class Stack:
if self.is_visible:
self.initBindings()
def _calcMouseBind(self, binding_format):
return self.game.app.opt.calcCustomMouseButtonsBinding(binding_format)
# bindings {view widgets bind to controller}
def initBindings(self):
group = self.group
bind(group, "<1>", self.__clickEventHandler)
bind(group, self._calcMouseBind("<{mouse_button1}>"),
self.__clickEventHandler)
# bind(group, "<B1-Motion>", self.__motionEventHandler)
bind(group, "<Motion>", self.__motionEventHandler)
bind(group, "<ButtonRelease-1>", self.__releaseEventHandler)
bind(group, "<Control-1>", self.__controlclickEventHandler)
bind(group, "<Shift-1>", self.__shiftclickEventHandler)
bind(group, "<Double-1>", self.__doubleclickEventHandler)
bind(group, "<3>", self.__rightclickEventHandler)
bind(group, "<2>", self.__middleclickEventHandler)
bind(group, "<Control-3>", self.__middleclickEventHandler)
# bind(group, "<Control-2>", self.__controlmiddleclickEventHandler)
# bind(group, "<Shift-3>", self.__shiftrightclickEventHandler)
# bind(group, "<Double-2>", "")
bind(group, self._calcMouseBind("<ButtonRelease-{mouse_button1}>"),
self.__releaseEventHandler)
bind(group, self._calcMouseBind("<Control-{mouse_button1}>"),
self.__controlclickEventHandler)
bind(group, self._calcMouseBind("<Shift-{mouse_button1}>"),
self.__shiftclickEventHandler)
bind(group, self._calcMouseBind("<Double-{mouse_button1}>"),
self.__doubleclickEventHandler)
bind(group, self._calcMouseBind("<{mouse_button3}>"),
self.__rightclickEventHandler)
bind(group, self._calcMouseBind("<{mouse_button2}>"),
self.__middleclickEventHandler)
bind(group, self._calcMouseBind("<Control-{mouse_button3}>"),
self.__middleclickEventHandler)
# bind(group, self._calcMouseBind(
# "<Control-{mouse_button2}>"), self.__controlmiddleclickEventHandler)
# bind(group, self._calcMouseBind("<Shift-{mouse_button3}>"),
# self.__shiftrightclickEventHandler)
# bind(group, self._calcMouseBind("<Double-{mouse_button2}>"), "")
bind(group, "<Enter>", self.__enterEventHandler)
bind(group, "<Leave>", self.__leaveEventHandler)

View file

@ -264,6 +264,30 @@ class SelectCardsetDialogWithPreview(MfxDialog):
self.scale_y.grid(
row=2, column=0, sticky='ew', padx=padx, pady=pady)
#
# sliders at new position
cs = self.manager.get(self.tree.selection_key)
var = tkinter.IntVar()
self.x_offset = PysolScale(
left_frame, label=_('X offset:'),
from_=5, to=100, resolution=1,
orient='horizontal', variable=var,
value=cs.CARD_XOFFSET
)
self.x_offset.grid(row=3, column=0, sticky='ew',
padx=padx, pady=pady)
var = tkinter.IntVar()
self.y_offset = PysolScale(
left_frame, label=_('Y offset:'),
from_=5, to=100, resolution=1,
orient='horizontal', variable=var,
value=cs.CARD_YOFFSET
)
self.y_offset.grid(row=4, column=0, sticky='ew',
padx=padx, pady=pady)
self.auto_scale = tkinter.BooleanVar()
self.auto_scale.set(app.opt.auto_scale)
check = ttk.Checkbutton(
@ -272,7 +296,7 @@ class SelectCardsetDialogWithPreview(MfxDialog):
takefocus=False,
command=self._updateAutoScale
)
check.grid(row=3, column=0, columnspan=2, sticky='ew',
check.grid(row=5, column=0, columnspan=2, sticky='ew',
padx=padx, pady=pady)
#
self.preserve_aspect = tkinter.BooleanVar()
@ -283,7 +307,7 @@ class SelectCardsetDialogWithPreview(MfxDialog):
takefocus=False,
# command=self._updateScale
)
self.aspect_check.grid(row=4, column=0, sticky='ew',
self.aspect_check.grid(row=6, column=0, sticky='ew',
padx=padx, pady=pady)
self._updateAutoScale()
#
@ -312,9 +336,6 @@ class SelectCardsetDialogWithPreview(MfxDialog):
MfxDialog.destroy(self)
def initKw(self, kw):
if USE_PIL:
s = (_("&Info / Settings..."), 10)
else:
s = (_("&Info..."), 10)
kw = KwStruct(kw,
strings=(s, 'sep',
@ -328,10 +349,21 @@ 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
# save the values
try:
self.cardset_values = self.x_offset.get(), self.y_offset.get()
except Exception:
pass
if USE_PIL:
auto_scale = bool(self.auto_scale.get())
if button == 1:
self.app.menubar.tkopt.auto_scale.set(auto_scale)
# no changes
self.cardset_values = None
if auto_scale:
self.scale_values = (self.app.opt.scale_x,
self.app.opt.scale_y,
@ -350,7 +382,11 @@ class SelectCardsetDialogWithPreview(MfxDialog):
title = CARDSET.capitalize()+" "+cs.name
d = CardsetInfoDialog(self.top, title=title, cardset=cs,
images=self.preview_images)
try:
self.cardset_values = d.cardset_values
except Exception:
self.cardset_values = None
return
MfxDialog.mDone(self, button)
@ -380,6 +416,15 @@ class SelectCardsetDialogWithPreview(MfxDialog):
self.preview_key = -1
return
names, columns = cs.getPreviewCardNames()
# if cardset has changed, set default values
if key != self.preview_key:
self.x_offset.config(value=cs.CARD_XOFFSET)
self.x_offset.set(cs.CARD_XOFFSET)
self.y_offset.config(value=cs.CARD_YOFFSET)
self.y_offset.set(cs.CARD_YOFFSET)
try:
# ???names, columns = cs.getPreviewCardNames()
for n in names:
@ -482,39 +527,13 @@ class CardsetInfoDialog(MfxDialog):
info_frame.rowconfigure(frow, weight=1)
except Exception:
pass
if USE_PIL:
padx = 4
pady = 0
settings_frame = ttk.LabelFrame(frame, text=_('Settings'))
settings_frame.grid(row=row, column=0, columnspan=2, sticky='ew',
padx=0, pady=5, ipadx=5, ipady=5)
row += 1
var = tkinter.IntVar()
self.x_offset = PysolScale(
settings_frame, label=_('X offset:'),
from_=5, to=40, resolution=1,
orient='horizontal', variable=var,
value=cardset.CARD_XOFFSET,
# command=self._updateScale
)
self.x_offset.grid(row=0, column=0, sticky='ew',
padx=padx, pady=pady)
var = tkinter.IntVar()
self.y_offset = PysolScale(
settings_frame, label=_('Y offset:'),
from_=5, to=40, resolution=1,
orient='horizontal', variable=var,
value=cardset.CARD_YOFFSET,
# command=self._updateScale
)
self.y_offset.grid(row=1, column=0, sticky='ew',
padx=padx, pady=pady)
row += 1
# bg = top_frame["bg"]
bg = 'white'
text_w = tkinter.Text(frame, bd=1, relief="sunken", wrap="word",
padx=4, width=64, height=16, bg=bg)
padx=4, width=64, height=8, bg=bg)
text_w.grid(row=row, column=0, sticky='nsew')
sb = ttk.Scrollbar(frame)
sb.grid(row=row, column=1, sticky='ns')
@ -538,23 +557,3 @@ class CardsetInfoDialog(MfxDialog):
focus = self.createButtons(bottom_frame, kw)
# focus = text_w
self.mainloop(focus, kw.timeout)
def initKw(self, kw):
if USE_PIL:
strings = (_("&Save"), _("&Cancel"))
else:
strings = (_("&OK"),)
kw = KwStruct(kw,
strings=strings,
default=0,
resizable=True,
separator=True,
)
return MfxDialog.initKw(self, kw)
def mDone(self, button):
if USE_PIL and button == 0:
self.cardset_values = self.x_offset.get(), self.y_offset.get()
else:
self.cardset_values = None
MfxDialog.mDone(self, button)

View file

@ -244,9 +244,20 @@ class MfxTreeInCanvas(MfxScrolledCanvas):
# self.style.text_normal_bg = self.canvas.option_get(
# 'background', self.canvas.cget("background"))
#
bind(self.canvas, "<ButtonPress-1>", self.singleClick)
bind(self.canvas, "<Double-Button-1>", self.doubleClick)
# bind(self.canvas, "<ButtonRelease-1>", xxx)
from pysollib.options import calcCustomMouseButtonsBinding
bind(
self.canvas,
calcCustomMouseButtonsBinding("<ButtonPress-{mouse_button1}>"),
self.singleClick
)
bind(
self.canvas,
calcCustomMouseButtonsBinding("<Double-Button-{mouse_button1}>"),
self.doubleClick
)
# bind(self.canvas,
# calcCustomMouseButtonsBinding(
# "<ButtonRelease-{mouse_button1}>"), xxx)
self.pack(fill='both', expand=True)
def destroy(self):

View file

@ -320,7 +320,11 @@ class PysolAboutDialog(MfxMessageDialog):
url_label = ttk.Label(frame, text=kw.url, font=font,
foreground='blue', cursor='hand2')
url_label.pack()
url_label.bind('<1>', self._urlClicked)
from pysollib.options import calcCustomMouseButtonsBinding
url_label.bind(
calcCustomMouseButtonsBinding('<{mouse_button1}>'),
self._urlClicked
)
#
focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout)

View file

@ -158,6 +158,7 @@ class PysolToolbarTk:
def __init__(self, top, menubar, dir,
size=0, relief='flat', compound='none'):
from pysollib.options import calcCustomMouseButtonsBinding
self.top = top
self.menubar = menubar
self.side = -1
@ -192,7 +193,10 @@ class PysolToolbarTk:
):
if label is None:
sep = self._createSeparator()
sep.bind("<3>", self.rightclickHandler)
sep.bind(
calcCustomMouseButtonsBinding("<{mouse_button3}>"),
self.rightclickHandler
)
elif label == 'Pause':
self._createButton(label, f, check=True, tooltip=t)
else:
@ -209,8 +213,14 @@ class PysolToolbarTk:
self._createLabel("player", label=n_('Player'),
tooltip=_("Player options"))
#
self.player_label.bind("<1>", self.mOptPlayerOptions)
self.frame.bind("<3>", self.rightclickHandler)
self.player_label.bind(
calcCustomMouseButtonsBinding("<{mouse_button1}>"),
self.mOptPlayerOptions
)
self.frame.bind(
calcCustomMouseButtonsBinding("<{mouse_button3}>"),
self.rightclickHandler
)
#
self.setCompound(compound, force=True)

View file

@ -487,7 +487,12 @@ class AllGames_StatsDialog(MfxDialog):
self.nodes = {}
self.canvas = self.sc.canvas
self.canvas.dialog = self
bind(self.canvas, "<1>", self.singleClick)
from pysollib.options import calcCustomMouseButtonsBinding
bind(
self.canvas,
calcCustomMouseButtonsBinding("<{mouse_button1}>"),
self.singleClick
)
self.fillCanvas(player, title)
bbox = self.canvas.bbox("all")
# print bbox

View file

@ -171,7 +171,17 @@ class PysolToolbarTk:
#
self.frame = tkinter.Frame(top, relief=TkSettings.toolbar_relief,
bd=TkSettings.toolbar_borderwidth)
#
from pysollib.options import calcCustomMouseButtonsBinding
def _bind2sep(sep):
sep.bind(
calcCustomMouseButtonsBinding("<{mouse_button1}>"),
self.clickHandler)
sep.bind(
calcCustomMouseButtonsBinding("<{mouse_button3}>"),
self.rightclickHandler)
for label, f, t in (
(n_("New"), self.mNewGame, _("New game")),
(n_("Restart"), self.mRestart, _("Restart the\ncurrent game")),
@ -192,8 +202,7 @@ class PysolToolbarTk:
):
if label is None:
sep = self._createSeparator()
sep.bind("<1>", self.clickHandler)
sep.bind("<3>", self.rightclickHandler)
_bind2sep(sep)
elif label == 'Pause':
self._createButton(label, f, check=True, tooltip=t)
else:
@ -201,17 +210,17 @@ class PysolToolbarTk:
self.pause_button.config(variable=menubar.tkopt.pause)
sep = self._createFlatSeparator()
sep.bind("<1>", self.clickHandler)
sep.bind("<3>", self.rightclickHandler)
_bind2sep(sep)
self._createLabel("player", label=n_('Player'),
tooltip=_("Player options"))
#
self.player_label.bind("<1>", self.mOptPlayerOptions)
self.player_label.bind(
calcCustomMouseButtonsBinding("<{mouse_button1}>"),
self.mOptPlayerOptions)
# self.player_label.bind("<3>",self.mOptPlayerOptions)
self.popup = MfxMenu(master=None, label=n_('Toolbar'), tearoff=0)
createToolbarMenu(menubar, self.popup)
self.frame.bind("<1>", self.clickHandler)
self.frame.bind("<3>", self.rightclickHandler)
_bind2sep(self.frame)
#
self.setCompound(compound, force=True)

View file

@ -646,7 +646,13 @@ class PysolMenubarTkCommon:
if self.progress:
self.progress.update(step=1)
# macOS: tk creates the menu item "Help->PySolFC Help", therefore
# we will not create a duplicate "Help->Contents" item.
# The tk-provided menu item expects this callback.
self.top.createcommand('tk::mac::ShowHelp', self.mHelp)
menu = MfxMenu(self.menubar, label=n_("&Help"))
if WIN_SYSTEM != "aqua":
menu.add_command(
label=n_("&Contents"),
command=self.mHelp, accelerator=m+"F1")

View file

@ -104,7 +104,11 @@ class tkHTMLWriter(formatter.NullWriter):
url = self.anchor[0]
tag = "href_" + url
self.text.tag_add(tag, self.anchor_mark, "insert")
self.text.tag_bind(tag, "<1>", self.createCallback(url))
from pysollib.options import calcCustomMouseButtonsBinding
self.text.tag_bind(
tag,
calcCustomMouseButtonsBinding("<{mouse_button1}>"),
self.createCallback(url))
self.text.tag_bind(
tag, "<Enter>", lambda e: self.anchor_enter(url))
self.text.tag_bind(tag, "<Leave>", self.anchor_leave)

View file

@ -112,6 +112,7 @@ def make_help_toplevel(app, title=None):
from pysollib.winsystems import init_root_window
window = tkinter.Toplevel(class_=TITLE)
init_root_window(window, app)
window.tkraise()
return window

View file

@ -1,6 +1,6 @@
"""
Usage:
python setup.py py2app
Command to create a macOS app bundle:
PYTHONPATH=. python3 setup_osx.py py2app
"""
import os