"""
###############################################################################
Classes that encapsulate top-level interfaces.
Allows same GUI to be main, pop-up, or attached; content classes may inherit
from these directly, or be mixed together with them per usage mode; may also
be called directly without a subclass; designed to be mixed in after (further
to the right than) app-specific classes: else, subclass gets methods here
(destroy, okayToQuit), instead of from app-specific classes--can't redefine.
###############################################################################
"""
import os, glob
from tkinter import Tk, Toplevel, Frame, YES, BOTH, RIDGE
from tkinter.messagebox import showinfo, askyesno
class _window:
"""
mixin shared by main and pop-up windows
"""
foundicon = None # shared by all inst
iconpatt = '*.ico' # may be reset
iconmine = 'py.ico'
def configBorders(self, app, kind, iconfile):
if not iconfile: # no icon passed?
iconfile = self.findIcon() # try curr,tool dirs
title = app
if kind: title += ' - ' + kind
self.title(title) # on window border
self.iconname(app) # when minimized
if iconfile:
try:
self.iconbitmap(iconfile) # window icon image
except: # bad py or platform
pass
self.protocol('WM_DELETE_WINDOW', self.quit) # don't close silent
def findIcon(self):
if _window.foundicon: # already found one?
return _window.foundicon
iconfile = None # try curr dir first
iconshere = glob.glob(self.iconpatt) # assume just one
if iconshere: # del icon for red Tk
iconfile = iconshere[0]
else: # try tools dir icon
mymod = __import__(__name__) # import self for dir
path = __name__.split('.') # poss a package path
for mod in path[1:]: # follow path to end
mymod = getattr(mymod, mod) # only have leftmost
mydir = os.path.dirname(mymod.__file__)
myicon = os.path.join(mydir, self.iconmine) # use myicon, not tk
if os.path.exists(myicon): iconfile = myicon
_window.foundicon = iconfile # don't search again
return iconfile
class MainWindow(Tk, _window):
"""
when run in main top-level window
"""
def init(self, app, kind='', iconfile=None):
self.findIcon()
Tk.init(self)
self.__app = app
self.configBorders(app, kind, iconfile)
def quit(self):
if self.okayToQuit(): # threads running?
if askyesno(self.__app, 'Verify Quit Program?'):
self.destroy() # quit whole app
else:
showinfo(self.__app, 'Quit not allowed') # or in okayToQuit?
def destroy(self): # exit app silently
Tk.quit(self) # redef if exit ops
def okayToQuit(self): # redef me if used
return True # e.g., thread busy
class PopupWindow(Toplevel, _window):
"""
when run in secondary pop-up window
"""
def init(self, app, kind='', iconfile=None):
Toplevel.init(self)
self.__app = app
self.configBorders(app, kind, iconfile)
def quit(self): # redef me to change
if askyesno(self.__app, 'Verify Quit Window?'): # or call destroy
self.destroy() # quit this window
def destroy(self): # close win silently
Toplevel.destroy(self) # redef for close ops
class QuietPopupWindow(PopupWindow):
def quit(self):
self.destroy() # don't verify close
class ComponentWindow(Frame):
"""
when attached to another display
"""
def init(self, parent): # if not a frame
Frame.init(self, parent) # provide container
self.pack(expand=YES, fill=BOTH)
self.config(relief=RIDGE, border=2) # reconfig to change
def quit(self):
showinfo('Quit', 'Not supported in attachment mode')
# destroy from Frame: erase frame silent # redef for close ops
must import windows to test, else name is main in findIcon
from tkinter import Button, mainloop
def _selftest():
# mixin usage
class content:
"same code used as a Tk, Toplevel, and Frame"
def __init__(self):
Button(self, text='Larch', command=self.quit).pack()
Button(self, text='Sing ', command=self.destroy).pack()
class contentmix(MainWindow, content):
def __init__(self):
MainWindow.__init__(self, 'mixin', 'Main')
content.__init__(self)
contentmix()
class contentmix(PopupWindow, content):
def __init__(self):
PopupWindow.__init__(self, 'mixin', 'Popup')
content.__init__(self)
prev = contentmix()
class contentmix(ComponentWindow, content):
def __init__(self): # nested frame
ComponentWindow.__init__(self, prev) # on prior window
content.__init__(self) # Sing erases frame
contentmix()
# subclass usage
class contentsub(PopupWindow):
def __init__(self):
PopupWindow.__init__(self, 'popup', 'subclass')
Button(self, text='Pine', command=self.quit).pack()
Button(self, text='Sing', command=self.destroy).pack()
contentsub()
# non-class usage
win = PopupWindow('popup', 'attachment')
Button(win, text='Redwood', command=win.quit).pack()
Button(win, text='Sing ', command=win.destroy).pack()
mainloop()
if name == 'main':
_selftest()