""" ############################################################################### An extended Frame that makes window menus and toolbars automatically. Use GuiMakerFrameMenu for embedded components (makes frame-based menus). Use GuiMakerWindowMenu for top-level windows (makes Tk8.0 window menus). See the self-test code (and PyEdit) for an example layout tree format. ############################################################################### """
import sys from tkinter import * # widget classes from tkinter.messagebox import showinfo
class GuiMaker(Frame): menuBar = [] # class defaults toolBar = [] # change per instance in subclasses helpButton = True # set these in start() if need self
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill=BOTH) # make frame stretchable
self.start() # for subclass: set menu/toolBar
self.makeMenuBar() # done here: build menu bar
self.makeToolBar() # done here: build toolbar
self.makeWidgets() # for subclass: add middle part
def makeMenuBar(self):
"""
make menu bar at the top (Tk8.0 menus below)
expand=no, fill=x so same width on resize
"""
menubar = Frame(self, relief=RAISED, bd=2)
menubar.pack(side=TOP, fill=X)
for (name, key, items) in self.menuBar:
mbutton = Menubutton(menubar, text=name, underline=key)
mbutton.pack(side=LEFT)
pulldown = Menu(mbutton)
self.addMenuItems(pulldown, items)
mbutton.config(menu=pulldown)
if self.helpButton:
Button(menubar, text = 'Help',
cursor = 'gumby',
relief = FLAT,
command = self.help).pack(side=RIGHT)
def addMenuItems(self, menu, items):
for item in items: # scan nested items list
if item == 'separator': # string: add separator
menu.add_separator({})
elif type(item) == list: # list: disabled item list
for num in item:
menu.entryconfig(num, state=DISABLED)
elif type(item[2]) != list:
menu.add_command(label = item[0], # command:
underline = item[1], # add command
command = item[2]) # cmd=callable
else:
pullover = Menu(menu)
self.addMenuItems(pullover, item[2]) # sublist:
menu.add_cascade(label = item[0], # make submenu
underline = item[1], # add cascade
menu = pullover)
def makeToolBar(self):
"""
make button bar at bottom, if any
expand=no, fill=x so same width on resize
this could support images too: see Chapter 9,
would need prebuilt gifs or PIL for thumbnails
"""
if self.toolBar:
toolbar = Frame(self, cursor='hand2', relief=SUNKEN, bd=2)
toolbar.pack(side=BOTTOM, fill=X)
for (name, action, where) in self.toolBar:
Button(toolbar, text=name, command=action).pack(where)
def makeWidgets(self):
"""
make 'middle' part last, so menu/toolbar
is always on top/bottom and clipped last;
override this default, pack middle any side;
for grid: grid middle part in a packed frame
"""
name = Label(self,
width=40, height=10,
relief=SUNKEN, bg='white',
text = self.__class__.__name__,
cursor = 'crosshair')
name.pack(expand=YES, fill=BOTH, side=TOP)
def help(self):
"override me in subclass"
showinfo('Help', 'Sorry, no help for ' + self.__class__.__name__)
def start(self):
"override me in subclass: set menu/toolbar with self"
pass
###############################################################################
###############################################################################
GuiMakerFrameMenu = GuiMaker # use this for embedded component menus
class GuiMakerWindowMenu(GuiMaker): # use this for top-level window menus def makeMenuBar(self): menubar = Menu(self.master) self.master.config(menu=menubar)
for (name, key, items) in self.menuBar:
pulldown = Menu(menubar)
self.addMenuItems(pulldown, items)
menubar.add_cascade(label=name, underline=key, menu=pulldown)
if self.helpButton:
if sys.platform[:3] == 'win':
menubar.add_command(label='Help', command=self.help)
else:
pulldown = Menu(menubar) # Linux needs real pull down
pulldown.add_command(label='About', command=self.help)
menubar.add_cascade(label='Help', menu=pulldown)
###############################################################################
###############################################################################
if name == 'main': from guimixin import GuiMixin # mix in a help method
menuBar = [
('File', 0,
[('Open', 0, lambda:0), # lambda:0 is a no-op
('Quit', 0, sys.exit)]), # use sys, no self here
('Edit', 0,
[('Cut', 0, lambda:0),
('Paste', 0, lambda:0)]) ]
toolBar = [('Quit', sys.exit, {'side': LEFT})]
class TestAppFrameMenu(GuiMixin, GuiMakerFrameMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar
class TestAppWindowMenu(GuiMixin, GuiMakerWindowMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar
class TestAppWindowMenuBasic(GuiMakerWindowMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar # guimaker help, not guimixin
root = Tk()
TestAppFrameMenu(Toplevel())
TestAppWindowMenu(Toplevel())
TestAppWindowMenuBasic(root)
root.mainloop()
///guimaker 和guimixin 使用例子 """ GUI demo implementation - combines maker, mixin, and this """
import sys, os from tkinter import * # widget classes from PP4E.Gui.Tools.guimixin import * # mix-in methods: quit, spawn, etc. from PP4E.Gui.Tools.guimaker import * # frame, plus menu/toolbar builder
class Hello(GuiMixin, GuiMakerWindowMenu): # or GuiMakerFrameMenu def start(self): self.hellos = 0 self.master.title("GuiMaker Demo") self.master.iconname("GuiMaker") def spawnme(): self.spawn('big_gui.py') # defer call vs lambda
self.menuBar = [ # a tree: 3 pull downs
('File', 0, # (pull-down)
[('New...', 0, spawnme),
('Open...', 0, self.fileOpen), # [menu items list]
('Quit', 0, self.quit)] # label,underline,action
),
('Edit', 0,
[('Cut', -1, self.notdone), # no underline|action
('Paste', -1, self.notdone), # lambda:0 works too
'separator', # add a separator
('Stuff', -1,
[('Clone', -1, self.clone), # cascaded submenu
('More', -1, self.more)]
),
('Delete', -1, lambda:0),
[5]] # disable 'delete'
),
('Play', 0,
[('Hello', 0, self.greeting),
('Popup...', 0, self.dialog),
('Demos', 0,
[('Toplevels', 0,
lambda: self.spawn(r'..\Tour\toplevel2.py')),
('Frames', 0,
lambda: self.spawn(r'..\Tour\demoAll-frm-ridge.py')),
('Images', 0,
lambda: self.spawn(r'..\Tour\buttonpics.py')),
('Alarm', 0,
lambda: self.spawn(r'..\Tour\alarm.py', wait=False)),
('Other...', -1, self.pickDemo)]
)]
)]
self.toolBar = [ # add 3 buttons
('Quit', self.quit, dict(side=RIGHT)), # or {'side': RIGHT}
('Hello', self.greeting, dict(side=LEFT)),
('Popup', self.dialog, dict(side=LEFT, expand=YES)) ]
def makeWidgets(self): # override default
middle = Label(self, text='Hello maker world!', # middle of window
width=40, height=10,
relief=SUNKEN, cursor='pencil', bg='white')
middle.pack(expand=YES, fill=BOTH)
def greeting(self):
self.hellos += 1
if self.hellos % 3:
print("hi")
else:
self.infobox("Three", 'HELLO!') # on every third press
def dialog(self):
button = self.question('OOPS!',
'You typed "rm*" ... continue?', # old style
'questhead', ('yes', 'no')) # args ignored
[lambda: None, self.quit][button]()
def fileOpen(self):
pick = self.selectOpenFile(file='big_gui.py')
if pick:
self.browser(pick) # browse my source file, or other
def more(self):
new = Toplevel()
Label(new, text='A new non-modal window').pack()
Button(new, text='Quit', command=self.quit).pack(side=LEFT)
Button(new, text='More', command=self.more).pack(side=RIGHT)
def pickDemo(self):
pick = self.selectOpenFile(dir='..')
if pick:
self.spawn(pick) # spawn any Python program
if name == 'main': Hello().mainloop() # make one, run one