| """Main Pynche (Pythonically Natural Color and Hue Editor) widget. | |
| This window provides the basic decorations, primarily including the menubar. | |
| It is used to bring up other windows. | |
| """ | |
| import sys | |
| import os | |
| from Tkinter import * | |
| import tkMessageBox | |
| import tkFileDialog | |
| import ColorDB | |
| # Milliseconds between interrupt checks | |
| KEEPALIVE_TIMER = 500 | |
| class PyncheWidget: | |
| def __init__(self, version, switchboard, master=None, extrapath=[]): | |
| self.__sb = switchboard | |
| self.__version = version | |
| self.__textwin = None | |
| self.__listwin = None | |
| self.__detailswin = None | |
| self.__helpwin = None | |
| self.__dialogstate = {} | |
| modal = self.__modal = not not master | |
| # If a master was given, we are running as a modal dialog servant to | |
| # some other application. We rearrange our UI in this case (there's | |
| # no File menu and we get `Okay' and `Cancel' buttons), and we do a | |
| # grab_set() to make ourselves modal | |
| if modal: | |
| self.__tkroot = tkroot = Toplevel(master, class_='Pynche') | |
| tkroot.grab_set() | |
| tkroot.withdraw() | |
| else: | |
| # Is there already a default root for Tk, say because we're | |
| # running under Guido's IDE? :-) Two conditions say no, either the | |
| # import fails or _default_root is None. | |
| tkroot = None | |
| try: | |
| from Tkinter import _default_root | |
| tkroot = self.__tkroot = _default_root | |
| except ImportError: | |
| pass | |
| if not tkroot: | |
| tkroot = self.__tkroot = Tk(className='Pynche') | |
| # but this isn't our top level widget, so make it invisible | |
| tkroot.withdraw() | |
| # create the menubar | |
| menubar = self.__menubar = Menu(tkroot) | |
| # | |
| # File menu | |
| # | |
| filemenu = self.__filemenu = Menu(menubar, tearoff=0) | |
| filemenu.add_command(label='Load palette...', | |
| command=self.__load, | |
| underline=0) | |
| if not modal: | |
| filemenu.add_command(label='Quit', | |
| command=self.__quit, | |
| accelerator='Alt-Q', | |
| underline=0) | |
| # | |
| # View menu | |
| # | |
| views = make_view_popups(self.__sb, self.__tkroot, extrapath) | |
| viewmenu = Menu(menubar, tearoff=0) | |
| for v in views: | |
| viewmenu.add_command(label=v.menutext(), | |
| command=v.popup, | |
| underline=v.underline()) | |
| # | |
| # Help menu | |
| # | |
| helpmenu = Menu(menubar, name='help', tearoff=0) | |
| helpmenu.add_command(label='About Pynche...', | |
| command=self.__popup_about, | |
| underline=0) | |
| helpmenu.add_command(label='Help...', | |
| command=self.__popup_usage, | |
| underline=0) | |
| # | |
| # Tie them all together | |
| # | |
| menubar.add_cascade(label='File', | |
| menu=filemenu, | |
| underline=0) | |
| menubar.add_cascade(label='View', | |
| menu=viewmenu, | |
| underline=0) | |
| menubar.add_cascade(label='Help', | |
| menu=helpmenu, | |
| underline=0) | |
| # now create the top level window | |
| root = self.__root = Toplevel(tkroot, class_='Pynche', menu=menubar) | |
| root.protocol('WM_DELETE_WINDOW', | |
| modal and self.__bell or self.__quit) | |
| root.title('Pynche %s' % version) | |
| root.iconname('Pynche') | |
| # Only bind accelerators for the File->Quit menu item if running as a | |
| # standalone app | |
| if not modal: | |
| root.bind('<Alt-q>', self.__quit) | |
| root.bind('<Alt-Q>', self.__quit) | |
| else: | |
| # We're a modal dialog so we have a new row of buttons | |
| bframe = Frame(root, borderwidth=1, relief=RAISED) | |
| bframe.grid(row=4, column=0, columnspan=2, | |
| sticky='EW', | |
| ipady=5) | |
| okay = Button(bframe, | |
| text='Okay', | |
| command=self.__okay) | |
| okay.pack(side=LEFT, expand=1) | |
| cancel = Button(bframe, | |
| text='Cancel', | |
| command=self.__cancel) | |
| cancel.pack(side=LEFT, expand=1) | |
| def __quit(self, event=None): | |
| self.__tkroot.quit() | |
| def __bell(self, event=None): | |
| self.__tkroot.bell() | |
| def __okay(self, event=None): | |
| self.__sb.withdraw_views() | |
| self.__tkroot.grab_release() | |
| self.__quit() | |
| def __cancel(self, event=None): | |
| self.__sb.canceled() | |
| self.__okay() | |
| def __keepalive(self): | |
| # Exercise the Python interpreter regularly so keyboard interrupts get | |
| # through. | |
| self.__tkroot.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive) | |
| def start(self): | |
| if not self.__modal: | |
| self.__keepalive() | |
| self.__tkroot.mainloop() | |
| def window(self): | |
| return self.__root | |
| def __popup_about(self, event=None): | |
| from Main import __version__ | |
| tkMessageBox.showinfo('About Pynche ' + __version__, | |
| '''\ | |
| Pynche %s | |
| The PYthonically Natural | |
| Color and Hue Editor | |
| For information | |
| contact: Barry A. Warsaw | |
| email: bwarsaw@python.org''' % __version__) | |
| def __popup_usage(self, event=None): | |
| if not self.__helpwin: | |
| self.__helpwin = Helpwin(self.__root, self.__quit) | |
| self.__helpwin.deiconify() | |
| def __load(self, event=None): | |
| while 1: | |
| idir, ifile = os.path.split(self.__sb.colordb().filename()) | |
| file = tkFileDialog.askopenfilename( | |
| filetypes=[('Text files', '*.txt'), | |
| ('All files', '*'), | |
| ], | |
| initialdir=idir, | |
| initialfile=ifile) | |
| if not file: | |
| # cancel button | |
| return | |
| try: | |
| colordb = ColorDB.get_colordb(file) | |
| except IOError: | |
| tkMessageBox.showerror('Read error', '''\ | |
| Could not open file for reading: | |
| %s''' % file) | |
| continue | |
| if colordb is None: | |
| tkMessageBox.showerror('Unrecognized color file type', '''\ | |
| Unrecognized color file type in file: | |
| %s''' % file) | |
| continue | |
| break | |
| self.__sb.set_colordb(colordb) | |
| def withdraw(self): | |
| self.__root.withdraw() | |
| def deiconify(self): | |
| self.__root.deiconify() | |
| class Helpwin: | |
| def __init__(self, master, quitfunc): | |
| from Main import docstring | |
| self.__root = root = Toplevel(master, class_='Pynche') | |
| root.protocol('WM_DELETE_WINDOW', self.__withdraw) | |
| root.title('Pynche Help Window') | |
| root.iconname('Pynche Help Window') | |
| root.bind('<Alt-q>', quitfunc) | |
| root.bind('<Alt-Q>', quitfunc) | |
| root.bind('<Alt-w>', self.__withdraw) | |
| root.bind('<Alt-W>', self.__withdraw) | |
| # more elaborate help is available in the README file | |
| readmefile = os.path.join(sys.path[0], 'README') | |
| try: | |
| fp = None | |
| try: | |
| fp = open(readmefile) | |
| contents = fp.read() | |
| # wax the last page, it contains Emacs cruft | |
| i = contents.rfind('\f') | |
| if i > 0: | |
| contents = contents[:i].rstrip() | |
| finally: | |
| if fp: | |
| fp.close() | |
| except IOError: | |
| sys.stderr.write("Couldn't open Pynche's README, " | |
| 'using docstring instead.\n') | |
| contents = docstring() | |
| self.__text = text = Text(root, relief=SUNKEN, | |
| width=80, height=24) | |
| self.__text.focus_set() | |
| text.insert(0.0, contents) | |
| scrollbar = Scrollbar(root) | |
| scrollbar.pack(fill=Y, side=RIGHT) | |
| text.pack(fill=BOTH, expand=YES) | |
| text.configure(yscrollcommand=(scrollbar, 'set')) | |
| scrollbar.configure(command=(text, 'yview')) | |
| def __withdraw(self, event=None): | |
| self.__root.withdraw() | |
| def deiconify(self): | |
| self.__root.deiconify() | |
| class PopupViewer: | |
| def __init__(self, module, name, switchboard, root): | |
| self.__m = module | |
| self.__name = name | |
| self.__sb = switchboard | |
| self.__root = root | |
| self.__menutext = module.ADDTOVIEW | |
| # find the underline character | |
| underline = module.ADDTOVIEW.find('%') | |
| if underline == -1: | |
| underline = 0 | |
| else: | |
| self.__menutext = module.ADDTOVIEW.replace('%', '', 1) | |
| self.__underline = underline | |
| self.__window = None | |
| def menutext(self): | |
| return self.__menutext | |
| def underline(self): | |
| return self.__underline | |
| def popup(self, event=None): | |
| if not self.__window: | |
| # class and module must have the same name | |
| class_ = getattr(self.__m, self.__name) | |
| self.__window = class_(self.__sb, self.__root) | |
| self.__sb.add_view(self.__window) | |
| self.__window.deiconify() | |
| def __cmp__(self, other): | |
| return cmp(self.__menutext, other.__menutext) | |
| def make_view_popups(switchboard, root, extrapath): | |
| viewers = [] | |
| # where we are in the file system | |
| dirs = [os.path.dirname(__file__)] + extrapath | |
| for dir in dirs: | |
| if dir == '': | |
| dir = '.' | |
| for file in os.listdir(dir): | |
| if file[-9:] == 'Viewer.py': | |
| name = file[:-3] | |
| try: | |
| module = __import__(name) | |
| except ImportError: | |
| # Pynche is running from inside a package, so get the | |
| # module using the explicit path. | |
| pkg = __import__('pynche.'+name) | |
| module = getattr(pkg, name) | |
| if hasattr(module, 'ADDTOVIEW') and module.ADDTOVIEW: | |
| # this is an external viewer | |
| v = PopupViewer(module, name, switchboard, root) | |
| viewers.append(v) | |
| # sort alphabetically | |
| viewers.sort() | |
| return viewers |