From 2efa8b15abd451c31b377ff3de975ade8d24516c Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 21 Jun 2019 18:13:07 +0200 Subject: Position/team selection buttons and frames made reusable and extendable --- jfr_playoff/gui/frames/__init__.py | 89 +++++++++++++++++++++++++ jfr_playoff/gui/frames/match.py | 32 ++++++++- jfr_playoff/gui/frames/team.py | 130 ++++++++++++------------------------- jfr_playoff/gui/frames/visual.py | 13 ++-- 4 files changed, 168 insertions(+), 96 deletions(-) diff --git a/jfr_playoff/gui/frames/__init__.py b/jfr_playoff/gui/frames/__init__.py index 9665663..4c4950e 100644 --- a/jfr_playoff/gui/frames/__init__.py +++ b/jfr_playoff/gui/frames/__init__.py @@ -4,6 +4,7 @@ from functools import partial import tkinter as tk from tkinter import ttk +import tkMessageBox def getIntVal(widget, default=0): try: @@ -219,3 +220,91 @@ class WidgetSelectionFrame(ScrollableFrame): if self.callback is not None: self.callback(self.value.get()) self.winfo_toplevel().destroy() + +class SelectionButton(ttk.Button): + @property + def defaultPrompt(self): + pass + + @property + def title(self): + pass + + @property + def errorMessage(self): + pass + + def getOptions(self): + pass + + def __init__(self, *args, **kwargs): + for arg in ['callback', 'prompt', 'dialogclass']: + setattr(self, arg, kwargs[arg] if arg in kwargs else None) + if arg in kwargs: + del kwargs[arg] + kwargs['command'] = self._choosePositions + if self.prompt is None: + self.prompt = self.defaultPrompt + ttk.Button.__init__(self, *args, **kwargs) + self.setPositions([]) + + def setPositions(self, values): + self.selected = values + self.configure( + text='[wybrano: %d]' % (len(values))) + if self.callback is not None: + self.callback(values) + + def _choosePositions(self): + options = self.getOptions() + if not len(options): + tkMessageBox.showerror( + self.title, self.errorMessage) + self.setPositions([]) + else: + dialog = tk.Toplevel(self) + dialog.title(self.title) + dialog.grab_set() + dialog.focus_force() + selectionFrame = self.dialogclass( + dialog, title=self.prompt, + options=options, + selected=lambda idx, option: idx+1 in self.selected, + callback=self.setPositions, vertical=True) + selectionFrame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + +class SelectionFrame(ScrollableFrame): + + def renderOption(self, container, option, idx): + pass + + def __init__(self, master, title='', options=[], + selected=None, callback=None, *args, **kwargs): + self.values = [] + self.title = title + self.options = options + self.selected = selected + self.callback = callback + ScrollableFrame.__init__(self, master=master, *args, **kwargs) + (ttk.Button(master, text='Zapisz', command=self._save)).pack( + side=tk.BOTTOM, fill=tk.Y) + + def _save(self): + if self.callback: + self.callback( + [idx+1 for idx, value + in enumerate(self.values) if value.get()]) + self.master.destroy() + + def renderHeader(self, container): + container.columnconfigure(1, weight=1) + (ttk.Label(container, text=self.title)).grid( + row=0, column=0, columnspan=2) + + def renderContent(self, container): + self.renderHeader(container) + for idx, option in enumerate(self.options): + self.values.append(tk.IntVar()) + self.renderOption(container, option, idx) + if self.selected and self.selected(idx, option): + self.values[idx].set(True) diff --git a/jfr_playoff/gui/frames/match.py b/jfr_playoff/gui/frames/match.py index 9c7dfc7..922e4cf 100644 --- a/jfr_playoff/gui/frames/match.py +++ b/jfr_playoff/gui/frames/match.py @@ -6,7 +6,9 @@ from tkinter import ttk from ..frames import GuiFrame, RepeatableFrame, ScrollableFrame from ..frames import WidgetRepeater, RepeatableEntry, getIntVal -from ..frames.team import DBSelectionField +from ..frames import SelectionFrame, SelectionButton +from ..frames.team import DBSelectionField, TeamList, TeamSelectionButton +from ..frames.visual import PositionsSelectionFrame class SwissSettingsFrame(RepeatableFrame): SOURCE_LINK = 0 @@ -139,3 +141,31 @@ class SwissesFrame(ScrollableFrame): self.swisses.pack(side=tk.TOP, fill=tk.BOTH, expand=True) __all__ = ['SwissesFrame'] + +class MatchSelectionButton(SelectionButton): + @property + def defaultPrompt(self): + return 'Wybierz mecze:' + + @property + def title(self): + return 'Wybór meczów' + + @property + def errorMessage(self): + return 'W turnieju nie zdefiniowano żadnych meczów' + + def getOptions(self): + return self.winfo_toplevel().getMatches() + + +class MatchSelectionFrame(SelectionFrame): + def renderOption(self, container, option, idx): + (ttk.Label(container, text='[%d]' % (idx+1))).grid( + row=idx+1, column=0) + (ttk.Checkbutton( + container, text='Mecz nr %d' % (option.getMatchID()), + variable=self.values[idx] + )).grid(row=idx+1, column=1, sticky=tk.W) + + diff --git a/jfr_playoff/gui/frames/team.py b/jfr_playoff/gui/frames/team.py index 6ae5ad3..9056070 100644 --- a/jfr_playoff/gui/frames/team.py +++ b/jfr_playoff/gui/frames/team.py @@ -3,10 +3,10 @@ import tkinter as tk from tkinter.font import Font from tkinter import ttk -import tkMessageBox from ..frames import GuiFrame, RepeatableFrame, ScrollableFrame from ..frames import WidgetRepeater, RepeatableEntry +from ..frames import SelectionButton, SelectionFrame from ..frames import getIntVal, setPanelState class ManualTeamRow(RepeatableFrame): @@ -53,84 +53,30 @@ class TeamManualSettingsFrame(GuiFrame): def getTeams(self): return [val for val in self.repeater.getValue() if len(val[0].strip())] -class TeamSelectionFrame(ScrollableFrame): - def __init__(self, master, title='', teams=[], - selected=None, callback=None, *args, **kwargs): - self.values = [] - self.title = title - self.teams = teams - self.selected = selected - self.callback = callback - ScrollableFrame.__init__(self, master=master, *args, **kwargs) - (ttk.Button(master, text='Zapisz', command=self._save)).pack( - side=tk.BOTTOM, fill=tk.Y) - - def _save(self): - if self.callback: - self.callback( - [idx+1 for idx, value - in enumerate(self.values) if value.get()]) - self.master.destroy() - - def renderHeader(self, container): - container.columnconfigure(1, weight=1) - (ttk.Label(container, text=self.title)).grid( - row=0, column=0, columnspan=2) - - def renderTeam(self, container, team, idx): +class TeamSelectionFrame(SelectionFrame): + def renderOption(self, container, option, idx): (ttk.Label(container, text='[%d]' % (idx+1))).grid( row=idx+1, column=0) (ttk.Checkbutton( - container, text=team[0], + container, text=option[0], variable=self.values[idx] )).grid(row=idx+1, column=1, sticky=tk.W) - def renderContent(self, container): - self.renderHeader(container) - for idx, team in enumerate(self.teams): - self.values.append(tk.IntVar()) - self.renderTeam(container, team, idx) - if self.selected and self.selected(idx, team): - self.values[idx].set(True) - -class TeamSelectionButton(ttk.Button): - def __init__(self, *args, **kwargs): - for arg in ['callback', 'prompt', 'dialogclass']: - setattr(self, arg, kwargs[arg] if arg in kwargs else None) - if arg in kwargs: - del kwargs[arg] - kwargs['command'] = self._choosePositions - if self.dialogclass is None: - self.dialogclass = TeamSelectionFrame - if self.prompt is None: - self.prompt = 'Wybierz teamy:' - ttk.Button.__init__(self, *args, **kwargs) - self.setPositions([]) - - def setPositions(self, values): - self.selected = values - self.configure( - text='[wybrano: %d]' % (len(values))) - if self.callback is not None: - self.callback(values) - - def _choosePositions(self): - teams = self.winfo_toplevel().getTeams() - if not len(teams): - tkMessageBox.showerror( - 'Wybór teamów', 'W turnieju nie ma teamów do wyboru') - self._setFinishingPositions([]) - else: - dialog = tk.Toplevel(self) - dialog.title('Wybór teamów') - dialog.grab_set() - dialog.focus_force() - selectionFrame = self.dialogclass( - dialog, title=self.prompt, - teams=teams, - selected=lambda idx, team: idx+1 in self.selected, - callback=self.setPositions, vertical=True) - selectionFrame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) +class TeamSelectionButton(SelectionButton): + @property + def prompt(self): + return 'Wybierz teamy:' + + @property + def title(self): + return 'Wybór teamów' + + @property + def errorMessage(self): + return 'W turnieju nie ma teamów do wyboru' + + def getOptions(self): + return self.winfo_toplevel().getTeams() class DBSelectionField(ttk.Entry): @@ -268,7 +214,8 @@ class TeamFetchSettingsFrame(GuiFrame): finishingPositionsBtn = TeamSelectionButton( self, callback=self._setFinishingPositions, prompt='Wybierz teamy, które zakończyły rozgrywki ' + \ - 'na swojej pozycji:') + 'na swojej pozycji:', + dialogclass=TeamSelectionFrame) finishingPositionsBtn.grid(row=3, column=3, sticky=tk.W) finishingPositionsBtn.setPositions([]) @@ -329,34 +276,39 @@ class TeamSettingsFrame(ScrollableFrame): return self.fetchSettingsFrame.getTeams() return [] +class TeamList(ttk.OptionMenu): + def __init__(self, *args, **kwargs): + ttk.OptionMenu.__init__(self, *args, **kwargs) + self.winfo_toplevel().bind( + '<>', self._refreshTeams, add='+') + self._refreshTeams(None) + self.configure(width=10) + + def _refreshTeams(self, event): + oldValue = self._variable.get() + options = [team[0] for team in self.winfo_toplevel().getTeams()] + self['menu'].delete(0, tk.END) + for option in options: + self['menu'].add_command( + label=option, command=tk._setit(self._variable, option)) + if oldValue not in options: + self._variable.set('') + class TeamAliasRow(RepeatableFrame): def renderContent(self): self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.teamName = tk.StringVar() - self._createList([]) + (TeamList(self, self.teamName, self.teamName.get())).grid( + row=0, column=0, sticky=tk.W+tk.E+tk.N) self.names = WidgetRepeater(self, RepeatableEntry) self.names.grid(row=0, column=1, sticky=tk.W+tk.E) - self.winfo_toplevel().bind( - '<>', self.refreshTeams, add='+') - self.refreshTeams(None) - - def _createList(self, options): - if self.teamName.get() not in options: - self.teamName.set('') - self.teamList = ttk.OptionMenu( - self, self.teamName, self.teamName.get(), *options) - self.teamList.grid(row=0, column=0, sticky=tk.W+tk.E+tk.N) def getValue(self): return ( self.teamName.get().strip(), [val.strip() for val in self.names.getValue()]) - def refreshTeams(self, event): - options = [team[0] for team in self.winfo_toplevel().getTeams()] - self.teamList.destroy() - self._createList(options) class TeamAliasFrame(ScrollableFrame): def renderContent(self, container): diff --git a/jfr_playoff/gui/frames/visual.py b/jfr_playoff/gui/frames/visual.py index 6ef7bf6..eea87f0 100644 --- a/jfr_playoff/gui/frames/visual.py +++ b/jfr_playoff/gui/frames/visual.py @@ -6,7 +6,8 @@ import tkColorChooser as tkcc from ..frames import GuiFrame, RepeatableFrame, ScrollableFrame from ..frames import WidgetRepeater -from ..frames.team import TeamSelectionButton, TeamSelectionFrame +from ..frames import SelectionFrame +from ..frames.team import TeamSelectionButton class VisualSettingsFrame(GuiFrame): def renderContent(self): @@ -232,22 +233,22 @@ class LineStylesFrame(GuiFrame): (ttk.Label(self, text='Kolory linii')).grid( row=0, column=0, columnspan=2, sticky=tk.W) -class FinalPositionsSelectionFrame(TeamSelectionFrame): +class PositionsSelectionFrame(SelectionFrame): COLUMN_COUNT=10 def __init__(self, *args, **kwargs): - TeamSelectionFrame.__init__(self, *args, **kwargs) + SelectionFrame.__init__(self, *args, **kwargs) self.winfo_toplevel().geometry( '%dx%d' % ( self.COLUMN_COUNT * 40, - (len(self.teams) / self.COLUMN_COUNT + 2) * 25 + 30 + (len(self.options) / self.COLUMN_COUNT + 2) * 25 + 30 )) def renderHeader(self, container): (ttk.Label(container, text=self.title)).grid( row=0, column=0, columnspan=self.COLUMN_COUNT, sticky=tk.W) - def renderTeam(self, container, team, idx): + def renderOption(self, container, option, idx): (ttk.Checkbutton( container, text=str(idx+1), variable=self.values[idx] @@ -266,7 +267,7 @@ class PositionStyleFrame(RepeatableFrame): (ttk.Label(self, text='Pozycje końcowe:')).grid(row=0, column=2) self.positionBtn = TeamSelectionButton( self, prompt='Wybierz pozycje końcowe:', - dialogclass=FinalPositionsSelectionFrame, + dialogclass=PositionsSelectionFrame, callback=self._setPositions) self.positionBtn.grid(row=0, column=3) (ttk.Label(self, text='Opis w legendzie:')).grid(row=0, column=4) -- cgit v1.2.3