summaryrefslogtreecommitdiff
path: root/jfr_playoff/gui/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'jfr_playoff/gui/__init__.py')
-rw-r--r--jfr_playoff/gui/__init__.py304
1 files changed, 304 insertions, 0 deletions
diff --git a/jfr_playoff/gui/__init__.py b/jfr_playoff/gui/__init__.py
new file mode 100644
index 0000000..37d6cef
--- /dev/null
+++ b/jfr_playoff/gui/__init__.py
@@ -0,0 +1,304 @@
+#coding=utf-8
+
+import codecs, copy, json, os, sys, tempfile, threading, traceback, webbrowser
+from collections import OrderedDict
+import logging as log
+
+import tkinter as tk
+from tkinter import ttk
+import tkFileDialog as tkfd
+import tkMessageBox as tkmb
+
+from jfr_playoff.filemanager import PlayoffFileManager
+from jfr_playoff.generator import PlayoffGenerator
+from jfr_playoff.settings import PlayoffSettings
+
+from .tabs import *
+from .icons import GuiImage
+from .frames import LabelButton, NumericSpinbox
+from .variables import NumericVar
+from .logframe import LogWindow
+
+class PlayoffGUI(tk.Tk):
+ def __init__(self):
+ tk.Tk.__init__(self)
+ ttk.Style().configure('TLabelframe.Label', foreground='black')
+ ttk.Style().configure('TLabelframe', padding=5)
+ self.geometry('920x640')
+ self.iconbitmap(GuiImage.get_path('icons', 'playoff', 'ico'))
+ self.tabs = {}
+ self.logWindow = LogWindow(self)
+ self.logWindow.title('Dziennik komunikatów')
+ self.logWindow.iconbitmap(GuiImage.get_path('icons', 'playoff', 'ico'))
+ self._buildMenu()
+ self.newFileIndex = 0
+ self._title = tk.StringVar()
+ self._title.trace('w', self._setTitle)
+ self._dirty = tk.BooleanVar()
+ self._dirty.trace('w', self._setTitle)
+ self._dirty.trace('w', self._setMenuButtons)
+ self._runTimer = None
+ self._runtimeError = None
+ self._filepath = None
+ self.protocol('WM_DELETE_WINDOW', self.onClose)
+
+ def run(self):
+ self.notebook = ttk.Notebook(self)
+ self.notebook.pack(fill=tk.BOTH, expand=True)
+ for tab in tabs.__all__:
+ self.tabs[tab] = globals()[tab](self.notebook)
+ self.notebook.add(self.tabs[tab], text=self.tabs[tab].title)
+ if len(sys.argv) > 1:
+ self.openFile(sys.argv[1])
+ else:
+ self.newFile()
+ self.bind('<<ValueChanged>>', self._onFileChange, add='+')
+ self.bind('<<BracketGenerated>>', self._onBracketGenerated, add='+')
+ self.bind('<<BracketError>>', self._onBracketError, add='+')
+ self.mainloop()
+
+ def _onFileChange(self, *args):
+ self._dirty.set(True)
+
+ def _checkSave(self):
+ if self._dirty.get():
+ if tkmb.askyesno(
+ 'Zapisz zmiany',
+ 'Czy chcesz zapisać zmiany w bieżącej drabince?'):
+ self.onSave()
+
+ def _setTitle(self, *args):
+ self.title('%s - %s%s' % (
+ 'TeamyPlayOff',
+ self._title.get(),
+ ' *' if self._dirty.get() else ''
+ ))
+
+ def _setMenuButtons(self, *args):
+ self.menuButtons['save'].configure(
+ state=tk.NORMAL if self._dirty.get() else tk.DISABLED)
+
+ def _setValues(self, config):
+ for tab in self.tabs.values():
+ tab.setValues(config)
+
+ def _resetValues(self):
+ self._setValues({})
+
+ def _buildMenu(self):
+ menu = tk.Frame(self)
+ menu.pack(side=tk.TOP, fill=tk.X)
+ statusBar = ttk.Label(menu)
+ statusBar.pack(side=tk.RIGHT)
+ self.menuButtons = {}
+ for icon, command, tooltip in [
+ ('new', self.onNewFile, 'Nowa drabinka...'),
+ ('open', self.onFileOpen, 'Otwórz drabinkę...'),
+ ('save', self.onSave, 'Zapisz'),
+ ('saveas', self.onSaveAs, 'Zapisz jako...')]:
+ self.menuButtons[icon] = LabelButton(
+ menu, image=GuiImage.get_icon(icon), command=command,
+ tooltip=tooltip, label=statusBar)
+ self.menuButtons[icon].pack(side=tk.LEFT)
+ (ttk.Separator(menu, orient=tk.VERTICAL)).pack(
+ side=tk.LEFT, fill=tk.Y, padx=3, pady=1)
+ for icon, command, tooltip in [
+ ('run-once', self.onRunOnce, 'Wygeneruj')]:
+ self.menuButtons[icon] = LabelButton(
+ menu, image=GuiImage.get_icon(icon), command=command,
+ tooltip=tooltip, label=statusBar)
+ self.menuButtons[icon].pack(side=tk.LEFT)
+ self.runningLabel = ttk.Label(menu, width=10, text='')
+ self.runningLabel.pack(side=tk.LEFT)
+ (ttk.Separator(menu, orient=tk.VERTICAL)).pack(
+ side=tk.LEFT, fill=tk.Y, padx=3, pady=1)
+ for icon, command, tooltip in [
+ ('run-timed', self.onRunTimed, 'Generuj co X sekund')]:
+ self.menuButtons[icon] = LabelButton(
+ menu, image=GuiImage.get_icon(icon), command=command,
+ tooltip=tooltip, label=statusBar)
+ self.menuButtons[icon].pack(side=tk.LEFT)
+ self.interval = NumericVar()
+ self.intervalField = NumericSpinbox(
+ menu, width=5,
+ textvariable=self.interval, from_=30, to=3600)
+ self.intervalField.pack(side=tk.LEFT)
+ self.intervalLabel = ttk.Label(menu, text='sekund')
+ self.intervalLabel.pack(side=tk.LEFT)
+ (ttk.Separator(menu, orient=tk.VERTICAL)).pack(
+ side=tk.LEFT, fill=tk.Y, padx=3, pady=1)
+ for icon, command, tooltip in [
+ ('log', self.onLogWindowOpen, 'Dziennik komunikatów')]:
+ self.menuButtons[icon] = LabelButton(
+ menu, image=GuiImage.get_icon(icon), command=command,
+ tooltip=tooltip, label=statusBar)
+ self.menuButtons[icon].pack(side=tk.LEFT)
+
+ def onNewFile(self):
+ self._checkSave()
+ self.newFile()
+
+ def onFileOpen(self):
+ self._checkSave()
+ filename = tkfd.askopenfilename(
+ title='Wybierz plik drabniki',
+ filetypes=(('JFR Teamy Play-Off files', '*.jtpo'),
+ ('JSON files', '*.json'),))
+ if filename:
+ self.openFile(filename)
+
+ def onSave(self):
+ if self._filepath is not None:
+ self.saveFile(self._filepath)
+ else:
+ self.onSaveAs()
+
+ def onSaveAs(self):
+ filename = tkfd.asksaveasfilename(
+ title='Wybierz plik drabniki',
+ filetypes=(('JFR Teamy Play-Off files', '*.jtpo'),
+ ('JSON files', '*.json'),))
+ if filename:
+ if not filename.lower().endswith('.jtpo'):
+ filename = filename + '.jtpo'
+ self.saveFile(filename)
+
+ def onClose(self, *args):
+ self._checkSave()
+ self.destroy()
+
+ def _run(self, config, interactive=True):
+ self._interactive = interactive
+ try:
+ tempPath = None
+ if not len(config.get('output', '')):
+ tempDir = tempfile.mkdtemp(prefix='jfrplayoff-')
+ tempPath = os.path.join(
+ tempDir, next(tempfile._get_candidate_names()))
+ config['output'] = tempPath + '.html'
+ self._outputPath = config['output']
+ settings = PlayoffSettings(config_obj=config)
+ generator = PlayoffGenerator(settings)
+ content = generator.generate_content()
+ file_manager = PlayoffFileManager(settings)
+ file_manager.write_content(content)
+ file_manager.copy_scripts()
+ file_manager.copy_styles()
+ file_manager.send_files()
+ self.event_generate('<<BracketGenerated>>', when='tail')
+ if tempPath is not None:
+ os.remove(config['output'])
+ except Exception as e:
+ log.getLogger().error(str(e))
+ traceback.print_exc()
+ if interactive:
+ self._runtimeError = e
+ self.event_generate('<<BracketError>>', when='tail')
+
+ def _onBracketGenerated(self, *args):
+ self._setRunWidgetState(tk.NORMAL)
+ if self._interactive:
+ if tkmb.askyesno(
+ 'Otwórz drabinkę',
+ 'Otworzyć drabinkę w domyślnej przeglądarce?'):
+ webbrowser.open(self._outputPath)
+
+ def _onBracketError(self, *args):
+ tkmb.showerror('Błąd generowania drabinki', str(self._runtimeError))
+ self._setRunWidgetState(tk.NORMAL)
+ self._runtimeError = None
+
+ def _setRunWidgetState(self, state):
+ self.menuButtons['run-once'].configure(state=state)
+ self.runningLabel.configure(
+ text='' if state == tk.NORMAL else 'pracuję...')
+
+ def _setTimerWidgetState(self, state):
+ for widget in [self.intervalField, self.intervalLabel]:
+ widget.configure(state=state)
+ self.menuButtons['run-timed'].configure(
+ image=GuiImage.get_icon('run-timed')
+ if state == tk.NORMAL else GuiImage.get_icon('stop-timed'))
+
+ def onRunOnce(self, interactive=True):
+ self._setRunWidgetState(tk.DISABLED)
+ if not interactive:
+ self._runTimer = self.after(
+ 1000 * self.interval.get(default=30), self.onRunOnce, False)
+ config = self.getConfig()
+ thread = threading.Thread(
+ target=self._run, args=(config, interactive,))
+ thread.start()
+
+ def onRunTimed(self):
+ if self._runTimer is None:
+ self.after(100, self.onRunOnce, False)
+ self._setTimerWidgetState(tk.DISABLED)
+ else:
+ self.after_cancel(self._runTimer)
+ self._runTimer = None
+ self._setTimerWidgetState(tk.NORMAL)
+
+ def onLogWindowOpen(self):
+ self.logWindow.update()
+ self.logWindow.deiconify()
+
+ def newFile(self):
+ self._filepath = None
+ self.newFileIndex += 1
+ self._title.set('Nowa drabinka %d' % (self.newFileIndex))
+ self._resetValues()
+ self.after(0, self._dirty.set, False)
+
+ def openFile(self, filepath):
+ self._filepath = filepath
+ self._title.set(os.path.basename(filepath))
+ self._setValues(json.load(open(filepath)))
+ self.after(0, self._dirty.set, False)
+
+ def saveFile(self, filepath):
+ json.dump(
+ self.getConfig(), codecs.open(filepath, 'w', encoding='utf8'),
+ indent=4, ensure_ascii=False)
+ self._filepath = filepath
+ self._title.set(os.path.basename(filepath))
+ self.after(0, self._dirty.set, False)
+
+ def getConfig(self):
+ config = OrderedDict()
+ for tab in self.tabs.values():
+ tabConfig = tab.getConfig()
+ if tabConfig is not None:
+ config = self._mergeConfig(config, tab.getConfig())
+ return config
+
+ def _mergeConfig(self, base, update):
+ result = copy.copy(base)
+ for key, value in update.iteritems():
+ if key in result:
+ if isinstance(result[key], dict):
+ result[key] = self._mergeConfig(
+ result[key], update[key])
+ else:
+ result[key] = update[key]
+ else:
+ result[key] = value
+ return result
+
+ def getDbConfig(self):
+ return self.tabs['NetworkTab'].getDB()
+
+ def getTeams(self):
+ return self.tabs['TeamsTab'].getTeams()
+
+ def getDBs(self):
+ return self.tabs['NetworkTab'].getDBList()
+
+ def getMatches(self):
+ return self.tabs['MatchesTab'].getMatches()
+
+ def getNewMatchID(self, match):
+ matches = self.tabs['MatchesTab'].getMatches()
+ if len(matches) > 0:
+ return max([m.getMatchID() for m in matches]) + 1
+ return 1