#!/usr/bin/env python3 # Copyright © 2012-13 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. It is provided for # educational purposes and is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. import os import sys import tkinter as tk import tkinter.ttk as ttk Spinbox = ttk.Spinbox if hasattr(ttk, "Spinbox") else tk.Spinbox if __name__ == "__main__": # For stand-alone testing with parallel TkUtil sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import Board import Shapes import TkUtil import TkUtil.Dialog from Globals import * class Window(TkUtil.Dialog.Dialog): def __init__(self, master, options): self.options = options board = self.options.board self.columns = tk.StringVar() self.columns.set(board.columns) self.rows = tk.StringVar() self.rows.set(board.rows) self.maxColors = tk.StringVar() self.maxColors.set(board.maxColors) self.delay = tk.StringVar() self.delay.set(board.delay) self.restore = tk.BooleanVar() self.restore.set(self.options.restore) self.showToolbar = tk.BooleanVar() self.showToolbar.set(self.options.showToolbar) super().__init__(master, "Preferences — {}".format(APPNAME), TkUtil.Dialog.OK_BUTTON|TkUtil.Dialog.CANCEL_BUTTON) def body(self, master): self.notebook = ttk.Notebook(master) self.notebook.enable_traversal() self.generalFrame = ttk.Frame(self.notebook) self.create_general_widgets(self.generalFrame) self.layout_general_widgets(self.generalFrame) self.advancedFrame = ttk.Frame(self.notebook) self.create_advanced_widgets(self.advancedFrame) self.layout_advanced_widgets(self.advancedFrame) self.notebook.add(self.generalFrame, text=GENERAL, underline=-1 if TkUtil.mac() else 0, padding=PAD) self.notebook.add(self.advancedFrame, text=ADVANCED, underline=-1 if TkUtil.mac() else 0, padding=PAD) self.notebook.pack(fill=tk.BOTH) self.create_bindings() return self.notebook def create_general_widgets(self, master): self.shapeLabel = TkUtil.Label(master, text="Shape", underline=0) self.shapeCombobox = ttk.Combobox(master, textvariable=self.options.shapeName, state="readonly", values=sorted(Shapes.ShapeForName.keys())) self.columnsLabel = TkUtil.Label(master, text="Columns", underline=2) self.columnsSpinbox = Spinbox(master, textvariable=self.columns, from_=Board.MIN_COLUMNS, to=Board.MAX_COLUMNS, width=3, justify=tk.RIGHT, validate="all") self.columnsSpinbox.config(validatecommand=( self.columnsSpinbox.register(self.validate_int), "columnsSpinbox", "%P")) self.rowsLabel = TkUtil.Label(master, text="Rows", underline=0) self.rowsSpinbox = Spinbox(master, textvariable=self.rows, from_=Board.MIN_ROWS, to=Board.MAX_ROWS, width=3, justify=tk.RIGHT, validate="all") self.rowsSpinbox.config(validatecommand=( self.rowsSpinbox.register(self.validate_int), "rowsSpinbox", "%P")) self.maxColorsLabel = TkUtil.Label(master, text="Max. Colors", underline=0) self.maxColorsSpinbox = Spinbox(master, textvariable=self.maxColors, from_=Board.MIN_MAX_COLORS, to=Board.MAX_MAX_COLORS, width=3, justify=tk.RIGHT, validate="all") self.maxColorsSpinbox.config(validatecommand=( self.maxColorsSpinbox.register(self.validate_int), "maxColorsSpinbox", "%P")) def layout_general_widgets(self, master): pad = dict(padx=PAD, pady=PAD) self.shapeLabel.grid(row=0, column=0, sticky=tk.W, **pad) self.shapeCombobox.grid(row=0, column=1, sticky=tk.W, columnspan=4, **pad) ttk.Separator(master).grid(row=1, column=0, columnspan=99, sticky=(tk.W, tk.E)) self.columnsLabel.grid(row=2, column=0, sticky=tk.W, **pad) self.columnsSpinbox.grid(row=2, column=1, sticky=tk.W, **pad) self.rowsLabel.grid(row=2, column=3, sticky=tk.E, **pad) self.rowsSpinbox.grid(row=2, column=4, sticky=tk.E, **pad) self.maxColorsLabel.grid(row=3, column=0, sticky=tk.W, **pad) self.maxColorsSpinbox.grid(row=3, column=1, sticky=tk.W, **pad) def validate_int(self, spinboxName, number): return TkUtil.validate_spinbox_int(getattr(self, spinboxName), number) def create_advanced_widgets(self, master): self.delayLabel = TkUtil.Label(master, text="Delay (ms)", underline=0) self.delaySpinbox = Spinbox(master, textvariable=self.delay, from_=Board.MIN_DELAY, to=Board.MAX_DELAY, increment=25, width=4, justify=tk.RIGHT, validate="all") self.delaySpinbox.config(validatecommand=( self.delaySpinbox.register(self.validate_int), "delaySpinbox", "%P")) self.zoomLabel = TkUtil.Label(master, text="Zoom (%)", underline=0) self.zoomSpinbox = Spinbox(master, textvariable=self.options.zoom, from_=Board.MIN_ZOOM, to=Board.MAX_ZOOM, increment=Board.ZOOM_INC, width=4, justify=tk.RIGHT, validate="all") self.zoomSpinbox.config(validatecommand=( self.zoomSpinbox.register(self.validate_int), "zoomSpinbox", "%P")) self.showToolbarCheckbutton = TkUtil.Checkbutton(master, text="Show Toolbar", underline=5, variable=self.showToolbar) self.restoreCheckbutton = TkUtil.Checkbutton(master, text="Restore Position", underline=0, variable=self.restore) def layout_advanced_widgets(self, master): pad = dict(padx=PAD, pady=PAD) options = dict(sticky=tk.W, **pad) self.delayLabel.grid(row=0, column=0, **options) self.delaySpinbox.grid(row=0, column=1, **options) self.zoomLabel.grid(row=1, column=0, **options) self.zoomSpinbox.grid(row=1, column=1, **options) self.showToolbarCheckbutton.grid(row=2, column=0, sticky=(tk.W, tk.E), columnspan=2, **pad) self.restoreCheckbutton.grid(row=3, column=0, sticky=(tk.W, tk.E), columnspan=2, **pad) def create_bindings(self): if not TkUtil.mac(): for letter in "dlmrstz": self.bind("<Alt-{}>".format(letter), self.handle_shortcut) # Don't need these thanks to Notebook.enable_traversal() #self.bind("<Alt-a>", # lambda event: self.notebook.select(self.advancedFrame)) #self.bind("<Alt-g>", # lambda event: self.notebook.select(self.generalFrame)) def handle_shortcut(self, event): key = event.keysym tabName = self.notebook.tab(self.notebook.select(), "text") methodForTabAndKey = { (GENERAL, "l"): self.columnsSpinbox.focus, (GENERAL, "m"): self.maxColorsSpinbox.focus, (GENERAL, "r"): self.rowsSpinbox.focus, (GENERAL, "s"): self.shapeCombobox.focus, (ADVANCED, "d"): self.delaySpinbox.focus, (ADVANCED, "r"): self.restoreCheckbutton.invoke, (ADVANCED, "t"): self.showToolbarCheckbutton.invoke, (ADVANCED, "z"): self.zoomSpinbox.focus} method = methodForTabAndKey.get((tabName, key)) if method is not None: method() def apply(self): columns = int(self.columns.get()) rows = int(self.rows.get()) maxColors = int(self.maxColors.get()) board = self.options.board newGame = (columns != board.columns or rows != board.rows or maxColors != board.maxColors) board.delay = int(self.delay.get()) self.options.showToolbar = bool(self.showToolbar.get()) self.options.restore = bool(self.restore.get()) self.options.ok = True if newGame: board.columns = columns board.rows = rows board.maxColors = maxColors board.new_game() if __name__ == "__main__": if sys.stdout.isatty(): import Options def close(event): window.destroy() board.destroy() application.quit() application = tk.Tk() shapeName = tk.StringVar() shapeName.set("Square") zoom = tk.StringVar() zoom.set(100) scoreText = tk.StringVar() board = Board.Board(application, zoom, shapeName, print, scoreText) options = Options.Options(False, True, False, shapeName, zoom, board) window = Window(application, options) if window.ok: print(options) application.bind("<Escape>", close) application.mainloop() else: print("Loaded OK")