File: pyedit-products/unzipped/PP4E/Gui/Tools/windows.py
"""
###############################################################################
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, sys
from tkinter import Tk, Toplevel, Frame, PhotoImage, YES, BOTH, RIDGE
from tkinter.messagebox import showinfo, askyesno
class _window:
"""
-------------------------------------------------------------------
mixin shared by main and pop-up windows
-------------------------------------------------------------------
"""
# share by all instances, may be reset
# Dec2015: support Linux app bar icons, not just Windows icons
# May2017: Mac uses icons alot, but requires a full app bundle
foundicon = None
iconpatt = '*.ico' if sys.platform.startswith('win') else '*.gif'
iconmine = 'py.ico' if sys.platform.startswith('win') else 'py.gif'
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:
if sys.platform.startswith('win'): # Windows all contexts
self.iconbitmap(iconfile) # window border icon
elif sys.platform.startswith('linux'):
imgobj = PhotoImage(file=iconfile) # Dec2015: try Linux
self.iconphoto(True, imgobj) # app bar icon
self.saveiconimg = imgobj # still need a ref?
elif sys.platform.startswith('darwin') or True:
raise NotImplementedError # [4.0] Sep2016: Mac OS X
# Mac TBD, neither yet
except Exception as why:
pass # bad file or platform
self.protocol('WM_DELETE_WINDOW', self.quit) # don't close silently
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
"""
# [4.0] see windows-notes.txt: this is overkill
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__)
"""
mydir = os.path.dirname(__file__) # import not needed!
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
note that destroy() on the sole/last Tk quits the entire
aplication, and is the same as a quit() on any widget;
Jan-2017: add quit info, for extra details text; used by
PyMailGUI to note number of windows with unsaved changes;
Feb-2017: add onNo quit callback because caller can't know
if return signals 'No' reply or not okayToQuit(); now
used by PyMailGUI to lift windows with unsaved changes;
Apr-2017: add withdraw to hide while building to avoid
flashes; caller must deiconify() later to show window;
-------------------------------------------------------------------
"""
def __init__(self, app, kind='', iconfile=None, withdraw=False):
self.findIcon()
Tk.__init__(self)
if withdraw:
self.withdraw() # [Apr17] see above
self.__app = app
self.configBorders(app, kind, iconfile)
def quit(self, info='', onNo=lambda: None):
if self.okayToQuit(): # threads running?
prompt = 'Verify Program Exit?' # [Jan17] add info
if askyesno(self.__app, info + prompt):
self.destroy() # quit whole app
else:
onNo() # [Feb17] add onNo
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
Jan-2017: because the original version of this class uses no
explicit parent (master) arg #1, the root Tk() (possibly a default)
that endures for the full program's run is the new window's parent;
this ensures that the new window isn't silently closed along with
a transient parent; if you require popup auto-closure with a
transient parent, pass the parent to the new closewith argument;
Feb-2017: added onNo callback for Quit, because caller may need
to process window without knowing if it has been closed or not;
used by PyMailGUI to refocus on text area after dialog closures;
Apr-2017: add withdraw to hide while building to avoid flashes;
at startup caller must deiconify() later to unhide/show window;
-------------------------------------------------------------------
"""
def __init__(self, app, kind='', iconfile=None, closewith=None, withdraw=False):
if not closewith:
Toplevel.__init__(self) # original, default
else:
Toplevel.__init__(self, closewith) # [Jan17] see above
if withdraw:
self.withdraw() # [Apr17] see above
self.__app = app
self.configBorders(app, kind, iconfile)
def quit(self, info='', onNo=lambda: None): # redef me to change
prompt = 'Verify Window Close?' # [Jan17] add info
if askyesno(self.__app, info + prompt): # or call destroy
self.destroy() # quit this window
else:
onNo() # [Feb17] add onNo
def destroy(self): # close win silently
Toplevel.destroy(self) # redef for close ops
# ex: pymailgui view
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