mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 10:26:02 +00:00 
			
		
		
		
	 3d569fd6dc
			
		
	
	
		3d569fd6dc
		
			
		
	
	
	
	
		
			
			* Tkinter functions and constructors which need a default root window raise now RuntimeError with descriptive message instead of obscure AttributeError or NameError if it is not created yet or cannot be created automatically. * Add tests for all functions which use default root window. * Fix import in the pynche script.
		
			
				
	
	
		
			313 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """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 *
 | ||
| from tkinter import messagebox, filedialog
 | ||
| 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
 | ||
|             # _default_root is None or it is unset.
 | ||
|             tkroot = getattr(tkinter, '_default_root', None)
 | ||
|             if not tkroot:
 | ||
|                 tkroot = Tk(className='Pynche')
 | ||
|             self.__tkroot = tkroot
 | ||
|             # 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__
 | ||
|         messagebox.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 = filedialog.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:
 | ||
|                 messagebox.showerror('Read error', '''\
 | ||
| Could not open file for reading:
 | ||
| %s''' % file)
 | ||
|                 continue
 | ||
|             if colordb is None:
 | ||
|                 messagebox.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()
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| import functools
 | ||
| @functools.total_ordering
 | ||
| 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 __eq__(self, other):
 | ||
|         if isinstance(self, PopupViewer):
 | ||
|             return self.__menutext == other.__menutext
 | ||
|         return NotImplemented
 | ||
| 
 | ||
|     def __lt__(self, other):
 | ||
|         if isinstance(self, PopupViewer):
 | ||
|             return self.__menutext < other.__menutext
 | ||
|         return NotImplemented
 | ||
| 
 | ||
| 
 | ||
| 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
 |