clear branch
This commit is contained in:
parent
f0e2de1c2f
commit
c290e39a1a
|
|
@ -1,356 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Application class - PyObjC
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import os, sys, traceback
|
||||
import objc
|
||||
from Foundation import NSObject, NSBundle, NSDefaultRunLoopMode, NSData, NSDate
|
||||
import AppKit
|
||||
from AppKit import NSApplication, NSResponder, NSScreen, NSMenu, NSMenuItem, \
|
||||
NSKeyDown, NSKeyUp, NSMouseMoved, NSLeftMouseDown, NSSystemDefined, \
|
||||
NSCommandKeyMask, NSPasteboard, NSStringPboardType, NSModalPanelRunLoopMode
|
||||
NSAnyEventMask = 0xffffffff
|
||||
from GUI import Globals, GApplications
|
||||
from GUI import application, export
|
||||
from GUI.GApplications import Application as GApplication
|
||||
from GUI import Event
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Globals.ns_screen_height = None
|
||||
Globals.ns_last_mouse_moved_event = None
|
||||
Globals.pending_exception = None
|
||||
Globals.ns_application = None
|
||||
|
||||
ns_distant_future = NSDate.distantFuture()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Application(GApplication):
|
||||
# _ns_app _PyGui_NSApplication
|
||||
# _ns_pasteboard NSPasteboard
|
||||
# _ns_key_window Window
|
||||
|
||||
_ns_menubar_update_pending = False
|
||||
_ns_files_opened = False
|
||||
_ns_using_clargs = False
|
||||
_ns_menus_updated = False
|
||||
|
||||
def __init__(self, **kwds):
|
||||
self._ns_app = Globals.ns_application
|
||||
self._ns_app.pygui_app = self
|
||||
self._ns_pasteboard = NSPasteboard.generalPasteboard()
|
||||
self._ns_key_window = None
|
||||
GApplication.__init__(self, **kwds)
|
||||
self.ns_init_application_name()
|
||||
|
||||
def destroy(self):
|
||||
del self.menus[:]
|
||||
import Windows
|
||||
Windows._ns_zombie_window = None
|
||||
self._ns_app.pygui_app = None
|
||||
self._ns_app = None
|
||||
self._ns_pasteboard = None
|
||||
GApplication.destroy(self)
|
||||
|
||||
def set_menus(self, menu_list):
|
||||
GApplication.set_menus(self, menu_list)
|
||||
self._update_menubar()
|
||||
|
||||
def _update_menubar(self):
|
||||
ns_app = self._ns_app
|
||||
ns_menubar = NSMenu.alloc().initWithTitle_("")
|
||||
menu_list = self._effective_menus()
|
||||
for menu in menu_list:
|
||||
ns_item = NSMenuItem.alloc()
|
||||
ns_item.initWithTitle_action_keyEquivalent_(menu.title, '', "")
|
||||
ns_menubar.addItem_(ns_item)
|
||||
ns_menu = menu._ns_menu
|
||||
# An NSMenu can only be a submenu of one menu at a time, so
|
||||
# remove it from the old menubar if necessary.
|
||||
old_supermenu = ns_menu.supermenu()
|
||||
if old_supermenu:
|
||||
i = old_supermenu.indexOfItemWithSubmenu_(ns_menu)
|
||||
old_supermenu.removeItemAtIndex_(i)
|
||||
ns_menubar.setSubmenu_forItem_(ns_menu, ns_item)
|
||||
# The menu you pass to setAppleMenu_ must *also* be a member of the
|
||||
# main menu.
|
||||
ns_app.setMainMenu_(ns_menubar)
|
||||
if menu_list:
|
||||
ns_app_menu = menu_list[0]._ns_menu
|
||||
ns_app.setAppleMenu_(ns_app_menu)
|
||||
|
||||
def handle_next_event(self, modal_window = None):
|
||||
ns_app = self._ns_app
|
||||
if modal_window:
|
||||
ns_mode = NSModalPanelRunLoopMode
|
||||
ns_modal_window = modal_window._ns_window
|
||||
else:
|
||||
ns_mode = NSDefaultRunLoopMode
|
||||
ns_modal_window = None
|
||||
ns_event = ns_app.nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
NSAnyEventMask, ns_distant_future, ns_mode, True)
|
||||
if ns_event:
|
||||
ns_window = ns_event.window()
|
||||
if not ns_window or not ns_modal_window or ns_window == ns_modal_window:
|
||||
ns_app.sendEvent_(ns_event)
|
||||
|
||||
def get_target_window(self):
|
||||
# NSApplication.keyWindow() isn't reliable enough. We keep track
|
||||
# of the key window ourselves.
|
||||
return self._ns_key_window
|
||||
|
||||
def zero_windows_allowed(self):
|
||||
return 1
|
||||
|
||||
def query_clipboard(self):
|
||||
pb = self._ns_pasteboard
|
||||
pb_types = pb.types()
|
||||
return NSStringPboardType in pb_types
|
||||
|
||||
def get_clipboard(self):
|
||||
pb = self._ns_pasteboard
|
||||
ns_data = pb.dataForType_(NSStringPboardType)
|
||||
if ns_data:
|
||||
return ns_data.bytes().tobytes()
|
||||
|
||||
def set_clipboard(self, data):
|
||||
ns_data = NSData.dataWithBytes_length_(data, len(data))
|
||||
pb = self._ns_pasteboard
|
||||
pb.clearContents()
|
||||
pb.setData_forType_(ns_data, NSStringPboardType)
|
||||
|
||||
def setup_menus(self, m):
|
||||
m.hide_app_cmd.enabled = True
|
||||
m.hide_other_apps_cmd.enabled = True
|
||||
m.show_all_apps_cmd.enabled = True
|
||||
if not self._ns_app.modalWindow():
|
||||
GApplication.setup_menus(self, m)
|
||||
|
||||
def process_args(self, args):
|
||||
# Note: When using py2app, argv_emulation should be disabled.
|
||||
if args and args[0].startswith("-psn"):
|
||||
# Launched from MacOSX Finder -- wait for file open/app launch messages
|
||||
pass
|
||||
else:
|
||||
# Not launched from Finder or using argv emulation
|
||||
self._ns_using_clargs = True
|
||||
GApplication.process_args(self, args)
|
||||
|
||||
def run(self, fast_exit = True):
|
||||
try:
|
||||
GApplication.run(self)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
except:
|
||||
traceback.print_exc()
|
||||
# A py2app bundled application seems to crash on exit if we don't
|
||||
# bail out really quickly here (Python 2.3, PyObjC 1.3.7, py2app 0.2.1,
|
||||
# MacOSX 10.4.4)
|
||||
if fast_exit:
|
||||
os._exit(0)
|
||||
|
||||
def event_loop(self):
|
||||
self._ns_app.run()
|
||||
|
||||
def _quit(self):
|
||||
self._quit_flag = True
|
||||
self._ns_app.stop_(self._ns_app)
|
||||
|
||||
def hide_app_cmd(self):
|
||||
self._ns_app.hide_(self)
|
||||
|
||||
def hide_other_apps_cmd(self):
|
||||
self._ns_app.hideOtherApplications_(self)
|
||||
|
||||
def show_all_apps_cmd(self):
|
||||
self._ns_app.unhideAllApplications_(self)
|
||||
|
||||
def ns_process_key_event(self, ns_event):
|
||||
# Perform menu setup before command-key events.
|
||||
# Send non-command key events to associated window if any,
|
||||
# otherwise pass them to the pygui application. This is necessary
|
||||
# because otherwise there is no way of receiving key events when
|
||||
# there are no windows.
|
||||
if ns_event.modifierFlags() & NSCommandKeyMask:
|
||||
NSApplication.sendEvent_(self._ns_app, ns_event)
|
||||
else:
|
||||
ns_window = ns_event.window()
|
||||
if ns_window:
|
||||
ns_window.sendEvent_(ns_event)
|
||||
else:
|
||||
event = Event(ns_event)
|
||||
self.handle(event.kind, event)
|
||||
|
||||
def ns_menu_needs_update(self, ns_menu):
|
||||
try:
|
||||
if not self._ns_menus_updated:
|
||||
self._perform_menu_setup()
|
||||
self._ns_menus_updated = True
|
||||
except Exception:
|
||||
self.report_exception()
|
||||
|
||||
def ns_init_application_name(self):
|
||||
# Arrange for the application name to be used as the title
|
||||
# of the application menu.
|
||||
ns_bundle = NSBundle.mainBundle()
|
||||
if ns_bundle:
|
||||
ns_info = ns_bundle.localizedInfoDictionary()
|
||||
if not ns_info:
|
||||
ns_info = ns_bundle.infoDictionary()
|
||||
if ns_info:
|
||||
if ns_info['CFBundleName'] == "Python":
|
||||
#print "GUI.Application: NSBundle infoDictionary =", ns_info ###
|
||||
ns_info['CFBundleName'] = Globals.application_name
|
||||
return
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
_ns_key_event_mask = AppKit.NSKeyDownMask | AppKit.NSKeyUpMask
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class _PyGui_NSApplication(NSApplication):
|
||||
|
||||
pygui_app = None
|
||||
|
||||
def sendEvent_(self, ns_event):
|
||||
# Perform special processing of key events.
|
||||
# Perform menu setup when menu bar is clicked.
|
||||
# Remember the most recent mouse-moved event to use as the
|
||||
# location of event types which do not have a location themselves.
|
||||
if Globals.pending_exception:
|
||||
raise_pending_exception()
|
||||
ns_type = ns_event.type()
|
||||
self.pygui_app._ns_menus_updated = False
|
||||
if (1 << ns_type) & _ns_key_event_mask:
|
||||
self.pygui_app.ns_process_key_event(ns_event)
|
||||
else:
|
||||
if ns_type == NSMouseMoved:
|
||||
Globals.ns_last_mouse_moved_event = ns_event
|
||||
ns_window = ns_event.window()
|
||||
if ns_window:
|
||||
ns_view = ns_window.contentView().hitTest_(ns_event.locationInWindow())
|
||||
if ns_view:
|
||||
ns_view.mouseMoved_(ns_event)
|
||||
else:
|
||||
NSApplication.sendEvent_(self, ns_event)
|
||||
|
||||
def menuNeedsUpdate_(self, ns_menu):
|
||||
self.pygui_app.ns_menu_needs_update(ns_menu)
|
||||
|
||||
def menuSelection_(self, ns_menu_item):
|
||||
try:
|
||||
command = ns_menu_item.representedObject()
|
||||
index = ns_menu_item.tag()
|
||||
if index >= 0:
|
||||
dispatch_to_app(self, command, index)
|
||||
else:
|
||||
dispatch_to_app(self, command)
|
||||
except:
|
||||
self.pygui_app.report_error()
|
||||
|
||||
def validateMenuItem_(self, item):
|
||||
return False
|
||||
|
||||
def undo_(self, sender):
|
||||
dispatch_to_app(self, 'undo_cmd')
|
||||
|
||||
def redo_(self, sender):
|
||||
dispatch_to_app(self, 'redo_cmd')
|
||||
|
||||
def cut_(self, sender):
|
||||
dispatch_to_app(self, 'cut_cmd')
|
||||
|
||||
def copy_(self, sender):
|
||||
dispatch_to_app(self, 'copy_cmd')
|
||||
|
||||
def paste_(self, sender):
|
||||
dispatch_to_app(self, 'paste_cmd')
|
||||
|
||||
def clear_(self, sender):
|
||||
dispatch_to_app(self, 'clear_cmd')
|
||||
|
||||
def selectAll_(self, sender):
|
||||
dispatch_to_app(self, 'select_all_cmd')
|
||||
|
||||
def application_openFile_(self, ns_app, path):
|
||||
app = self.pygui_app
|
||||
if app._ns_using_clargs:
|
||||
return True
|
||||
# Bizarrely, argv[0] gets passed to application_openFile_ under
|
||||
# some circumstances. We don't want to try to open it!
|
||||
if path == sys.argv[0]:
|
||||
return True
|
||||
app._ns_files_opened = True
|
||||
try:
|
||||
app.open_path(path)
|
||||
return True
|
||||
except Exception, e:
|
||||
app.report_error()
|
||||
return False
|
||||
|
||||
def applicationDidFinishLaunching_(self, notification):
|
||||
app = self.pygui_app
|
||||
if app._ns_using_clargs:
|
||||
return
|
||||
try:
|
||||
if not app._ns_files_opened:
|
||||
app.open_app()
|
||||
except Exception, e:
|
||||
app.report_error()
|
||||
return False
|
||||
|
||||
export(Application)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def raise_pending_exception():
|
||||
exc_type, exc_value, exc_tb = Globals.pending_exception
|
||||
Globals.pending_exception = None
|
||||
raise exc_type, exc_value, exc_tb
|
||||
|
||||
def create_ns_application():
|
||||
ns_app = _PyGui_NSApplication.sharedApplication()
|
||||
ns_app.setDelegate_(ns_app)
|
||||
Globals.ns_application = ns_app
|
||||
|
||||
def dispatch_to_app(ns_app, *args):
|
||||
app = ns_app.pygui_app
|
||||
if app:
|
||||
app.dispatch(*args)
|
||||
|
||||
Globals.ns_screen_height = NSScreen.mainScreen().frame().size.height
|
||||
|
||||
create_ns_application()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Disable this for now, since MachSignals.signal segfaults. :-(
|
||||
#
|
||||
#def _install_sigint_handler():
|
||||
# print "_install_sigint_handler" ###
|
||||
# from Foundation import NSRunLoop
|
||||
# run_loop = NSRunLoop.currentRunLoop()
|
||||
# if not run_loop:
|
||||
# print "...No current run loop" ###
|
||||
# sys.exit(1) ###
|
||||
# MachSignals.signal(signal.SIGINT, _sigint_handler)
|
||||
# #from PyObjCTools.AppHelper import installMachInterrupt
|
||||
# #installMachInterrupt()
|
||||
# print "...done" ###
|
||||
#
|
||||
#def _sigint_handler(signum):
|
||||
# print "_sigint_handler" ###
|
||||
# raise KeyboardInterrupt
|
||||
|
||||
#def _install_sigint_handler():
|
||||
# import signal
|
||||
# signal.signal(signal.SIGINT, _raise_keyboard_interrupt)
|
||||
#
|
||||
#def _raise_keyboard_interrupt(signum, frame):
|
||||
# raise KeyboardInterrupt
|
||||
|
||||
#_install_sigint_handler()
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#
|
||||
# Python GUI - Basic alert functions - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import \
|
||||
NSRunAlertPanel, NSRunCriticalAlertPanel, NSRunInformationalAlertPanel
|
||||
|
||||
def alert(kind, prompt, ok_label, **kwds):
|
||||
alert_n(kind, prompt, ok_label, None, None)
|
||||
|
||||
def alert2(kind, prompt, yes_label, no_label, **kwds):
|
||||
return alert_n(kind, prompt, yes_label, no_label, None)
|
||||
|
||||
def alert3(kind, prompt, yes_label, no_label, other_label, **kwds):
|
||||
return alert_n(kind, prompt, yes_label, no_label, other_label)
|
||||
|
||||
def alert_n(kind, prompt, label1, label2, label3):
|
||||
splat = prompt.split("\n", 1)
|
||||
title = splat[0]
|
||||
if len(splat) > 1:
|
||||
msg = splat[1]
|
||||
else:
|
||||
msg = ""
|
||||
if kind == 'caution':
|
||||
return NSRunCriticalAlertPanel(title, msg, label1, label2, label3)
|
||||
elif kind == 'note':
|
||||
return NSRunInformationalAlertPanel(title, msg, label1, label2, label3)
|
||||
else:
|
||||
return NSRunAlertPanel(title, msg, label1, label2, label3)
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#
|
||||
# Python GUI - File selection dialogs - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSOpenPanel, NSSavePanel, NSOKButton
|
||||
from GUI.Files import FileRef
|
||||
from GUI import application
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
def _request_old(prompt, default_dir, file_types, dir, multiple):
|
||||
ns_panel = NSOpenPanel.openPanel()
|
||||
if prompt.endswith(":"):
|
||||
prompt = prompt[:-1]
|
||||
ns_panel.setTitle_(prompt)
|
||||
ns_panel.setCanChooseFiles_(not dir)
|
||||
ns_panel.setCanChooseDirectories_(dir)
|
||||
ns_panel.setAllowsMultipleSelection_(multiple)
|
||||
if default_dir:
|
||||
ns_dir = default_dir.path
|
||||
else:
|
||||
ns_dir = None
|
||||
if file_types:
|
||||
ns_types = []
|
||||
for type in file_types:
|
||||
ns_types.extend(type._ns_file_types())
|
||||
else:
|
||||
ns_types = None
|
||||
result = ns_panel.runModalForDirectory_file_types_(ns_dir, None, ns_types)
|
||||
if result == NSOKButton:
|
||||
if multiple:
|
||||
return [FileRef(path = path) for path in ns_panel.filenames()]
|
||||
else:
|
||||
return FileRef(path = ns_panel.filename())
|
||||
else:
|
||||
return None
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
def _request_new(prompt, default_dir, default_name, file_type, dir):
|
||||
ns_panel = NSSavePanel.savePanel()
|
||||
#if prompt.endswith(":"):
|
||||
# prompt = prompt[:-1]
|
||||
#if prompt.lower().endswith(" as"):
|
||||
# prompt = prompt[:-3]
|
||||
#ns_panel.setTitle_(prompt)
|
||||
#print "_request_new: setting label to", repr(prompt) ###
|
||||
ns_panel.setNameFieldLabel_(prompt)
|
||||
if default_dir:
|
||||
ns_dir = default_dir.path
|
||||
else:
|
||||
ns_dir = None
|
||||
if file_type:
|
||||
suffix = file_type.suffix
|
||||
if suffix:
|
||||
ns_panel.setCanSelectHiddenExtension_(True)
|
||||
if not file_type.mac_type or file_type.mac_force_suffix:
|
||||
ns_panel.setRequiredFileType_(suffix)
|
||||
result = ns_panel.runModalForDirectory_file_(ns_dir, default_name)
|
||||
if result == NSOKButton:
|
||||
return FileRef(path = ns_panel.filename())
|
||||
else:
|
||||
return None
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#
|
||||
# Python GUI - Buttons - PyObjC version
|
||||
#
|
||||
|
||||
import AppKit
|
||||
from GUI import export
|
||||
from GUI.StdFonts import system_font
|
||||
from GUI.ButtonBasedControls import ButtonBasedControl
|
||||
from GUI.GButtons import Button as GButton
|
||||
|
||||
_style_to_ns_key_equivalent = {
|
||||
'default': "\x0d",
|
||||
'cancel': "\x1b",
|
||||
}
|
||||
|
||||
_ns_key_equivalent_to_style = {
|
||||
"\x0d": 'default',
|
||||
"\x1b": 'cancel',
|
||||
}
|
||||
|
||||
class Button(ButtonBasedControl, GButton):
|
||||
|
||||
def __init__(self, title = "New Button", font = system_font, **kwds):
|
||||
ns_button = self._create_ns_button(title = title, font = font,
|
||||
ns_button_type = AppKit.NSMomentaryLight,
|
||||
ns_bezel_style = AppKit.NSRoundedBezelStyle,
|
||||
padding = (10, 2)
|
||||
)
|
||||
GButton.__init__(self, _ns_view = ns_button, **kwds)
|
||||
|
||||
def get_style(self):
|
||||
ns_key = self._ns_view.getKeyEquivalent()
|
||||
return _ns_key_equivalent_to_style.get(ns_key, 'normal')
|
||||
|
||||
def set_style(self, style):
|
||||
ns_key = _style_to_ns_key_equivalent.get(style, "")
|
||||
self._ns_view.setKeyEquivalent_(ns_key)
|
||||
|
||||
def activate(self):
|
||||
self._ns_view.performClick_(None)
|
||||
|
||||
# def key_down(self, e): ###
|
||||
# print "Button.key_down:", e ###
|
||||
|
||||
export(Button)
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - PyObjC version
|
||||
#
|
||||
# Mixin class for controls based on NSButton
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from Foundation import NSMutableDictionary, NSAttributedString
|
||||
from AppKit import NSMutableParagraphStyle, NSFontAttributeName, \
|
||||
NSForegroundColorAttributeName, NSParagraphStyleAttributeName, \
|
||||
NSButton
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler, \
|
||||
ns_set_action, ns_size_to_fit
|
||||
from GUI import Control
|
||||
from GUI.StdColors import black
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class ButtonBasedControl(object):
|
||||
|
||||
_ns_handle_mouse = True
|
||||
|
||||
_color = None
|
||||
|
||||
def _create_ns_button(self, title, font, ns_button_type, ns_bezel_style,
|
||||
padding = (0, 0)):
|
||||
ns_button = PyGUI_NSButton.alloc().init()
|
||||
ns_button.pygui_component = self
|
||||
ns_button.setButtonType_(ns_button_type)
|
||||
ns_button.setBezelStyle_(ns_bezel_style)
|
||||
ns_button.setTitle_(title)
|
||||
ns_button.setFont_(font._ns_font)
|
||||
num_lines = title.count("\n") + 1
|
||||
ns_size_to_fit(ns_button, padding = padding,
|
||||
height = font.line_height * num_lines + 5)
|
||||
ns_set_action(ns_button, 'doAction:')
|
||||
return ns_button
|
||||
|
||||
def set_title(self, title):
|
||||
Control.set_title(self, title)
|
||||
self._ns_update_attributed_title()
|
||||
|
||||
def set_font(self, font):
|
||||
Control.set_font(self, font)
|
||||
self._ns_update_attributed_title()
|
||||
|
||||
def set_just(self, just):
|
||||
Control.set_just(self, just)
|
||||
self._ns_update_attributed_title()
|
||||
|
||||
def get_color(self):
|
||||
if self._color:
|
||||
return self._color
|
||||
else:
|
||||
return black
|
||||
|
||||
def set_color(self, color):
|
||||
self._color = color
|
||||
self._ns_update_attributed_title()
|
||||
|
||||
# There is no direct way of setting the text colour of the title;
|
||||
# it must be done using an attributed string. But when doing
|
||||
# this, the attributes must include the font and alignment
|
||||
# as well. So when using a custom color, we construct a new
|
||||
# attributed string whenever the title, font, alignment or color
|
||||
# is changed.
|
||||
|
||||
def _ns_update_attributed_title(self):
|
||||
if self._color:
|
||||
ns_button = self._ns_view
|
||||
ns_attrs = NSMutableDictionary.alloc().init()
|
||||
ns_attrs[NSFontAttributeName] = ns_button.font()
|
||||
ns_attrs[NSForegroundColorAttributeName] = self._color._ns_color
|
||||
ns_parstyle = NSMutableParagraphStyle.alloc().init()
|
||||
ns_parstyle.setAlignment_(ns_button.alignment())
|
||||
ns_attrs[NSParagraphStyleAttributeName] = ns_parstyle
|
||||
ns_attstr = NSAttributedString.alloc().initWithString_attributes_(
|
||||
ns_button.title(), ns_attrs)
|
||||
ns_button.setAttributedTitle_(ns_attstr)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSButton(NSButton, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
|
@ -1,348 +0,0 @@
|
|||
#
|
||||
# Python GUI - Drawing - PyObjC
|
||||
#
|
||||
|
||||
from array import array
|
||||
from Foundation import NSPoint, NSMakeRect, NSString, NSRect, NSZeroRect
|
||||
from AppKit import NSGraphicsContext, NSBezierPath, NSEvenOddWindingRule, \
|
||||
NSFontAttributeName, NSForegroundColorAttributeName, \
|
||||
NSCompositeCopy, NSCompositeSourceOver, NSAffineTransform
|
||||
from GUI import export
|
||||
from GUI.StdColors import black, white
|
||||
from GUI.GCanvases import Canvas as GCanvas
|
||||
import math
|
||||
import sys
|
||||
|
||||
class Canvas(GCanvas):
|
||||
|
||||
def __init__(self):
|
||||
self._ns_path = NSBezierPath.bezierPath()
|
||||
self._ns_path.setWindingRule_(NSEvenOddWindingRule)
|
||||
self._stack = []
|
||||
ctx = NSGraphicsContext.currentContext()
|
||||
ctx.setCompositingOperation_(NSCompositeSourceOver)
|
||||
GCanvas.__init__(self)
|
||||
self._printing = not ctx.isDrawingToScreen()
|
||||
self.initgraphics()
|
||||
self.transformstack = [[]]
|
||||
|
||||
def get_pencolor(self):
|
||||
return self._pencolor
|
||||
|
||||
def set_pencolor(self, c):
|
||||
self._pencolor = c
|
||||
|
||||
def get_fillcolor(self):
|
||||
return self._fillcolor
|
||||
|
||||
def set_fillcolor(self, c):
|
||||
self._fillcolor = c
|
||||
|
||||
def get_textcolor(self):
|
||||
return self._textcolor
|
||||
|
||||
def set_textcolor(self, c):
|
||||
self._textcolor = c
|
||||
|
||||
def get_backcolor(self):
|
||||
return self._backcolor
|
||||
|
||||
def set_backcolor(self, c):
|
||||
self._backcolor = c
|
||||
|
||||
def get_pensize(self):
|
||||
return self._pensize
|
||||
|
||||
def set_pensize(self, d):
|
||||
self._pensize = d
|
||||
self._ns_path.setLineWidth_(d)
|
||||
|
||||
def get_font(self):
|
||||
return self._font
|
||||
|
||||
def set_font(self, f):
|
||||
self._font = f
|
||||
|
||||
def get_current_point(self):
|
||||
return self._ns_path.currentPoint()
|
||||
|
||||
def newpath(self):
|
||||
self._ns_path.removeAllPoints()
|
||||
self.minx = sys.maxint
|
||||
self.miny = sys.maxint
|
||||
self.maxx = -sys.maxint/2
|
||||
self.maxy = -sys.maxint/2
|
||||
#for i in range(len(self.transformstack)):
|
||||
#j = self.transformstack.pop()
|
||||
#transforms = {"translate":self.translate,"rotate":self.rotate,"scale":self.scale}
|
||||
#transforms[j[0]](*j[1:])
|
||||
|
||||
def moveto(self, x, y):
|
||||
x, y = self._transform(x, y)
|
||||
self.minx = min(self.minx, x)
|
||||
self.miny = min(self.miny, y)
|
||||
self.maxx = max(self.maxx, x)
|
||||
self.maxy = max(self.maxy, y)
|
||||
self._ns_path.moveToPoint_((x, y))
|
||||
|
||||
def rmoveto(self, dx, dy):
|
||||
self._ns_path.relativeMoveToPoint_((dx, dy))
|
||||
|
||||
def lineto(self, x, y):
|
||||
x, y = self._transform(x, y)
|
||||
self.minx = min(self.minx, x)
|
||||
self.miny = min(self.miny, y)
|
||||
self.maxx = max(self.maxx, x)
|
||||
self.maxy = max(self.maxy, y)
|
||||
self._ns_path.lineToPoint_((x, y))
|
||||
|
||||
def rlineto(self, dx, dy):
|
||||
self._ns_path.relativeLineToPoint_((dx, dy))
|
||||
|
||||
def curveto(self, cp1, cp2, ep):
|
||||
cp1 = self._transform(*cp1)
|
||||
cp2 = self._transform(*cp2)
|
||||
ep = self._transform(*ep)
|
||||
self.minx = min(self.minx, cp1[0], cp2[0], ep[0])
|
||||
self.miny = min(self.miny, cp1[1], cp2[1], ep[1])
|
||||
self.maxx = max(self.maxx, cp1[0], cp2[0], ep[0])
|
||||
self.maxy = max(self.maxy, cp1[1], cp2[1], ep[1])
|
||||
self._ns_path.curveToPoint_controlPoint1_controlPoint2_(
|
||||
ep, cp1, cp2)
|
||||
|
||||
def rcurveto(self, cp1, cp2, ep):
|
||||
self._ns_path.relativeCurveToPoint_controlPoint1_controlPoint2_(
|
||||
ep, cp1, cp2)
|
||||
|
||||
def arc(self, c, r, a0, a1):
|
||||
c = self._transform(*c)
|
||||
self._ns_path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(
|
||||
c, r, a0, a1)
|
||||
|
||||
def rect(self, rect):
|
||||
try:
|
||||
rect = self._transform(rect[0],rect[1]) + self._transform(rect[2], rect[3])
|
||||
except:
|
||||
print "Error here - line 113"
|
||||
self._ns_path.appendBezierPathWithRect_(_ns_rect(rect))
|
||||
|
||||
def oval(self, rect):
|
||||
rect = (self._transform(*rect[0]), self._transform(*rect[1]))
|
||||
self._ns_path.appendBezierPathWithOvalInRect(_ns_rect(rect))
|
||||
|
||||
def lines(self, points):
|
||||
# Due to a memory leak in PyObjC 2.3, we need to be very careful
|
||||
# about the type of object that we pass to appendBezierPathWithPoints_count_.
|
||||
# If 'points' is a numpy array, we convert it to an array.array of type 'f',
|
||||
# else we fall back on iterating over the points in Python.
|
||||
# ns = self._ns_path
|
||||
# ns.moveToPoint_(points[0])
|
||||
# ns.appendBezierPathWithPoints_count_(points, len(points))
|
||||
try:
|
||||
p = points.flat
|
||||
except AttributeError:
|
||||
GCanvas.lines(self, points)
|
||||
else:
|
||||
a = array('f', p)
|
||||
ns = self._ns_path
|
||||
ns.moveToPoint_(points[0])
|
||||
ns.appendBezierPathWithPoints_count_(a, len(points))
|
||||
|
||||
|
||||
def poly(self, points):
|
||||
# ns = self._ns_path
|
||||
# ns.moveToPoint_(points[0])
|
||||
# ns.appendBezierPathWithPoints_count_(points, len(points))
|
||||
# ns.closePath()
|
||||
self.lines(points)
|
||||
self.closepath()
|
||||
|
||||
def closepath(self):
|
||||
self._ns_path.closePath()
|
||||
|
||||
def clip(self):
|
||||
ns = self._ns_path
|
||||
ns.addClip()
|
||||
|
||||
def rectclip(self, (l, t, r, b)):
|
||||
ns_rect = NSMakeRect(l, t, r - l, b - t)
|
||||
NSBezierPath.clipRect_(ns_rect)
|
||||
|
||||
def gsave(self):
|
||||
self._stack.append((
|
||||
self._pencolor, self._fillcolor, self._textcolor, self._backcolor,
|
||||
self._pensize, self._font))
|
||||
self.transformstack.append([])
|
||||
NSGraphicsContext.currentContext().saveGraphicsState()
|
||||
|
||||
def grestore(self):
|
||||
(self._pencolor, self._fillcolor, self._textcolor, self._backcolor,
|
||||
self._pensize, self._font) = self._stack.pop()
|
||||
self.transformstack.pop()
|
||||
NSGraphicsContext.currentContext().restoreGraphicsState()
|
||||
|
||||
def stroke(self):
|
||||
ns = self._ns_path
|
||||
self._pencolor._ns_color.set()
|
||||
ns.stroke()
|
||||
|
||||
def fill(self):
|
||||
ns = self._ns_path
|
||||
# self._fillcolor._ns_color.set()
|
||||
if self._fillcolor.image:
|
||||
ns.addClip()
|
||||
self._fillcolor._ns_color.drawInRect_fromRect_operation_fraction_(NSRect((self.minx,self.miny),(self.maxx-self.minx,self.maxy-self.miny)),NSZeroRect,NSCompositeSourceOver,1.0)
|
||||
else:
|
||||
self._fillcolor._ns_color.setFill()
|
||||
ns.fill()
|
||||
|
||||
def erase(self):
|
||||
ns = self._ns_path
|
||||
self._backcolor._ns_color.set()
|
||||
ctx = NSGraphicsContext.currentContext()
|
||||
ctx.setCompositingOperation_(NSCompositeCopy)
|
||||
ns.fill()
|
||||
ctx.setCompositingOperation_(NSCompositeSourceOver)
|
||||
|
||||
def fill_stroke(self):
|
||||
ns = self._ns_path
|
||||
self._pencolor._ns_color.set()
|
||||
ns.stroke()
|
||||
# self._fillcolor._ns_color.set()
|
||||
if self._fillcolor.image:
|
||||
ns.addClip()
|
||||
self._fillcolor._ns_color.drawInRect_fromRect_operation_fraction_(NSRect((self.minx,self.miny),(self.maxx-self.minx,self.maxy-self.miny)),NSZeroRect,NSCompositeSourceOver,1.0)
|
||||
else:
|
||||
self._fillcolor._ns_color.setFill()
|
||||
ns.fill()
|
||||
|
||||
def show_text(self, text):
|
||||
x, y = self._ns_path.currentPoint()
|
||||
font = self._font
|
||||
ns_font = font._ns_font
|
||||
ns_color = self._textcolor._ns_color
|
||||
ns_string = NSString.stringWithString_(text)
|
||||
ns_attrs = {
|
||||
NSFontAttributeName: ns_font,
|
||||
NSForegroundColorAttributeName: ns_color,
|
||||
}
|
||||
# print "Canvas.show_text:", repr(text) ###
|
||||
# print "family:", ns_font.familyName() ###
|
||||
# print "size:", ns_font.pointSize() ###
|
||||
# print "ascender:", ns_font.ascender() ###
|
||||
# print "descender:", ns_font.descender() ###
|
||||
# print "capHeight:", ns_font.capHeight() ###
|
||||
# print "leading:", ns_font.leading() ###
|
||||
# print "matrix:", ns_font.matrix() ###
|
||||
# print "defaultLineHeightForFont:", ns_font.defaultLineHeightForFont() ###
|
||||
h = ns_font.defaultLineHeightForFont()
|
||||
d = -ns_font.descender()
|
||||
dy = h - d
|
||||
if ns_font.familyName() == "Courier New":
|
||||
dy += ns_font.pointSize() * 0.229167
|
||||
ns_point = NSPoint(x, y - dy)
|
||||
#print "drawing at:", ns_point ###
|
||||
ns_string.drawAtPoint_withAttributes_(ns_point, ns_attrs)
|
||||
dx = ns_font.widthOfString_(ns_string)
|
||||
#self._ns_path.relativeMoveToPoint_(NSPoint(x + dx, y))
|
||||
self._ns_path.relativeMoveToPoint_((dx, 0))
|
||||
|
||||
def _ns_frame_rect(self, (l, t, r, b)):
|
||||
p = self._pensize
|
||||
q = 0.5 * p
|
||||
return NSMakeRect(l + q, t + q, r - l - p, b - t - p)
|
||||
|
||||
def stroke_rect(self, r):
|
||||
self._pencolor._ns_color.set()
|
||||
NSBezierPath.setDefaultLineWidth_(self._pensize)
|
||||
NSBezierPath.strokeRect_(_ns_rect(r))
|
||||
|
||||
def frame_rect(self, r):
|
||||
self._pencolor._ns_color.set()
|
||||
NSBezierPath.setDefaultLineWidth_(self._pensize)
|
||||
NSBezierPath.strokeRect_(self._ns_frame_rect(r))
|
||||
|
||||
def fill_rect(self, r):
|
||||
self._fillcolor._ns_color.set()
|
||||
NSBezierPath.fillRect_(_ns_rect(r))
|
||||
|
||||
def erase_rect(self, r):
|
||||
self._backcolor._ns_color.set()
|
||||
NSBezierPath.fillRect_(_ns_rect(r))
|
||||
|
||||
def _ns_oval_path(self, ns_rect):
|
||||
ns_path = NSBezierPath.bezierPathWithOvalInRect_(ns_rect)
|
||||
ns_path.setLineWidth_(self._pensize)
|
||||
return ns_path
|
||||
|
||||
def stroke_oval(self, r):
|
||||
self._pencolor._ns_color.set()
|
||||
self._ns_oval_path(_ns_rect(r)).stroke()
|
||||
|
||||
def frame_oval(self, r):
|
||||
self._pencolor._ns_color.set()
|
||||
self._ns_oval_path(self._ns_frame_rect(r)).stroke()
|
||||
|
||||
def fill_oval(self, r):
|
||||
self._fillcolor._ns_color.set()
|
||||
self._ns_oval_path(_ns_rect(r)).fill()
|
||||
|
||||
def erase_oval(self, r):
|
||||
self._backcolor._ns_color.set()
|
||||
self._ns_oval_path(_ns_rect(r)).fill()
|
||||
|
||||
def _ns_arc_path(self, c, r, sa, ea):
|
||||
ns_path = NSBezierPath.bezierPath()
|
||||
ns_path.setLineWidth_(self._pensize)
|
||||
ns_path.\
|
||||
appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(
|
||||
c, r, sa, ea)
|
||||
return ns_path
|
||||
|
||||
def stroke_arc(self, center, radius, start_angle, arc_angle):
|
||||
ns_path = self._ns_arc_path(center, radius, start_angle, arc_angle)
|
||||
self._pencolor._ns_color.set()
|
||||
ns_path.stroke()
|
||||
|
||||
def frame_arc(self, center, radius, start_angle, arc_angle):
|
||||
r = radius - 0.5 * self._pensize
|
||||
ns_path = self._ns_arc_path(center, r, start_angle, arc_angle)
|
||||
self._pencolor._ns_color.set()
|
||||
ns_path.stroke()
|
||||
|
||||
def translate(self, dx, dy):
|
||||
matrix = NSAffineTransform.transform()
|
||||
matrix.translateXBy_yBy_(dx, dy)
|
||||
self._ns_path.transformUsingAffineTransform_(matrix)
|
||||
self.transformstack[-1].append(["translate",dx,dy])
|
||||
|
||||
def rotate(self, rotation):
|
||||
matrix = NSAffineTransform.transform()
|
||||
matrix.rotateByDegrees_(rotation)
|
||||
self._ns_path.transformUsingAffineTransform_(matrix)
|
||||
self.transformstack[-1].append(["rotate",rotation])
|
||||
|
||||
def scale(self, sx, sy):
|
||||
matrix = NSAffineTransform.transform()
|
||||
matrix.scaleXBy_yBy_(sx, sy)
|
||||
self._ns_path.transformUsingAffineTransform_(matrix)
|
||||
self.transformstack[-1].append(["scale",sx,sy])
|
||||
def _transform(self, x, y):
|
||||
for i in self.transformstack: #reversed(self.transformstack):
|
||||
for j in i: # reversed(i):
|
||||
if j[0]=="translate":
|
||||
x = x+j[1]
|
||||
y = y+j[2]
|
||||
elif j[0]=="rotate":
|
||||
x = x*math.cos(j[1])-y*math.sin(j[1])
|
||||
y = x*math.sin(j[1])+y*math.cos(j[1])
|
||||
elif j[0]=="scale":
|
||||
x = x*j[1]
|
||||
y = y*j[2]
|
||||
return x, y
|
||||
|
||||
def _ns_rect((l, t, r, b)):
|
||||
return NSMakeRect(l, t, r - l, b - t)
|
||||
|
||||
export(Canvas)
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#
|
||||
# Python GUI - Check boxes - PyObjC
|
||||
#
|
||||
|
||||
import AppKit
|
||||
from AppKit import NSOnState, NSOffState, NSMixedState
|
||||
from GUI import export
|
||||
from GUI.Actions import Action
|
||||
from GUI.StdFonts import system_font
|
||||
from GUI.ButtonBasedControls import ButtonBasedControl
|
||||
from GUI.GCheckBoxes import CheckBox as GCheckBox
|
||||
|
||||
class CheckBox(ButtonBasedControl, GCheckBox):
|
||||
|
||||
_ns_mixed = False
|
||||
|
||||
def __init__(self, title = "New Check Box", font = system_font, **kwds):
|
||||
ns_button = self._create_ns_button(title = title, font = font,
|
||||
ns_button_type = AppKit.NSSwitchButton,
|
||||
ns_bezel_style = AppKit.NSRoundedBezelStyle)
|
||||
#if mixed:
|
||||
# self._ns_mixed = True
|
||||
# ns_button.setAllowsMixedState_(True)
|
||||
GCheckBox.__init__(self, _ns_view = ns_button, **kwds)
|
||||
|
||||
def get_mixed(self):
|
||||
return self._ns_view.allowsMixedState()
|
||||
|
||||
def set_mixed(self, x):
|
||||
self._ns_view.setAllowsMixedState_(x)
|
||||
|
||||
def get_on(self):
|
||||
state = self._ns_view.state()
|
||||
if state == NSMixedState:
|
||||
return 'mixed'
|
||||
else:
|
||||
return state <> NSOffState
|
||||
|
||||
def set_on(self, v):
|
||||
if v == 'mixed' and self.mixed:
|
||||
state = NSMixedState
|
||||
elif v:
|
||||
state = NSOnState
|
||||
else:
|
||||
state = NSOffState
|
||||
self._ns_view.setState_(state)
|
||||
|
||||
def do_action(self):
|
||||
if not self._auto_toggle:
|
||||
self.on = not self.on
|
||||
Action.do_action(self)
|
||||
|
||||
export(CheckBox)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#
|
||||
# Python GUI - Colors - PyObjC
|
||||
#
|
||||
|
||||
from AppKit import NSColor, NSCalibratedRGBColorSpace
|
||||
from GUI import export
|
||||
from GUI.GColors import Color as GColor
|
||||
|
||||
NSColor.setIgnoresAlpha_(False)
|
||||
|
||||
class Color(GColor):
|
||||
|
||||
def _from_ns_color(cls, ns_color):
|
||||
color = cls.__new__(cls)
|
||||
color._ns_color = ns_color.colorUsingColorSpaceName_(
|
||||
NSCalibratedRGBColorSpace)
|
||||
return color
|
||||
|
||||
_from_ns_color = classmethod(_from_ns_color)
|
||||
|
||||
def __init__(self, red, green, blue, alpha = 1.0, image = False, im = ''):
|
||||
self.image = image
|
||||
if image:
|
||||
# self._ns_color = NSColor.colorWithPatternImage_(im._ns_image)
|
||||
self._ns_color = im._ns_image
|
||||
else:
|
||||
self._ns_color = NSColor.colorWithCalibratedRed_green_blue_alpha_(
|
||||
red, green, blue, alpha)
|
||||
|
||||
def get_red(self):
|
||||
return self._ns_color.redComponent()
|
||||
|
||||
def get_green(self):
|
||||
return self._ns_color.greenComponent()
|
||||
|
||||
def get_blue(self):
|
||||
return self._ns_color.blueComponent()
|
||||
|
||||
def get_alpha(self):
|
||||
return self._ns_color.alphaComponent()
|
||||
|
||||
def get_rgb(self):
|
||||
return self.get_rgba()[:3]
|
||||
|
||||
def get_rgba(self):
|
||||
m = self._ns_color.getRed_green_blue_alpha_
|
||||
try:
|
||||
return m()
|
||||
except TypeError:
|
||||
return m(None, None, None, None)
|
||||
|
||||
class ImageColor(object):
|
||||
"""docstring for ImageColor"""
|
||||
def __init__(self, red, green, blue, alpha = 1.0, image = False, im = ''):
|
||||
super(ImageColor, self).__init__()
|
||||
self.arg = arg
|
||||
|
||||
|
||||
export(Color)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#
|
||||
# Python GUI - Color constants and functions - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSColor
|
||||
from GUI import Color
|
||||
|
||||
rgb = Color
|
||||
|
||||
selection_forecolor = Color._from_ns_color(NSColor.selectedTextColor())
|
||||
selection_backcolor = Color._from_ns_color(NSColor.selectedTextBackgroundColor())
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
#
|
||||
# Python GUI - Components - PyObjC
|
||||
#
|
||||
|
||||
from Foundation import NSRect, NSPoint, NSSize, NSObject
|
||||
from GUI import export
|
||||
from GUI import Globals, application
|
||||
from GUI import Event
|
||||
from GUI.GComponents import Component as GComponent
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Globals._ns_view_to_component = {} # Mapping from NSView to corresponding Component
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Component(GComponent):
|
||||
|
||||
_has_local_coords = True
|
||||
_generic_tabbing = False
|
||||
_ns_pass_mouse_events_to_platform = False
|
||||
_ns_handle_mouse = False
|
||||
_ns_accept_first_responder = False
|
||||
|
||||
def __init__(self, _ns_view, _ns_inner_view = None, _ns_responder = None,
|
||||
_ns_set_autoresizing_mask = True, **kwds):
|
||||
self._ns_view = _ns_view
|
||||
if not _ns_inner_view:
|
||||
_ns_inner_view = _ns_view
|
||||
self._ns_inner_view = _ns_inner_view
|
||||
self._ns_responder = _ns_responder or _ns_inner_view
|
||||
Globals._ns_view_to_component[_ns_view] = self
|
||||
GComponent.__init__(self, **kwds)
|
||||
|
||||
def destroy(self):
|
||||
#print "Component.destroy:", self ###
|
||||
GComponent.destroy(self)
|
||||
_ns_view = self._ns_view
|
||||
if _ns_view in Globals._ns_view_to_component:
|
||||
#print "Component.destroy: removing", _ns_view, "from mapping" ###
|
||||
del Globals._ns_view_to_component[_ns_view]
|
||||
#print "Component.destroy: breaking link to", self._ns_view ###
|
||||
self._ns_view = None
|
||||
#if self._ns_inner_view: print "Component.destroy: breaking inner link to", self._ns_inner_view ###
|
||||
self._ns_inner_view = None
|
||||
self._ns_responder = None
|
||||
|
||||
def get_bounds(self):
|
||||
(l, t), (w, h) = self._ns_view.frame()
|
||||
return (l, t, l + w, t + h)
|
||||
|
||||
def set_bounds(self, (l, t, r, b)):
|
||||
ns = self._ns_view
|
||||
w0, h0 = ns.frame().size
|
||||
w1 = r - l
|
||||
h1 = b - t
|
||||
ns_frame = ((l, t), (w1, h1))
|
||||
old_ns_frame = ns.frame()
|
||||
ns.setFrame_(ns_frame)
|
||||
sv = ns.superview()
|
||||
if sv:
|
||||
sv.setNeedsDisplayInRect_(old_ns_frame)
|
||||
sv.setNeedsDisplayInRect_(ns_frame)
|
||||
if w0 != w1 or h0 != h1:
|
||||
self._resized((w1 - w0, h1 - h0))
|
||||
|
||||
def become_target(self):
|
||||
ns_view = self._ns_view
|
||||
ns_window = ns_view.window()
|
||||
if ns_window:
|
||||
self._ns_accept_first_responder = True
|
||||
ns_window.makeFirstResponder_(ns_view)
|
||||
self._ns_accept_first_responder = False
|
||||
|
||||
def _ns_pass_to_platform(self, event, method_name):
|
||||
#print "Component._ns_pass_to_platform:", self ###
|
||||
h = self._ns_responder
|
||||
b = h.__class__.__bases__[0]
|
||||
m = getattr(b, method_name)
|
||||
#print "...ns responder =", object.__repr__(h) ###
|
||||
#print "...ns base class =", b ###
|
||||
#print "...ns method =", m ###
|
||||
m(h, event._ns_event)
|
||||
|
||||
def mouse_down(self, event):
|
||||
if self._ns_handle_mouse:
|
||||
self._ns_pass_to_platform(event, ns_mouse_down_methods[event.button])
|
||||
|
||||
def mouse_drag(self, event):
|
||||
if self._ns_handle_mouse:
|
||||
self._ns_pass_to_platform(event, 'mouseDragged_')
|
||||
|
||||
def mouse_up(self, event):
|
||||
if self._ns_handle_mouse:
|
||||
self._ns_pass_to_platform(event, ns_mouse_up_methods[event.button])
|
||||
|
||||
def mouse_move(self, event):
|
||||
#self._ns_pass_to_platform(event, 'mouseMoved_')
|
||||
pass
|
||||
|
||||
def mouse_enter(self, event):
|
||||
#self._ns_pass_to_platform(event, 'mouseEntered_')
|
||||
pass
|
||||
|
||||
def mouse_leave(self, event):
|
||||
#self._ns_pass_to_platform(event, 'mouseExited_')
|
||||
pass
|
||||
|
||||
def key_down(self, event):
|
||||
#print "Component.key_down:", repr(event.char), "for", self ###
|
||||
self._ns_pass_to_platform(event, 'keyDown_')
|
||||
|
||||
def key_up(self, event):
|
||||
self._ns_pass_to_platform(event, 'keyUp_')
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
ns_mouse_down_methods = {
|
||||
'left': 'mouseDown_', 'middle': 'otherMouseDown_', 'right': 'rightMouseDown_'
|
||||
}
|
||||
|
||||
ns_mouse_up_methods = {
|
||||
'left': 'mouseUp_', 'middle': 'otherMouseUp_', 'right': 'rightMouseUp_'
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
export(Component)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#
|
||||
# Python GUI - Containers - PyObjC version
|
||||
#
|
||||
|
||||
from AppKit import NSView
|
||||
from GUI.Utils import PyGUI_Flipped_NSView
|
||||
from GUI import export
|
||||
from GUI.GContainers import Container as GContainer
|
||||
|
||||
class Container(GContainer):
|
||||
# _ns_inner_view NSView Containing NSView for subcomponents
|
||||
|
||||
# def __init__(self, _ns_view, **kwds):
|
||||
# GContainer.__init__(self, _ns_view = _ns_view, **kwds)
|
||||
|
||||
# def destroy(self):
|
||||
# #print "Container.destroy:", self ###
|
||||
# GContainer.destroy(self)
|
||||
# #print "Container.destroy: breaking inner link to", self._ns_inner_view ###
|
||||
|
||||
def _add(self, comp):
|
||||
GContainer._add(self, comp)
|
||||
self._ns_inner_view.addSubview_(comp._ns_view)
|
||||
|
||||
def _remove(self, comp):
|
||||
GContainer._remove(self, comp)
|
||||
comp._ns_view.removeFromSuperview()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
export(Container)
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#
|
||||
# Python GUI - Controls - PyObjC
|
||||
#
|
||||
|
||||
from math import ceil
|
||||
from Foundation import NSSize
|
||||
import AppKit
|
||||
from GUI import export
|
||||
from GUI import StdColors
|
||||
from GUI import Color
|
||||
from GUI import Font
|
||||
from GUI.GControls import Control as GControl
|
||||
|
||||
_ns_alignment_from_just = {
|
||||
'left': AppKit.NSLeftTextAlignment,
|
||||
'center': AppKit.NSCenterTextAlignment,
|
||||
'centre': AppKit.NSCenterTextAlignment,
|
||||
'right': AppKit.NSRightTextAlignment,
|
||||
'flush': AppKit.NSJustifiedTextAlignment,
|
||||
'': AppKit.NSNaturalTextAlignment,
|
||||
}
|
||||
|
||||
_ns_alignment_to_just = {
|
||||
AppKit.NSLeftTextAlignment: 'left',
|
||||
AppKit.NSCenterTextAlignment: 'center',
|
||||
AppKit.NSRightTextAlignment: 'right',
|
||||
AppKit.NSJustifiedTextAlignment: 'flush',
|
||||
AppKit.NSNaturalTextAlignment: '',
|
||||
}
|
||||
|
||||
class Control(GControl):
|
||||
|
||||
#_vertical_padding = 5
|
||||
|
||||
def get_title(self):
|
||||
return self._ns_cell().title()
|
||||
|
||||
def set_title(self, v):
|
||||
self._ns_cell().setTitle_(v)
|
||||
|
||||
def get_enabled(self):
|
||||
return self._ns_cell().enabled()
|
||||
|
||||
def set_enabled(self, v):
|
||||
self._ns_cell().setEnabled_(v)
|
||||
|
||||
def get_color(self):
|
||||
return StdColors.black
|
||||
|
||||
def set_color(self, v):
|
||||
pass
|
||||
|
||||
def get_font(self):
|
||||
return Font._from_ns_font(self._ns_cell().font())
|
||||
|
||||
def set_font(self, f):
|
||||
self._ns_cell().setFont_(f._ns_font)
|
||||
|
||||
def get_just(self):
|
||||
return _ns_alignment_to_just[self._ns_cell().alignment()]
|
||||
|
||||
def set_just(self, v):
|
||||
self._ns_cell().setAlignment_(_ns_alignment_from_just[v])
|
||||
|
||||
def _ns_cell(self):
|
||||
return self._ns_inner_view.cell()
|
||||
|
||||
export(Control)
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#
|
||||
# Python GUI - Cursors - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSCursor
|
||||
from GUI import export
|
||||
from GUI.GCursors import Cursor as GCursor
|
||||
|
||||
class Cursor(GCursor):
|
||||
#
|
||||
# _ns_cursor NSCursor
|
||||
|
||||
def _from_ns_cursor(cls, ns_cursor):
|
||||
cursor = cls.__new__(cls)
|
||||
cursor._ns_cursor = ns_cursor
|
||||
return cursor
|
||||
|
||||
_from_ns_cursor = classmethod(_from_ns_cursor)
|
||||
|
||||
def _init_from_image_and_hotspot(self, image, hotspot):
|
||||
#print "Cursor._init_from_image_and_hotspot:", image, hotspot ###
|
||||
ns_image = image._ns_image.copy()
|
||||
ns_image.setFlipped_(False)
|
||||
self._ns_cursor = NSCursor.alloc().initWithImage_hotSpot_(
|
||||
ns_image, hotspot)
|
||||
|
||||
export(Cursor)
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#
|
||||
# Python GUI - Dialogs - Cocoa
|
||||
#
|
||||
|
||||
from GUI import export
|
||||
from GUI.GDialogs import Dialog #as GDialog
|
||||
|
||||
#class Dialog(GDialog):
|
||||
#
|
||||
# _default_keys = ['\r']
|
||||
# _cancel_keys = ['\x1b']
|
||||
#
|
||||
# def key_down(self, event):
|
||||
# # Cocoa already takes care of default/cancel button activation
|
||||
# self.pass_to_next_handler('key_down', event)
|
||||
|
||||
export(Dialog)
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
#
|
||||
# Python GUI - DrawableContainers - PyObjC
|
||||
#
|
||||
|
||||
from Foundation import NSMakeRect
|
||||
from AppKit import NSView, NSScrollView, NSColor
|
||||
from GUI import export
|
||||
from GUI.Utils import PyGUI_Flipped_NSView
|
||||
from GUI import Canvas
|
||||
from GUI.Geometry import rect_to_ns_rect
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_ViewBase
|
||||
from GUI.GDrawableContainers import default_size, \
|
||||
DrawableContainer as GDrawableContainer
|
||||
|
||||
ns_gray = NSColor.grayColor()
|
||||
|
||||
class DrawableContainer(GDrawableContainer):
|
||||
|
||||
def __init__(self, **kwds):
|
||||
width, height = default_size
|
||||
ns_frame = NSMakeRect(0, 0, width, height)
|
||||
ns_inner_view = PyGUI_User_NSView.alloc().initWithFrame_(ns_frame)
|
||||
if self._ns_scrollable:
|
||||
ns_view = NSScrollView.alloc().initWithFrame_(ns_frame)
|
||||
ns_view.setDocumentView_(ns_inner_view)
|
||||
ns_view.setBackgroundColor_(ns_gray)
|
||||
else:
|
||||
ns_view = ns_inner_view
|
||||
ns_inner_view.pygui_component = self
|
||||
GDrawableContainer.__init__(self, _ns_view = ns_view, _ns_inner_view = ns_inner_view)
|
||||
self.set(**kwds)
|
||||
|
||||
def destroy(self):
|
||||
#print "View.destroy:", self ###
|
||||
ns_inner_view = self._ns_inner_view
|
||||
GDrawableContainer.destroy(self)
|
||||
if ns_inner_view:
|
||||
#print "View.destroy: breaking back link from", ns_inner_view ###
|
||||
ns_inner_view.pygui_component = None
|
||||
|
||||
def get_background_color(self):
|
||||
ns_view = self._ns_inner_view
|
||||
if ns_view.drawsBackground():
|
||||
return Color._from_ns_color(ns_view.backgroundColor())
|
||||
|
||||
def set_background_color(self, x):
|
||||
ns_view = self._ns_inner_view
|
||||
if x:
|
||||
ns_view.setBackgroundColor_(x._ns_color)
|
||||
ns_view.setDrawsBackground_(True)
|
||||
else:
|
||||
ns_view.setDrawsBackground_(False)
|
||||
|
||||
def invalidate(self):
|
||||
self._ns_inner_view.setNeedsDisplay_(True)
|
||||
|
||||
def invalidate_rect(self, r):
|
||||
self._ns_inner_view.setNeedsDisplayInRect_(rect_to_ns_rect(r))
|
||||
|
||||
def with_canvas(self, proc):
|
||||
ns_view = self._ns_view
|
||||
ns_view.lockFocus()
|
||||
proc(Canvas())
|
||||
ns_view.unlockFocus()
|
||||
|
||||
def update(self):
|
||||
self._ns_view.displayIfNeeded()
|
||||
|
||||
def track_mouse(self):
|
||||
return self._ns_track_mouse(self._ns_inner_view)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_User_NSView(PyGUI_Flipped_NSView, PyGUI_NS_ViewBase):
|
||||
#
|
||||
# pygui_component View
|
||||
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
def drawRect_(self, ns_rect):
|
||||
(l, t), (w, h) = ns_rect
|
||||
rect = (l, t, l + w, t + h)
|
||||
self.pygui_component.draw(Canvas(), rect)
|
||||
|
||||
export(DrawableContainer)
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#
|
||||
# PyGUI - Edit command handling - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSMenuItem
|
||||
from GUI import export
|
||||
|
||||
class EditCmdHandler(object):
|
||||
# Mixin for Components whose _ns_responder handles the
|
||||
# standard editing commands.
|
||||
|
||||
def setup_menus(self, m):
|
||||
def validate(cmd_name, ns_selector):
|
||||
ns_menu_item = NSMenuItem.alloc().\
|
||||
initWithTitle_action_keyEquivalent_("", ns_selector, "")
|
||||
m[cmd_name].enabled = ns_target.validateMenuItem_(ns_menu_item)
|
||||
ns_target = self.window._ns_window
|
||||
if ns_target:
|
||||
validate('undo_cmd', 'undo:')
|
||||
validate('redo_cmd', 'redo:')
|
||||
ns_target = self._ns_edit_cmd_target()
|
||||
if ns_target:
|
||||
validate('cut_cmd', 'cut:')
|
||||
validate('copy_cmd', 'copy:')
|
||||
validate('paste_cmd', 'paste:')
|
||||
validate('clear_cmd', 'delete:')
|
||||
validate('select_all_cmd', 'selectAll:')
|
||||
|
||||
def undo_cmd(self):
|
||||
ns_window = self.window._ns_window
|
||||
if ns_window:
|
||||
ns_window.undo_(None)
|
||||
|
||||
def redo_cmd(self):
|
||||
ns_window = self.window._ns_window
|
||||
if ns_window:
|
||||
ns_window.redo_(None)
|
||||
|
||||
def cut_cmd(self):
|
||||
self._ns_edit_cmd('cut_')
|
||||
|
||||
def copy_cmd(self):
|
||||
self._ns_edit_cmd('copy_')
|
||||
|
||||
def paste_cmd(self):
|
||||
self._ns_edit_cmd('paste_')
|
||||
|
||||
def clear_cmd(self):
|
||||
self._ns_edit_cmd('delete_')
|
||||
|
||||
def select_all_cmd(self):
|
||||
self._ns_edit_cmd('selectAll_')
|
||||
|
||||
def _ns_edit_cmd(self, ns_method_name):
|
||||
ns_target = self._ns_edit_cmd_target()
|
||||
if ns_target:
|
||||
getattr(ns_target, ns_method_name)(None)
|
||||
|
||||
def _ns_edit_cmd_target(self):
|
||||
return self._ns_responder
|
||||
|
||||
export(EditCmdHandler)
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
#
|
||||
# Python GUI - Events - PyObjC version
|
||||
#
|
||||
|
||||
import AppKit
|
||||
from AppKit import NSEvent, \
|
||||
NSShiftKeyMask, NSControlKeyMask, NSCommandKeyMask, NSAlternateKeyMask
|
||||
from GUI import export
|
||||
from GUI import Globals
|
||||
from GUI.GEvents import Event as GEvent
|
||||
|
||||
_ns_event_type_to_kind = {
|
||||
AppKit.NSLeftMouseDown: 'mouse_down',
|
||||
AppKit.NSLeftMouseUp: 'mouse_up',
|
||||
AppKit.NSRightMouseDown: 'mouse_down',
|
||||
AppKit.NSRightMouseUp: 'mouse_up',
|
||||
AppKit.NSOtherMouseDown: 'mouse_down',
|
||||
AppKit.NSOtherMouseUp: 'mouse_up',
|
||||
AppKit.NSMouseMoved: 'mouse_move',
|
||||
AppKit.NSLeftMouseDragged: 'mouse_drag',
|
||||
AppKit.NSRightMouseDragged: 'mouse_drag',
|
||||
AppKit.NSOtherMouseDragged: 'mouse_drag',
|
||||
AppKit.NSMouseEntered: 'mouse_enter',
|
||||
AppKit.NSMouseExited: 'mouse_leave',
|
||||
AppKit.NSKeyDown: 'key_down',
|
||||
AppKit.NSKeyUp: 'key_up',
|
||||
AppKit.NSFlagsChanged: 'flags_changed',
|
||||
AppKit.NSAppKitDefined: 'app_kit_defined',
|
||||
AppKit.NSSystemDefined: 'system_defined',
|
||||
AppKit.NSApplicationDefined: 'application_defined',
|
||||
AppKit.NSPeriodic: 'periodic',
|
||||
AppKit.NSCursorUpdate: 'cursor_update',
|
||||
}
|
||||
|
||||
_ns_event_type_to_button = {
|
||||
AppKit.NSLeftMouseDown: 'left',
|
||||
AppKit.NSLeftMouseUp: 'left',
|
||||
AppKit.NSRightMouseDown: 'right',
|
||||
AppKit.NSRightMouseUp: 'right',
|
||||
AppKit.NSOtherMouseDown: 'middle',
|
||||
AppKit.NSOtherMouseUp: 'middle',
|
||||
AppKit.NSLeftMouseDragged: 'left',
|
||||
AppKit.NSRightMouseDragged: 'right',
|
||||
AppKit.NSOtherMouseDragged: 'middle',
|
||||
}
|
||||
|
||||
_ns_keycode_to_keyname = {
|
||||
AppKit.NSUpArrowFunctionKey: 'up_arrow',
|
||||
AppKit.NSDownArrowFunctionKey: 'down_arrow',
|
||||
AppKit.NSLeftArrowFunctionKey: 'left_arrow',
|
||||
AppKit.NSRightArrowFunctionKey: 'right_arrow',
|
||||
AppKit.NSF1FunctionKey: 'f1',
|
||||
AppKit.NSF2FunctionKey: 'f2',
|
||||
AppKit.NSF3FunctionKey: 'f3',
|
||||
AppKit.NSF4FunctionKey: 'f4',
|
||||
AppKit.NSF5FunctionKey: 'f5',
|
||||
AppKit.NSF6FunctionKey: 'f6',
|
||||
AppKit.NSF7FunctionKey: 'f7',
|
||||
AppKit.NSF8FunctionKey: 'f8',
|
||||
AppKit.NSF9FunctionKey: 'f9',
|
||||
AppKit.NSF10FunctionKey: 'f10',
|
||||
AppKit.NSF11FunctionKey: 'f11',
|
||||
AppKit.NSF12FunctionKey: 'f12',
|
||||
AppKit.NSF13FunctionKey: 'f13',
|
||||
AppKit.NSF14FunctionKey: 'f14',
|
||||
AppKit.NSF15FunctionKey : 'f15',
|
||||
AppKit.NSDeleteFunctionKey: 'delete',
|
||||
AppKit.NSHomeFunctionKey: 'home',
|
||||
AppKit.NSEndFunctionKey: 'end',
|
||||
AppKit.NSPageUpFunctionKey: 'page_up',
|
||||
AppKit.NSPageDownFunctionKey: 'page_down',
|
||||
AppKit.NSClearLineFunctionKey: 'clear',
|
||||
#AppKit.NSHelpFunctionKey: 'help',
|
||||
AppKit.NSHelpFunctionKey: 'insert',
|
||||
"\r": 'return',
|
||||
"\x03": 'enter',
|
||||
}
|
||||
|
||||
_mouse_events = [
|
||||
'mouse_down', 'mouse_drag', 'mouse_up',
|
||||
'mouse_move', 'mouse_enter', 'mouse_exit'
|
||||
]
|
||||
|
||||
_key_events = [
|
||||
'key_down', 'key_up'
|
||||
]
|
||||
|
||||
_ns_screen_height = None
|
||||
|
||||
class Event(GEvent):
|
||||
"""Platform-dependent modifiers (boolean):
|
||||
command The Macintosh Command key.
|
||||
option The Macintosh Option key.
|
||||
"""
|
||||
|
||||
global_position = (0, 0)
|
||||
position = (0, 0)
|
||||
button = ''
|
||||
num_clicks = 0
|
||||
char = ""
|
||||
unichars = ""
|
||||
key = ''
|
||||
auto = False
|
||||
delta = (0, 0)
|
||||
|
||||
def __init__(self, ns_event):
|
||||
self._ns_event = ns_event
|
||||
_ns_type = ns_event.type()
|
||||
kind = _ns_event_type_to_kind[_ns_type]
|
||||
self.kind = kind
|
||||
self.time = ns_event.timestamp()
|
||||
ns_window = ns_event.window()
|
||||
is_mouse_event = kind in _mouse_events
|
||||
if is_mouse_event:
|
||||
ns_win_pos = ns_event.locationInWindow()
|
||||
x, y = ns_window.convertBaseToScreen_(ns_win_pos)
|
||||
else:
|
||||
ns_last_mouse = Globals.ns_last_mouse_moved_event
|
||||
if ns_last_mouse:
|
||||
ns_window = ns_last_mouse.window()
|
||||
if ns_window:
|
||||
ns_win_pos = ns_last_mouse.locationInWindow()
|
||||
x, y = ns_window.convertBaseToScreen_(ns_win_pos)
|
||||
else:
|
||||
x, y = ns_last_mouse.locationInWindow()
|
||||
else:
|
||||
x, y = NSEvent.mouseLocation()
|
||||
h = Globals.ns_screen_height
|
||||
self.global_position = (x, h - y)
|
||||
if is_mouse_event:
|
||||
self.button = _ns_event_type_to_button.get(_ns_type, '')
|
||||
if kind == 'mouse_down':
|
||||
self.num_clicks = ns_event.clickCount()
|
||||
self.delta = (ns_event.deltaX(), ns_event.deltaY())
|
||||
ns_flags = ns_event.modifierFlags()
|
||||
self.shift = self.extend_contig = (ns_flags & NSShiftKeyMask) <> 0
|
||||
self.control = (ns_flags & NSControlKeyMask) <> 0
|
||||
self.command = self.extend_noncontig = (ns_flags & NSCommandKeyMask) <> 0
|
||||
self.option = (ns_flags & NSAlternateKeyMask) <> 0
|
||||
if kind in _key_events:
|
||||
self.auto = ns_event.isARepeat()
|
||||
ns_chars = ns_event.characters()
|
||||
#print "Event.__init__: ns_chars =", repr(ns_chars) ###
|
||||
self.unichars = ns_chars
|
||||
if len(ns_chars) == 1:
|
||||
if ns_chars == "\x19" and ns_event.keyCode() == 48:
|
||||
self.char = "\t"
|
||||
elif ns_chars == "\x7f":
|
||||
self.char = "\x08"
|
||||
elif ns_chars <= "\x7e":
|
||||
self.char = str(ns_chars)
|
||||
#else:
|
||||
# self.char = ns_chars
|
||||
ns_unmod = ns_event.charactersIgnoringModifiers()
|
||||
key = _ns_keycode_to_keyname.get(ns_chars, '')
|
||||
if not key and u"\x20" <= ns_unmod <= u"\x7e":
|
||||
key = str(ns_unmod)
|
||||
self.key = key
|
||||
if key == 'enter':
|
||||
self.char = "\r"
|
||||
elif key == 'delete':
|
||||
self.char = "\x7f"
|
||||
|
||||
def _platform_modifiers_str(self):
|
||||
return " command:%s option:%s" % (self.command, self.option)
|
||||
|
||||
export(Event)
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#
|
||||
# Python GUI - File references and types - Cocoa
|
||||
#
|
||||
|
||||
from struct import unpack
|
||||
from Foundation import NSFileTypeForHFSTypeCode, \
|
||||
NSFileManager, NSFileHFSCreatorCode, NSFileHFSTypeCode
|
||||
from GUI.GFiles import FileRef as GFileRef, DirRef, FileType as GFileType
|
||||
|
||||
class FileType(GFileType):
|
||||
|
||||
def _ns_file_types(self):
|
||||
# Return list of Cocoa file type specifications matched
|
||||
# by this file type.
|
||||
result = []
|
||||
mac_type = self._mac_type
|
||||
if mac_type:
|
||||
result.append(NSFileTypeForHFSTypeCode(mac_type))
|
||||
suffix = self._suffix
|
||||
if suffix:
|
||||
result.append(suffix)
|
||||
return result
|
||||
|
||||
|
||||
class FileRef(GFileRef):
|
||||
|
||||
def _set_type(self, file_type):
|
||||
creator = file_type.mac_creator
|
||||
type = file_type.mac_type
|
||||
if creator is not None or type is not None:
|
||||
fm = NSFileManager.defaultManager()
|
||||
attrs = {}
|
||||
if creator is not None:
|
||||
attrs[NSFileHFSCreatorCode] = four_char_code(creator)
|
||||
if type is not None:
|
||||
attrs[NSFileHFSTypeCode] = four_char_code(type)
|
||||
#print "FileRef: Setting attributes of %r to %s" % ( ###
|
||||
# self.path, attrs) ###
|
||||
fm.changeFileAttributes_atPath_(attrs, self.path)
|
||||
|
||||
|
||||
def four_char_code(chars):
|
||||
return unpack(">L", chars)[0]
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#
|
||||
# Python GUI - Fonts - PyObjC
|
||||
#
|
||||
|
||||
import sys
|
||||
from AppKit import NSFont, NSFontManager, NSBoldFontMask, NSItalicFontMask, \
|
||||
NSLayoutManager
|
||||
from GUI import export
|
||||
from GUI.GFonts import Font as GFont
|
||||
|
||||
_ns_font_manager = NSFontManager.sharedFontManager()
|
||||
_ns_layout_manager = NSLayoutManager.alloc().init()
|
||||
|
||||
class Font(GFont):
|
||||
# _ns_font NSFont
|
||||
|
||||
def _from_ns_font(cls, ns_font):
|
||||
font = cls.__new__(cls)
|
||||
font._ns_font = ns_font
|
||||
return font
|
||||
|
||||
_from_ns_font = classmethod(_from_ns_font)
|
||||
|
||||
def __init__(self, family, size = 12, style = []):
|
||||
traits = 0
|
||||
if 'bold' in style:
|
||||
traits |= NSBoldFontMask
|
||||
if 'italic' in style:
|
||||
traits |= NSItalicFontMask
|
||||
self._ns_font = _ns_font_manager.fontWithFamily_traits_weight_size_(
|
||||
family, traits, 5, size)
|
||||
if not self._ns_font:
|
||||
import StdFonts
|
||||
self._ns_font = StdFonts.application_font._ns_font
|
||||
|
||||
def get_family(self):
|
||||
return self._ns_font.familyName()
|
||||
|
||||
def get_size(self):
|
||||
return self._ns_font.pointSize()
|
||||
|
||||
def get_style(self):
|
||||
style = []
|
||||
traits = _ns_font_manager.traitsOfFont_(self._ns_font)
|
||||
if traits & NSBoldFontMask:
|
||||
style.append('bold')
|
||||
if traits & NSItalicFontMask:
|
||||
style.append('italic')
|
||||
return style
|
||||
|
||||
def get_ascent(self):
|
||||
return self._ns_font.ascender()
|
||||
|
||||
def get_descent(self):
|
||||
return -self._ns_font.descender()
|
||||
|
||||
def get_height(self):
|
||||
ns_font = self._ns_font
|
||||
a = ns_font.ascender()
|
||||
d = ns_font.descender()
|
||||
return a - d
|
||||
|
||||
def get_cap_height(self):
|
||||
return self._ns_font.capHeight()
|
||||
|
||||
def get_x_height(self):
|
||||
return self._ns_font.xHeight()
|
||||
|
||||
def get_line_height(self):
|
||||
# Adding 1 here to match what NSTextField seems to do
|
||||
return _ns_layout_manager.defaultLineHeightForFont_(self._ns_font) + 1
|
||||
|
||||
def width(self, s, start = 0, end = sys.maxint):
|
||||
return self._ns_font.widthOfString_(s[start:end])
|
||||
|
||||
export(Font)
|
||||
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Frames - Cocoa
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from GUI.GFrames import Frame as GFrame
|
||||
from GUI import export
|
||||
from GUI.Utils import NSMultiClass
|
||||
from GUI.Utils import PyGUI_NS_EventHandler, PyGUI_Flipped_NSView
|
||||
|
||||
class Frame(GFrame):
|
||||
|
||||
def __init__(self, **kwds):
|
||||
ns_view = PyGUI_Frame.alloc().initWithFrame_(((0, 0), (100, 100)))
|
||||
ns_view.pygui_component = self
|
||||
GFrame.__init__(self, _ns_view = ns_view, **kwds)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_Frame(PyGUI_Flipped_NSView, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
export(Frame)
|
||||
185
GUI/Cocoa/GL.py
185
GUI/Cocoa/GL.py
|
|
@ -1,185 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL View - Cocoa
|
||||
#
|
||||
|
||||
__all__ = ['GLConfig', 'GLView', 'GLPixmap']
|
||||
|
||||
import AppKit
|
||||
from Foundation import NSSize
|
||||
from AppKit import NSOpenGLPixelFormat, NSOpenGLView, \
|
||||
NSBitmapImageRep, NSCachedImageRep, NSImage, NSAlphaFirstBitmapFormat, \
|
||||
NSFloatingPointSamplesBitmapFormat
|
||||
from Foundation import NSMakeRect
|
||||
from OpenGL.GL import glViewport, glFlush, glFinish, glReadPixels, \
|
||||
GL_RGB, GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, \
|
||||
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_FLOAT, \
|
||||
glReadPixelsub, glTexImage2D, glPixelStorei, GL_UNPACK_ALIGNMENT
|
||||
from OpenGL.GLU import gluBuild2DMipmaps
|
||||
from GUI.GGLViews import GLView as GGLView
|
||||
from GUI.GGLPixmaps import GLPixmap as GGLPixmap
|
||||
from GUI.GGLConfig import GLConfig as GGLConfig
|
||||
from GUI.GLContexts import GLContext
|
||||
from GUI.GLTextures import Texture
|
||||
from GUI.GLDisplayLists import DisplayList
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_ViewBase
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class GLConfig(GGLConfig):
|
||||
# _ns_pixel_format NSOpenGLPixelFormat
|
||||
|
||||
def _ns_get_pixel_format(self, offscreen = False):
|
||||
attrs = [AppKit.NSOpenGLPFAColorSize, self._color_size]
|
||||
if self._double_buffer:
|
||||
attrs += [AppKit.NSOpenGLPFADoubleBuffer]
|
||||
if self._alpha:
|
||||
attrs += [AppKit.NSOpenGLPFAAlphaSize, self._alpha_size]
|
||||
if self._stereo:
|
||||
attrs += [AppKit.NSOpenGLPFAStereo]
|
||||
if self._aux_buffers:
|
||||
attrs += [AppKit.NSOpenGLPFAAuxBuffers, self._aux_buffers]
|
||||
if self._depth_buffer:
|
||||
attrs += [AppKit.NSOpenGLPFADepthSize, self._depth_size]
|
||||
if self._stencil_buffer:
|
||||
attrs += [AppKit.NSOpenGLPFAStencilSize, self._stencil_size]
|
||||
if self._accum_buffer:
|
||||
attrs += [AppKit.NSOpenGLPFAAccumSize, self._accum_size]
|
||||
if self._multisample:
|
||||
attrs += [AppKit.NSOpenGLPFASampleBuffers, 1]
|
||||
attrs += [AppKit.NSOpenGLPFASamples, self._samples_per_pixel]
|
||||
if offscreen:
|
||||
attrs += [AppKit.NSOpenGLPFAOffScreen]
|
||||
attrs.append(0)
|
||||
ns_pf = NSOpenGLPixelFormat.alloc().initWithAttributes_(attrs)
|
||||
if not ns_pf and self._double_buffer:
|
||||
attrs.remove(AppKit.NSOpenGLPFADoubleBuffer)
|
||||
ns_pf = NSOpenGLPixelFormat.alloc().initWithAttributes_(attrs)
|
||||
if not ns_pf:
|
||||
raise GLConfigError
|
||||
return ns_pf
|
||||
|
||||
def _ns_set_pixel_format(self, ns_pf):
|
||||
def ns_attr(attr):
|
||||
return ns_pf.getValues_forAttribute_forVirtualScreen_(attr, 0)[0]
|
||||
self._ns_pixel_format = ns_pf
|
||||
self._double_buffer = ns_attr(AppKit.NSOpenGLPFADoubleBuffer)
|
||||
self._color_size = ns_attr(AppKit.NSOpenGLPFAColorSize)
|
||||
self._alpha_size = ns_attr(AppKit.NSOpenGLPFAAlphaSize)
|
||||
self._alpha = self._alpha_size > 0
|
||||
self._stereo = ns_attr(AppKit.NSOpenGLPFAStereo)
|
||||
self._aux_buffers = ns_attr(AppKit.NSOpenGLPFAAuxBuffers)
|
||||
self._depth_size = ns_attr(AppKit.NSOpenGLPFADepthSize)
|
||||
self._depth_buffer = self._depth_size > 0
|
||||
self._stencil_size = ns_attr(AppKit.NSOpenGLPFAStencilSize)
|
||||
self._stencil_buffer = self._stencil_size > 0
|
||||
self._accum_size = ns_attr(AppKit.NSOpenGLPFAAccumSize)
|
||||
self._accum_buffer = self._accum_size > 0
|
||||
self._multisample = ns_attr(AppKit.NSOpenGLPFASampleBuffers) > 0
|
||||
self._samples_per_pixel = ns_attr(AppKit.NSOpenGLPFASamples)
|
||||
|
||||
def supported(self, mode = 'both'):
|
||||
try:
|
||||
ns_pf = self._ns_get_pixel_format()
|
||||
pf = GLConfig.__new__()
|
||||
pf._ns_set_pixel_format(ns_pf)
|
||||
return pf
|
||||
except GLConfigError:
|
||||
return None
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class GLView(GGLView):
|
||||
# _ns_view NSOpenGLView
|
||||
# _ns_context NSOpenGLContext
|
||||
# _ns_flush function for flushing/swapping buffers
|
||||
|
||||
def __init__(self, config = None, share_group = None, **kwds):
|
||||
pf = GLConfig._from_args(config, kwds)
|
||||
ns_pf = pf._ns_get_pixel_format()
|
||||
width, height = GGLView._default_size
|
||||
ns_rect = NSMakeRect(0, 0, width, height)
|
||||
ns_view = _PyGUI_NSOpenGLView.alloc().initWithFrame_pixelFormat_(
|
||||
ns_rect, ns_pf)
|
||||
ns_view.pygui_component = self
|
||||
GGLView.__init__(self, _ns_view = ns_view)
|
||||
GLContext.__init__(self, share_group = share_group, _ns_pixel_format = ns_pf)
|
||||
ns_context = self._ns_context
|
||||
ns_view.setOpenGLContext_(ns_context)
|
||||
#ns_context.setView_(ns_view) # Docs say this is needed, but
|
||||
# prints warning and seems to work without.
|
||||
if pf.double_buffer:
|
||||
self._ns_flush = ns_context.flushBuffer
|
||||
else:
|
||||
self._ns_flush = glFlush
|
||||
self.set(**kwds)
|
||||
self.with_context(self._init_context)
|
||||
|
||||
def destroy(self):
|
||||
#print "GLView.destroy:", self ###
|
||||
ns_view = self._ns_view
|
||||
GGLView.destroy(self)
|
||||
#print "GLView.destroy: breaking back link from", ns_view ###
|
||||
ns_view.pygui_component = None
|
||||
|
||||
def invalidate(self):
|
||||
self._ns_view.setNeedsDisplay_(True)
|
||||
|
||||
def update(self):
|
||||
self._ns_view.displayIfNeeded()
|
||||
|
||||
def track_mouse(self):
|
||||
return self._ns_track_mouse(self._ns_view)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class GLPixmap(GGLPixmap):
|
||||
|
||||
def __init__(self, width, height, config = None, share_group = None, **kwds):
|
||||
pf = GLConfig._from_args(config, kwds)
|
||||
ns_pf = pf._ns_get_pixel_format()
|
||||
ns_size = NSSize(width, height)
|
||||
ns_cache = NSCachedImageRep.alloc().initWithSize_depth_separate_alpha_(
|
||||
ns_size, 0, True, True)
|
||||
ns_image = NSImage.alloc().initWithSize_(ns_size)
|
||||
GLContext.__init__(self, share_group = share_group, _ns_pixel_format = ns_pf)
|
||||
self._ns_context.setView_(ns_cache.window().contentView())
|
||||
self._init_with_ns_image(ns_image, flipped = False)
|
||||
self._ns_cache = ns_cache
|
||||
self.with_context(self._init_context)
|
||||
|
||||
def _ns_flush(self):
|
||||
glFlush()
|
||||
width, height = self.size
|
||||
pixels = glReadPixels(0, 0, int(width), int(height), GL_RGBA, GL_UNSIGNED_BYTE)
|
||||
bytes_per_row = int(width) * 4
|
||||
ns_new_bitmap = NSBitmapImageRep.alloc().\
|
||||
initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
||||
(pixels, "", "", "", ""), int(width), int(height), 8, 4, True, False, AppKit.NSDeviceRGBColorSpace, bytes_per_row, 0)
|
||||
ns_image = NSImage.alloc().initWithSize_(NSSize(width, height))
|
||||
ns_image.addRepresentation_(ns_new_bitmap)
|
||||
ns_image.lockFocus()
|
||||
ns_image.unlockFocus()
|
||||
self._ns_image = ns_image
|
||||
self._ns_bitmap_image_rep = ns_new_bitmap
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class _PyGUI_NSOpenGLView(NSOpenGLView, PyGUI_NS_ViewBase):
|
||||
__metaclass__ = NSMultiClass
|
||||
#
|
||||
# pygui_component GLView
|
||||
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
def isFlipped(self):
|
||||
return True
|
||||
|
||||
def reshape(self):
|
||||
comp = self.pygui_component
|
||||
if comp.window:
|
||||
comp.with_context(comp._update_viewport)
|
||||
|
||||
def drawRect_(self, rect):
|
||||
comp = self.pygui_component
|
||||
comp.with_context(comp._render, flush = True)
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Contexts - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSOpenGLContext
|
||||
from GUI.GGLContexts import GLContext as GGLContext
|
||||
|
||||
class GLContext(GGLContext):
|
||||
# _ns_context NSOpenGLContext
|
||||
|
||||
def __init__(self, share_group, _ns_pixel_format):
|
||||
GGLContext.__init__(self, share_group)
|
||||
shared_context = self._get_shared_context()
|
||||
if shared_context:
|
||||
ns_share = shared_context._ns_context
|
||||
else:
|
||||
ns_share = None
|
||||
ns_context = NSOpenGLContext.alloc().initWithFormat_shareContext_(
|
||||
_ns_pixel_format, ns_share)
|
||||
self._ns_context = ns_context
|
||||
|
||||
def _with_context(self, proc, flush):
|
||||
#print "GLContext._with_context: Entering context", self._ns_context ###
|
||||
old_context = NSOpenGLContext.currentContext()
|
||||
self._ns_context.makeCurrentContext()
|
||||
try:
|
||||
self._with_share_group(proc)
|
||||
if flush:
|
||||
self._ns_flush()
|
||||
finally:
|
||||
#print "GL: Restoring previous context" ###
|
||||
if old_context:
|
||||
old_context.makeCurrentContext()
|
||||
else:
|
||||
NSOpenGLContext.clearCurrentContext()
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Textures - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSAlphaFirstBitmapFormat, NSFloatingPointSamplesBitmapFormat
|
||||
from OpenGL import GL
|
||||
from GUI.GGLTextures import Texture as GTexture
|
||||
|
||||
class Texture(GTexture):
|
||||
|
||||
def _gl_get_texture_data(self, image):
|
||||
ns_rep = image._ns_bitmap_image_rep
|
||||
if ns_rep.numberOfPlanes() <> 1:
|
||||
raise ValueError("Cannot use planar image data as GL texture")
|
||||
ns_format = ns_rep.bitmapFormat()
|
||||
if ns_format & NSAlphaFirstBitmapFormat:
|
||||
raise ValueError("Cannot use alpha-first image data as GL texture")
|
||||
fp_samples = ns_format & NSFloatingPointSamplesBitmapFormat <> 0
|
||||
bits_per_pixel = ns_rep.bitsPerPixel()
|
||||
bytes_per_row = ns_rep.bytesPerRow()
|
||||
samples_per_pixel = ns_rep.samplesPerPixel()
|
||||
if bits_per_pixel % samples_per_pixel <> 0:
|
||||
raise ValueError("Image data format not usable as GL texture")
|
||||
bits_per_sample = bits_per_pixel / samples_per_pixel
|
||||
try:
|
||||
gl_format = format_map[samples_per_pixel]
|
||||
gl_type = type_map[bits_per_sample, fp_samples]
|
||||
except KeyError:
|
||||
raise ValueError("Image data format not usable as GL texture")
|
||||
data = ns_rep.bitmapData()
|
||||
if 0:
|
||||
print "GUI.GLTexture._gl_get_texture_data_and_format:" ###
|
||||
print "format =", gl_format_map.get(gl_format) ###
|
||||
print "type =", gl_type_map.get(gl_type) ###
|
||||
print "data length =", len(data) ###
|
||||
print repr(data[:16]) ###
|
||||
GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)
|
||||
return gl_format, gl_type, str(data)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
format_map = {
|
||||
3: GL.GL_RGB,
|
||||
4: GL.GL_RGBA,
|
||||
1: GL.GL_LUMINANCE,
|
||||
2: GL.GL_LUMINANCE_ALPHA,
|
||||
}
|
||||
|
||||
type_map = {
|
||||
(8, 0): GL.GL_UNSIGNED_BYTE,
|
||||
(16, 0): GL.GL_UNSIGNED_SHORT,
|
||||
(32, 0): GL.GL_UNSIGNED_INT,
|
||||
(32, 1): GL.GL_FLOAT,
|
||||
}
|
||||
|
||||
gl_format_map = {
|
||||
GL.GL_RGB: 'GL_RGB',
|
||||
GL.GL_RGBA: 'GL_RGBA',
|
||||
GL.GL_LUMINANCE: 'GL_LUMINANCE',
|
||||
GL.GL_LUMINANCE_ALPHA: 'GL_LUMINANCE_ALPHA',
|
||||
}
|
||||
|
||||
gl_type_map = {
|
||||
GL.GL_UNSIGNED_BYTE: 'GL_UNSIGNED_BYTE',
|
||||
GL.GL_UNSIGNED_SHORT: 'GL_UNSIGNED_SHORT',
|
||||
GL.GL_UNSIGNED_INT: 'GL_UNSIGNED_INT',
|
||||
GL.GL_FLOAT: 'GL_FLOAT',
|
||||
}
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#
|
||||
# Python GUI - Points and Rectangles - PyObjC
|
||||
#
|
||||
|
||||
from Foundation import NSMakeRect
|
||||
from GUI.GGeometry import *
|
||||
|
||||
def rect_to_ns_rect((l, t, r, b)):
|
||||
return NSMakeRect(l, t, r - l, b - t)
|
||||
|
||||
def ns_rect_to_rect(((l, t), (w, h))):
|
||||
return (l, t, l + w, t + h)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#
|
||||
# Python GUI - Images - Cocoa
|
||||
#
|
||||
|
||||
from Foundation import NSData
|
||||
from AppKit import NSImage, NSBitmapImageRep
|
||||
from GUI import export
|
||||
from GUI.GImages import Image as GImage
|
||||
|
||||
class Image(GImage):
|
||||
# _ns_bitmap_image_rep
|
||||
|
||||
def _init_from_file(self, file):
|
||||
#ns_image = NSImage.alloc().initWithContentsOfFile_(file)
|
||||
#if not ns_image:
|
||||
ns_data = NSData.dataWithContentsOfFile_(file)
|
||||
if not ns_data:
|
||||
raise EnvironmentError("Unable to read image file: %s" % file)
|
||||
ns_rep = NSBitmapImageRep.imageRepWithData_(ns_data)
|
||||
if not ns_rep:
|
||||
raise ValueError("Unrecognised image file type: %s" % file)
|
||||
ns_rep.setSize_((ns_rep.pixelsWide(), ns_rep.pixelsHigh()))
|
||||
self._ns_image = NSImage.alloc().initWithContentsOfFile_(file)
|
||||
# self.ns_image.addRepresentation_(ns_rep)
|
||||
self._init_from_ns_rep(ns_rep)
|
||||
|
||||
def _init_from_ns_rep(self, ns_rep):
|
||||
self._ns_image = NSImage.alloc().init()
|
||||
self._ns_image.addRepresentation_(ns_rep)
|
||||
self._ns_bitmap_image_rep = ns_rep
|
||||
self._init_with_ns_image(self._ns_image, flipped = True)
|
||||
|
||||
export(Image)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#
|
||||
# Python GUI - Common Image/Pixmap code - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSCompositeSourceOver
|
||||
from GUI import export
|
||||
from GUI.Geometry import rect_to_ns_rect
|
||||
from GUI.GImageBases import ImageBase as GImageBase
|
||||
|
||||
class ImageBase(GImageBase):
|
||||
#
|
||||
# Code common to Image, Pixmap and GLPixmap classes
|
||||
|
||||
def _init_with_ns_image(self, ns_image, flipped):
|
||||
ns_image.setFlipped_(flipped)
|
||||
self._ns_image = ns_image
|
||||
|
||||
def get_size(self):
|
||||
return tuple(self._ns_image.size())
|
||||
|
||||
def get_width(self):
|
||||
return self._ns_image.size()[0]
|
||||
|
||||
def get_height(self):
|
||||
return self._ns_image.size()[1]
|
||||
|
||||
def draw(self, canvas, src_rect, dst_rect):
|
||||
ns_src_rect = rect_to_ns_rect(src_rect)
|
||||
ns_dst_rect = rect_to_ns_rect(dst_rect)
|
||||
self._ns_image.drawInRect_fromRect_operation_fraction_(
|
||||
ns_dst_rect, ns_src_rect, NSCompositeSourceOver, 1.0)
|
||||
|
||||
export(ImageBase)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#
|
||||
# Python GUI - Labels - PyObjC
|
||||
#
|
||||
|
||||
import AppKit
|
||||
from AppKit import NSView
|
||||
from GUI import export
|
||||
from GUI.StdFonts import system_font
|
||||
from GUI.TextFieldBasedControls import TextFieldBasedControl
|
||||
from GUI.GLabels import Label as GLabel
|
||||
|
||||
ns_label_autoresizing_mask = (AppKit.NSViewWidthSizable
|
||||
| AppKit.NSViewHeightSizable)
|
||||
|
||||
class Label(TextFieldBasedControl, GLabel):
|
||||
|
||||
def __init__(self, text = "New Label", font = system_font, **kwds):
|
||||
ns_textfield = self._create_ns_textfield(editable = False,
|
||||
text = text, font = font)
|
||||
# width, height = ns_textfield.frame().size
|
||||
# ns_view = NSView.alloc().initWithFrame_(((0, 0), (width, height + 5)))
|
||||
# ns_view.addSubview_(ns_textfield)
|
||||
# ns_textfield.setFrameOrigin_((0, 2))
|
||||
# ns_textfield.setAutoresizingMask_(ns_label_autoresizing_mask)
|
||||
ns_view = ns_textfield
|
||||
GLabel.__init__(self, _ns_view = ns_view, _ns_inner_view = ns_textfield, **kwds)
|
||||
|
||||
export(Label)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#--------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Pop-up list control - Cocoa
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
from AppKit import NSPopUpButton
|
||||
from GUI import export
|
||||
from GUI.GListButtons import ListButton as GListButton
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler, \
|
||||
ns_set_action, ns_size_to_fit
|
||||
|
||||
class ListButton(GListButton):
|
||||
|
||||
_ns_handle_mouse = True
|
||||
|
||||
def __init__(self, **kwds):
|
||||
titles, values = self._extract_initial_items(kwds)
|
||||
self._titles = titles
|
||||
self._values = values
|
||||
frame = ((0, 0), (100, 20))
|
||||
ns = PyGUI_NSPopUpButton.alloc().initWithFrame_pullsDown_(frame, False)
|
||||
ns.pygui_component = self
|
||||
ns_set_action(ns, 'doAction:')
|
||||
self._ns_update_items(ns)
|
||||
ns_size_to_fit(ns)
|
||||
GListButton.__init__(self, _ns_view = ns, **kwds)
|
||||
|
||||
def _update_items(self):
|
||||
self._ns_update_items(self._ns_view)
|
||||
|
||||
def _ns_update_items(self, ns):
|
||||
ns.removeAllItems()
|
||||
ns.addItemsWithTitles_(self._titles)
|
||||
|
||||
def _get_selected_index(self):
|
||||
return self._ns_view.indexOfSelectedItem()
|
||||
|
||||
def _set_selected_index(self, i):
|
||||
self._ns_view.selectItemAtIndex_(i)
|
||||
|
||||
#--------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSPopUpButton(NSPopUpButton, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
export(ListButton)
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#
|
||||
# Python GUI - Menus - PyObjC
|
||||
#
|
||||
|
||||
from AppKit import NSMenu, NSMenuItem, NSOnState, \
|
||||
NSCommandKeyMask, NSShiftKeyMask, NSAlternateKeyMask
|
||||
from GUI import export
|
||||
from GUI import Globals
|
||||
from GUI.GMenus import Menu as GMenu, MenuItem
|
||||
|
||||
#_ns_standard_actions = {
|
||||
# 'undo_cmd': 'undo:',
|
||||
# 'redo_cmd': 'redo:',
|
||||
# 'cut_cmd': 'cut:',
|
||||
# 'copy_cmd': 'copy:',
|
||||
# 'paste_cmd': 'paste:',
|
||||
# 'clear_cmd': 'clear:',
|
||||
# 'select_all_cmd': 'selectAll:',
|
||||
#}
|
||||
|
||||
class Menu(GMenu):
|
||||
|
||||
def __init__(self, title, items, **kwds):
|
||||
#print "Menu: creating with items", items ###
|
||||
GMenu.__init__(self, title, items, **kwds)
|
||||
ns_menu = NSMenu.alloc().initWithTitle_(title)
|
||||
ns_menu.setAutoenablesItems_(False)
|
||||
ns_menu.setDelegate_(Globals.ns_application)
|
||||
self._ns_menu = ns_menu
|
||||
|
||||
def _clear_platform_menu(self):
|
||||
ns_menu = self._ns_menu
|
||||
n = ns_menu.numberOfItems()
|
||||
while n:
|
||||
n -= 1
|
||||
ns_menu.removeItemAtIndex_(n)
|
||||
|
||||
def _add_separator_to_platform_menu(self):
|
||||
ns_item = NSMenuItem.separatorItem()
|
||||
self._ns_menu.addItem_(ns_item)
|
||||
|
||||
def _add_item_to_platform_menu(self, item, name, command = None, index = None):
|
||||
key = item._key or ""
|
||||
if item._shift:
|
||||
key = key.upper()
|
||||
else:
|
||||
key = key.lower()
|
||||
ns_item = NSMenuItem.alloc()
|
||||
#ns_action = _ns_standard_actions.get(command, 'menuSelection:')
|
||||
ns_action = 'menuSelection:'
|
||||
ns_item.initWithTitle_action_keyEquivalent_(name, ns_action, key)
|
||||
ns_item.setEnabled_(item.enabled)
|
||||
if item.checked:
|
||||
ns_item.setState_(NSOnState)
|
||||
ns_modifiers = NSCommandKeyMask
|
||||
if item._option:
|
||||
ns_modifiers |= NSAlternateKeyMask
|
||||
ns_item.setKeyEquivalentModifierMask_(ns_modifiers)
|
||||
ns_item.setRepresentedObject_(command)
|
||||
if index is not None:
|
||||
ns_tag = index
|
||||
else:
|
||||
ns_tag = -1
|
||||
ns_item.setTag_(ns_tag)
|
||||
self._ns_menu.addItem_(ns_item)
|
||||
|
||||
export(Menu)
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#--------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - NumPy interface - Cocoa
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
from AppKit import NSBitmapImageRep, \
|
||||
NSAlphaNonpremultipliedBitmapFormat, NSCalibratedRGBColorSpace
|
||||
from GUI import Image
|
||||
|
||||
# HACK! PyObjC 2.3 incorrectly wraps the following method, so we change the
|
||||
# signature and pass the bitmap data in using ctypes.
|
||||
NSBitmapImageRep.__dict__['initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_'].signature = '@52@0:4^v8i12i16i20i24c28c32@36I40i44i48'
|
||||
import ctypes
|
||||
planes_t = ctypes.c_void_p * 5
|
||||
|
||||
def image_from_ndarray(array, format, size = None):
|
||||
"""
|
||||
Creates an Image from a numpy ndarray object. The format
|
||||
may be 'RGB' or 'RGBA'. If a size is specified, the array
|
||||
will be implicitly reshaped to that size, otherwise the size
|
||||
is inferred from the first two dimensions of the array.
|
||||
"""
|
||||
if array.itemsize <> 1:
|
||||
raise ValueError("Color component size must be 1 byte")
|
||||
if size is not None:
|
||||
width, height = size
|
||||
data_size = array.size
|
||||
pixel_size = data_size // (width * height)
|
||||
if pixel_size <> len(format):
|
||||
raise ValueError("Array has wrong shape for specified size and format")
|
||||
else:
|
||||
height, width, pixel_size = array.shape
|
||||
if pixel_size <> len(format):
|
||||
raise ValueError("Array has wrong shape for specified format")
|
||||
bps = 8
|
||||
spp = pixel_size
|
||||
alpha = format.endswith("A")
|
||||
csp = NSCalibratedRGBColorSpace
|
||||
bpp = bps * spp
|
||||
bpr = width * pixel_size
|
||||
fmt = NSAlphaNonpremultipliedBitmapFormat
|
||||
ns_rep = NSBitmapImageRep.alloc()
|
||||
planes = planes_t(array.ctypes.data, 0, 0, 0, 0)
|
||||
ns_rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(
|
||||
ctypes.addressof(planes), width, height, bps, spp, alpha, False, csp, fmt, bpr, bpp)
|
||||
image = Image.__new__(Image)
|
||||
image._init_from_ns_rep(ns_rep)
|
||||
image._data = array
|
||||
return image
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#--------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - PIL interface - Cocoa
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
import ctypes
|
||||
from AppKit import NSBitmapImageRep, \
|
||||
NSAlphaNonpremultipliedBitmapFormat, NSFloatingPointSamplesBitmapFormat, \
|
||||
NSDeviceCMYKColorSpace, NSCalibratedRGBColorSpace
|
||||
from GUI import Image
|
||||
|
||||
def hack_objc_sig():
|
||||
#print "GUI[Cocoa].PIL: Hacking objc method signature" ###
|
||||
# HACK! PyObjC 2.3 incorrectly wraps the following method, so we change the
|
||||
# signature and pass the bitmap data in using ctypes.
|
||||
NSBitmapImageRep.__dict__['initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_'].signature = '@52@0:4^v8i12i16i20i24c28c32@36I40i44i48'
|
||||
|
||||
planes_t = ctypes.c_char_p * 5
|
||||
|
||||
debug_pil = False
|
||||
|
||||
def image_from_pil_image(pil_image):
|
||||
"""Creates an Image from a Python Imaging Library (PIL)
|
||||
Image object."""
|
||||
mode = pil_image.mode
|
||||
w, h = pil_image.size
|
||||
data = pil_image.tostring()
|
||||
alpha = False
|
||||
cmyk = False
|
||||
floating = False
|
||||
if mode == "1":
|
||||
bps = 1; spp = 1
|
||||
elif mode == "L":
|
||||
bps = 8; spp = 1
|
||||
elif mode == "RGB":
|
||||
bps = 8; spp = 3
|
||||
elif mode == "RGBA":
|
||||
bps = 8; spp = 4; alpha = True
|
||||
elif mode == "CMYK":
|
||||
bps = 8; spp = 4; cmyk = True
|
||||
elif mode == "I":
|
||||
bps = 32; spp = 1
|
||||
elif mode == "F":
|
||||
bps = 32; spp = 1; floating = True
|
||||
else:
|
||||
raise ValueError("Unsupported PIL image mode '%s'" % mode)
|
||||
if cmyk:
|
||||
csp = NSDeviceCMYKColorSpace
|
||||
else:
|
||||
csp = NSCalibratedRGBColorSpace
|
||||
fmt = NSAlphaNonpremultipliedBitmapFormat
|
||||
if floating:
|
||||
fmt |= NSFloatingPointSamplesBitmapFormat
|
||||
bpp = bps * spp
|
||||
bpr = w * ((bpp + 7) // 8)
|
||||
if debug_pil:
|
||||
print "GUI.PIL:"
|
||||
print "image size =", (w, h)
|
||||
print "data size =", len(data)
|
||||
print "bits per sample =", bps
|
||||
print "samples per pixel =", spp
|
||||
print "bits per pixel =", bpp
|
||||
print "bytes per row =", bpr
|
||||
hack_objc_sig()
|
||||
ns_rep = NSBitmapImageRep.alloc()
|
||||
planes = planes_t(data, "", "", "", "")
|
||||
ns_rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(
|
||||
ctypes.addressof(planes), w, h, bps, spp, alpha, False, csp, fmt, bpr, bpp)
|
||||
# planes = (data, "", "", "", "")
|
||||
# ns_rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
||||
# planes, w, h, bps, spp, alpha, False, csp, bpr, bpp)
|
||||
image = Image.__new__(Image)
|
||||
image._init_from_ns_rep(ns_rep)
|
||||
image._data = data
|
||||
return image
|
||||
|
||||
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#
|
||||
# Python GUI - Pixmaps - Cocoa
|
||||
#
|
||||
|
||||
from Foundation import NSSize
|
||||
from AppKit import NSImage, NSCachedImageRep, NSBitmapImageRep, \
|
||||
NSCalibratedRGBColorSpace, NSImageCacheNever, NSGraphicsContext, \
|
||||
NSAffineTransform
|
||||
from GUI import export
|
||||
from GUI import Canvas
|
||||
from GUI.GPixmaps import Pixmap as GPixmap
|
||||
|
||||
class Pixmap(GPixmap):
|
||||
# _ns_bitmap_image_rep NSBitmapImageRep
|
||||
|
||||
def __init__(self, width, height):
|
||||
GPixmap.__init__(self)
|
||||
#ns_size = NSSize(width, height)
|
||||
#ns_image = NSImage.alloc().initWithSize_(ns_size)
|
||||
ns_image = NSImage.alloc().init()
|
||||
ns_image.setCacheMode_(NSImageCacheNever)
|
||||
row_bytes = 4 * width
|
||||
ns_bitmap = NSBitmapImageRep.alloc().\
|
||||
initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
||||
None, width, height, 8, 4, True, False, NSCalibratedRGBColorSpace, row_bytes, 32)
|
||||
ns_image.addRepresentation_(ns_bitmap)
|
||||
ns_bitmap_context = NSGraphicsContext.graphicsContextWithBitmapImageRep_(ns_bitmap)
|
||||
ns_graphics_context = FlippedNSGraphicsContext.alloc().initWithBase_(ns_bitmap_context)
|
||||
ns_tr = NSAffineTransform.transform()
|
||||
ns_tr.translateXBy_yBy_(0.0, height)
|
||||
ns_tr.scaleXBy_yBy_(1.0, -1.0)
|
||||
# Using __class__ to get +saveGraphicsState instead of -saveGraphicsState
|
||||
NSGraphicsContext.__class__.saveGraphicsState()
|
||||
try:
|
||||
NSGraphicsContext.setCurrentContext_(ns_graphics_context)
|
||||
ns_tr.concat()
|
||||
finally:
|
||||
NSGraphicsContext.__class__.restoreGraphicsState()
|
||||
self._init_with_ns_image(ns_image, flipped = True) #False)
|
||||
self._ns_bitmap_image_rep = ns_bitmap
|
||||
self._ns_graphics_context = ns_graphics_context
|
||||
|
||||
def with_canvas(self, proc):
|
||||
NSGraphicsContext.__class__.saveGraphicsState()
|
||||
NSGraphicsContext.setCurrentContext_(self._ns_graphics_context)
|
||||
try:
|
||||
canvas = Canvas()
|
||||
proc(canvas)
|
||||
finally:
|
||||
NSGraphicsContext.__class__.restoreGraphicsState()
|
||||
|
||||
class FlippedNSGraphicsContext(NSGraphicsContext):
|
||||
|
||||
def initWithBase_(self, base):
|
||||
self.base = base
|
||||
self.graphics_port = base.graphicsPort()
|
||||
return self
|
||||
|
||||
def isFlipped(self):
|
||||
return True
|
||||
|
||||
def graphicsPort(self):
|
||||
return self.graphics_port
|
||||
|
||||
def isDrawingToScreen(self):
|
||||
return self.base.isDrawingToScreen()
|
||||
|
||||
def setCompositingOperation_(self, x):
|
||||
self.base.setCompositingOperation_(x)
|
||||
|
||||
def focusStack(self):
|
||||
return self.base.focusStack()
|
||||
|
||||
def saveGraphicsState(self):
|
||||
return self.base.saveGraphicsState()
|
||||
|
||||
def restoreGraphicsState(self):
|
||||
return self.base.restoreGraphicsState()
|
||||
|
||||
export(Pixmap)
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Printing - Cocoa
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from AppKit import NSPrintInfo, NSPageLayout, NSPrintOperation, \
|
||||
NSKeyedArchiver, NSKeyedUnarchiver, NSData, NSAutoPagination, \
|
||||
NSPortraitOrientation, NSLandscapeOrientation, NSOKButton
|
||||
from GUI.GPrinting import PageSetup as GPageSetup, Printable as GPrintable
|
||||
|
||||
ns_to_generic_orientation = {
|
||||
NSPortraitOrientation: 'portrait',
|
||||
NSLandscapeOrientation: 'landscape',
|
||||
}
|
||||
|
||||
generic_to_ns_orientation = {
|
||||
'portrait': NSPortraitOrientation,
|
||||
'landscape': NSLandscapeOrientation,
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PageSetup(GPageSetup):
|
||||
|
||||
def __init__(self):
|
||||
ns_pi = NSPrintInfo.sharedPrintInfo().copy()
|
||||
ns_pi.setLeftMargin_(36)
|
||||
ns_pi.setTopMargin_(36)
|
||||
ns_pi.setRightMargin_(36)
|
||||
ns_pi.setBottomMargin_(36)
|
||||
ns_pi.setHorizontalPagination_(NSAutoPagination)
|
||||
self._ns_print_info = ns_pi
|
||||
|
||||
def __getstate__(self):
|
||||
state = GPageSetup.__getstate__(self)
|
||||
data = NSKeyedArchiver.archivedDataWithRootObject_(self._ns_print_info)
|
||||
state['_ns_print_info'] = data.bytes()
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
bytes = state.pop('_ns_print_info', None)
|
||||
if bytes:
|
||||
data = NSData.dataWithBytes_length_(bytes, len(bytes))
|
||||
self._ns_print_info = NSKeyedArchiver.unarchiveObjectWithData_(data)
|
||||
else:
|
||||
GPageSetup.__setstate__(self, state)
|
||||
|
||||
def copy(self, other):
|
||||
result = PageSetup.__new__()
|
||||
result._ns_print_info = other._ns_print_info.copy()
|
||||
|
||||
def get_paper_name(self):
|
||||
return self._ns_print_info.paperName()
|
||||
|
||||
def set_paper_name(self, x):
|
||||
self._ns_print_info.setPaperName_(x)
|
||||
|
||||
def get_paper_size(self):
|
||||
return tuple(self._ns_print_info.paperSize())
|
||||
|
||||
def set_paper_size(self, x):
|
||||
self._ns_print_info.setPaperSize_(x)
|
||||
|
||||
def get_paper_width(self):
|
||||
return self.paper_size[0]
|
||||
|
||||
def set_paper_width(self, x):
|
||||
self.paper_size = x, self.paper_height
|
||||
|
||||
def get_paper_height(self):
|
||||
return self.paper_size[1]
|
||||
|
||||
def set_paper_height(self, x):
|
||||
self.paper_size = self.paper_width, x
|
||||
|
||||
def get_left_margin(self):
|
||||
return self._ns_print_info.leftMargin()
|
||||
|
||||
def set_get_left_margin(self, x):
|
||||
self._ns_print_info.setLefMargin_(x)
|
||||
|
||||
def get_right_margin(self):
|
||||
return self._ns_print_info.rightMargin()
|
||||
|
||||
def set_get_right_margin(self, x):
|
||||
self._ns_print_info.setRightMargin_(x)
|
||||
|
||||
def get_top_margin(self):
|
||||
return self._ns_print_info.topMargin()
|
||||
|
||||
def set_get_top_margin(self, x):
|
||||
self._ns_print_info.setTopMargin_(x)
|
||||
|
||||
def get_bottom_margin(self):
|
||||
return self._ns_print_info.bottomMargin()
|
||||
|
||||
def set_get_bottom_margin(self, x):
|
||||
self._ns_print_info.setBottomMargin_(x)
|
||||
|
||||
def get_orientation(self):
|
||||
return ns_to_generic_orientation[self._ns_print_info.orientation()]
|
||||
|
||||
def set_orientation(self, x):
|
||||
nso = generic_to_ns_orientation.get(x, 'portrait')
|
||||
self._ns_print_info.setOrientation_(nso)
|
||||
|
||||
def get_printable_rect(self):
|
||||
l, b, w, h = self._ns_print_info.imageablePageBounds()
|
||||
return (l, b - h, l + w, b)
|
||||
|
||||
def get_printer_name(self):
|
||||
return self._ns_print_info.printer().name()
|
||||
|
||||
def set_printer_name(self, x):
|
||||
self._ns_print_info.setPrinter_(NSPrinter.printerWithName_(x))
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Printable(GPrintable):
|
||||
|
||||
def print_view(self, page_setup, prompt = True):
|
||||
ns_op = NSPrintOperation.printOperationWithView_printInfo_(
|
||||
self._ns_inner_view, page_setup._ns_print_info)
|
||||
ns_op.setShowsPrintPanel_(prompt)
|
||||
ns_op.runOperation()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def present_page_setup_dialog(page_setup):
|
||||
result = NSPageLayout.pageLayout().runModalWithPrintInfo_(page_setup._ns_print_info)
|
||||
return result == NSOKButton
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#
|
||||
# Python GUI - Radio buttons - PyObjC
|
||||
#
|
||||
|
||||
import AppKit
|
||||
from AppKit import NSOnState, NSOffState
|
||||
from GUI import export
|
||||
from GUI.StdFonts import system_font
|
||||
from GUI.ButtonBasedControls import ButtonBasedControl
|
||||
from GUI.GRadioButtons import RadioButton as GRadioButton
|
||||
|
||||
class RadioButton(ButtonBasedControl, GRadioButton):
|
||||
|
||||
def __init__(self, title = "New Radio Button", font = system_font, **kwds):
|
||||
ns_button = self._create_ns_button(title = title, font = font,
|
||||
ns_button_type = AppKit.NSRadioButton,
|
||||
ns_bezel_style = AppKit.NSRoundedBezelStyle)
|
||||
GRadioButton.__init__(self, _ns_view = ns_button, **kwds)
|
||||
|
||||
def do_action(self):
|
||||
if self._group:
|
||||
self._group.value = self._value
|
||||
else:
|
||||
self._ns_view.setState_(NSOffState)
|
||||
|
||||
def _value_changed(self):
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
if self._group and self._value == self._group._value:
|
||||
state = NSOnState
|
||||
else:
|
||||
state = NSOffState
|
||||
self._ns_view.setState_(state)
|
||||
|
||||
export(RadioButton)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#
|
||||
# Python GUI - Radio groups - PyObjC
|
||||
#
|
||||
|
||||
from GUI import export
|
||||
from GUI.GRadioGroups import RadioGroup as GRadioGroup
|
||||
|
||||
class RadioGroup(GRadioGroup):
|
||||
|
||||
def __init__(self, items = [], **kwds):
|
||||
GRadioGroup.__init__(self, items, **kwds)
|
||||
|
||||
def _item_added(self, item):
|
||||
item._update()
|
||||
|
||||
def _item_removed(self, item):
|
||||
pass
|
||||
|
||||
def _value_changed(self):
|
||||
for item in self._items:
|
||||
item._update()
|
||||
|
||||
export(RadioGroup)
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#
|
||||
# Python GUI - Scrollable Views - PyObjC
|
||||
#
|
||||
|
||||
from Foundation import NSPoint, NSMakeRect
|
||||
from AppKit import NSScrollView
|
||||
from GUI import export
|
||||
from GUI.GScrollableViews import ScrollableView as GScrollableView, \
|
||||
default_extent, default_line_scroll_amount, default_scrolling
|
||||
from GUI.Geometry import ns_rect_to_rect
|
||||
|
||||
class ScrollableView(GScrollableView):
|
||||
|
||||
_ns_scrollable = True
|
||||
|
||||
def __init__(self, extent = default_extent,
|
||||
line_scroll_amount = default_line_scroll_amount,
|
||||
scrolling = default_scrolling,
|
||||
**kwds):
|
||||
GScrollableView.__init__(self,
|
||||
extent = extent, line_scroll_amount = line_scroll_amount,
|
||||
scrolling = scrolling, **kwds)
|
||||
|
||||
def get_hscrolling(self):
|
||||
return self._ns_view.hasHorizontalScroller()
|
||||
|
||||
def set_hscrolling(self, value):
|
||||
self._ns_view.setHasHorizontalScroller_(value)
|
||||
|
||||
def get_vscrolling(self):
|
||||
return self._ns_view.hasVerticalScroller()
|
||||
|
||||
def set_vscrolling(self, value):
|
||||
self._ns_view.setHasVerticalScroller_(value)
|
||||
|
||||
# def get_extent(self):
|
||||
# (l, t), (w, h) = self._ns_inner_view.bounds()
|
||||
# return (l, t, l + w, t + h)
|
||||
|
||||
def get_extent(self):
|
||||
return self._ns_inner_view.bounds().size
|
||||
|
||||
# def set_extent(self, (l, t, r, b)):
|
||||
# w = r - l
|
||||
# h = b - t
|
||||
# ns_docview = self._ns_inner_view
|
||||
# ns_docview.setFrame_(NSMakeRect(0, 0, w, h))
|
||||
# ns_docview.setBounds_(NSMakeRect(l, t, w, h))
|
||||
# self.invalidate()
|
||||
|
||||
def set_extent(self, (w, h)):
|
||||
r = NSMakeRect(0, 0, w, h)
|
||||
ns_docview = self._ns_inner_view
|
||||
ns_docview.setFrame_(r)
|
||||
ns_docview.setBounds_(r)
|
||||
self.invalidate()
|
||||
|
||||
def get_content_size(self):
|
||||
return self._ns_view.contentSize()
|
||||
|
||||
def set_content_size(self, size):
|
||||
ns = self._ns_view
|
||||
self.size = NSScrollView.\
|
||||
frameSizeForContentSize_hasHorizontalScroller_hasVerticalScroller_borderType_(
|
||||
size, ns.hasHorizontalScroller(), ns.hasVerticalScroller(), ns.borderType())
|
||||
|
||||
def get_scroll_offset(self):
|
||||
ns_clip_view = self._ns_view.contentView()
|
||||
x, y = ns_clip_view.bounds().origin
|
||||
return x, y
|
||||
|
||||
def set_scroll_offset(self, (x, y)):
|
||||
ns_view = self._ns_view
|
||||
ns_clip_view = ns_view.contentView()
|
||||
new_pt = ns_clip_view.constrainScrollPoint_(NSPoint(x, y))
|
||||
ns_clip_view.scrollToPoint_(new_pt)
|
||||
ns_view.reflectScrolledClipView_(ns_clip_view)
|
||||
|
||||
def get_line_scroll_amount(self):
|
||||
ns_view = self._ns_view
|
||||
x = ns_view.horizontalLineScroll()
|
||||
y = ns_view.verticalLineScroll()
|
||||
return x, y
|
||||
|
||||
def set_line_scroll_amount(self, (x, y)):
|
||||
ns_view = self._ns_view
|
||||
ns_view.setHorizontalLineScroll_(x)
|
||||
ns_view.setVerticalLineScroll_(y)
|
||||
ns_view.setHorizontalPageScroll_(x)
|
||||
ns_view.setVerticalPageScroll_(y)
|
||||
|
||||
def viewed_rect(self):
|
||||
ns_rect = self._ns_view.contentView().documentVisibleRect()
|
||||
return ns_rect_to_rect(ns_rect)
|
||||
|
||||
export(ScrollableView)
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Slider - Cocoa
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from AppKit import NSSlider
|
||||
from GUI import export
|
||||
from GUI.StdFonts import system_font
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler, \
|
||||
ns_set_action, ns_size_to_fit
|
||||
from GUI.GSliders import Slider as GSlider
|
||||
|
||||
class Slider(GSlider):
|
||||
|
||||
_ns_handle_mouse = True
|
||||
|
||||
def __init__(self, orient = 'h', ticks = 0, **kwds):
|
||||
length = 100
|
||||
if ticks:
|
||||
breadth = 30
|
||||
else:
|
||||
breadth = 22 # Same as default height of a text-containing control
|
||||
if orient == 'h':
|
||||
ns_frame = ((0, 0), (length, breadth))
|
||||
elif orient == 'v':
|
||||
ns_frame = ((0, 0), (breadth, length))
|
||||
else:
|
||||
raise ValueError("Invalid orientation, should be 'h' or 'v'")
|
||||
ns_slider = PyGUI_NSSlider.alloc().initWithFrame_(ns_frame)
|
||||
ns_slider.pygui_component = self
|
||||
ns_set_action(ns_slider, 'doAction:')
|
||||
GSlider.__init__(self, _ns_view = ns_slider, **kwds)
|
||||
self.set_ticks(ticks)
|
||||
self._last_value = None
|
||||
|
||||
def get_min_value(self):
|
||||
return self._ns_view.minValue()
|
||||
|
||||
def set_min_value(self, x):
|
||||
self._ns_view.setMinValue_(x)
|
||||
|
||||
def get_max_value(self):
|
||||
return self._ns_view.maxValue()
|
||||
|
||||
def set_max_value(self, x):
|
||||
self._ns_view.setMaxValue_(x)
|
||||
|
||||
def get_value(self):
|
||||
return self._ns_view.doubleValue()
|
||||
|
||||
def set_value(self, x):
|
||||
self._ns_view.setDoubleValue_(x)
|
||||
|
||||
def get_ticks(self):
|
||||
return self._ns_view.numberOfTickMarks()
|
||||
|
||||
def set_ticks(self, x):
|
||||
self._ns_view.setNumberOfTickMarks_(x)
|
||||
|
||||
def get_discrete(self):
|
||||
return self._ns_view.allowsTickMarkValuesOnly()
|
||||
|
||||
def set_discrete(self, x):
|
||||
self._ns_view.setAllowsTickMarkValuesOnly_(x)
|
||||
|
||||
def get_live(self):
|
||||
return self._ns_view.isContinuous()
|
||||
|
||||
def set_live(self, x):
|
||||
self._ns_view.setContinuous_(x)
|
||||
|
||||
def do_action(self):
|
||||
value = self._ns_view.doubleValue()
|
||||
if value <> self._last_value:
|
||||
self._last_value = value
|
||||
GSlider.do_action(self)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSSlider(NSSlider, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
export(Slider)
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#
|
||||
# Python GUI - Standard Cursors - Cocoa
|
||||
#
|
||||
|
||||
from AppKit import NSCursor
|
||||
from GUI import Cursor
|
||||
|
||||
__all__ = [
|
||||
'arrow',
|
||||
'ibeam',
|
||||
'crosshair',
|
||||
'fist',
|
||||
'hand',
|
||||
'finger',
|
||||
'invisible',
|
||||
]
|
||||
|
||||
_empty_cursor = None
|
||||
|
||||
def _make_empty_cursor():
|
||||
global _empty_cursor
|
||||
if not _empty_cursor:
|
||||
from AppKit import NSCursor, NSImage, NSBitmapImageRep, NSDeviceRGBColorSpace
|
||||
from GUI import Cursor
|
||||
import sys
|
||||
if sys.version_info >= (3, 0):
|
||||
b = bytes([0])
|
||||
else:
|
||||
b = "\x00"
|
||||
d = b * 1024
|
||||
ns_bitmap = NSBitmapImageRep.alloc().\
|
||||
initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_\
|
||||
((d, d, d, d, d), 16, 16, 8, 4, True, False, NSDeviceRGBColorSpace, 64, 32)
|
||||
ns_image = NSImage.alloc().initWithSize_((16, 16))
|
||||
ns_image.addRepresentation_(ns_bitmap)
|
||||
ns_cursor = NSCursor.alloc().initWithImage_hotSpot_(ns_image, (0, 0))
|
||||
_empty_cursor = Cursor._from_ns_cursor(ns_cursor)
|
||||
_empty_cursor._data = d
|
||||
return _empty_cursor
|
||||
|
||||
arrow = Cursor._from_ns_cursor(NSCursor.arrowCursor())
|
||||
ibeam = Cursor._from_ns_cursor(NSCursor.IBeamCursor())
|
||||
crosshair = Cursor._from_ns_cursor(NSCursor.crosshairCursor())
|
||||
fist = Cursor._from_ns_cursor(NSCursor.closedHandCursor())
|
||||
hand = Cursor._from_ns_cursor(NSCursor.openHandCursor())
|
||||
finger = Cursor._from_ns_cursor(NSCursor.pointingHandCursor())
|
||||
invisible = _make_empty_cursor()
|
||||
|
||||
mac_poof = Cursor._from_ns_cursor(NSCursor.disappearingItemCursor())
|
||||
|
||||
del NSCursor
|
||||
del Cursor
|
||||
del _make_empty_cursor
|
||||
|
||||
def empty_cursor():
|
||||
return invisible
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# Python GUI - Standard Fonts - PyObjC
|
||||
#
|
||||
|
||||
from AppKit import NSFont
|
||||
from GUI import Font
|
||||
|
||||
system_font = Font._from_ns_font(NSFont.systemFontOfSize_(0))
|
||||
application_font = Font._from_ns_font(NSFont.userFontOfSize_(0))
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#
|
||||
# Python GUI - Standard Menus - PyObjC
|
||||
#
|
||||
|
||||
from GUI.GStdMenus import build_menus, \
|
||||
fundamental_cmds, help_cmds, pref_cmds, file_cmds, print_cmds, edit_cmds
|
||||
|
||||
fundamental_cmds += ['hide_app_cmd', 'hide_other_apps_cmd', 'show_all_apps_cmd']
|
||||
|
||||
_appl_menu_items = [
|
||||
("About <app>", 'about_cmd'),
|
||||
"-",
|
||||
("Preferences...", 'preferences_cmd'),
|
||||
"-",
|
||||
("Hide <app>/H", 'hide_app_cmd'),
|
||||
("Hide Others", 'hide_other_apps_cmd'),
|
||||
("Show All", 'show_all_apps_cmd'),
|
||||
"-",
|
||||
("Quit <app>/Q", 'quit_cmd'),
|
||||
]
|
||||
|
||||
_file_menu_items = [
|
||||
("New/N", 'new_cmd'),
|
||||
("Open.../O", 'open_cmd'),
|
||||
("Close/W", 'close_cmd'),
|
||||
"-",
|
||||
("Save/S", 'save_cmd'),
|
||||
("Save As...", 'save_as_cmd'),
|
||||
("Revert", 'revert_cmd'),
|
||||
"-",
|
||||
("Page Setup...", 'page_setup_cmd'),
|
||||
("Print.../P", 'print_cmd'),
|
||||
]
|
||||
|
||||
_edit_menu_items = [
|
||||
("Undo/Z", 'undo_cmd'),
|
||||
("Redo/^Z", 'redo_cmd'),
|
||||
"-",
|
||||
("Cut/X", 'cut_cmd'),
|
||||
("Copy/C", 'copy_cmd'),
|
||||
("Paste/V", 'paste_cmd'),
|
||||
("Delete", 'clear_cmd'),
|
||||
"-",
|
||||
("Select All/A", 'select_all_cmd'),
|
||||
]
|
||||
|
||||
_help_menu_items = [
|
||||
("Help", 'help_cmd'),
|
||||
]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def basic_menus(substitutions = {}, include = None, exclude = None):
|
||||
return build_menus([
|
||||
("@", _appl_menu_items, False),
|
||||
("File", _file_menu_items, False),
|
||||
("Edit", _edit_menu_items, False),
|
||||
("Help", _help_menu_items, True),
|
||||
],
|
||||
substitutions = substitutions,
|
||||
include = include,
|
||||
exclude = exclude)
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
#
|
||||
# PyGUI - Tasks - Cocoa
|
||||
#
|
||||
|
||||
import sys
|
||||
from weakref import WeakValueDictionary
|
||||
from Foundation import NSTimer, NSRunLoop, NSDefaultRunLoopMode
|
||||
from AppKit import NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode
|
||||
from GUI import export
|
||||
from GUI import Globals
|
||||
from GUI.GTasks import Task as GTask
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
#
|
||||
# Doing things this convoluted way to work around a memory
|
||||
# leak in PyObjC. Need to avoid having the NSTimer trigger
|
||||
# creation of a bound method each time it fires or the bound
|
||||
# methods leak. Also can't use the userInfo of the NSTimer as
|
||||
# it seems to leak too.
|
||||
|
||||
ns_timer_to_task = WeakValueDictionary()
|
||||
|
||||
class TaskTrigger(object):
|
||||
pass
|
||||
|
||||
def fire_(ns_timer):
|
||||
ns_timer_to_task[ns_timer]._ns_fire()
|
||||
|
||||
trigger = TaskTrigger()
|
||||
trigger.fire_ = fire_
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class Task(GTask):
|
||||
|
||||
def __init__(self, proc, interval, repeat = 0, start = 1):
|
||||
self._proc = proc
|
||||
self._interval = interval
|
||||
self._repeat = repeat
|
||||
self._ns_timer = None
|
||||
if start:
|
||||
self.start()
|
||||
|
||||
def destroy(self):
|
||||
#print "Task.destroy:", self ###
|
||||
self.stop()
|
||||
|
||||
def get_scheduled(self):
|
||||
return self._ns_timer is not None
|
||||
|
||||
def get_interval(self):
|
||||
return self._interval
|
||||
|
||||
def get_repeat(self):
|
||||
return self._repeat
|
||||
|
||||
def start(self):
|
||||
self.stop()
|
||||
#ns_timer = \
|
||||
# NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
# self._interval, self._target, '_ns_fire', None, self._repeat)
|
||||
ns_timer = \
|
||||
NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
self._interval, trigger, 'fire:', None, self._repeat)
|
||||
self._ns_timer = ns_timer
|
||||
ns_timer_to_task[ns_timer] = self
|
||||
ns_run_loop = NSRunLoop.currentRunLoop()
|
||||
ns_run_loop.addTimer_forMode_(
|
||||
ns_timer, NSDefaultRunLoopMode)
|
||||
ns_run_loop.addTimer_forMode_(
|
||||
ns_timer, NSEventTrackingRunLoopMode)
|
||||
ns_run_loop.addTimer_forMode_(
|
||||
ns_timer, NSModalPanelRunLoopMode)
|
||||
|
||||
def stop(self):
|
||||
ns_timer = self._ns_timer
|
||||
if ns_timer:
|
||||
ns_timer.invalidate()
|
||||
del ns_timer_to_task[ns_timer]
|
||||
self._ns_timer = None
|
||||
|
||||
def _ns_fire(self):
|
||||
try:
|
||||
self._proc()
|
||||
except:
|
||||
Globals.pending_exception = sys.exc_info()
|
||||
self.stop()
|
||||
|
||||
export(Task)
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Text Editor - Cocoa
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from AppKit import NSTextView, NSScrollView, NSViewWidthSizable, \
|
||||
NSMutableParagraphStyle
|
||||
from GUI import export
|
||||
from GUI import StdFonts
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler
|
||||
from GUI.GTextEditors import TextEditor as GTextEditor
|
||||
|
||||
NUM_TAB_STOPS = 32
|
||||
|
||||
class TextEditor(GTextEditor):
|
||||
|
||||
_ns_handle_mouse = True
|
||||
|
||||
def __init__(self, scrolling = 'hv', **kwds):
|
||||
width = 100
|
||||
height = 100
|
||||
frame = ((0, 0), (width, height))
|
||||
ns_outer = NSScrollView.alloc().initWithFrame_(frame)
|
||||
ns_outer.setHasHorizontalScroller_('h' in scrolling)
|
||||
ns_outer.setHasVerticalScroller_('v' in scrolling)
|
||||
if 'h' in scrolling:
|
||||
cwidth = 2000
|
||||
else:
|
||||
cwidth = ns_outer.contentSize()[0]
|
||||
frame = ((0, 0), (cwidth, height))
|
||||
ns_inner = PyGUI_NSTextView.alloc().initWithFrame_(frame)
|
||||
ns_inner.pygui_component = self
|
||||
ps = NSMutableParagraphStyle.alloc().init()
|
||||
ps.setDefaultTabInterval_(ps.tabStops()[0].location())
|
||||
ps.setTabStops_([])
|
||||
ns_inner.setDefaultParagraphStyle_(ps)
|
||||
ns_inner.setAllowsUndo_(True)
|
||||
ns_outer.setDocumentView_(ns_inner)
|
||||
if 'h' not in scrolling:
|
||||
ns_inner.setAutoresizingMask_(NSViewWidthSizable)
|
||||
if 'font' not in kwds:
|
||||
kwds['font'] = StdFonts.application_font
|
||||
GTextEditor.__init__(self, ns_outer,
|
||||
_ns_inner_view = ns_inner, **kwds)
|
||||
|
||||
def get_text(self):
|
||||
return self._ns_inner_view.string()
|
||||
|
||||
def set_text(self, value):
|
||||
self._ns_inner_view.setString_(value)
|
||||
self._ns_apply_style()
|
||||
|
||||
def get_text_length(self):
|
||||
return self._ns_inner_view.textStorage().length()
|
||||
|
||||
def get_selection(self):
|
||||
start, length = self._ns_inner_view.selectedRanges()[0].rangeValue()
|
||||
return (start, start + length)
|
||||
|
||||
def set_selection(self, value):
|
||||
start, stop = value
|
||||
self._ns_inner_view.setSelectedRange_((start, stop - start))
|
||||
|
||||
def get_font(self):
|
||||
return self._font
|
||||
|
||||
def set_font(self, font):
|
||||
self._font = font
|
||||
self._ns_inner_view.setFont_(font._ns_font)
|
||||
|
||||
def get_tab_spacing(self):
|
||||
#ns_storage = self._ns_inner_view.textStorage()
|
||||
#ps, _ = ns_storage.attribute_atIndex_effectiveRange_("NSParagraphStyle", 0)
|
||||
ps = self._ns_inner_view.defaultParagraphStyle()
|
||||
return ps.defaultTabInterval()
|
||||
|
||||
def set_tab_spacing(self, x):
|
||||
ps = NSMutableParagraphStyle.alloc().init()
|
||||
ps.setTabStops_([])
|
||||
ps.setDefaultTabInterval_(x)
|
||||
self._ns_inner_view.setDefaultParagraphStyle_(ps)
|
||||
self._ns_apply_style()
|
||||
|
||||
def paste_cmd(self):
|
||||
GTextEditor.paste_cmd(self)
|
||||
self._ns_apply_style()
|
||||
|
||||
def _ns_apply_style(self):
|
||||
ns_textview = self._ns_inner_view
|
||||
ps = ns_textview.defaultParagraphStyle()
|
||||
font = ns_textview.font()
|
||||
ns_storage = self._ns_inner_view.textStorage()
|
||||
ns_storage.setAttributes_range_(
|
||||
{"NSParagraphStyle": ps, "NSFont": font},
|
||||
(0, self.text_length))
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSTextView(NSTextView, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
export(TextEditor)
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#
|
||||
# Python GUI - Text Fields - PyObjC
|
||||
#
|
||||
|
||||
from Foundation import NSRange
|
||||
from GUI import export
|
||||
from GUI.StdFonts import system_font #application_font
|
||||
from GUI import EditCmdHandler
|
||||
from GUI.TextFieldBasedControls import TextFieldBasedControl
|
||||
from GUI.GTextFields import TextField as GTextField
|
||||
|
||||
class TextField(TextFieldBasedControl, GTextField):
|
||||
|
||||
#_vertical_padding = 5
|
||||
_intercept_tab_key = False
|
||||
|
||||
def __init__(self, text = "", font = system_font,
|
||||
multiline = False, password = False, border = True, **kwds):
|
||||
ns_textfield = self._create_ns_textfield(editable = True,
|
||||
multiline = multiline, password = password,
|
||||
text = text, font = font, border = border)
|
||||
GTextField.__init__(self, _ns_view = ns_textfield,
|
||||
multiline = multiline, **kwds)
|
||||
|
||||
def get_selection(self):
|
||||
ns_editor = self._ns_editor()
|
||||
if ns_editor:
|
||||
start, length = ns_editor.selectedRange()
|
||||
return (start, start + length)
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def set_selection(self, (start, end)):
|
||||
self.become_target()
|
||||
ns_editor = self._ns_editor()
|
||||
if ns_editor:
|
||||
ns_editor.setSelectedRange_(NSRange(start, end - start))
|
||||
|
||||
def select_all(self):
|
||||
self.become_target()
|
||||
self._ns_view.selectText_(None)
|
||||
|
||||
def _ns_editor(self):
|
||||
return self._ns_view.currentEditor()
|
||||
|
||||
def _ns_edit_cmd_target(self):
|
||||
return self._ns_editor()
|
||||
|
||||
export(TextField)
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
#
|
||||
# Python GUI - PyObjC
|
||||
#
|
||||
# Base class for controls based on an NSTextField
|
||||
#
|
||||
|
||||
from Foundation import NSRect, NSPoint, NSSize
|
||||
from AppKit import NSTextField, NSSecureTextField, NSTextFieldCell
|
||||
from GUI.Utils import NSMultiClass
|
||||
from GUI import Color
|
||||
from GUI.Utils import ns_size_to_fit, PyGUI_NS_EventHandler
|
||||
from GUI.StdFonts import system_font
|
||||
|
||||
class TextFieldBasedControl(object):
|
||||
|
||||
_ns_handle_mouse = True
|
||||
|
||||
def _create_ns_textfield(self, editable, text, font,
|
||||
multiline = False, password = False, border = False,
|
||||
padding = (0, 0)):
|
||||
self._ns_is_password = password
|
||||
if password:
|
||||
ns_class = PyGUI_NSSecureTextField
|
||||
else:
|
||||
ns_class = PyGUI_NSTextField
|
||||
ns_frame = NSRect(NSPoint(0, 0), NSSize(20, 10))
|
||||
ns_textfield = ns_class.alloc().initWithFrame_(ns_frame)
|
||||
ns_textfield.pygui_component = self
|
||||
if multiline and not password:
|
||||
ns_textfield.pygui_multiline = True
|
||||
# Be careful here -- calling setBordered_ seems to affect isBezeled as well
|
||||
if editable:
|
||||
ns_textfield.setBezeled_(border)
|
||||
else:
|
||||
ns_textfield.setBordered_(border)
|
||||
if not editable:
|
||||
ns_textfield.setDrawsBackground_(False)
|
||||
ns_textfield.setEditable_(editable)
|
||||
ns_textfield.setSelectable_(editable)
|
||||
ns_textfield.setFont_(font._ns_font)
|
||||
ns_textfield.setStringValue_(text)
|
||||
ns_size_to_fit(ns_textfield, padding = padding)
|
||||
return ns_textfield
|
||||
|
||||
def get_border(self):
|
||||
ns_textfield = self._ns_inner_view
|
||||
if ns_textfield.isEditable():
|
||||
return ns_textfield.isBezeled()
|
||||
else:
|
||||
return ns_textfield.isBordered()
|
||||
|
||||
def set_border(self, border):
|
||||
ns_textfield = self._ns_inner_view
|
||||
if ns_textfield.isEditable():
|
||||
ns_textfield.setBezeled_(border)
|
||||
else:
|
||||
ns_textfield.setBordered_(border)
|
||||
|
||||
def get_text(self):
|
||||
return self._ns_inner_view.stringValue()
|
||||
|
||||
def set_text(self, v):
|
||||
self._ns_inner_view.setStringValue_(v)
|
||||
|
||||
def get_color(self):
|
||||
return Color._from_ns_color(self._ns_inner_view.textColor())
|
||||
|
||||
def set_color(self, v):
|
||||
self._ns_inner_view.setTextColor_(v._ns_color)
|
||||
|
||||
def _get_vertical_padding(self):
|
||||
if self.border:
|
||||
return 5
|
||||
else:
|
||||
return 0
|
||||
|
||||
_vertical_padding = property(_get_vertical_padding)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSTextField(NSTextField): #, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
|
||||
pygui_multiline = False
|
||||
|
||||
class PyGUI_NSSecureTextField(NSSecureTextField): #, PyGUI_NS_EventHandler):
|
||||
__metaclass__ = NSMultiClass
|
||||
|
||||
pygui_multiline = False
|
||||
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Utilities - PyObjC
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from math import ceil
|
||||
from inspect import getmro
|
||||
from Foundation import NSObject
|
||||
from AppKit import NSView
|
||||
from GUI import Event, Globals
|
||||
|
||||
def NSMultiClass(name, bases, dic):
|
||||
# Workaround for PyObjC classes not supporting
|
||||
# multiple inheritance properly. Note: MRO is
|
||||
# right to left across the bases.
|
||||
main = bases[0]
|
||||
slots = list(dic.get('__slots__', ()))
|
||||
dic2 = {}
|
||||
for mix in bases[1:]:
|
||||
for cls in getmro(mix)[::-1]:
|
||||
slots2 = cls.__dict__.get('__slots__')
|
||||
if slots2:
|
||||
for slot in slots2:
|
||||
if slot not in slots:
|
||||
slots.append(slot)
|
||||
dic2.update(cls.__dict__)
|
||||
dic2.update(dic)
|
||||
if slots:
|
||||
dic2['__slots__'] = slots
|
||||
cls = type(main)(name, (main,), dic2)
|
||||
return cls
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_Flipped_NSView(NSView):
|
||||
# An NSView with a flipped coordinate system.
|
||||
|
||||
def isFlipped(self):
|
||||
return True
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSActionTarget(NSObject):
|
||||
# A shared instance of this class is used as the target of
|
||||
# all action messages from the NSViews of Components. It
|
||||
# performs the action by calling the similarly-named method of
|
||||
# the corresponding Component.
|
||||
|
||||
def doAction_(self, ns_sender):
|
||||
self.call_method('do_action', ns_sender)
|
||||
|
||||
def call_method(self, method_name, ns_sender):
|
||||
component = Globals._ns_view_to_component.get(ns_sender)
|
||||
if component:
|
||||
getattr(component, method_name)()
|
||||
|
||||
_ns_action_target = PyGUI_NSActionTarget.alloc().init()
|
||||
|
||||
def ns_set_action(ns_control, method_name):
|
||||
# Arrange for the 'action' message of the NSControl to
|
||||
# invoke the indicated method of its associated Component.
|
||||
ns_control.setAction_(method_name)
|
||||
ns_control.setTarget_(_ns_action_target)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NS_EventHandler:
|
||||
# Methods to be mixed in with NSView subclasses that are
|
||||
# to relay mouse and keyboard events to a Component.
|
||||
#
|
||||
# pygui_component Component
|
||||
|
||||
def mouseDown_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def mouseUp_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def mouseDragged_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def rightMouseDown_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def rightMouseUp_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def rightMouseDragged_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def otherMouseDown_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def otherMouseUp_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def otherMouseDragged_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def mouseMoved_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def mouseEntered_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def mouseExited_(self, ns_event):
|
||||
self._ns_mouse_event(ns_event)
|
||||
|
||||
def keyDown_(self, ns_event):
|
||||
#print "PyGUI_NS_EventHandler.keyDown_:", repr(ns_event.characters()), \
|
||||
# "for", object.__repr__(self) ###
|
||||
self._ns_other_event(ns_event)
|
||||
|
||||
def keyUp_(self, ns_event):
|
||||
#print "PyGUI_NS_EventHandler.keyUp_ for", self ###
|
||||
self._ns_other_event(ns_event)
|
||||
|
||||
def _ns_mouse_event(self, ns_event):
|
||||
#print "PyGUI_NS_EventHandler._ns_mouse_event" ###
|
||||
event = self._ns_mouse_event_to_event(ns_event)
|
||||
#print "...sending to", self.pygui_component ###
|
||||
component = self.pygui_component
|
||||
if component:
|
||||
component.handle_event_here(event)
|
||||
|
||||
def _ns_mouse_event_to_event(self, ns_event):
|
||||
event = Event(ns_event)
|
||||
event.position = tuple(self._ns_event_position(ns_event))
|
||||
return event
|
||||
|
||||
def _ns_event_position(self, ns_event):
|
||||
#print "PyGUI_NS_EventHandler._ns_event_position:", self ###
|
||||
#print "...mro =", self.__class__.__mro__ ###
|
||||
ns_win_pos = ns_event.locationInWindow()
|
||||
return self.convertPoint_fromView_(ns_win_pos, None)
|
||||
|
||||
def _ns_other_event(self, ns_event):
|
||||
#print "PyGUI_NS_EventHandler._ns_other_event for", self ###
|
||||
event = Event(ns_event)
|
||||
component = self.pygui_component
|
||||
if component:
|
||||
#print "...passing", event.kind, "to", component ###
|
||||
component.handle_event(event)
|
||||
|
||||
def acceptsFirstResponder(self):
|
||||
###return True
|
||||
return self.pygui_component._ns_accept_first_responder
|
||||
|
||||
# def canBecomeKeyView(self):
|
||||
# return self.pygui_component._tab_stop
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NS_ViewBase(PyGUI_NS_EventHandler):
|
||||
# Methods to be mixed in with PyGUI_NSView classes.
|
||||
#
|
||||
# pygui_component ViewBase
|
||||
|
||||
__slots__ = ['tracking_rect']
|
||||
|
||||
# tracking_rect = None
|
||||
|
||||
def becomeFirstResponder(self):
|
||||
self.pygui_component.targeted()
|
||||
return True
|
||||
|
||||
def resignFirstResponder(self):
|
||||
self.pygui_component.untargeted()
|
||||
return True
|
||||
|
||||
def resetCursorRects(self):
|
||||
#print "PyGUI_NS_ViewBase: resetCursorRects" ###
|
||||
self.removeCursorRects()
|
||||
self.tracking_rect = self.addTrackingRect_owner_userData_assumeInside_(
|
||||
self.visibleRect(), self, 0, False)
|
||||
self.pygui_component._ns_reset_cursor_rects()
|
||||
|
||||
def removeCursorRects(self):
|
||||
#print "PyGUI_NS_ViewBase: removeCursorRects" ###
|
||||
tag = getattr(self, 'tracking_rect', None)
|
||||
if tag:
|
||||
self.removeTrackingRect_(tag)
|
||||
self.tracking_rect = None
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def ns_size_to_fit(ns_control, padding = (0, 0), height = None):
|
||||
# Set size of control to fit its contents, plus the given padding.
|
||||
# Height may be overridden, because some controls don't seem to
|
||||
# calculate it properly.
|
||||
# Auto sizing can result in fractional sizes, which seems to cause
|
||||
# problems when NS autoresizing occurs later. So we round the size up
|
||||
# to whole numbers of pixels.
|
||||
ns_control.sizeToFit()
|
||||
w, h = ns_control.frame().size
|
||||
pw, ph = padding
|
||||
ns_control.setFrameSize_((ceil(w + pw), ceil((height or h) + ph)))
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#
|
||||
# Python GUI - Views - PyObjC
|
||||
#
|
||||
|
||||
from GUI import export
|
||||
from GUI.GViews import View as GView
|
||||
|
||||
class View(GView):
|
||||
|
||||
_ns_scrollable = False
|
||||
|
||||
export(View)
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#
|
||||
# Python GUI - View Base - PyObjC
|
||||
#
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
from GUI import Globals, export
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Event
|
||||
from GUI.Utils import PyGUI_NS_EventHandler
|
||||
from GUI.GViewBases import ViewBase as GViewBase
|
||||
|
||||
ns_tracking_mask = (
|
||||
AppKit.NSLeftMouseDraggedMask |
|
||||
AppKit.NSRightMouseDraggedMask |
|
||||
AppKit.NSOtherMouseDraggedMask |
|
||||
AppKit.NSLeftMouseUpMask |
|
||||
AppKit.NSRightMouseUpMask |
|
||||
AppKit.NSOtherMouseUpMask)
|
||||
|
||||
# Need to use NSDefaultRunLoopMode here otherwise timers don't fire.
|
||||
ns_tracking_mode = Foundation.NSDefaultRunLoopMode # AppKit.NSEventTrackingRunLoopMode
|
||||
|
||||
ns_distant_future = Foundation.NSDate.distantFuture()
|
||||
|
||||
|
||||
class ViewBase(GViewBase):
|
||||
|
||||
def _change_container(self, new_container):
|
||||
self._ns_inner_view.removeCursorRects()
|
||||
super(ViewBase, self)._change_container(new_container)
|
||||
|
||||
def _ns_track_mouse(self, ns_view):
|
||||
ns_app = Globals.ns_application
|
||||
tracking = True
|
||||
while tracking:
|
||||
ns_event = ns_app.nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
ns_tracking_mask, ns_distant_future, ns_tracking_mode, True)
|
||||
event = ns_view._ns_mouse_event_to_event(ns_event)
|
||||
yield event
|
||||
tracking = event.kind <> 'mouse_up'
|
||||
|
||||
def _cursor_changed(self):
|
||||
#print "ViewBase._cursor_changed:", self ###
|
||||
ns_view = self._ns_view
|
||||
ns_window = ns_view.window()
|
||||
if ns_window:
|
||||
# invalidateCursorRectsForView_ doesn't seem to trigger
|
||||
# resetCursorRects on the view.
|
||||
#ns_window.invalidateCursorRectsForView_(ns_view)
|
||||
ns_window.resetCursorRects()
|
||||
|
||||
def _ns_reset_cursor_rects(self):
|
||||
#print "ViewBase._ns_reset_cursor_rects:", self ###
|
||||
cursor = self._cursor
|
||||
if cursor:
|
||||
ns_view = self._ns_inner_view
|
||||
ns_rect = ns_view.visibleRect()
|
||||
ns_view.addCursorRect_cursor_(ns_rect, cursor._ns_cursor)
|
||||
|
||||
export(ViewBase)
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Windows - PyObjC version
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from Foundation import NSRect, NSPoint, NSSize, NSObject
|
||||
import AppKit
|
||||
from AppKit import NSWindow, NSScreen, NSTextView, NSMenu
|
||||
from GUI import export
|
||||
from GUI import Globals
|
||||
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler, PyGUI_Flipped_NSView
|
||||
from GUI import application
|
||||
from GUI import Event
|
||||
from GUI.GWindows import Window as GWindow
|
||||
|
||||
_default_options_for_style = {
|
||||
'standard':
|
||||
{'movable': 1, 'closable': 1, 'hidable': 1, 'resizable': 1},
|
||||
'nonmodal_dialog':
|
||||
{'movable': 1, 'closable': 0, 'hidable': 1, 'resizable': 0},
|
||||
'modal_dialog':
|
||||
{'movable': 1, 'closable': 0, 'hidable': 0, 'resizable': 0},
|
||||
'alert':
|
||||
{'movable': 1, 'closable': 0, 'hidable': 0, 'resizable': 0},
|
||||
'fullscreen':
|
||||
{'movable': 0, 'closable': 0, 'hidable': 0, 'resizable': 0},
|
||||
#{'movable': 1, 'closable': 1, 'hidable': 1, 'resizable': 1},
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Window(GWindow):
|
||||
# _ns_window PyGUI_NSWindow
|
||||
# _ns_style_mask int
|
||||
|
||||
def __init__(self, style = 'standard', zoomable = None, **kwds):
|
||||
# We ignore zoomable, since it's the same as resizable.
|
||||
self._style = style
|
||||
options = dict(_default_options_for_style[style])
|
||||
for option in ['movable', 'closable', 'hidable', 'resizable']:
|
||||
if option in kwds:
|
||||
options[option] = kwds.pop(option)
|
||||
self._ns_style_mask = self._ns_window_style_mask(**options)
|
||||
if style == 'fullscreen':
|
||||
ns_rect = NSScreen.mainScreen().frame()
|
||||
else:
|
||||
ns_rect = NSRect(NSPoint(0, 0), NSSize(self._default_width, self._default_height))
|
||||
ns_window = PyGUI_NSWindow.alloc()
|
||||
ns_window.initWithContentRect_styleMask_backing_defer_(
|
||||
ns_rect, self._ns_style_mask, AppKit.NSBackingStoreBuffered, True)
|
||||
ns_content = PyGUI_NS_ContentView.alloc()
|
||||
ns_content.initWithFrame_(NSRect(NSPoint(0, 0), NSSize(0, 0)))
|
||||
ns_content.pygui_component = self
|
||||
ns_window.setContentView_(ns_content)
|
||||
ns_window.setAcceptsMouseMovedEvents_(True)
|
||||
ns_window.setDelegate_(ns_window)
|
||||
ns_window.pygui_component = self
|
||||
self._ns_window = ns_window
|
||||
GWindow.__init__(self, style = style, closable = options['closable'],
|
||||
_ns_view = ns_window.contentView(), _ns_responder = ns_window,
|
||||
_ns_set_autoresizing_mask = False,
|
||||
**kwds)
|
||||
|
||||
def _ns_window_style_mask(self, movable, closable, hidable, resizable):
|
||||
if movable or closable or hidable or resizable:
|
||||
mask = AppKit.NSTitledWindowMask
|
||||
if closable:
|
||||
mask |= AppKit.NSClosableWindowMask
|
||||
if hidable:
|
||||
mask |= AppKit.NSMiniaturizableWindowMask
|
||||
if resizable:
|
||||
mask |= AppKit.NSResizableWindowMask
|
||||
else:
|
||||
mask = AppKit.NSBorderlessWindowMask
|
||||
return mask
|
||||
|
||||
def destroy(self):
|
||||
#print "Window.destroy:", self ###
|
||||
self.hide()
|
||||
app = application()
|
||||
if app._ns_key_window is self:
|
||||
app._ns_key_window = None
|
||||
GWindow.destroy(self)
|
||||
# We can't drop all references to the NSWindow yet, because this method
|
||||
# can be called from its windowShouldClose: method, and allowing an
|
||||
# NSWindow to be released while executing one of its own methods seems
|
||||
# to be a very bad idea (Cocoa hangs). So we hide the NSWindow and store
|
||||
# a reference to it in a global. It will be released the next time a
|
||||
# window is closed and the global is re-used.
|
||||
global _ns_zombie_window
|
||||
_ns_zombie_window = self._ns_window
|
||||
self._ns_window.pygui_component = None
|
||||
#self._ns_window = None
|
||||
|
||||
def get_bounds(self):
|
||||
ns_window = self._ns_window
|
||||
ns_frame = ns_window.frame()
|
||||
(l, y), (w, h) = ns_window.contentRectForFrameRect_styleMask_(
|
||||
ns_frame, self._ns_style_mask)
|
||||
b = Globals.ns_screen_height - y
|
||||
result = (l, b - h, l + w, b)
|
||||
return result
|
||||
|
||||
def set_bounds(self, (l, t, r, b)):
|
||||
y = Globals.ns_screen_height - b
|
||||
ns_rect = NSRect(NSPoint(l, y), NSSize(r - l, b - t))
|
||||
ns_window = self._ns_window
|
||||
ns_frame = ns_window.frameRectForContentRect_styleMask_(
|
||||
ns_rect, self._ns_style_mask)
|
||||
ns_window.setFrame_display_(ns_frame, False)
|
||||
|
||||
def get_title(self):
|
||||
return self._ns_window.title()
|
||||
|
||||
def set_title(self, v):
|
||||
self._ns_window.setTitle_(v)
|
||||
|
||||
def get_visible(self):
|
||||
return self._ns_window.isVisible()
|
||||
|
||||
def set_visible(self, v):
|
||||
# At some mysterious time between creating a window and showing
|
||||
# it for the first time, Cocoa adjusts its position so that it
|
||||
# doesn't extend above the menu bar. This is a nuisance for
|
||||
# our fullscreen windows, so we need to readjust the position
|
||||
# before showing.
|
||||
if v:
|
||||
if self._style == 'fullscreen':
|
||||
self._ns_window.setFrameOrigin_(NSPoint(0, 0))
|
||||
self._ns_window.orderFront_(None)
|
||||
else:
|
||||
self._ns_window.orderOut_(None)
|
||||
|
||||
def _show(self):
|
||||
self.visible = True
|
||||
self._ns_window.makeKeyWindow()
|
||||
|
||||
def get_target(self):
|
||||
ns_window = self._ns_window
|
||||
ns_view = ns_window.firstResponder()
|
||||
while ns_view and ns_view is not ns_window:
|
||||
component = Globals._ns_view_to_component.get(ns_view)
|
||||
if component:
|
||||
return component
|
||||
ns_view = ns_view.superview()
|
||||
return self
|
||||
|
||||
def center(self):
|
||||
self._ns_window.center()
|
||||
|
||||
def _stagger(self):
|
||||
key_win = application()._ns_key_window
|
||||
if key_win:
|
||||
(x, y), (w, h) = key_win._ns_window.frame()
|
||||
p = self._ns_window.cascadeTopLeftFromPoint_(NSPoint(x, y + h))
|
||||
self._ns_window.setFrameTopLeftPoint_(p)
|
||||
else:
|
||||
(x, y), (w, h) = NSScreen.mainScreen().visibleFrame()
|
||||
ns_vis_topleft = NSPoint(x, y + h)
|
||||
self._ns_window.setFrameTopLeftPoint_(ns_vis_topleft)
|
||||
|
||||
def _document_needs_saving(self, state):
|
||||
self._ns_window.setDocumentEdited_(state)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NSWindow(NSWindow, PyGUI_NS_EventHandler):
|
||||
# pygui_component Window
|
||||
# resize_delta point or None
|
||||
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component', 'resize_delta', 'pygui_field_editor']
|
||||
|
||||
# pygui_component = None
|
||||
# resize_delta = None
|
||||
# pygui_field_editor = None
|
||||
|
||||
def _ns_event_position(self, ns_event):
|
||||
return ns_event.locationInWindow()
|
||||
|
||||
def windowShouldClose_(self, sender):
|
||||
# We use this to detect when the Aqua window closing button
|
||||
# is pressed, and do the closing ourselves.
|
||||
self.pygui_component.close_cmd()
|
||||
return False
|
||||
|
||||
# The NSWindow is made its own delegate.
|
||||
|
||||
def windowWillResize_toSize_(self, ns_win, new_ns_size):
|
||||
w0, h0 = self.frame().size
|
||||
w1, h1 = new_ns_size
|
||||
self.resize_delta = (w1 - w0, h1 - h0)
|
||||
return new_ns_size
|
||||
|
||||
def windowDidResize_(self, notification):
|
||||
delta = getattr(self, 'resize_delta', None)
|
||||
if delta:
|
||||
self.pygui_component._resized(delta)
|
||||
self.resize_delta = None
|
||||
|
||||
def windowDidBecomeKey_(self, notification):
|
||||
app = application()
|
||||
app._ns_key_window = self.pygui_component
|
||||
app._update_menubar()
|
||||
|
||||
def windowDidResignKey_(self, notification):
|
||||
app = application()
|
||||
app._ns_key_window = None
|
||||
app._update_menubar()
|
||||
|
||||
def windowWillReturnFieldEditor_toObject_(self, ns_window, ns_obj):
|
||||
# Return special field editor for newline handling in text fields.
|
||||
#print "Window: Field editor requested for", object.__repr__(ns_obj) ###
|
||||
#editor = self.pygui_field_editor
|
||||
#if not editor:
|
||||
try:
|
||||
editor = self.pygui_field_editor
|
||||
except AttributeError:
|
||||
#print "...creating new field editor" ###
|
||||
editor = PyGUI_FieldEditor.alloc().initWithFrame_(
|
||||
NSRect(NSPoint(0, 0), NSSize(0, 0)))
|
||||
editor.setFieldEditor_(True)
|
||||
editor.setDrawsBackground_(False)
|
||||
self.pygui_field_editor = editor
|
||||
return editor
|
||||
|
||||
# Need the following two methods so that a fullscreen window can become
|
||||
# the main window. Otherwise it can't, because it has no title bar.
|
||||
|
||||
def canBecomeKeyWindow(self):
|
||||
return self.isVisible()
|
||||
|
||||
def canBecomeMainWindow(self):
|
||||
#print "PyGUI_NSWindow.canBecomeMainWindow"
|
||||
return self.isVisible()
|
||||
|
||||
def windowDidBecomeMain_(self, notification):
|
||||
#print "PyGUI_NSWindow.windowDidBecomeMain_:", self.pygui_component.title ###
|
||||
comp = self.pygui_component
|
||||
if comp and comp._style == 'fullscreen':
|
||||
#print "...hiding menu bar" ###
|
||||
NSMenu.setMenuBarVisible_(False)
|
||||
#self.setFrameOrigin_(NSPoint(0, 0))
|
||||
|
||||
def windowDidResignMain_(self, notification):
|
||||
#print "PyGUI_NSWindow.windowDidResignMain_:", self.pygui_component.title ###
|
||||
comp = self.pygui_component
|
||||
if comp and comp._style == 'fullscreen':
|
||||
#print "...showing menu bar" ###
|
||||
NSMenu.setMenuBarVisible_(True)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_NS_ContentView(PyGUI_Flipped_NSView, PyGUI_NS_EventHandler):
|
||||
# pygui_component Window
|
||||
|
||||
__metaclass__ = NSMultiClass
|
||||
__slots__ = ['pygui_component']
|
||||
|
||||
def acceptsFirstResponder(self):
|
||||
return False
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_FieldEditorBase(NSTextView):
|
||||
# Special field editor for use by TextFields. Intercepts
|
||||
# return key events and handles them our own way.
|
||||
|
||||
def keyDown_(self, ns_event):
|
||||
#print "PyGUI_FieldEditorBase.keyDown_ for", self.pygui_component ###
|
||||
if ns_event.characters() == "\r":
|
||||
if self.pygui_component._multiline:
|
||||
self.insertText_("\n")
|
||||
return
|
||||
NSTextView.keyDown_(self, ns_event)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class PyGUI_FieldEditor(PyGUI_FieldEditorBase, PyGUI_NS_EventHandler):
|
||||
|
||||
__metaclass__ = NSMultiClass
|
||||
|
||||
def get_pygui_component(self):
|
||||
pygui_nstextfield = self.superview().superview()
|
||||
component = pygui_nstextfield.pygui_component
|
||||
component._ns_responder = self
|
||||
return component
|
||||
|
||||
pygui_component = property(get_pygui_component)
|
||||
|
||||
export(Window)
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Actions - Generic
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Exceptions import ApplicationError
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def action_property(name, doc):
|
||||
attr = intern('_' + name)
|
||||
def getter(self):
|
||||
return getattr(self, attr)
|
||||
def setter(self, value):
|
||||
setattr(self, attr, value)
|
||||
return property(getter, setter, None, doc)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class ActionBase(object):
|
||||
"""Mixin class providing base support for action properties."""
|
||||
|
||||
def do_named_action(self, name):
|
||||
#print "ActionBase.do_named_action:", repr(name) ###
|
||||
action = getattr(self, name)
|
||||
#print "...action =", repr(action) ###
|
||||
if action:
|
||||
try:
|
||||
if isinstance(action, tuple):
|
||||
args = action[1:]
|
||||
action = action[0]
|
||||
else:
|
||||
args = ()
|
||||
if isinstance(action, str):
|
||||
#print "...handling", action ###
|
||||
self.handle(action, *args)
|
||||
else:
|
||||
action(*args)
|
||||
except ApplicationError:
|
||||
raise
|
||||
except:
|
||||
import sys
|
||||
et, ev, tb = sys.exc_info()
|
||||
raise et, et("%s (while doing action %r%r)" % (ev, action, args)), tb
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Action(ActionBase):
|
||||
"""Mixin class providing a single action property called 'action'."""
|
||||
|
||||
action = action_property('action', """Action to be performed.
|
||||
May be <action> or (<action>, <arg>...) where <action> is either
|
||||
a message name or a callable object.""")
|
||||
|
||||
_action = None
|
||||
|
||||
def do_action(self):
|
||||
"Invoke the action."
|
||||
self.do_named_action('action')
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Alert functions - Generic
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
from GUI import BaseAlertFunctions
|
||||
|
||||
def alert(kind, prompt, ok_label = "OK", **kwds):
|
||||
"""Displays an alert box with one button. Does not return a value.
|
||||
Kind may be 'stop' for conditions preventing continuation,
|
||||
'caution' for warning messages, 'note' for informational
|
||||
messages, and 'query' for asking a question of the user."""
|
||||
|
||||
BaseAlertFunctions.alert(kind, prompt, ok_label, **kwds)
|
||||
|
||||
|
||||
def alert2(kind, prompt, yes_label = "Yes", no_label = "No",
|
||||
**kwds):
|
||||
"""Displays an alert with two buttons. Returns 1 if the
|
||||
first button is pressed, 0 if the second button is pressed.
|
||||
The 'default' and 'cancel' arguments specify which buttons,
|
||||
if any, are activated by the standard keyboard equivalents,
|
||||
and take the values 1, 0 or None."""
|
||||
|
||||
return BaseAlertFunctions.alert2(kind, prompt, yes_label, no_label,**kwds)
|
||||
|
||||
|
||||
def alert3(kind, prompt,
|
||||
yes_label = "Yes", no_label = "No", other_label = "Cancel",
|
||||
**kwds):
|
||||
"""Displays an alert with 3 buttons. Returns 1 if the
|
||||
first button is pressed, 0 if the second button is pressed,
|
||||
and -1 if the third button is pressed. The 'default' and 'cancel'
|
||||
arguments specify which buttons, if any, are activated by the
|
||||
standard keyboard equivalents, and take the values 1, 0, -1 or None."""
|
||||
|
||||
return BaseAlertFunctions.alert3(kind, prompt, yes_label, no_label, other_label, **kwds)
|
||||
|
||||
|
||||
def stop_alert(*args, **kwds):
|
||||
"""Displays a 1-button alert of type 'stop'. See alert()."""
|
||||
alert('stop', *args, **kwds)
|
||||
|
||||
def note_alert(*args, **kwds):
|
||||
"""Displays a 1-button alert of type 'note'. See alert()."""
|
||||
alert('note', *args, **kwds)
|
||||
|
||||
def confirm(*args, **kwds):
|
||||
"""Displays a 2-button alert of type 'caution'. See alert2()."""
|
||||
return alert2('caution', *args, **kwds)
|
||||
|
||||
def ask(*args, **kwds):
|
||||
"""Displays a 2-button alert of type 'query'. See alert2()."""
|
||||
return alert2('query', *args, **kwds)
|
||||
|
||||
def confirm_or_cancel(*args, **kwds):
|
||||
"""Displays a 3-button alert of type 'caution'. See alert3()."""
|
||||
return alert3('caution', *args, **kwds)
|
||||
|
||||
def ask_or_cancel(*args, **kwds):
|
||||
"""Displays a 3-button alert of type 'query'. See alert3()."""
|
||||
return alert3('query', *args, **kwds)
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#
|
||||
# Python GUI - Basic alert functions - Generic
|
||||
#
|
||||
|
||||
from GUI.AlertClasses import Alert, Alert2, Alert3
|
||||
|
||||
def present_and_destroy(dlog):
|
||||
dlog.center()
|
||||
try:
|
||||
return dlog.present()
|
||||
finally:
|
||||
dlog.destroy()
|
||||
|
||||
|
||||
def alert(kind, prompt, ok_label, **kwds):
|
||||
present_and_destroy(Alert(kind, prompt, ok_label))
|
||||
|
||||
|
||||
def alert2(kind, prompt, yes_label, no_label, **kwds):
|
||||
return present_and_destroy(
|
||||
Alert2(kind, prompt, yes_label, no_label, **kwds))
|
||||
|
||||
|
||||
def alert3(kind, prompt, yes_label, no_label, other_label, **kwds):
|
||||
return present_and_destroy(
|
||||
Alert3(kind, prompt, yes_label, no_label, other_label, **kwds))
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#---------------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Column layout component - Generic
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
from LayoutUtils import equalize_components
|
||||
from GUI import Frame, export
|
||||
|
||||
class Column(Frame):
|
||||
|
||||
def __init__(self, items, spacing = 10, align = 'l', equalize = '',
|
||||
expand = None, padding = (0, 0), **kwds):
|
||||
Frame.__init__(self)
|
||||
hpad, vpad = padding
|
||||
if expand is not None and not isinstance(expand, int):
|
||||
expand = items.index(expand)
|
||||
equalize_components(items, equalize)
|
||||
width = 0
|
||||
for item in items:
|
||||
if item:
|
||||
width = max(width, item.width)
|
||||
y = vpad
|
||||
gap = 0
|
||||
vanchor = 't'
|
||||
hanchor = align
|
||||
for i, item in enumerate(items):
|
||||
if item:
|
||||
y += gap
|
||||
if 'l' in align:
|
||||
x = 0
|
||||
if 'r' in align:
|
||||
item.width = width
|
||||
elif align == 'r':
|
||||
x = width - item.width
|
||||
else:
|
||||
x = (width - item.width) // 2
|
||||
item.position = (x + hpad, y)
|
||||
if i == expand:
|
||||
item.anchor = 'tb' + hanchor
|
||||
vanchor = 'b'
|
||||
else:
|
||||
item.anchor = vanchor + hanchor
|
||||
y += item.height
|
||||
if i == expand:
|
||||
vanchor = 'b'
|
||||
gap = spacing
|
||||
self.size = (width + 2 * hpad, y + vpad)
|
||||
self.add(items)
|
||||
self.set(**kwds)
|
||||
|
||||
export(Column)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Facilities for compatibility across Python versions
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
try:
|
||||
from __builtin__ import set
|
||||
except ImportError:
|
||||
from sets import Set as set
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
#
|
||||
# Python GUI - Documents - Generic
|
||||
#
|
||||
|
||||
import os, tempfile
|
||||
from GUI import export
|
||||
from GUI.Alerts import confirm, confirm_or_cancel
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Model
|
||||
from GUI import MessageHandler
|
||||
from GUI.Files import FileRef, DirRef
|
||||
from GUI.FileDialogs import request_new_file
|
||||
from GUI import application
|
||||
from GUI.Exceptions import Cancel, UnimplementedMethod, ApplicationError
|
||||
from GUI.Printing import PageSetup, present_page_setup_dialog
|
||||
|
||||
_next_doc_number = 1 # Counter for generating default titles
|
||||
|
||||
class Document(Model, MessageHandler):
|
||||
"""A Document represents an
|
||||
application data structure that can be stored in a file. It
|
||||
implements the standard parts of asking the user for file names and
|
||||
reading and writing files.
|
||||
|
||||
Each Document can have one or more windows associated with it. When
|
||||
the last window belonging to a document is closed, the document itself
|
||||
is closed.
|
||||
|
||||
A Document provides support for keeping track of whether it has been
|
||||
edited, and asking the user whether to save changes when it is
|
||||
closed."""
|
||||
|
||||
# The following attribute prevents a Document that is the parent
|
||||
# of a Model from being pickled along with that Model.
|
||||
pickle_as_parent_model = False
|
||||
|
||||
needs_saving = overridable_property('needs_saving',
|
||||
"True if the document has been edited and needs to be saved.")
|
||||
|
||||
file = overridable_property('file',
|
||||
"""FileRef of the file that the document was read from or last written
|
||||
to, or None. Changing this causes update_title to be called.""")
|
||||
|
||||
file_type = overridable_property('file_type',
|
||||
"""FileType specifying the type of file handled by this document.""")
|
||||
|
||||
title = overridable_property('title',
|
||||
"""The title of the document. Changing this causes update_title of each
|
||||
associated window to be called.""")
|
||||
|
||||
windows = overridable_property('windows',
|
||||
"List of windows associated with the document. Do not modify directly.")
|
||||
|
||||
page_setup = overridable_property('page_setup',
|
||||
"The PageSetup to be used for printing this document.")
|
||||
|
||||
binary = True # True if files are to be opened in binary mode
|
||||
|
||||
_file_type = None # Type of file to create when saving
|
||||
_needs_saving = 0 # True if has been edited
|
||||
_file = None # FileRef of associated file, if any
|
||||
_title = None # Title for use in window banners, etc.
|
||||
_windows = None # List of associated windows
|
||||
_page_setup = None # Document-specific PageSetup instance
|
||||
|
||||
#
|
||||
# Initialisation and destruction
|
||||
#
|
||||
|
||||
def __init__(self, **kwds):
|
||||
self._windows = []
|
||||
Model.__init__(self, **kwds)
|
||||
application()._add_document(self)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy any associated windows, then destroy document contents."""
|
||||
#print "Document.destroy:", self ###
|
||||
for win in self._windows[:]:
|
||||
win.destroy()
|
||||
application()._remove_document(self)
|
||||
self.destroy_contents()
|
||||
Model.destroy(self)
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
def get_needs_saving(self):
|
||||
return self._needs_saving
|
||||
|
||||
def set_needs_saving(self, x):
|
||||
if self._needs_saving <> x:
|
||||
self._needs_saving = x
|
||||
for window in self._windows:
|
||||
window._document_needs_saving(x)
|
||||
|
||||
def get_file(self):
|
||||
return self._file
|
||||
|
||||
def set_file(self, x):
|
||||
self._file = x
|
||||
if x is not None:
|
||||
application()._last_directory = x.dir
|
||||
self.update_title()
|
||||
|
||||
def get_file_type(self):
|
||||
return self._file_type
|
||||
|
||||
def set_file_type(self, x):
|
||||
self._file_type = x
|
||||
|
||||
def get_title(self):
|
||||
t = self._title
|
||||
if t == None:
|
||||
t = self.make_title()
|
||||
self._title = t
|
||||
return t
|
||||
|
||||
def set_title(self, x):
|
||||
self._title = x
|
||||
for win in self._windows:
|
||||
win.update_title()
|
||||
|
||||
def get_windows(self):
|
||||
return self._windows
|
||||
|
||||
def get_page_setup(self):
|
||||
ps = self._page_setup
|
||||
if not ps:
|
||||
ps = PageSetup()
|
||||
self._page_setup = ps
|
||||
return ps
|
||||
|
||||
def set_page_setup(self, ps):
|
||||
self._page_setup = ps
|
||||
|
||||
#
|
||||
# Methods
|
||||
#
|
||||
|
||||
def changed(self):
|
||||
"Set the needs_saving property to true."
|
||||
self.needs_saving = 1
|
||||
|
||||
def new_contents(self):
|
||||
"""Should initialise the document to the appropriate state following a New
|
||||
command."""
|
||||
pass
|
||||
|
||||
def read_contents(self, file):
|
||||
"""Should initialise the document's contents by reading it from the given
|
||||
file object."""
|
||||
raise UnimplementedMethod(self, 'read_contents')
|
||||
|
||||
def write_contents(self, file):
|
||||
"""Should write the document's contents to the given file object."""
|
||||
raise UnimplementedMethod(self, 'write_contents')
|
||||
|
||||
def destroy_contents(self):
|
||||
"""Called when the contents of the document are about to be discarded.
|
||||
If the contents contains any Model objects, they should be destroyed."""
|
||||
|
||||
def save_changes(self):
|
||||
"""If the document has been edited, ask the user whether to save changes,
|
||||
and do so if requested."""
|
||||
if self._needs_saving:
|
||||
result = confirm_or_cancel('Save changes to "%s"?' % self.title,
|
||||
"Save", "Don't Save", "Cancel")
|
||||
if result < 0:
|
||||
raise Cancel
|
||||
if result:
|
||||
self.save_cmd()
|
||||
|
||||
def save_cmd(self):
|
||||
"""Implements the standard Save command. Writes the document to its
|
||||
associated file, asking the user for one first if necessary."""
|
||||
if self._file == None:
|
||||
self.get_new_file_name()
|
||||
try:
|
||||
self.write()
|
||||
except EnvironmentError, e:
|
||||
raise ApplicationError("Unable to save '%s'." % self._file.name, e)
|
||||
|
||||
def save_as_cmd(self):
|
||||
"""Implements the standard Save As... command. Asks the user for a new file
|
||||
and writes the document to it."""
|
||||
self.get_new_file_name()
|
||||
self.save_cmd()
|
||||
|
||||
def revert_cmd(self):
|
||||
"""Implements the standard Revert command. Discards the current contents
|
||||
of the document and re-reads it from the associated file."""
|
||||
if self._file != None:
|
||||
if confirm(
|
||||
'Revert to the last saved version of "%s"?' % self.title,
|
||||
"Revert", "Cancel"):
|
||||
self.destroy_contents()
|
||||
self.read()
|
||||
|
||||
def close_cmd(self):
|
||||
"""Implements the standard Close command. Asks whether to save any
|
||||
changes, then destroys the document."""
|
||||
self.save_changes()
|
||||
self.destroy()
|
||||
|
||||
def page_setup_cmd(self):
|
||||
if present_page_setup_dialog(self.page_setup):
|
||||
self.changed()
|
||||
|
||||
def make_title(self):
|
||||
"""Generates a title for the document. If associated with a file,
|
||||
uses its last pathname component, else generates 'Untitled-n'."""
|
||||
global _next_doc_number
|
||||
if self._file != None:
|
||||
return os.path.basename(self._file)
|
||||
else:
|
||||
n = _next_doc_number
|
||||
_next_doc_number = n + 1
|
||||
return "Untitled-%d" % n
|
||||
|
||||
def update_title(self):
|
||||
"""Called when the file property changes, to update the
|
||||
title property appropriately."""
|
||||
file = self._file
|
||||
if file:
|
||||
self.title = file.name
|
||||
|
||||
def get_default_save_directory(self):
|
||||
"""Called when the user is about to be asked for a location in which
|
||||
to save a document that has not been saved before, to find a default
|
||||
directory for request_new_file(). Should return a DirRef or FileRef,
|
||||
or None if there is no particular preferred location."""
|
||||
return None
|
||||
|
||||
def get_default_save_filename(self):
|
||||
"""Called when the user is about to be asked for a location in which
|
||||
to save a document that has not been saved before, to find a default
|
||||
file name for request_new_file(). Should return a suggested file name,
|
||||
or an empty string to require the user to enter a file name."""
|
||||
return ""
|
||||
|
||||
#
|
||||
# Internal methods
|
||||
#
|
||||
|
||||
def get_new_file_name(self):
|
||||
"""Ask the user for a new file and associate the document with it."""
|
||||
old_file = self.file
|
||||
if old_file:
|
||||
old_name = old_file.name
|
||||
old_dir = old_file.dir
|
||||
else:
|
||||
old_name = self.get_default_save_filename()
|
||||
old_dir = self.get_default_save_directory()
|
||||
#print "Document.get_new_file_name: old_dir =", old_dir, "old_name =", old_name ###
|
||||
new_file = request_new_file(
|
||||
#'Save "%s" as:' % self.title,
|
||||
default_dir = old_dir,
|
||||
default_name = old_name,
|
||||
file_type = self.file_type or application().save_file_type)
|
||||
if new_file is None:
|
||||
raise Cancel()
|
||||
self.file = new_file
|
||||
|
||||
def read(self):
|
||||
"""Read the document from its currently associated file. The
|
||||
document must be associated with a file and not have any existing
|
||||
contents when this is called."""
|
||||
if self.binary:
|
||||
mode = "rb"
|
||||
else:
|
||||
mode = "rU"
|
||||
file = self.file.open(mode)
|
||||
try:
|
||||
self.read_contents(file)
|
||||
finally:
|
||||
file.close()
|
||||
self.needs_saving = 0
|
||||
|
||||
def write(self):
|
||||
"""Write the document to its currently associated file. The
|
||||
document must be associated with a file when this is called.
|
||||
The document is initially written to a temporary file which
|
||||
is then renamed, so if writing fails part way through, the
|
||||
original file is undisturbed."""
|
||||
if self.binary:
|
||||
mode = "wb"
|
||||
else:
|
||||
mode = "w"
|
||||
dir_path = self.file.dir.path
|
||||
fd, temp_path = tempfile.mkstemp(dir = dir_path, text = not self.binary)
|
||||
file = os.fdopen(fd, mode)
|
||||
try:
|
||||
try:
|
||||
self.write_contents(file)
|
||||
finally:
|
||||
file.close()
|
||||
except EnvironmentError:
|
||||
os.unlink(fd)
|
||||
raise
|
||||
path = self.file.path
|
||||
try:
|
||||
os.unlink(path)
|
||||
except EnvironmentError:
|
||||
pass
|
||||
os.rename(temp_path, path)
|
||||
self.needs_saving = 0
|
||||
|
||||
def setup_menus(self, m):
|
||||
#print "Document.setup_menus" ###
|
||||
if self._needs_saving or not self._file:
|
||||
m.save_cmd.enabled = 1
|
||||
if self._needs_saving and self._file:
|
||||
m.revert_cmd.enabled = 1
|
||||
m.save_as_cmd.enabled = 1
|
||||
m.page_setup_cmd.enabled = 1
|
||||
|
||||
def next_handler(self):
|
||||
return application()
|
||||
|
||||
export(Document)
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#
|
||||
# PyGUI - Enumerated type facilities
|
||||
#
|
||||
|
||||
class EnumMap(dict):
|
||||
|
||||
def __init__(self, __name__, *args, **kwds):
|
||||
self.name = __name__
|
||||
dict.__init__(self, *args, **kwds)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
raise ValueError("Invalid %s '%s', should be one of %s" %
|
||||
(self.name, key, ", ".join(["'%s'" % val for val in self.keys()])))
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#
|
||||
# Exceptions.py - GUI exception classes
|
||||
#
|
||||
|
||||
class Cancel(Exception):
|
||||
"""Exception raised when user cancels an operation."""
|
||||
pass
|
||||
|
||||
|
||||
#class Quit(Exception):
|
||||
# """Exception raised to exit the main event loop."""
|
||||
# pass
|
||||
|
||||
|
||||
class Error(StandardError):
|
||||
|
||||
def __init__(self, obj, mess):
|
||||
self.obj = obj
|
||||
self.mess = mess
|
||||
Exception.__init__(self, "%s: %s" % (obj, mess))
|
||||
|
||||
|
||||
class ApplicationError(StandardError):
|
||||
"""Exception used for reporting errors to the user."""
|
||||
|
||||
def __init__(self, message, detail = None):
|
||||
self.message = message
|
||||
self.detail = detail
|
||||
if detail:
|
||||
message = "%s\n\n%s" % (message, detail)
|
||||
StandardError.__init__(self, message)
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnimplementedMethod(NotImplementedError):
|
||||
|
||||
def __init__(self, obj, meth_name):
|
||||
self.obj = obj
|
||||
StandardError.__init__(self, "%s.%s not implemented" % \
|
||||
(obj.__class__.__name__, meth_name))
|
||||
|
||||
|
||||
class ArgumentError(TypeError):
|
||||
|
||||
def __init__(self, obj, meth_name, arg_name, value):
|
||||
self.obj = obj
|
||||
self.meth_name = meth_name
|
||||
self.arg_name = arg_name
|
||||
self.value = value
|
||||
TypeError.__init__(self,
|
||||
"%s: Invalid value %s for argument %s of method %s",
|
||||
(obj, value, arg_name, meth_name))
|
||||
|
||||
|
||||
class SetAttributeError(AttributeError):
|
||||
|
||||
def __init__(self, obj, attr):
|
||||
self.obj = obj
|
||||
self.attr = attr
|
||||
AttributeError.__init__(self, "Attribute '%s' of %s cannot be set" % (attr, obj))
|
||||
|
||||
|
||||
class UsageError(StandardError):
|
||||
pass
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#
|
||||
# Python GUI - File selection dialogs - Generic
|
||||
#
|
||||
|
||||
from GUI.BaseFileDialogs import _request_old, _request_new
|
||||
|
||||
|
||||
def request_old_file(prompt = "Open File", default_dir = None, file_types = None):
|
||||
"""Present a dialog for selecting an existing file.
|
||||
Returns a FileRef, or None if cancelled."""
|
||||
|
||||
return _request_old(prompt, default_dir, file_types,
|
||||
dir = False, multiple = False)
|
||||
|
||||
|
||||
def request_old_files(prompt = "Choose Files", default_dir = None, file_types = None):
|
||||
"""Present a dialog for selecting a set of existing files.
|
||||
Returns a list of FileRefs, or None if cancelled."""
|
||||
|
||||
return _request_old(prompt, default_dir, file_types,
|
||||
dir = False, multiple = True)
|
||||
|
||||
|
||||
def request_old_directory(prompt = "Choose Folder", default_dir = None):
|
||||
"""Present a dialog for selecting an existing directory.
|
||||
Returns a FileRef, or None if cancelled."""
|
||||
|
||||
return _request_old(prompt, default_dir, file_types = None,
|
||||
dir = True, multiple = False)
|
||||
|
||||
|
||||
def request_old_directories(prompt = "Choose Folders", default_dir = None,
|
||||
multiple = False):
|
||||
"""Present a dialog for selecting a set of existing directories.
|
||||
Returns a list of FileRefs, or None if cancelled."""
|
||||
|
||||
return _request_old(prompt, default_dir, file_types = None,
|
||||
dir = True, multiple = True)
|
||||
|
||||
|
||||
def request_new_file(prompt = "Save As:", default_dir = None,
|
||||
default_name = "", file_type = None):
|
||||
"""Present a dialog requesting a name and location for a new file.
|
||||
Returns a FileRef, or None if cancelled."""
|
||||
|
||||
return _request_new(prompt, default_dir, default_name, file_type,
|
||||
dir = False)
|
||||
|
||||
|
||||
def request_new_directory(prompt = "Create Folder:", default_dir = None,
|
||||
default_name = ""):
|
||||
"""Present a dialog requesting a name and location for a new directory.
|
||||
Returns a FileRef, or None if cancelled."""
|
||||
|
||||
return _request_new(prompt, default_dir, default_name, file_type = None,
|
||||
dir = True)
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#
|
||||
# Python GUI - Alerts - Generic
|
||||
#
|
||||
|
||||
from GUI import BaseAlert
|
||||
from GUI import Button
|
||||
from GUI.StdButtons import DefaultButton, CancelButton
|
||||
|
||||
|
||||
class Alert(BaseAlert):
|
||||
|
||||
def __init__(self, kind, prompt,
|
||||
ok_label = "OK", default = 1, **kwds):
|
||||
BaseAlert.__init__(self, kind, prompt,
|
||||
button_labels = [ok_label], default = default, **kwds)
|
||||
|
||||
def _create_buttons(self, ok_label):
|
||||
self.yes_button = DefaultButton(title = ok_label, action = self.yes)
|
||||
#self.default_button = self.ok_button
|
||||
|
||||
def _layout_buttons(self):
|
||||
self.place(self.yes_button,
|
||||
right = self.label.right,
|
||||
top = self.label + self._label_button_spacing)
|
||||
|
||||
|
||||
class Alert2(BaseAlert):
|
||||
|
||||
def __init__(self, kind, prompt,
|
||||
yes_label = "Yes", no_label = "No",
|
||||
default = 1, cancel = 0, **kwds):
|
||||
BaseAlert.__init__(self, kind, prompt,
|
||||
button_labels = [yes_label, no_label],
|
||||
default = default, cancel = cancel, **kwds)
|
||||
|
||||
def _create_buttons(self, yes_label, no_label):
|
||||
self.yes_button = DefaultButton(title = yes_label, action = self.yes)
|
||||
self.no_button = CancelButton(title = no_label, action = self.no)
|
||||
|
||||
def _layout_buttons(self):
|
||||
self.place_row([self.no_button, self.yes_button],
|
||||
right = self.label.right,
|
||||
top = self.label + self._label_button_spacing)
|
||||
|
||||
|
||||
class Alert3(BaseAlert):
|
||||
|
||||
_minimum_width = 300
|
||||
|
||||
def __init__(self, kind, prompt,
|
||||
yes_label = "Yes", no_label = "No", other_label = "Cancel",
|
||||
default = 1, cancel = -1, **kwds):
|
||||
BaseAlert.__init__(self, kind, prompt,
|
||||
button_labels = [yes_label, no_label, other_label],
|
||||
default = default, cancel = cancel, **kwds)
|
||||
|
||||
def _create_buttons(self, yes_label, no_label, cancel_label):
|
||||
self.yes_button = DefaultButton(title = yes_label, action = self.yes)
|
||||
self.no_button = CancelButton(title = no_label, action = self.no)
|
||||
self.other_button = Button(title = cancel_label, action = self.other)
|
||||
|
||||
def _layout_buttons(self):
|
||||
self.place_row([self.other_button, self.yes_button],
|
||||
right = self.label.right,
|
||||
top = self.label + self._label_button_spacing)
|
||||
self.place(self.no_button,
|
||||
left = self._left_margin, top = self.label + self._label_button_spacing)
|
||||
|
|
@ -1,547 +0,0 @@
|
|||
#
|
||||
# Python GUI - Application class - Generic
|
||||
#
|
||||
|
||||
import os, sys, traceback
|
||||
from GUI import Globals
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
from GUI import MessageHandler
|
||||
from GUI.Exceptions import Cancel, UnimplementedMethod, UsageError, \
|
||||
ApplicationError #, Quit
|
||||
from GUI.StdMenus import basic_menus
|
||||
from GUI.GMenus import MenuState
|
||||
from GUI.Files import FileRef
|
||||
from GUI.Printing import PageSetup, present_page_setup_dialog
|
||||
|
||||
class Application(Properties, MessageHandler):
|
||||
"""The user should create exactly one Application object,
|
||||
or subclass thereof. It implements the main event loop
|
||||
and other application-wide behaviour."""
|
||||
|
||||
_windows = None # List of all existing Windows
|
||||
_documents = None # List of all existing Documents
|
||||
_menus = None # Menus to appear in all Windows
|
||||
_clipboard = None
|
||||
_save_file_type = None
|
||||
_exit_event_loop_flag = False
|
||||
_last_directory = None
|
||||
|
||||
windows = overridable_property('windows',
|
||||
"""A list of all existing Windows.""")
|
||||
|
||||
documents = overridable_property('documents',
|
||||
"""A list of all existing documents.""")
|
||||
|
||||
menus = overridable_property('menus',
|
||||
"""A list of Menus that are to be available from all Windows.""")
|
||||
|
||||
open_file_types = overridable_property('open_file_types',
|
||||
"""List of FileTypes openable by the default Open... command.""")
|
||||
|
||||
save_file_type = overridable_property('save_file_type',
|
||||
"""Default FileType for Documents that do not specify their own.""")
|
||||
|
||||
file_type = overridable_property('file_type',
|
||||
"""Write only. Sets open_file_types and save_file_type.""")
|
||||
|
||||
target = overridable_property('target',
|
||||
"""Current target for key events and menu messages.""")
|
||||
|
||||
target_window = overridable_property('target_window',
|
||||
"""Window containing the current target, or None if there are no windows.""")
|
||||
|
||||
page_setup = overridable_property('page_setup',
|
||||
"""Default PageSetup instance.""")
|
||||
|
||||
def __init__(self, title = None):
|
||||
if Globals._application is not None:
|
||||
raise UsageError("More than one Application instance created")
|
||||
if title:
|
||||
Globals.application_name = title
|
||||
self._open_file_types = []
|
||||
self._windows = []
|
||||
self._documents = []
|
||||
self._update_list = []
|
||||
self._idle_tasks = []
|
||||
self._page_setup = None
|
||||
Globals._application = self
|
||||
self._quit_flag = False
|
||||
|
||||
def destroy(self):
|
||||
Globals._application = None
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
|
||||
# def get_std_menus(self):
|
||||
# """Returns a list of Menus containing the standard
|
||||
# framework-defined menu commands in their standard
|
||||
# positions."""
|
||||
# return basic_menus()
|
||||
#
|
||||
# std_menus = property(get_std_menus)
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
def get_windows(self):
|
||||
return self._windows
|
||||
|
||||
def get_documents(self):
|
||||
return self._documents
|
||||
|
||||
def get_menus(self):
|
||||
menus = self._menus
|
||||
if menus is None:
|
||||
menus = []
|
||||
return menus
|
||||
|
||||
def set_menus(self, menus):
|
||||
self._menus = menus
|
||||
|
||||
def get_open_file_types(self):
|
||||
return self._open_file_types
|
||||
|
||||
def set_open_file_types(self, x):
|
||||
self._open_file_types = x
|
||||
|
||||
def get_save_file_type(self):
|
||||
return self._save_file_type
|
||||
|
||||
def set_save_file_type(self, x):
|
||||
self._save_file_type = x
|
||||
|
||||
def set_file_type(self, x):
|
||||
self._open_file_types = [x]
|
||||
self._save_file_type = x
|
||||
|
||||
def get_page_setup(self):
|
||||
# This property is initialised lazily, because on Windows it turn out
|
||||
# that calling PageSetupDlg() before the application's first window is
|
||||
# created causes the app not to be brought to the foreground initially.
|
||||
ps = self._page_setup
|
||||
if not ps:
|
||||
ps = PageSetup()
|
||||
self._page_setup = ps
|
||||
return ps
|
||||
|
||||
def set_page_setup(self, x):
|
||||
self._page_setup = x
|
||||
|
||||
#
|
||||
# Event loop
|
||||
#
|
||||
|
||||
def run(self):
|
||||
"""The main event loop. Runs until _quit() is called, or
|
||||
KeyboardInterrupt or SystemExit is raised."""
|
||||
# Implementations may override this together with _quit() to use
|
||||
# a different means of causing the main event loop to exit.
|
||||
self.process_args(sys.argv[1:])
|
||||
if self._menus is None:
|
||||
self.menus = basic_menus()
|
||||
while not self._quit_flag:
|
||||
try:
|
||||
self.event_loop()
|
||||
#except (KeyboardInterrupt, Quit), e:
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
self.report_error()
|
||||
|
||||
def _quit(self):
|
||||
# Causes the main event loop to exit.
|
||||
self._quit_flag = True
|
||||
self._exit_event_loop()
|
||||
|
||||
def event_loop(self):
|
||||
"""Loop reading and handling events until exit_event_loop() is called."""
|
||||
# Implementations may override this together with exit_event_loop() to
|
||||
# implement non-modal event loops in a different way.
|
||||
self._event_loop(None)
|
||||
|
||||
def _event_loop(self, modal_window):
|
||||
# Generic modal and non-modal event loop.
|
||||
# Loop reading and handling events for the given window, or for all
|
||||
# windows if window is None, until exit_event_loop() is called.
|
||||
# Enabled application-wide menu items should be selectable in any case.
|
||||
# If an exception other than Cancel is raised, it should either be
|
||||
# reported using report_error() or propagated. Implementations may
|
||||
# override this together with _exit_event_loop() if handling events
|
||||
# individually is not desirable.
|
||||
save = self._exit_event_loop_flag
|
||||
self._exit_event_loop_flag = False
|
||||
try:
|
||||
while not self._exit_event_loop_flag:
|
||||
try:
|
||||
self.handle_next_event(modal_window)
|
||||
except Cancel:
|
||||
pass
|
||||
finally:
|
||||
self._exit_event_loop_flag = save
|
||||
|
||||
def exit_event_loop(self):
|
||||
"""Cause the current call to event_loop() or modal_event_loop()
|
||||
to exit."""
|
||||
self._exit_event_loop()
|
||||
|
||||
def _exit_event_loop(self):
|
||||
# Exit the generic _event_loop implementation.
|
||||
self._exit_event_loop_flag = True
|
||||
|
||||
# def event_loop_until(self, exit):
|
||||
# """Loop reading and handling events until exit() returns
|
||||
# true, _quit_flag is set or an exception other than Cancel
|
||||
# is raised."""
|
||||
# while not exit() and not self._quit_flag:
|
||||
# try:
|
||||
# self.handle_next_event()
|
||||
# except Cancel:
|
||||
# pass
|
||||
|
||||
# def handle_events(self):
|
||||
# """Handle events until an exception occurs. Waits for at least one event;
|
||||
# may handle more, at the discretion of the implementation."""
|
||||
# self.handle_next_event()
|
||||
|
||||
def handle_next_event(self, modal_window):
|
||||
# Wait for the next event to arrive and handle it. Transparently handles
|
||||
# any internal events such as window updates, etc., and executes any idle
|
||||
# tasks that become due while waiting for an event. If modal_window is
|
||||
# not None, restrict interaction to that window (but allow use of enabled
|
||||
# application-wide menu items).
|
||||
#
|
||||
# This only needs to be implemented if the generic _event_loop() is being
|
||||
# used.
|
||||
raise UnimplementedMethod(self, "handle_next_event")
|
||||
|
||||
#
|
||||
# Menu commands
|
||||
#
|
||||
|
||||
def setup_menus(self, m):
|
||||
m.new_cmd.enabled = 1
|
||||
m.open_cmd.enabled = 1
|
||||
m.page_setup_cmd.enabled = 1
|
||||
m.quit_cmd.enabled = 1
|
||||
|
||||
def new_cmd(self):
|
||||
"Handle the New menu command."
|
||||
doc = self.make_new_document()
|
||||
if not doc:
|
||||
raise UsageError(
|
||||
"Application.make_document(None) did not return a Document.")
|
||||
doc.new_contents()
|
||||
self.make_window(doc)
|
||||
|
||||
def open_cmd(self):
|
||||
"Handle the Open... menu command."
|
||||
from FileDialogs import request_old_file
|
||||
dir = self.get_default_open_directory()
|
||||
fileref = request_old_file(default_dir = dir,
|
||||
file_types = self._open_file_types)
|
||||
if fileref:
|
||||
self.open_fileref(fileref)
|
||||
else:
|
||||
raise Cancel
|
||||
|
||||
def get_default_open_directory(self):
|
||||
"""Called by the default implementation of open_cmd() to find an initial
|
||||
directory for request_old_file(). Should return a DirRef or FileRef, or
|
||||
None if there is no preferred location. By default it returns the last
|
||||
directory in which a document was opened or saved during this session,
|
||||
if any."""
|
||||
return self._last_directory
|
||||
|
||||
def page_setup_cmd(self):
|
||||
present_page_setup_dialog(self.page_setup)
|
||||
|
||||
def quit_cmd(self):
|
||||
"""Handle the Quit menu command."""
|
||||
while self._documents:
|
||||
self._documents[0].close_cmd()
|
||||
windows = self._windows
|
||||
while windows:
|
||||
window = windows[-1]
|
||||
window.destroy()
|
||||
assert not (windows and windows[-1] is window), \
|
||||
"%r failed to remove itself from application on destruction" % window
|
||||
self._quit()
|
||||
|
||||
#
|
||||
# Opening files
|
||||
#
|
||||
|
||||
def process_args(self, args):
|
||||
"""Process command line arguments. Called by run() when the application
|
||||
is starting up."""
|
||||
if not args:
|
||||
self.open_app()
|
||||
else:
|
||||
for arg in args:
|
||||
if os.path.exists(arg):
|
||||
arg = os.path.abspath(arg)
|
||||
self.open_path(arg)
|
||||
|
||||
def open_app(self):
|
||||
"""Called by run() when the application is opened with no arguments."""
|
||||
pass
|
||||
|
||||
def open_path(self, path):
|
||||
"""Open document specified by a pathname. Called for each command line
|
||||
argument when the application is starting up."""
|
||||
self.open_fileref(FileRef(path = path))
|
||||
|
||||
def open_fileref(self, fileref):
|
||||
"""Open document specified by a FileRef."""
|
||||
doc = self.make_file_document(fileref)
|
||||
if not doc:
|
||||
raise ApplicationError("The file '%s' is not recognised by %s." % (
|
||||
fileref.name, Globals.application_name))
|
||||
doc.set_file(fileref)
|
||||
try:
|
||||
doc.read()
|
||||
except EnvironmentError, e:
|
||||
raise ApplicationError("Unable to open '%s'." % fileref.name, e)
|
||||
self.make_window(doc)
|
||||
|
||||
#
|
||||
# Message dispatching
|
||||
#
|
||||
|
||||
# def dispatch(self, message, *args):
|
||||
# target_window = self._find_target_window()
|
||||
# if target_window:
|
||||
# target_window.dispatch(message, *args)
|
||||
# else:
|
||||
# self.handle(message, *args)
|
||||
|
||||
def dispatch(self, message, *args):
|
||||
self.target.handle(message, *args)
|
||||
|
||||
def dispatch_menu_command(self, command):
|
||||
if isinstance(command, tuple):
|
||||
name, index = command
|
||||
self.dispatch(name, index)
|
||||
else:
|
||||
self.dispatch(command)
|
||||
|
||||
def get_target(self):
|
||||
# Implementations may override this to locate the target in a
|
||||
# different way if they choose not to implement the Window.target
|
||||
# property. Should return self if no other target can be found.
|
||||
window = self.target_window
|
||||
if window:
|
||||
return window.target
|
||||
else:
|
||||
return self
|
||||
|
||||
def get_target_window(self):
|
||||
"""Return the window to which messages should be dispatched, or None."""
|
||||
raise NotImplementedError
|
||||
|
||||
#
|
||||
# Abstract
|
||||
#
|
||||
|
||||
def make_new_document(self):
|
||||
"""Create a new Document object of the appropriate
|
||||
class in response to a New command."""
|
||||
return self.make_document(None)
|
||||
|
||||
def make_file_document(self, fileref):
|
||||
"""Create a new Document object of the appropriate
|
||||
class for the given FileRef."""
|
||||
return self.make_document(fileref)
|
||||
|
||||
def make_document(self, fileref):
|
||||
"""Should create a new Document object of the appropriate
|
||||
class for the given FileRef, or if FileRef is None, a new
|
||||
empty Document of the appropriate class for the New command."""
|
||||
return None
|
||||
|
||||
def make_window(self, document):
|
||||
"""Should create a Window set up appropriately for viewing
|
||||
the given Document."""
|
||||
raise UnimplementedMethod(self, 'make_window')
|
||||
|
||||
#
|
||||
# Clipboard
|
||||
#
|
||||
|
||||
def query_clipboard(self):
|
||||
"Tests whether the clipboard contains any data."
|
||||
return not not self._clipboard
|
||||
|
||||
def get_clipboard(self):
|
||||
return self._clipboard
|
||||
|
||||
def set_clipboard(self, x):
|
||||
self._clipboard = x
|
||||
|
||||
#
|
||||
# Window list management
|
||||
#
|
||||
|
||||
def _add_window(self, window):
|
||||
if window not in self._windows:
|
||||
self._windows.append(window)
|
||||
|
||||
def _remove_window(self, window):
|
||||
if window in self._windows:
|
||||
self._windows.remove(window)
|
||||
|
||||
#
|
||||
# Document list management
|
||||
#
|
||||
|
||||
def _add_document(self, doc):
|
||||
if doc not in self._documents:
|
||||
self._documents.append(doc)
|
||||
|
||||
def _remove_document(self, doc):
|
||||
if doc in self._documents:
|
||||
self._documents.remove(doc)
|
||||
|
||||
#
|
||||
# Exception reporting
|
||||
#
|
||||
|
||||
def report_error(self):
|
||||
"""Display an appropriate error message for the most recent
|
||||
exception caught."""
|
||||
try:
|
||||
raise
|
||||
except Cancel:
|
||||
pass
|
||||
except ApplicationError, e:
|
||||
from GUI.Alerts import stop_alert
|
||||
stop_alert(str(e))
|
||||
except:
|
||||
self.report_exception()
|
||||
|
||||
def report_exception(self):
|
||||
"""Display an alert box describing the most recent exception, and
|
||||
giving the options Continue, Traceback or Abort. Traceback displays
|
||||
a traceback and continues; Abort raises SystemExit."""
|
||||
try:
|
||||
exc_type, exc_val, exc_tb = sys.exc_info()
|
||||
exc_desc = "%s: %s" % (exc_type.__name__, exc_val)
|
||||
self.print_traceback(exc_desc, exc_tb)
|
||||
from GUI.Alerts import alert3
|
||||
message = "Sorry, something went wrong."
|
||||
result = alert3('stop', "%s\n\n%s" % (message, exc_desc),
|
||||
"Continue", "Abort", "Traceback",
|
||||
default = 1, cancel = None, width = 450, lines = 5)
|
||||
if result == 1: # Continue
|
||||
return
|
||||
elif result == -1: # Traceback
|
||||
self.display_traceback(exc_desc, exc_tb)
|
||||
return
|
||||
else: # Abort
|
||||
raise SystemExit
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
os._exit(1)
|
||||
except:
|
||||
print >>sys.stderr, "---------- Exception while reporting exception ----------"
|
||||
traceback.print_exc()
|
||||
print >>sys.stderr, "------------------ Original exception -------------------"
|
||||
traceback.print_exception(exc_type, exc_val, exc_tb)
|
||||
#os._exit(1)
|
||||
|
||||
def display_traceback(self, exc_desc, exc_tb):
|
||||
"""Display an exception description and traceback.
|
||||
TODO: display this in a scrolling window."""
|
||||
self.print_traceback(exc_desc, exc_tb)
|
||||
|
||||
def print_traceback(self, exc_desc, exc_tb):
|
||||
"""Print exception description and traceback to standard error."""
|
||||
import traceback
|
||||
sys.stderr.write("\nTraceback (most recent call last):\n")
|
||||
traceback.print_tb(exc_tb)
|
||||
sys.stderr.write("%s\n\n" % exc_desc)
|
||||
|
||||
#
|
||||
# Other
|
||||
#
|
||||
|
||||
def zero_windows_allowed(self):
|
||||
"""Platforms should implement this to return false if there
|
||||
must be at least one window open at all times. Returning false
|
||||
here forces the Quit command to be used instead of Close when
|
||||
there is only one window open."""
|
||||
# TODO: Move this somewhere more global.
|
||||
raise UnimplementedMethod(self, 'zero_windows_allowed')
|
||||
|
||||
def _perform_menu_setup(self, menus = None):
|
||||
"""Given a list of Menu objects, perform menu setup processing
|
||||
and update associated platform menus ready for popping up or
|
||||
pulling down."""
|
||||
if menus is None:
|
||||
menus = self._effective_menus()
|
||||
menu_state = MenuState(menus)
|
||||
menu_state.reset()
|
||||
self._dispatch_menu_setup(menu_state)
|
||||
for menu in menus:
|
||||
menu._update_platform_menu()
|
||||
|
||||
def _dispatch_menu_setup(self, menu_state):
|
||||
self.dispatch('_setup_menus', menu_state)
|
||||
|
||||
def _effective_menus(self):
|
||||
"""Return a list of the menus in effect for the currently active
|
||||
window, including both application-wide and window-specific menus,
|
||||
in an appropriate order according to platform conventions."""
|
||||
window = self.target_window
|
||||
return self._effective_menus_for_window(window)
|
||||
|
||||
def _effective_menus_for_window(self, window):
|
||||
"""Return a list of the menus in effect for the specified
|
||||
window, including both application-wide and window-specific menus,
|
||||
in an appropriate order according to platform conventions."""
|
||||
menus = self.menus
|
||||
if window:
|
||||
menus = menus + window.menus
|
||||
regular_menus = []
|
||||
special_menus = []
|
||||
for menu in menus:
|
||||
if menu.special:
|
||||
special_menus.insert(0, menu)
|
||||
else:
|
||||
regular_menus.append(menu)
|
||||
return regular_menus + special_menus
|
||||
|
||||
# def _may_close_a_window(self):
|
||||
# # On implementations where at least one window is needed in order to
|
||||
# # interact with the application, check whether closing a window would
|
||||
# # leave no more visible windows.
|
||||
# if self.zero_windows_allowed():
|
||||
# return True
|
||||
# count = 0
|
||||
# for window in self.windows:
|
||||
# if window.visible:
|
||||
# count += 1
|
||||
# if count >= 2:
|
||||
# return True
|
||||
# return False
|
||||
|
||||
def _check_for_no_windows(self):
|
||||
# On implementations where at least one window is needed in order to
|
||||
# interact with the application, check whether there are no more visible
|
||||
# windows and take appropriate action.
|
||||
if not self.zero_windows_allowed():
|
||||
for window in self.windows:
|
||||
if window.visible:
|
||||
return
|
||||
self.no_visible_windows()
|
||||
|
||||
def no_visible_windows(self):
|
||||
"""On platforms that require a window in order to interact with the
|
||||
application, this is called when there are no more visible windows.
|
||||
The default action is to close the application; subclasses may override
|
||||
it to take some other action, such as creating a new window."""
|
||||
self.quit_cmd()
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
#
|
||||
# Python GUI - Alert base class - Generic
|
||||
#
|
||||
|
||||
import textwrap
|
||||
from GUI import ModalDialog
|
||||
from GUI import Label
|
||||
|
||||
class BaseAlert(ModalDialog):
|
||||
|
||||
_wrapwidth = 50
|
||||
_minimum_width = 200
|
||||
_left_margin = 24
|
||||
_right_margin = 24
|
||||
_top_margin = 14
|
||||
_bottom_margin = 20
|
||||
_icon_spacing = 16
|
||||
_label_button_spacing = 20
|
||||
_default_width = 380
|
||||
_default_lines = 3
|
||||
|
||||
yes_button = None
|
||||
no_button = None
|
||||
other_button = None
|
||||
|
||||
def __init__(self, kind, prompt, width = None, lines = None,
|
||||
button_labels = None, default = None, cancel = None):
|
||||
#if width is None:
|
||||
# width = self._default_width
|
||||
#if lines is None:
|
||||
# lines = self._default_lines
|
||||
ModalDialog.__init__(self, style = 'alert')
|
||||
self.label = Label(text = self._wrap(prompt), lines = lines)
|
||||
if self.label.width < self._minimum_width:
|
||||
self.label.width = self._minimum_width
|
||||
self._create_buttons(*button_labels)
|
||||
#self.default_button = self._find_button(default)
|
||||
#self.cancel_button = self._find_button(cancel)
|
||||
self._layout(kind)
|
||||
|
||||
def _layout(self, kind):
|
||||
icon_width, icon_height = self._layout_icon(kind)
|
||||
label_left = self._left_margin
|
||||
if icon_width:
|
||||
label_left += icon_width + self._icon_spacing
|
||||
if self.label.height < icon_height:
|
||||
self.label.height = icon_height
|
||||
self.place(self.label,
|
||||
left = label_left,
|
||||
top = self._top_margin)# + icon_height/4)
|
||||
#_wrap_text(self.label, self._default_width - label_left - self._right_margin)
|
||||
self._layout_buttons()
|
||||
self.shrink_wrap(padding = (self._right_margin, self._bottom_margin))
|
||||
|
||||
def _layout_icon(self, kind):
|
||||
# Place icon for the given alert kind, if any, and return its size.
|
||||
# If there is no icon, return (0, 0).
|
||||
return (0, 0)
|
||||
|
||||
def _wrap(self, text):
|
||||
width = self._wrapwidth
|
||||
return "\n\n".join(
|
||||
[textwrap.fill(para, width)
|
||||
for para in text.split("\n\n")])
|
||||
|
||||
def _find_button(self, value):
|
||||
#print "BaseAlert._find_button:", value ###
|
||||
if value == 1:
|
||||
result = self.yes_button
|
||||
elif value == 0:
|
||||
result = self.no_button
|
||||
elif value == -1:
|
||||
result = self.other_button
|
||||
else:
|
||||
result = None
|
||||
#print "BaseAlert._find_button: result =", result ###
|
||||
return result
|
||||
|
||||
def yes(self):
|
||||
self.dismiss(1)
|
||||
|
||||
def no(self):
|
||||
self.dismiss(0)
|
||||
|
||||
def other(self):
|
||||
self.dismiss(-1)
|
||||
|
||||
#def _wrap_text(label, label_width):
|
||||
# hard_lines = [text.split()
|
||||
# for text in label.text.split("\n")]
|
||||
# words = hard_lines[0]
|
||||
# for hard_line in hard_lines[1:]:
|
||||
# words.append("\n")
|
||||
# words.extend(hard_line)
|
||||
# font = label.font
|
||||
# space_width = font.width(" ")
|
||||
# lines = []
|
||||
# line = []
|
||||
# line_width = 0
|
||||
# for word in words:
|
||||
# word_width = font.width(word)
|
||||
# if word == "\n" or (line_width > 0
|
||||
# and line_width + space_width + word_width > label_width):
|
||||
# lines.append(line)
|
||||
# line = []
|
||||
# line_width = 0
|
||||
# if word <> "\n":
|
||||
# line.append(word)
|
||||
# if line_width > 0:
|
||||
# line_width += space_width
|
||||
# line_width += word_width
|
||||
# if line:
|
||||
# lines.append(line)
|
||||
# label.text = "\n".join([" ".join(line) for line in lines])
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#
|
||||
# Python GUI - Buttons - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Actions import Action
|
||||
from GUI import Control
|
||||
|
||||
class Button(Control, Action):
|
||||
""" A pushbutton control."""
|
||||
|
||||
style = overridable_property('style',
|
||||
"One of 'normal', 'default', 'cancel'")
|
||||
|
||||
def activate(self):
|
||||
"""Highlight the button momentarily and then perform its action."""
|
||||
self.flash()
|
||||
self.do_action()
|
||||
|
||||
def flash(self):
|
||||
"""Highlight the button momentarily as though it had been clicked,
|
||||
without performing the action."""
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#
|
||||
# Python GUI - Canvas Paths - Generic
|
||||
#
|
||||
|
||||
class CanvasPaths:
|
||||
# Mixin class providing generic implementations of
|
||||
# canvas path construction operators.
|
||||
|
||||
def __init__(self):
|
||||
self.newpath()
|
||||
|
||||
def newpath(self):
|
||||
self._path = []
|
||||
self._current_subpath = None
|
||||
self._current_point = (0, 0)
|
||||
|
||||
def moveto(self, x, y):
|
||||
self._current_subpath = None
|
||||
self._current_point = self._coords(x, y)
|
||||
|
||||
def rmoveto(self, dx, dy):
|
||||
x, y = self._current_point
|
||||
self.moveto(x + dx, y + dy)
|
||||
|
||||
def lineto(self, x, y):
|
||||
subpath = self._current_subpath
|
||||
if subpath is None:
|
||||
subpath = [self._current_point]
|
||||
self._path.append(subpath)
|
||||
self._current_subpath = subpath
|
||||
p = self._coords(x, y)
|
||||
subpath.append(p)
|
||||
self._current_point = p
|
||||
|
||||
def rlineto(self, dx, dy):
|
||||
x, y = self._current_point
|
||||
self.lineto(x + dx, y + dy)
|
||||
|
||||
def closepath(self):
|
||||
subpath = self._current_subpath
|
||||
if subpath:
|
||||
subpath.append(subpath[0])
|
||||
self._current_subpath = None
|
||||
|
||||
def get_current_point(self):
|
||||
return self._current_point
|
||||
|
||||
# Implementations may set _coords to one of the following
|
||||
|
||||
def _int_coords(self, x, y):
|
||||
return int(round(x)), int(round(y))
|
||||
|
||||
def _float_coords(self, x, y):
|
||||
return x, y
|
||||
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
#
|
||||
# Python GUI - Drawing - Generic
|
||||
#
|
||||
|
||||
from GUI.StdColors import black, white
|
||||
from GUI.StdFonts import application_font
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
class Canvas(Properties):
|
||||
|
||||
_default_forecolor = black
|
||||
_default_backcolor = white
|
||||
_printing = False
|
||||
|
||||
pencolor = overridable_property('pencolor', "Current color for stroking paths.")
|
||||
fillcolor = overridable_property('fillcolor', "Current color for filling paths.")
|
||||
textcolor = overridable_property('textcolor', "Current color for drawint text.")
|
||||
forecolor = overridable_property('forecolor', "Sets pen, fill and text colors to the same color.")
|
||||
backcolor = overridable_property('backcolor', "Current color for erasing regions.")
|
||||
pensize = overridable_property('pensize', "Width of pen for framing and stroking.")
|
||||
font = overridable_property('font', "Font for drawing text.")
|
||||
current_point = overridable_property('current_point', "The current point, or None.")
|
||||
printing = overridable_property('printing', "True if drawing destination is a non-display device.")
|
||||
|
||||
#forecolor = overridable_property('forecolor', "Sets both pencolor and fillcolor.")
|
||||
|
||||
def __init__(self):
|
||||
self.newpath()
|
||||
|
||||
def get_printing(self):
|
||||
return self._printing
|
||||
|
||||
def initgraphics(self):
|
||||
self.set_forecolor(self._default_forecolor)
|
||||
self.set_backcolor(self._default_backcolor)
|
||||
self.set_pensize(1)
|
||||
self.set_font(application_font)
|
||||
|
||||
def set_forecolor(self, c):
|
||||
self.pencolor = c
|
||||
self.fillcolor = c
|
||||
self.textcolor = c
|
||||
|
||||
def rmoveto(self, dx, dy):
|
||||
x0, y0 = self._current_point
|
||||
# x0, y0 = self._current_point()
|
||||
self.moveto(x0 + dx, y0 + dy)
|
||||
|
||||
def rlineto(self, dx, dy):
|
||||
x0, y0 = self.current_point
|
||||
self.lineto(x0 + dx, y0 + dy)
|
||||
|
||||
def curve(self, sp, cp1, cp2, ep):
|
||||
self.moveto(sp)
|
||||
self.curveto(cp1, cp2, ep)
|
||||
|
||||
def rcurveto(self, cp1, cp2, ep):
|
||||
x0, y0 = self.current_point
|
||||
x1, y1 = cp1
|
||||
x2, y2 = cp2
|
||||
x3, y3 = ep
|
||||
self.curveto(
|
||||
(x0 + x1, y0 + y1),
|
||||
(x0 + x2, y0 + y2),
|
||||
(x0 + x3, y0 + y3))
|
||||
|
||||
def fill_stroke(self):
|
||||
self.fill()
|
||||
self.stroke()
|
||||
|
||||
# Rectangles
|
||||
|
||||
def _pen_inset_rect(self, rect):
|
||||
l, t, r, b = rect
|
||||
p = 0.5 * self.pensize
|
||||
return (l + p, t + p, r - p, b - p)
|
||||
|
||||
def rect(self, rect):
|
||||
l, t, r, b = rect
|
||||
self.moveto(l, t)
|
||||
self.lineto(r, t)
|
||||
self.lineto(r, b)
|
||||
self.lineto(l, b)
|
||||
self.closepath()
|
||||
|
||||
def rect_frame(self, rect):
|
||||
self.rect(self._pen_inset_rect(rect))
|
||||
|
||||
def fill_rect(self, rect):
|
||||
self.newpath()
|
||||
self.rect(rect)
|
||||
self.fill()
|
||||
|
||||
def stroke_rect(self, rect):
|
||||
self.newpath()
|
||||
self.rect(rect)
|
||||
self.stroke()
|
||||
|
||||
def frame_rect(self, rect):
|
||||
self.newpath()
|
||||
self.rect_frame(rect)
|
||||
self.stroke()
|
||||
|
||||
def fill_stroke_rect(self, rect):
|
||||
self.rect_path(rect)
|
||||
self.fill_stroke()
|
||||
|
||||
def fill_frame_rect(self, rect):
|
||||
self.fill_rect(rect)
|
||||
self.frame_rect(rect)
|
||||
|
||||
def erase_rect(self, rect):
|
||||
self.newpath()
|
||||
self.rect(rect)
|
||||
self.erase()
|
||||
|
||||
# Ovals
|
||||
|
||||
def oval_frame(self, rect):
|
||||
self.oval(self._pen_inset_rect(rect))
|
||||
|
||||
def fill_oval(self, rect):
|
||||
self.newpath()
|
||||
self.oval_frame(rect)
|
||||
self.fill()
|
||||
|
||||
def stroke_oval(self, rect):
|
||||
self.newpath()
|
||||
self.oval(rect)
|
||||
self.stroke()
|
||||
|
||||
def frame_oval(self, rect):
|
||||
self.newpath()
|
||||
self.oval_frame(rect)
|
||||
self.stroke()
|
||||
|
||||
def fill_stroke_oval(self, rect):
|
||||
self.newpath()
|
||||
self.oval(rect)
|
||||
self.fill_stroke()
|
||||
|
||||
def fill_frame_oval(self, rect):
|
||||
self.fill_oval(rect)
|
||||
self.frame_oval()
|
||||
|
||||
def erase_oval(self, rect):
|
||||
self.newpath()
|
||||
self.oval(rect)
|
||||
self.erase()
|
||||
|
||||
# Arcs
|
||||
|
||||
def _arc_path(self, c, r, a0, a1):
|
||||
# x, y = c
|
||||
# a0r = a0 * deg
|
||||
# x0 = x + r * cos(a0r)
|
||||
# y0 = y + r * sin(a0r)
|
||||
self.newpath()
|
||||
# self.moveto(x0, y0)
|
||||
self.arc(c, r, a0, a1)
|
||||
|
||||
def _arc_frame_path(self, c, r, a0, a1):
|
||||
self._arc_path(c, r - 0.5 * self.pensize, a0, a1)
|
||||
|
||||
def stroke_arc(self, c, r, a0, a1):
|
||||
self._arc_path(c, r, a0, a1)
|
||||
self.stroke()
|
||||
|
||||
def frame_arc(self, c, r, a0, a1):
|
||||
self._arc_frame_path(c, r, a0, a1)
|
||||
self.stroke()
|
||||
|
||||
# Wedges
|
||||
|
||||
def wedge(self, c, r, a0, a1):
|
||||
self.moveto(*c)
|
||||
self.arc(c, r, a0, a1)
|
||||
self.closepath()
|
||||
|
||||
def fill_wedge(self, c, r, a0, a1):
|
||||
self.newpath()
|
||||
self.wedge(c, r, a0, a1)
|
||||
self.fill()
|
||||
|
||||
def stroke_wedge(self, c, r, a0, a1):
|
||||
self.newpath()
|
||||
self.wedge(c, r, a0, a1)
|
||||
self.stroke()
|
||||
|
||||
def fill_stroke_wedge(self, c, r, a0, a1):
|
||||
self.newpath()
|
||||
self.wedge(c, r, a0, a1)
|
||||
self.fill_stroke()
|
||||
|
||||
def erase_wedge(self, c, r, a0, a1):
|
||||
self.newpath()
|
||||
self.wedge(c, r, a0, a1)
|
||||
self.erase()
|
||||
|
||||
# Polylines
|
||||
|
||||
def lines(self, points):
|
||||
point_iter = iter(points)
|
||||
self.moveto(*point_iter.next())
|
||||
for p in point_iter:
|
||||
self.lineto(*p)
|
||||
|
||||
def linesto(self, points):
|
||||
for p in points:
|
||||
self.lineto(*p)
|
||||
|
||||
def stroke_lines(self, points):
|
||||
self.newpath()
|
||||
self.lines(points)
|
||||
self.stroke()
|
||||
|
||||
# Polycurves
|
||||
|
||||
def curves(self, points):
|
||||
self.moveto(*points[0])
|
||||
for i in xrange(1, len(points), 3):
|
||||
self.curveto(*points[i:i+3])
|
||||
|
||||
def curvesto(self, points):
|
||||
for i in xrange(0, len(points), 3):
|
||||
self.curveto(*points[i:i+3])
|
||||
|
||||
def stroke_curves(self, points):
|
||||
self.newpath()
|
||||
self.curves(points)
|
||||
self.stroke()
|
||||
|
||||
# Polygons
|
||||
|
||||
def poly(self, points):
|
||||
self.lines(points)
|
||||
self.closepath()
|
||||
|
||||
def fill_poly(self, points):
|
||||
self.newpath()
|
||||
self.poly(points)
|
||||
self.fill()
|
||||
|
||||
def stroke_poly(self, points):
|
||||
self.newpath()
|
||||
self.poly(points)
|
||||
self.stroke()
|
||||
|
||||
def fill_stroke_poly(self, points):
|
||||
self.newpath()
|
||||
self.poly(points)
|
||||
self.fill_stroke()
|
||||
|
||||
def erase_poly(self, points):
|
||||
self.newpath()
|
||||
self.poly(points)
|
||||
self.erase()
|
||||
|
||||
# Loops
|
||||
|
||||
def loop(self, points):
|
||||
self.curves(points)
|
||||
self.closepath()
|
||||
|
||||
def fill_loop(self, points):
|
||||
self.newpath()
|
||||
self.loop(points)
|
||||
self.fill()
|
||||
|
||||
def stroke_loop(self, points):
|
||||
self.newpath()
|
||||
self.loop(points)
|
||||
self.stroke()
|
||||
|
||||
def fill_stroke_loop(self, points):
|
||||
self.newpath()
|
||||
self.loop(points)
|
||||
self.fill_stroke()
|
||||
|
||||
def erase_loop(self, points):
|
||||
self.newpath()
|
||||
self.loop(points)
|
||||
self.erase()
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#
|
||||
# Python GUI - Check boxes - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Control
|
||||
from GUI.Actions import Action
|
||||
|
||||
class CheckBox(Control, Action):
|
||||
"""A CheckBox is a control used to represent a binary choice."""
|
||||
|
||||
def __init__(self, **kwds):
|
||||
Control.__init__(self, **kwds)
|
||||
|
||||
on = overridable_property('on', "Boolean value of the check box.")
|
||||
|
||||
auto_toggle = overridable_property('auto_toggle', """If true,
|
||||
the check box's 'on' property will automatically be toggled
|
||||
before performing the action, if any.""")
|
||||
|
||||
mixed = overridable_property('mixed', """If true, the check box
|
||||
is capable of displaying a mixed state.""")
|
||||
|
||||
_auto_toggle = True
|
||||
_mixed = False
|
||||
|
||||
def get_auto_toggle(self):
|
||||
return self._auto_toggle
|
||||
|
||||
def set_auto_toggle(self, v):
|
||||
self._auto_toggle = v
|
||||
|
||||
def get_mixed(self):
|
||||
return self._mixed
|
||||
|
||||
def set_mixed(self, v):
|
||||
self._mixed = v
|
||||
|
||||
def get_value(self):
|
||||
return self.on
|
||||
|
||||
def set_value(self, x):
|
||||
self.on = x
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#
|
||||
# Python GUI - Colors - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
|
||||
class Color(object):
|
||||
"""A drawing color.
|
||||
|
||||
Constructors:
|
||||
rgb(red, green, blue, alpha = 1.0)
|
||||
where red, green, blue, alpha are in the range 0.0 to 1.0
|
||||
|
||||
Properties:
|
||||
red --> float
|
||||
green --> float
|
||||
blue --> float
|
||||
rgb --> (red, green, blue)
|
||||
rgba --> (red, green, blue, alpha)
|
||||
"""
|
||||
|
||||
red = overridable_property('red', "Red component (0.0 to 1.0)")
|
||||
green = overridable_property('green', "Blue component (0.0 to 1.0)")
|
||||
blue = overridable_property('blue', "Blue component (0.0 to 1.0)")
|
||||
alpha = overridable_property('alpha', "Alpha (opacity) component")
|
||||
rgb = overridable_property('rgb', "Tuple of (red, green, blue) (0.0 to 1.0)")
|
||||
rgba = overridable_property('rgba',
|
||||
"Tuple of (red, green, blue, alpha) (0.0 to 1.0)")
|
||||
|
||||
def get_alpha(self):
|
||||
return 1.0
|
||||
|
||||
def get_rgb(self):
|
||||
return (self.red, self.green, self.blue)
|
||||
|
||||
def set_rgb(self, x):
|
||||
self.red, self.green, self.blue = x
|
||||
|
||||
def get_rgba(self):
|
||||
return (self.red, self.green, self.blue, self.alpha)
|
||||
|
||||
def set_rgba(self, x):
|
||||
self.red, self.green, self.blue, self.alpha = x
|
||||
|
||||
def __str__(self):
|
||||
return "Color(%g,%g,%g,%g)" % self.rgba
|
||||
|
|
@ -1,477 +0,0 @@
|
|||
#
|
||||
# Python GUI - Components - Generic
|
||||
#
|
||||
|
||||
import os
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
from GUI import MessageHandler
|
||||
from GUI.Geometry import add_pt, sub_pt, rect_size, rect_sized, rect_topleft
|
||||
from GUI import application
|
||||
|
||||
_user_tab_stop = os.environ.get("PYGUI_KEYBOARD_NAVIGATION") or None
|
||||
# Allow "False", "True", "0", "1"
|
||||
if _user_tab_stop is not None:
|
||||
_user_tab_stop = _user_tab_stop.strip().capitalize()
|
||||
try:
|
||||
_user_tab_stop = {"False": False, "True": True}[_user_tab_stop]
|
||||
except KeyError:
|
||||
try:
|
||||
_user_tab_stop = int(_user_tab_stop)
|
||||
except ValueError:
|
||||
sys.stderr.write("PYGUI_KEYBOARD_NAVIGATION: Unrecognized value %r"
|
||||
% _user_tab_stop)
|
||||
_user_tab_stop = None
|
||||
|
||||
class Component(Properties, MessageHandler):
|
||||
"""Component is an abstract class representing a user
|
||||
interface component."""
|
||||
|
||||
left = overridable_property('left', "Position of left edge relative to container.")
|
||||
top = overridable_property('top', "Position of top edge relative to container.")
|
||||
right = overridable_property('right', "Position of right edge relative to container.")
|
||||
bottom = overridable_property('bottom', "Position of bottom edge relative to container.")
|
||||
|
||||
x = overridable_property('x', "Horizontal position relative to container.")
|
||||
y = overridable_property('y', "Vertical position relative to container.")
|
||||
width = overridable_property('width')
|
||||
height = overridable_property('height')
|
||||
|
||||
position = overridable_property('position', "Position relative to container.")
|
||||
size = overridable_property('size')
|
||||
|
||||
bounds = overridable_property('bounds', "Bounding rectangle in container's coordinates.")
|
||||
|
||||
container = overridable_property('container',
|
||||
"Container which contains this Component. Setting this property has the "
|
||||
"effect of removing the component from its previous container, if any, "
|
||||
"and adding it to the new one, if any.")
|
||||
|
||||
# visible = overridable_property('visible',
|
||||
# "Whether the component is currently shown.")
|
||||
|
||||
tab_stop = overridable_property('tab_stop',
|
||||
"Whether tab key can navigate into this control.")
|
||||
|
||||
anchor = overridable_property('anchor', "A string of 'ltrb' controlling behaviour when container is resized.")
|
||||
|
||||
border = overridable_property('border', "True if the component should have a border.")
|
||||
|
||||
_is_scrollable = False # Overridden by scrollable subclasses
|
||||
_generic_tabbing = True # Whether to use generic tab navigation code
|
||||
_default_tab_stop = False
|
||||
_user_tab_stop_override = False # Whether user preference overrides _default_tab_stop
|
||||
_tab_stop = None
|
||||
|
||||
#
|
||||
# Class variables defined by implementations:
|
||||
#
|
||||
# _has_local_coords bool True if component has a local coordinate system
|
||||
#
|
||||
|
||||
_container = None
|
||||
_border = False
|
||||
hmove = 0
|
||||
vmove = 0
|
||||
hstretch = 0
|
||||
vstretch = 0
|
||||
|
||||
def __init__(self, tab_stop = None, **kwds):
|
||||
Properties.__init__(self, **kwds)
|
||||
if tab_stop is None:
|
||||
tab_stop = self._get_default_tab_stop()
|
||||
self.tab_stop = tab_stop
|
||||
|
||||
def destroy(self):
|
||||
self.container = None
|
||||
|
||||
#
|
||||
# Geometry properties
|
||||
#
|
||||
# Default implementations of position and size properties
|
||||
# in terms of the bounds property. A minimal implementation
|
||||
# need only implement get_bounds and set_bounds.
|
||||
#
|
||||
# It is the implementation's responsibility to call _resized()
|
||||
# whenever the size of the component changes, either by
|
||||
# explicit assignment to geometry properties or by the user
|
||||
# resizing the containing window. It should not be called if
|
||||
# setting a geometry property does not cause the size to change.
|
||||
#
|
||||
|
||||
def get_left(self):
|
||||
return self.position[0]
|
||||
|
||||
def set_left(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (v, t, r, b)
|
||||
|
||||
def get_top(self):
|
||||
return self.bounds[1]
|
||||
|
||||
def set_top(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, v, r, b)
|
||||
|
||||
def get_right(self):
|
||||
return self.bounds[2]
|
||||
|
||||
def set_right(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, t, v, b)
|
||||
|
||||
def get_bottom(self):
|
||||
return self.bounds[3]
|
||||
|
||||
def set_bottom(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, t, r, v)
|
||||
|
||||
def get_x(self):
|
||||
return self.bounds[0]
|
||||
|
||||
def set_x(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (v, t, v + r - l, b)
|
||||
|
||||
def get_y(self):
|
||||
return self.bounds[1]
|
||||
|
||||
def set_y(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, v, r, v + b - t)
|
||||
|
||||
def get_position(self):
|
||||
l, t, r, b = self.bounds
|
||||
return (l, t)
|
||||
|
||||
def set_position(self, (x, y)):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (x, y, x + r - l, y + b - t)
|
||||
|
||||
def get_width(self):
|
||||
l, t, r, b = self.bounds
|
||||
return r - l
|
||||
|
||||
def set_width(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, t, l + v, b)
|
||||
|
||||
def get_height(self):
|
||||
l, t, r, b = self.bounds
|
||||
return b - t
|
||||
|
||||
def set_height(self, v):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, t, r, t + v)
|
||||
|
||||
def get_size(self):
|
||||
l, t, r, b = self.bounds
|
||||
return (r - l, b - t)
|
||||
|
||||
def set_size(self, (w, h)):
|
||||
l, t, r, b = self.bounds
|
||||
self.bounds = (l, t, l + w, t + h)
|
||||
|
||||
#
|
||||
# Container management
|
||||
#
|
||||
|
||||
def get_container(self):
|
||||
return self._container
|
||||
|
||||
def set_container(self, new_container):
|
||||
if self._container != new_container:
|
||||
self._change_container(new_container)
|
||||
|
||||
def _change_container(self, new_container):
|
||||
old_container = self._container
|
||||
if old_container:
|
||||
self._container = None
|
||||
old_container._remove(self)
|
||||
if new_container:
|
||||
self._container = new_container
|
||||
new_container._add(self)
|
||||
|
||||
#
|
||||
# Message dispatching
|
||||
#
|
||||
|
||||
def become_target(self):
|
||||
"""Arrange for this object to be the first to handle messages
|
||||
dispatched to the containing Window. If the component is not
|
||||
contained in a Window, the effect is undefined."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_target(self):
|
||||
"""Return true if this is the current target within the containing
|
||||
Window. If the component is not contained in a Window, the result
|
||||
is undefined."""
|
||||
return self.window and self.window.target is self
|
||||
|
||||
#
|
||||
# Message handling
|
||||
#
|
||||
|
||||
def next_handler(self):
|
||||
return self._container
|
||||
|
||||
#
|
||||
# Visibility control
|
||||
#
|
||||
|
||||
# def show(self):
|
||||
# """Make the Component visible (provided its container is visible)."""
|
||||
# self.visible = 1
|
||||
#
|
||||
# def hide(self):
|
||||
# """Make the Component invisible."""
|
||||
# self.visible = 0
|
||||
|
||||
#
|
||||
# Border
|
||||
#
|
||||
|
||||
def get_border(self):
|
||||
return self._border
|
||||
|
||||
def set_border(self, x):
|
||||
self._border = x
|
||||
|
||||
#
|
||||
# Resizing
|
||||
#
|
||||
|
||||
def get_anchor(self):
|
||||
if self.hmove:
|
||||
s1 = 'r'
|
||||
elif self.hstretch:
|
||||
s1 = 'lr'
|
||||
else:
|
||||
s1 = 'l'
|
||||
if self.vmove:
|
||||
s2 = 'b'
|
||||
elif self.vstretch:
|
||||
s2 = 'tb'
|
||||
else:
|
||||
s2 = 't'
|
||||
return s1 + s2
|
||||
|
||||
def set_anchor(self, s):
|
||||
if 'r' in s:
|
||||
if 'l' in s:
|
||||
self.hstretch = True
|
||||
self.hmove = False
|
||||
else:
|
||||
self.hstretch = False
|
||||
self.hmove = True
|
||||
else:
|
||||
self.hstretch = False
|
||||
self.hmove = False
|
||||
if 'b' in s:
|
||||
if 't' in s:
|
||||
self.vstretch = True
|
||||
self.vmove = False
|
||||
else:
|
||||
self.vstretch = False
|
||||
self.vmove = True
|
||||
else:
|
||||
self.vstretch = False
|
||||
self.vmove = False
|
||||
|
||||
def get_auto_layout(self):
|
||||
return self._auto_layout
|
||||
|
||||
def set_auto_layout(self, x):
|
||||
self._auto_layout = x
|
||||
|
||||
def _resized(self, delta):
|
||||
# Called whenever the size of the component changes for
|
||||
# any reason.
|
||||
pass
|
||||
|
||||
def container_resized(self, delta):
|
||||
"""Called whenever the component's container changes size and the
|
||||
container's auto_layout property is true. The default implementation
|
||||
repositions and resizes this component according to its resizing
|
||||
options."""
|
||||
dw, dh = delta
|
||||
left, top, right, bottom = self.bounds
|
||||
if self.hmove:
|
||||
left += dw
|
||||
right += dw
|
||||
elif self.hstretch:
|
||||
right += dw
|
||||
if self.vmove:
|
||||
top += dh
|
||||
bottom += dh
|
||||
elif self.vstretch:
|
||||
bottom += dh
|
||||
self.bounds = (left, top, right, bottom)
|
||||
|
||||
#
|
||||
# Update region maintenance
|
||||
#
|
||||
|
||||
def invalidate(self):
|
||||
"""Mark the whole Component as needing to be redrawn."""
|
||||
self.invalidate_rect(self.viewed_rect())
|
||||
|
||||
# def invalidate_rect(self, r):
|
||||
# print "GComponent.invalidate_rect:", self, r ###
|
||||
# container = self._container
|
||||
# if container:
|
||||
# container.invalidate_rect(r)
|
||||
|
||||
# def _invalidate_in_container(self):
|
||||
# container = self._container
|
||||
# if container:
|
||||
# container._invalidate_subcomponent(self)
|
||||
|
||||
#
|
||||
# Coordinate transformation
|
||||
#
|
||||
|
||||
def local_to_global(self, p):
|
||||
p = self.local_to_container(p)
|
||||
parent = self._container
|
||||
if parent:
|
||||
return parent.local_to_global(p)
|
||||
else:
|
||||
return p
|
||||
|
||||
def global_to_local(self, p):
|
||||
parent = self._container
|
||||
if parent:
|
||||
p = parent.global_to_local(p)
|
||||
return self.container_to_local(p)
|
||||
|
||||
def local_to_container(self, p):
|
||||
if self._has_local_coords:
|
||||
return add_pt(p, self.local_to_container_offset())
|
||||
else:
|
||||
return p
|
||||
|
||||
def container_to_local(self, p):
|
||||
if self._has_local_coords:
|
||||
return sub_pt(p, self.local_to_container_offset())
|
||||
else:
|
||||
return p
|
||||
|
||||
def local_to_container_offset(self):
|
||||
if self._has_local_coords:
|
||||
return self.position
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def transform_from(self, other, p):
|
||||
return transform_coords(other, self, p)
|
||||
|
||||
def transform_to(self, other, p):
|
||||
return transform_coords(self, other, p)
|
||||
|
||||
#
|
||||
# Placement specification support
|
||||
#
|
||||
|
||||
def __add__(self, offset):
|
||||
return (self, offset)
|
||||
|
||||
def __sub__(self, offset):
|
||||
return (self, -offset)
|
||||
|
||||
#
|
||||
# Tabbing
|
||||
#
|
||||
|
||||
# def get_tabbable(self):
|
||||
# return self._tabbable
|
||||
#
|
||||
# def set_tabbable(self, value):
|
||||
# if self._tabbable <> value:
|
||||
# self._tabbable = value
|
||||
# self._invalidate_tab_chain()
|
||||
|
||||
def get_tab_stop(self):
|
||||
return self._tab_stop
|
||||
|
||||
def set_tab_stop(self, x):
|
||||
if self._tab_stop <> x:
|
||||
self._tab_stop = x
|
||||
self._invalidate_tab_chain()
|
||||
|
||||
def _get_default_tab_stop(self):
|
||||
if self._user_tab_stop_override:
|
||||
result = _user_tab_stop
|
||||
else:
|
||||
result = None
|
||||
if result is None:
|
||||
result = self._default_tab_stop
|
||||
return result
|
||||
|
||||
def _tab_out(self):
|
||||
pass
|
||||
|
||||
def _tab_in(self):
|
||||
self.become_target()
|
||||
|
||||
def _build_tab_chain(self, chain):
|
||||
if self._tab_stop:
|
||||
chain.append(self)
|
||||
|
||||
def _invalidate_tab_chain(self):
|
||||
window = self.window
|
||||
if window:
|
||||
window._invalidate_tab_chain()
|
||||
|
||||
def _is_targetable(self):
|
||||
return True
|
||||
|
||||
#
|
||||
# Other
|
||||
#
|
||||
|
||||
window = overridable_property('window', """The Window ultimately containing
|
||||
this Component, or None.""")
|
||||
|
||||
def get_window(self):
|
||||
container = self._container
|
||||
if container:
|
||||
return container.window
|
||||
else:
|
||||
return None
|
||||
|
||||
def reset_blink(self):
|
||||
application().reset_blink()
|
||||
|
||||
def viewed_rect(self):
|
||||
"""Returns the rectangle in local coordinates that is
|
||||
currently visible within the component."""
|
||||
if self._has_local_coords:
|
||||
width, height = self.size
|
||||
return (0, 0, width, height)
|
||||
else:
|
||||
return self.bounds
|
||||
|
||||
def broadcast(self, message, *args):
|
||||
"""Traverse the component hierarchy, calling each component's handler for
|
||||
the given message, if any."""
|
||||
method = getattr(self, message, None)
|
||||
if method:
|
||||
method(*args)
|
||||
|
||||
def _dispatch_mouse_event(self, event):
|
||||
self._handle_mouse_event(event)
|
||||
|
||||
def _handle_mouse_event(self, event):
|
||||
self.handle(event.kind, event)
|
||||
|
||||
|
||||
def transform_coords(from_component, to_component, p):
|
||||
if from_component:
|
||||
g = from_component.local_to_global(p)
|
||||
else:
|
||||
g = p
|
||||
if to_component:
|
||||
return to_component.global_to_local(g)
|
||||
else:
|
||||
return g
|
||||
|
|
@ -1,382 +0,0 @@
|
|||
#
|
||||
# Python GUI - Containers - Generic
|
||||
#
|
||||
|
||||
try:
|
||||
maketrans = str.maketrans
|
||||
except AttributeError:
|
||||
from string import maketrans
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Exceptions import ArgumentError
|
||||
from GUI.Geometry import pt_in_rect
|
||||
from GUI import Component
|
||||
|
||||
anchor_to_sticky = maketrans("ltrb", "wnes")
|
||||
|
||||
class Container(Component):
|
||||
"""A Container is a Component that can contain other Components.
|
||||
The sub-components are clipped to the boundary of their container."""
|
||||
|
||||
contents = overridable_property('contents',
|
||||
"List of subcomponents. Do not modify directly.")
|
||||
|
||||
content_width = overridable_property('content_width', "Width of the content area.")
|
||||
content_height = overridable_property('content_height', "Height of the content area.")
|
||||
content_size = overridable_property('content_size', "Size of the content area.")
|
||||
|
||||
auto_layout = overridable_property('auto_layout',
|
||||
"Automatically adjust layout of subcomponents when resized.")
|
||||
|
||||
_auto_layout = True
|
||||
|
||||
# _contents [Component]
|
||||
|
||||
def __init__(self, **kw):
|
||||
self._contents = []
|
||||
Component.__init__(self, **kw)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy this Container and all of its contents."""
|
||||
contents = self._contents
|
||||
while contents:
|
||||
comp = contents[-1]
|
||||
comp.destroy()
|
||||
assert not contents or contents[-1] is not comp, \
|
||||
"%r failed to remove itself from container on destruction" % comp
|
||||
Component.destroy(self)
|
||||
|
||||
#
|
||||
# Content area
|
||||
#
|
||||
|
||||
def get_content_width(self):
|
||||
return self.content_size[0]
|
||||
|
||||
def set_content_width(self, w):
|
||||
self.content_size = w, self.content_height
|
||||
|
||||
def get_content_height(self):
|
||||
return self.content_size[1]
|
||||
|
||||
def set_content_height(self, h):
|
||||
self.content_size = self.content_width, h
|
||||
|
||||
get_content_size = Component.get_size
|
||||
set_content_size = Component.set_size
|
||||
|
||||
#
|
||||
# Subcomponent Management
|
||||
#
|
||||
|
||||
def get_contents(self):
|
||||
return self._contents
|
||||
|
||||
def add(self, comp):
|
||||
"""Add the given Component as a subcomponent."""
|
||||
if comp:
|
||||
if isinstance(comp, Component):
|
||||
comp.container = self
|
||||
else:
|
||||
for item in comp:
|
||||
self.add(item)
|
||||
|
||||
def remove(self, comp):
|
||||
"""Remove subcomponent, if present."""
|
||||
if isinstance(comp, Component):
|
||||
if comp in self._contents:
|
||||
comp.container = None
|
||||
else:
|
||||
for item in comp:
|
||||
self.remove(item)
|
||||
|
||||
def _add(self, comp):
|
||||
# Called by comp.set_container() to implement subcomponent addition.
|
||||
self._contents.append(comp)
|
||||
self._invalidate_tab_chain()
|
||||
self.added(comp)
|
||||
|
||||
def _remove(self, comp):
|
||||
# Called by comp.set_container() to implement subcomponent removal.
|
||||
self._contents.remove(comp)
|
||||
self._invalidate_tab_chain()
|
||||
self.removed(comp)
|
||||
|
||||
def added(self, comp):
|
||||
"""Called after a subcomponent has been added."""
|
||||
pass
|
||||
|
||||
def removed(self, comp):
|
||||
"""Called after a subcomponent has been removed."""
|
||||
pass
|
||||
|
||||
#
|
||||
# The infamous 'place' method and friends.
|
||||
#
|
||||
|
||||
_place_default_spacing = 8
|
||||
|
||||
def place(self, item,
|
||||
left = None, right = None, top = None, bottom = None,
|
||||
sticky = 'nw', scrolling = '', border = None, anchor = None):
|
||||
"""Add a component to the frame with positioning,
|
||||
resizing and scrolling options. See the manual for details."""
|
||||
self._place([item], left = left, right = right, top = top, bottom = bottom,
|
||||
sticky = sticky, scrolling = scrolling, border = border, anchor = anchor)
|
||||
|
||||
def place_row(self, items,
|
||||
left = None, right = None, top = None, bottom = None,
|
||||
sticky = 'nw', scrolling = '', border = None, spacing = None,
|
||||
anchor = None):
|
||||
"""Add a row of components to the frame with positioning,
|
||||
resizing and scrolling options. See the manual for details."""
|
||||
if left is not None and right is not None:
|
||||
raise ValueError("Cannot specify both left and right to place_row")
|
||||
elif left is None and right is not None:
|
||||
direction = 'left'
|
||||
items = items[:]
|
||||
items.reverse()
|
||||
else:
|
||||
direction = 'right'
|
||||
self._place(items, left = left, right = right, top = top, bottom = bottom,
|
||||
sticky = sticky, scrolling = scrolling, border = border,
|
||||
direction = direction, spacing = spacing, anchor = anchor)
|
||||
|
||||
def place_column(self, items,
|
||||
left = None, right = None, top = None, bottom = None,
|
||||
sticky = 'nw', scrolling = '', border = None, spacing = None,
|
||||
anchor = None):
|
||||
"""Add a column of components to the frame with positioning,
|
||||
resizing and scrolling options. See the manual for details."""
|
||||
if top is not None and bottom is not None:
|
||||
raise ValueError("Cannot specify both top and bottom to place_column")
|
||||
elif top is None and bottom is not None:
|
||||
direction = 'up'
|
||||
items = items[:]
|
||||
items.reverse()
|
||||
else:
|
||||
direction = 'down'
|
||||
self._place(items, left = left, right = right, top = top, bottom = bottom,
|
||||
sticky = sticky, scrolling = scrolling, border = border,
|
||||
direction = direction, spacing = spacing, anchor = anchor)
|
||||
|
||||
def _place(self, items,
|
||||
left = None,
|
||||
right = None,
|
||||
top = None,
|
||||
bottom = None,
|
||||
sticky = 'nw',
|
||||
scrolling = '',
|
||||
direction = 'right',
|
||||
spacing = None,
|
||||
border = None,
|
||||
anchor = None):
|
||||
|
||||
def side(spec, name):
|
||||
# Process a side specification taking the form of either
|
||||
# (1) an offset, (2) a reference component, or (3) a
|
||||
# tuple (component, offset). Returns a tuple (ref, offset)
|
||||
# where ref is the reference component or None (representing
|
||||
# the Frame being placed into). Checks that the reference
|
||||
# component, if any, is directly contained by this Frame.
|
||||
ref = None
|
||||
offset = None
|
||||
if spec is not None:
|
||||
if isinstance(spec, tuple):
|
||||
ref, offset = spec
|
||||
elif isinstance(spec, Component):
|
||||
ref = spec
|
||||
offset = 0
|
||||
elif isinstance(spec, (int, float)):
|
||||
offset = spec
|
||||
else:
|
||||
raise ArgumentError(self, 'place', name, spec)
|
||||
if ref is self:
|
||||
ref = None
|
||||
elif ref:
|
||||
con = ref.container
|
||||
#if con is not self and isinstance(con, ScrollFrame):
|
||||
# ref = con
|
||||
# con = ref.container
|
||||
if con is not self:
|
||||
raise ValueError("Reference component for place() is not"
|
||||
" directly contained by the frame being placed into.")
|
||||
return ref, offset
|
||||
|
||||
if spacing is None:
|
||||
spacing = self._place_default_spacing
|
||||
|
||||
# Decode the sticky options
|
||||
if anchor is not None:
|
||||
sticky = anchor.translate(anchor_to_sticky)
|
||||
hmove = vmove = hstretch = vstretch = 0
|
||||
if 'e' in sticky:
|
||||
if 'w' in sticky:
|
||||
hstretch = 1
|
||||
else:
|
||||
hmove = 1
|
||||
if 's' in sticky:
|
||||
if 'n' in sticky:
|
||||
vstretch = 1
|
||||
else:
|
||||
vmove = 1
|
||||
|
||||
# Translate the direction argument
|
||||
try:
|
||||
dir = {'right':0, 'down':1, 'left':2, 'up':3}[direction]
|
||||
except KeyError:
|
||||
raise ArgumentError(self, 'place', 'direction', direction)
|
||||
|
||||
# Unpack the side arguments
|
||||
left_obj, left_off = side(left, 'left')
|
||||
right_obj, right_off = side(right, 'right')
|
||||
top_obj, top_off = side(top, 'top')
|
||||
bottom_obj, bottom_off = side(bottom, 'bottom')
|
||||
|
||||
# Process the items
|
||||
#if not isinstance(items, list):
|
||||
# items = [items]
|
||||
for item in items:
|
||||
x, y = item.position
|
||||
w, h = item.size
|
||||
# Calculate left edge position
|
||||
if left_obj:
|
||||
l = left_obj.left + left_obj.width + left_off
|
||||
elif left_off is not None:
|
||||
if left_off < 0:
|
||||
l = self.width + left_off
|
||||
else:
|
||||
l = left_off
|
||||
else:
|
||||
l = None
|
||||
# Calculate top edge position
|
||||
if top_obj:
|
||||
t = top_obj.top + top_obj.height + top_off
|
||||
elif top_off is not None:
|
||||
if top_off < 0:
|
||||
t = self.height + top_off
|
||||
else:
|
||||
t = top_off
|
||||
else:
|
||||
t = None
|
||||
# Calculate right edge position
|
||||
if right_obj:
|
||||
r = right_obj.left + right_off
|
||||
elif right_off is not None:
|
||||
if right_off <= 0:
|
||||
r = self.width + right_off
|
||||
else:
|
||||
r = right_off
|
||||
else:
|
||||
r = None
|
||||
# Calculate bottom edge position
|
||||
if bottom_obj:
|
||||
b = bottom_obj.top + bottom_off
|
||||
elif bottom_off is not None:
|
||||
if bottom_off <= 0:
|
||||
b = self.height + bottom_off
|
||||
else:
|
||||
b = bottom_off
|
||||
else:
|
||||
b = None
|
||||
# Fill in unspecified positions
|
||||
if l is None:
|
||||
if r is not None:
|
||||
l = r - w
|
||||
else:
|
||||
l = x
|
||||
if r is None:
|
||||
r = l + w
|
||||
if t is None:
|
||||
if b is not None:
|
||||
t = b - h
|
||||
else:
|
||||
t = y
|
||||
if b is None:
|
||||
b = t + h
|
||||
if scrolling:
|
||||
item.scrolling = scrolling
|
||||
# Position, resize and add the item
|
||||
item.bounds = (l, t, r, b)
|
||||
self.add(item)
|
||||
# Record resizing and border options
|
||||
item.hmove = hmove
|
||||
item.vmove = vmove
|
||||
item.hstretch = hstretch
|
||||
item.vstretch = vstretch
|
||||
if border is not None:
|
||||
item.border = border
|
||||
# Step to the next item
|
||||
if dir == 0:
|
||||
left_obj = item
|
||||
left_off = spacing
|
||||
elif dir == 1:
|
||||
top_obj = item
|
||||
top_off = spacing
|
||||
elif dir == 2:
|
||||
right_obj = item
|
||||
right_off = -spacing
|
||||
else:
|
||||
bottom_obj = item
|
||||
bottom_off = -spacing
|
||||
|
||||
#
|
||||
# Resizing
|
||||
#
|
||||
|
||||
def _resized(self, delta):
|
||||
if self._auto_layout:
|
||||
self.resized(delta)
|
||||
|
||||
def resized(self, delta):
|
||||
for c in self._contents:
|
||||
c.container_resized(delta)
|
||||
|
||||
def resize(self, auto_layout = False, **kwds):
|
||||
"""Change the geometry of the component, with control over whether
|
||||
the layout of subcomponents is updated. The default is not to do so.
|
||||
Keyword arguments to this method may be any of the properties
|
||||
affecting position and size (i.e. left, top, right, bottom, x, y,
|
||||
width, height, position, size, bounds)."""
|
||||
old_auto_layout = self.auto_layout
|
||||
try:
|
||||
self.auto_layout = auto_layout
|
||||
self.set(**kwds)
|
||||
finally:
|
||||
self.auto_layout = old_auto_layout
|
||||
|
||||
#
|
||||
# Tabbing
|
||||
#
|
||||
|
||||
def _build_tab_chain(self, chain):
|
||||
Component._build_tab_chain(self, chain)
|
||||
for c in self._contents:
|
||||
c._build_tab_chain(chain)
|
||||
|
||||
#
|
||||
# Other
|
||||
#
|
||||
|
||||
def shrink_wrap(self, padding = None):
|
||||
"""Adjust the size of the component so that it neatly encloses its
|
||||
contents. If padding is specified, it specifies the amount of space
|
||||
to leave at right and bottom, otherwise the minimum distance from the
|
||||
left and top sides to the nearest components is used."""
|
||||
contents = self.contents
|
||||
if not contents:
|
||||
return
|
||||
if padding:
|
||||
hpad, vpad = padding
|
||||
else:
|
||||
hpad = min([item.left for item in contents])
|
||||
vpad = min([item.top for item in contents])
|
||||
rights = [item.right for item in contents]
|
||||
bottoms = [item.bottom for item in contents]
|
||||
self.resize(size = (max(rights) + hpad, max(bottoms) + vpad))
|
||||
|
||||
def broadcast(self, message, *args):
|
||||
"""Traverse the component hierarchy, calling each component's handler for
|
||||
the given message, if any."""
|
||||
Component.broadcast(self, message, *args)
|
||||
for comp in self._contents:
|
||||
comp.broadcast(message, *args)
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#
|
||||
# Python GUI - Controls - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Component
|
||||
|
||||
class Control(Component):
|
||||
"""Abstract base class for components such as buttons, check
|
||||
boxes and text entry boxes."""
|
||||
|
||||
title = overridable_property('title', "Title of the control.")
|
||||
value = overridable_property('value', "Value of the control.")
|
||||
enabled = overridable_property('enabled', "True if user can manipulate the control.")
|
||||
font = overridable_property('font')
|
||||
color = overridable_property('color')
|
||||
just = overridable_property('just', "Justification ('left', 'center' or 'right').")
|
||||
lines = overridable_property('lines',
|
||||
"Height of the control measured in lines of the current font.")
|
||||
tab_stop = overridable_property('tab_stop',
|
||||
"Whether tab key can navigate into this control.")
|
||||
|
||||
_vertical_padding = 0 # Extra height to add when setting 'lines' property
|
||||
_default_tab_stop = True
|
||||
_user_tab_stop_override = True
|
||||
|
||||
def __init__(self, font = None, lines = None, **kwds):
|
||||
Component.__init__(self, **kwds)
|
||||
# If font and lines are both specified, must set font first.
|
||||
if font:
|
||||
self.font = font
|
||||
if lines is not None:
|
||||
self.lines = lines
|
||||
|
||||
def get_lines(self):
|
||||
return int(round((self.height - self._vertical_padding) / self.font.line_height))
|
||||
|
||||
def set_lines(self, num_lines):
|
||||
self.height = self._calc_height(self.font, num_lines)
|
||||
|
||||
def _calc_height(self, font, num_lines = 1):
|
||||
return num_lines * font.line_height + self._vertical_padding
|
||||
|
||||
def _is_targetable(self):
|
||||
return self.enabled
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#--------------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Cursors - Generic
|
||||
#
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
from GUI.Properties import Properties
|
||||
from GUI.Resources import lookup_resource, find_resource, get_resource
|
||||
from GUI import Image
|
||||
|
||||
def _hotspot_for_resource(resource_name):
|
||||
path = lookup_resource(resource_name, "hot")
|
||||
if path:
|
||||
f = open(path, "rU")
|
||||
xs, ys = f.readline().split()
|
||||
return int(xs), int(ys)
|
||||
else:
|
||||
return None
|
||||
|
||||
class Cursor(Properties):
|
||||
"""A Cursor is an image representing the mouse pointer.
|
||||
|
||||
Constructors:
|
||||
Cursor(resource_name, hotspot)
|
||||
Cursor(image, hotspot)
|
||||
"""
|
||||
|
||||
def from_resource(cls, name, hotspot = None, **kwds):
|
||||
def load(path):
|
||||
image = Image.from_resource(name, **kwds)
|
||||
return cls(image, hotspot or _hotspot_for_resource(name))
|
||||
return get_resource(load, name)
|
||||
|
||||
from_resource = classmethod(from_resource)
|
||||
|
||||
def __init__(self, spec, hotspot = None):
|
||||
"""Construct a Cursor from a resource or Image and a hotspot point.
|
||||
The hotspot defaults to the centre of the image."""
|
||||
if isinstance(spec, basestring):
|
||||
self._init_from_resource(spec, hotspot)
|
||||
else:
|
||||
self._init_from_image(spec, hotspot)
|
||||
|
||||
def _init_from_resource(self, resource_name, hotspot):
|
||||
image = Image(file = find_resource(resource_name))
|
||||
if not hotspot:
|
||||
hotspot = _hotspot_for_resource(resource_name)
|
||||
self._init_from_image(image, hotspot)
|
||||
|
||||
def _init_from_image(self, image, hotspot):
|
||||
if not hotspot:
|
||||
width, height = image.size
|
||||
hotspot = (width // 2, height // 2)
|
||||
self._init_from_image_and_hotspot(image, hotspot)
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#
|
||||
# Python GUI - Dialogs - Generic
|
||||
#
|
||||
|
||||
from GUI import Globals
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Actions import ActionBase, action_property
|
||||
from GUI import Window
|
||||
|
||||
class Dialog(Window, ActionBase):
|
||||
|
||||
_default_keys = "\r"
|
||||
_cancel_keys = "\x1b"
|
||||
|
||||
# default_button = overridable_property('default_button',
|
||||
# "Button to be activated by the default key.")
|
||||
#
|
||||
# cancel_button = overridable_property('cancel_button',
|
||||
# "Button to be activated by the cancel key.")
|
||||
#
|
||||
# _default_button = None
|
||||
# _cancel_button = None
|
||||
|
||||
default_action = action_property('default_action',
|
||||
"Action to perform when Return or Enter is pressed.")
|
||||
|
||||
cancel_action = action_property('cancel_action',
|
||||
"Action to perform when Escape is pressed.")
|
||||
|
||||
_default_action = 'ok'
|
||||
_cancel_action ='cancel'
|
||||
|
||||
def __init__(self, style = 'nonmodal_dialog',
|
||||
closable = 0, zoomable = 0, resizable = 0, **kwds):
|
||||
if 'title' not in kwds:
|
||||
kwds['title'] = Globals.application_name
|
||||
Window.__init__(self, style = style,
|
||||
closable = closable, zoomable = zoomable, resizable = resizable,
|
||||
**kwds)
|
||||
|
||||
# def get_default_button(self):
|
||||
# return self._default_button
|
||||
#
|
||||
# def set_default_button(self, button):
|
||||
# self._default_button = button
|
||||
# if button:
|
||||
# button.style = 'default'
|
||||
#
|
||||
# def get_cancel_button(self):
|
||||
# return self._cancel_button
|
||||
#
|
||||
# def set_cancel_button(self, button):
|
||||
# self._cancel_button = button
|
||||
# if button:
|
||||
# button.style = 'cancel'
|
||||
|
||||
def key_down(self, event):
|
||||
#print "GDialog.key_down:", repr(event.char) ###
|
||||
c = event.char
|
||||
if c:
|
||||
if c in self._default_keys:
|
||||
self.do_default_action()
|
||||
return
|
||||
elif c in self._cancel_keys:
|
||||
self.do_cancel_action()
|
||||
return
|
||||
Window.key_down(self, event)
|
||||
|
||||
def do_default_action(self):
|
||||
self.do_named_action('default_action')
|
||||
|
||||
def do_cancel_action(self):
|
||||
self.do_named_action('cancel_action')
|
||||
|
||||
# def _activate_button(self, button):
|
||||
# if button:
|
||||
# button.activate()
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#--------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - DrawableContainer - Generic
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
from GUI.Geometry import rect_sized
|
||||
from GUI import Container
|
||||
from GUI import ViewBase
|
||||
from GUI.Printing import Printable
|
||||
|
||||
default_size = (100, 100)
|
||||
|
||||
class DrawableContainer(ViewBase, Container, Printable):
|
||||
|
||||
#
|
||||
# Construction and destruction
|
||||
#
|
||||
|
||||
def __init__(self, **kwds):
|
||||
Container.__init__(self, **kwds)
|
||||
ViewBase.__init__(self)
|
||||
|
||||
def destroy(self):
|
||||
ViewBase.destroy(self)
|
||||
Container.destroy(self)
|
||||
|
||||
def setup_menus(self, m):
|
||||
ViewBase.setup_menus(self, m)
|
||||
Container.setup_menus(self, m)
|
||||
|
||||
def viewed_rect(self):
|
||||
"""Return the rectangle in local coordinates bounding the currently
|
||||
visible part of the extent."""
|
||||
return rect_sized((0, 0), self.size)
|
||||
|
||||
def with_canvas(self, proc):
|
||||
"""Call the procedure with a canvas suitable for drawing in this
|
||||
view. The canvas is only valid for the duration of the call, and
|
||||
should not be retained beyond it."""
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self):
|
||||
"""Redraw invalidated regions immediately, without waiting for a
|
||||
return to the event loop."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_print_extent(self):
|
||||
return self.content_size
|
||||
|
||||
def _draw_background(self, canvas, clip_rect):
|
||||
return clip_rect
|
||||
|
||||
#
|
||||
# Callbacks
|
||||
#
|
||||
|
||||
def draw(self, canvas, rect):
|
||||
"""Called when the view needs to be drawn. The rect is the bounding
|
||||
rectangle of the region needing to be drawn. The default implementation
|
||||
does nothing."""
|
||||
pass
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#
|
||||
# PyGUI - Edit command handling - Generic
|
||||
#
|
||||
|
||||
from GUI import application
|
||||
|
||||
class EditCmdHandler(object):
|
||||
# Mixin for objects that implement the standard editing commands.
|
||||
|
||||
_may_be_password = False
|
||||
|
||||
def setup_menus(self, m):
|
||||
selbeg, selend = self.selection
|
||||
anysel = selbeg < selend
|
||||
anyscrap = application().query_clipboard()
|
||||
passwd = self._may_be_password and self.password
|
||||
m.cut_cmd.enabled = anysel and not passwd
|
||||
m.copy_cmd.enabled = anysel and not passwd
|
||||
m.paste_cmd.enabled = anyscrap
|
||||
m.clear_cmd.enabled = anysel
|
||||
m.select_all_cmd.enabled = True
|
||||
|
||||
def select_all_cmd(self):
|
||||
self.select_all()
|
||||
|
||||
def select_all(self):
|
||||
self.selection = (0, self.get_text_length())
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#
|
||||
# Python GUI - Events - Generic
|
||||
#
|
||||
|
||||
class Event(object):
|
||||
|
||||
"""An input event.
|
||||
|
||||
Attributes:
|
||||
|
||||
kind Type of event. One of 'mouse_down', 'mouse_up', 'key_down',
|
||||
'key_up'.
|
||||
|
||||
global_position Position of mouse in screen coordinates at the time of the event.
|
||||
|
||||
position For mouse events, position in local coordinates of the View that
|
||||
was the target of this event. Undefined for other event types.
|
||||
|
||||
time Time of event, in platform-dependent units.
|
||||
|
||||
button Button identifier for mouse down/up events.
|
||||
|
||||
num_clicks Number of consecutive clicks within double-click time.
|
||||
|
||||
char For key events, an ASCII character. Undefined for other event types.
|
||||
|
||||
key For non-printing keys, a value identifying the key. Undefined for other event types.
|
||||
|
||||
auto True if key-down event is an autorepeat (not supported on all platforms).
|
||||
|
||||
Platform-independent modifiers (boolean):
|
||||
|
||||
shift The Shift key.
|
||||
control The Control key.
|
||||
option The additional modifier key.
|
||||
extend_contig The contiguous selection extension modifier key.
|
||||
extend_noncontig The noncontiguous selection extension modifier key.
|
||||
"""
|
||||
|
||||
kind = None
|
||||
global_position = None
|
||||
position = None
|
||||
time = None
|
||||
button = None
|
||||
num_clicks = 0
|
||||
char = None
|
||||
key = None
|
||||
auto = False
|
||||
shift = False
|
||||
control = False
|
||||
option = False
|
||||
extend_contig = False
|
||||
extend_noncontig = False
|
||||
delta = (0, 0)
|
||||
_keycode = 0 # Platform-dependent key code
|
||||
_originator = None # Component to which originally delivered by platform
|
||||
_not_handled = False # Reached default event method of originating component
|
||||
|
||||
def position_in(self, view):
|
||||
"""Return the position of this event in the coordinate system
|
||||
of the specified view."""
|
||||
return view.global_to_local(self.global_position)
|
||||
|
||||
def __str__(self):
|
||||
return "<GUI.Event: %s global:%s local:%s time:%s clicks:%s char:%r" \
|
||||
" key:%s shift:%s control:%s option:%s extend_contig:%s" \
|
||||
" extend_noncontig:%s auto:%s%s>" \
|
||||
% (self.kind, self.global_position, self.position, self.time,
|
||||
self.num_clicks, self.char, self.key, self.shift, self.control,
|
||||
self.option, self.extend_contig, self.extend_noncontig, self.auto,
|
||||
self._platform_modifiers_str())
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
#
|
||||
# Python GUI - File references and types - Generic
|
||||
#
|
||||
# Classes for dealing with file references and file types
|
||||
# in as platform-independent a manner as possible.
|
||||
#
|
||||
# In this view of things, a file reference consists
|
||||
# of two parts:
|
||||
#
|
||||
# 1) A directory reference, whose nature is
|
||||
# platform-dependent,
|
||||
#
|
||||
# 2) A name.
|
||||
#
|
||||
|
||||
import os
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
class FileRef(Properties):
|
||||
"""A FileRef represents a file system object in a platform-independent way.
|
||||
It consists of two parts, a directory specification and the name of an
|
||||
object within that directory. The directory specification always refers
|
||||
to an existing directory, but the named object may or may not exist.
|
||||
|
||||
Constructors:
|
||||
FileRef(dir = DirRef or path, name = string)
|
||||
FileRef(path = string)
|
||||
"""
|
||||
|
||||
dir = overridable_property('dir', "DirRef representing the parent directory.")
|
||||
name = overridable_property('name', "Name of the object within the parent directory.")
|
||||
path = overridable_property('path', "Full pathname of the object.")
|
||||
|
||||
_dir = None # DirRef representing the parent directory
|
||||
_name = None # Name, including type suffix if any
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
|
||||
def __init__(self, dir = None, name = None, path = None):
|
||||
if dir and name and not path:
|
||||
if not isinstance(dir, DirRef):
|
||||
dir = DirRef(dir)
|
||||
elif path and not (dir or name):
|
||||
dirpath, name = os.path.split(path)
|
||||
dir = DirRef(path = dirpath)
|
||||
else:
|
||||
raise TypeError("Invalid argument combination to FileRef constructor")
|
||||
self._dir = dir
|
||||
self._name = name
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
def get_dir(self):
|
||||
return self._dir
|
||||
|
||||
def get_name(self):
|
||||
"Return the name of the file."
|
||||
return self._name
|
||||
|
||||
def get_path(self):
|
||||
return os.path.join(self._dir.path, self._name)
|
||||
|
||||
#
|
||||
# Methods
|
||||
#
|
||||
|
||||
def open(self, mode, file_type = None):
|
||||
"""Open as a file with the given mode and return a file object. On
|
||||
platforms which have file-type metadata (e.g. Macintosh), if the
|
||||
mode contains 'w' and a file_type is specified, the newly-created
|
||||
file will be given the specified type."""
|
||||
f = open(self.path, mode)
|
||||
if "w" in mode and file_type:
|
||||
self._set_type(file_type)
|
||||
return f
|
||||
|
||||
def mkdir(self):
|
||||
"""Create a directory with the name and parent directory specified
|
||||
by this FileRef. Returns a DirRef for the created directory."""
|
||||
return DirRef(os.mkdir(self.path))
|
||||
|
||||
def _set_type(self, file_type):
|
||||
# Platforms which have file-type metadata (e.g. Macintosh) use this
|
||||
# to set the type of a file.
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return "FileRef(%r,%r)" % (self.dir.path, self.name)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
class DirRef(Properties):
|
||||
"""A DirRef is an object representing a directory in the
|
||||
file system. Its representation is completely platform
|
||||
dependent.
|
||||
|
||||
Constructor:
|
||||
DirRef(path = string)
|
||||
"""
|
||||
|
||||
_path = None
|
||||
|
||||
path = overridable_property('path', "Full pathname of the directory.")
|
||||
|
||||
def __init__(self, path):
|
||||
self._path = path
|
||||
|
||||
def get_path(self):
|
||||
return self._path
|
||||
|
||||
def __str__(self):
|
||||
return "DirRef(%r)" % self.path
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
class FileType(Properties):
|
||||
"""A FileType is a multi-platform representation of a file type."""
|
||||
|
||||
_name = None
|
||||
_suffix = None
|
||||
_mac_creator = None
|
||||
_mac_type = None
|
||||
_mac_force_suffix = True
|
||||
|
||||
name = overridable_property('name', "Human-readable description of the file type")
|
||||
suffix = overridable_property('suffix', "Filename suffix (without dot)")
|
||||
mac_creator = overridable_property('mac_creator', "Macintosh 4-character creator code")
|
||||
mac_type = overridable_property('mac_type', "Macintosh 4-character type code")
|
||||
mac_force_suffix = overridable_property('mac_force_suffix', "Enforce filename suffix on MacOSX")
|
||||
|
||||
def get_name(self):
|
||||
return self._name
|
||||
|
||||
def set_name(self, x):
|
||||
self._name = x
|
||||
|
||||
def get_suffix(self):
|
||||
return self._suffix
|
||||
|
||||
def set_suffix(self, x):
|
||||
self._suffix = x
|
||||
|
||||
def get_mac_creator(self):
|
||||
return self._mac_creator
|
||||
|
||||
def set_mac_creator(self, x):
|
||||
self._mac_creator = x
|
||||
|
||||
def get_mac_type(self):
|
||||
return self._mac_type
|
||||
|
||||
def set_mac_type(self, x):
|
||||
self._mac_type = x
|
||||
|
||||
def get_mac_force_suffix(self):
|
||||
return self._mac_force_suffix
|
||||
|
||||
def set_mac_force_suffix(self, x):
|
||||
self._mac_force_suffix = x
|
||||
|
||||
def _matches(self, name, mac_type):
|
||||
# Return true if the given name or type code matches that of
|
||||
# this file type.
|
||||
this_mac_type = self._mac_type
|
||||
this_suffix = self._suffix
|
||||
if this_mac_type and mac_type == this_mac_type:
|
||||
return True
|
||||
# Allow generic text files to match typeless files for MacOSX
|
||||
if not this_suffix and this_mac_type == "TEXT" and mac_type == "\0\0\0\0":
|
||||
return True
|
||||
if this_suffix and _matches_suffix(name, this_suffix):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _add_suffix(self, name):
|
||||
# Force the given name to have the appropriate suffix for this file
|
||||
# type. Platforms which have other means of representing file types
|
||||
# (e.g. Macintosh) may override this.
|
||||
suffix = self._suffix
|
||||
if suffix and not _matches_suffix(name, suffix):
|
||||
name = "%s.%s" % (name, suffix)
|
||||
return name
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def _matches_suffix(name, suffix):
|
||||
# Test case-insensitively whether the given filename has
|
||||
# the given suffix.
|
||||
return name.lower().endswith("." + suffix.lower())
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
#
|
||||
# Python GUI - Fonts - Generic
|
||||
#
|
||||
|
||||
import sys
|
||||
from GUI.Properties import overridable_property
|
||||
|
||||
class Font(object):
|
||||
"""A Font object represents a set of characters of a particular
|
||||
typeface, style and size. Font objects are immutable.
|
||||
|
||||
Constructors:
|
||||
Font(family, size, style)
|
||||
family = family name
|
||||
size = size in points
|
||||
style = a list of 'bold', 'italic'
|
||||
|
||||
Properties:
|
||||
family --> string
|
||||
size --> number
|
||||
style --> ['bold', 'italic']
|
||||
ascent --> number
|
||||
descent --> number
|
||||
leading --> number
|
||||
height --> number
|
||||
cap_height --> number
|
||||
x_height --> number
|
||||
line_height --> number
|
||||
"""
|
||||
|
||||
family = overridable_property('family', "Family name ('Times', 'Helvetica', etc.)")
|
||||
size = overridable_property('size', "Size in points")
|
||||
style = overridable_property('style', "A list of 'bold', 'italic'")
|
||||
ascent = overridable_property('ascent', "Distance from baseline to top of highest character")
|
||||
descent = overridable_property('descent', "Distance from baseline to bottom of lowest character")
|
||||
height = overridable_property('height', "Sum of ascent and descent")
|
||||
cap_height = overridable_property('cap_height', "Height above baseline of capital letters")
|
||||
x_height = overridable_property('x_height', "Height above baseline of lowercase letters without ascenders")
|
||||
leading = overridable_property('leading', "Recommended extra space between lines")
|
||||
line_height = overridable_property('line_height', "Recommended distance between baselines")
|
||||
|
||||
def get_cap_height(self):
|
||||
# Approximation for platforms not supporting this
|
||||
return self.ascent
|
||||
|
||||
def get_x_height(self):
|
||||
# Approximation for platforms not supporting this
|
||||
return self.ascent - self.descent
|
||||
|
||||
def get_leading(self):
|
||||
return self.line_height - self.height
|
||||
|
||||
def but(self, family = None, size = None, style = None,
|
||||
style_includes = None, style_excludes = None):
|
||||
"""Return a new Font that is the same as this one except for the
|
||||
specified characteristics."""
|
||||
if not family:
|
||||
family = self.family
|
||||
if not size:
|
||||
size = self.size
|
||||
if style is None:
|
||||
style = self.style
|
||||
style = style[:]
|
||||
if style_includes:
|
||||
for item in style_includes:
|
||||
style.append(item)
|
||||
if style_excludes:
|
||||
for item in style_excludes:
|
||||
if item in style:
|
||||
style.remove(item)
|
||||
return self.__class__(family, size, style)
|
||||
|
||||
def width(self, s, start = 0, end = None):
|
||||
"""width(s [,start [,end ]])
|
||||
The width of the specified part of the given string in this font."""
|
||||
if start or end is not None:
|
||||
if end is None:
|
||||
end = len(s)
|
||||
s = s[start:end]
|
||||
return self._width(s)
|
||||
|
||||
def _width(self, s):
|
||||
raise NotImplementedError
|
||||
|
||||
def x_to_pos(self, s, x):
|
||||
"""Given a number of pixels measured from the left of
|
||||
the given string, returns the nearest inter-character position.
|
||||
Returns 0 if x is negative, and len(string) if x is beyond the
|
||||
right end of the string."""
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
return "Font(%r,%g,%s)" % (self.family, self.size, self.style)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#
|
||||
# Python GUI - Frames - Generic
|
||||
#
|
||||
|
||||
from GUI import Container
|
||||
|
||||
class Frame(Container):
|
||||
"""A Frame is a general-purpose instantiable subclass of Container."""
|
||||
|
||||
_default_size = (100, 100)
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Pixel Formats - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
class GLConfig(Properties):
|
||||
"""Class holding the attributes of an OpenGL context configuration."""
|
||||
|
||||
# NOTE: When adding a property here, also add it to
|
||||
# _pixel_format_attribute_names below.
|
||||
|
||||
double_buffer = overridable_property("double_buffer", "True if context is to be double-buffered.")
|
||||
alpha = overridable_property("alpha", "True if there is to be an alpha channel.")
|
||||
color_size = overridable_property("color_size", "Number of bits per colour buffer component.")
|
||||
alpha_size = overridable_property("alpha_size", "Number of bits per alpha channel component.")
|
||||
stereo = overridable_property("stereo", "True if stereoscopic context is required.")
|
||||
aux_buffers = overridable_property("aux_buffers", "Number of auxiliary colour buffers to allocate.")
|
||||
depth_buffer = overridable_property("depth_buffer", "True if a depth buffer is required.")
|
||||
depth_size = overridable_property("depth_size", "Number of bits per depth buffer element.")
|
||||
stencil_buffer = overridable_property("stencil_buffer", "True if a stencil buffer is required.")
|
||||
stencil_size = overridable_property("stencil_size", "Number of bits per stencil buffer element.")
|
||||
accum_buffer = overridable_property("accum_buffer", "True if an accumulation buffer is required.")
|
||||
accum_size = overridable_property("accum_size", "Number of bits per accumulation buffer component.")
|
||||
multisample = overridable_property("multisample", "True if a multisampled context is required.")
|
||||
samples_per_pixel = overridable_property("samples_per_pixel", "Number of samples per multisampled pixel.")
|
||||
|
||||
_double_buffer = True
|
||||
_alpha = True
|
||||
_color_size = 8
|
||||
_alpha_size = 8
|
||||
_stereo = False
|
||||
_aux_buffers = 0
|
||||
_depth_buffer = True
|
||||
_depth_size = 32
|
||||
_stencil_buffer = False
|
||||
_stencil_size = 8
|
||||
_accum_buffer = False
|
||||
_accum_size = 8
|
||||
_multisample = False
|
||||
_samples_per_pixel = 4
|
||||
|
||||
_pixel_format_attribute_names = (
|
||||
'double_buffer', 'alpha', 'color_size', 'alpha_size',
|
||||
'stereo', 'aux_buffers', 'depth_buffer', 'depth_size',
|
||||
'stencil_buffer', 'stencil_size', 'accum_buffer', 'accum_size',
|
||||
'multisample', 'samples_per_pixel',
|
||||
)
|
||||
|
||||
def _from_args(cls, config, kwds):
|
||||
# Extract pixel format arguments from arguments of GLView.__init__
|
||||
# or GLPixmap.__init__ and return a GLConfig. Used keyword
|
||||
# arguments are removed from kwds.
|
||||
pf_kwds = {}
|
||||
for name in cls._pixel_format_attribute_names:
|
||||
if name in kwds:
|
||||
pf_kwds[name] = kwds.pop(name)
|
||||
if config and pf_kwds:
|
||||
raise TypeError("Explicit config cannot be used with other configuration keyword arguments")
|
||||
if not config:
|
||||
config = cls(**pf_kwds)
|
||||
return config
|
||||
|
||||
_from_args = classmethod(_from_args)
|
||||
|
||||
def get_double_buffer(self):
|
||||
return self._double_buffer
|
||||
|
||||
def set_double_buffer(self, x):
|
||||
self._double_buffer = x
|
||||
|
||||
def get_alpha(self):
|
||||
return self._alpha
|
||||
|
||||
def set_alpha(self, x):
|
||||
self._alpha = x
|
||||
|
||||
def get_color_size(self):
|
||||
return self._color_size
|
||||
|
||||
def set_color_size(self, x):
|
||||
self._color_size = x
|
||||
|
||||
def get_alpha_size(self):
|
||||
return self._alpha_size
|
||||
|
||||
def set_alpha_size(self, x):
|
||||
self._alpha_size = x
|
||||
|
||||
def get_stereo(self):
|
||||
return self._stereo
|
||||
|
||||
def set_stereo(self, x):
|
||||
self._stereo = x
|
||||
|
||||
def get_aux_buffers(self):
|
||||
return self._aux_buffers
|
||||
|
||||
def set_aux_buffers(self, x):
|
||||
self._aux_buffers = x
|
||||
|
||||
def get_depth_buffer(self):
|
||||
return self._depth_buffer
|
||||
|
||||
def set_depth_buffer(self, x):
|
||||
self._depth_buffer = x
|
||||
|
||||
def get_depth_size(self):
|
||||
return self._depth_size
|
||||
|
||||
def set_depth_size(self, x):
|
||||
self._depth_size = x
|
||||
|
||||
def get_stencil_buffer(self):
|
||||
return self._stencil_buffer
|
||||
|
||||
def set_stencil_buffer(self, x):
|
||||
self._stencil_buffer = x
|
||||
|
||||
def get_stencil_size(self):
|
||||
return self._stencil_size
|
||||
|
||||
def set_stencil_size(self, x):
|
||||
self._stencil_size = x
|
||||
|
||||
def get_accum_buffer(self):
|
||||
return self._accum_buffer
|
||||
|
||||
def set_accum_buffer(self, x):
|
||||
self._accum_buffer = x
|
||||
|
||||
def get_accum_size(self):
|
||||
return self._accum_size
|
||||
|
||||
def set_accum_size(self, x):
|
||||
self._accum_size = x
|
||||
|
||||
def get_multisample(self):
|
||||
return self._multisample
|
||||
|
||||
def set_multisample(self, x):
|
||||
self._multisample = x
|
||||
|
||||
def get_samples_per_pixel(self):
|
||||
return self._samples_per_pixel
|
||||
|
||||
def set_samples_per_pixel(self, x):
|
||||
self._samples_per_pixel = x
|
||||
|
||||
def supported(self):
|
||||
"""Determine whether the combination of attributes requested by this configuration
|
||||
can be satisfied. If successful, a new GLConfig object is returned whose
|
||||
attributes reflect those actually allocated. Otherwise, a GLConfigError is
|
||||
raised."""
|
||||
raise NotImplementedError
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class GLConfigError(ValueError):
|
||||
|
||||
def __init__(self, msg = "OpenGL configuration not available"):
|
||||
ValueError.__init__(self, msg)
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Contexts - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.GLShareGroups import ShareGroup
|
||||
|
||||
_current_share_group = None
|
||||
|
||||
class GLContext(object):
|
||||
"""Abstract base class for objects having an OpenGL context."""
|
||||
#
|
||||
# _share_group ShareGroup
|
||||
#
|
||||
|
||||
share_group = overridable_property('share_group',
|
||||
"ShareGroup to which this context should belong, or None.")
|
||||
|
||||
def __init__(self, share_group):
|
||||
if not share_group:
|
||||
share_group = ShareGroup()
|
||||
self._share_group = share_group
|
||||
if share_group:
|
||||
share_group._add(self)
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
def init_context(self):
|
||||
"""This method is called once after the associated OpenGL context
|
||||
is created. When called, this object's OpenGL context is the current
|
||||
context and the viewport is set to (0, 0, width, height). This method
|
||||
may be used to establish any desired initial OpenGL state."""
|
||||
pass
|
||||
|
||||
def get_share_group(self):
|
||||
return self._share_group
|
||||
|
||||
def _get_shared_context(self):
|
||||
"""Return another arbitrarily-chosen member of the share group of this
|
||||
context, or None if this context has no share group or there are no
|
||||
other members."""
|
||||
return self._share_group._some_member(exclude = self)
|
||||
|
||||
def with_context(self, proc, flush = False):
|
||||
"""The proc should be a callable object of no arguments. Calls
|
||||
the proc with the associated OpenGL context as the current context.
|
||||
If flush is true, after calling proc, a glFlush followed by a
|
||||
buffer flush or swap is performed as appropriate."""
|
||||
self._with_context(proc, flush)
|
||||
|
||||
def _with_context(self, proc, flush):
|
||||
# Subclasses override this to implement with_context.
|
||||
# Should call _with_share_group(proc).
|
||||
# Signature can be changed if with_context is overridden to match.
|
||||
raise NotImplementedError
|
||||
|
||||
def _with_share_group(self, proc):
|
||||
global _current_share_group
|
||||
old_share_group = _current_share_group
|
||||
_current_share_group = self._share_group
|
||||
try:
|
||||
proc()
|
||||
finally:
|
||||
_current_share_group = old_share_group
|
||||
|
||||
|
||||
def current_share_group():
|
||||
group = _current_share_group
|
||||
if not group:
|
||||
raise ValueError("No current PyGUI OpenGL context")
|
||||
return group
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Pixel Formats - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
class GLPixelFormat(Properties):
|
||||
"""Class holding the attributes of an OpenGL pixel format."""
|
||||
|
||||
# NOTE: When adding a property here, also add it to
|
||||
# _pixel_format_attribute_names below.
|
||||
|
||||
double_buffer = overridable_property("double_buffer", "True if context is to be double-buffered.")
|
||||
alpha = overridable_property("alpha", "True if there is to be an alpha channel.")
|
||||
color_size = overridable_property("color_size", "Number of bits per colour buffer component.")
|
||||
alpha_size = overridable_property("alpha_size", "Number of bits per alpha channel component.")
|
||||
stereo = overridable_property("stereo", "True if stereoscopic context is required.")
|
||||
aux_buffers = overridable_property("aux_buffers", "Number of auxiliary colour buffers to allocate.")
|
||||
depth_buffer = overridable_property("depth_buffer", "True if a depth buffer is required.")
|
||||
depth_size = overridable_property("depth_size", "Number of bits per depth buffer element.")
|
||||
stencil_buffer = overridable_property("stencil_buffer", "True if a stencil buffer is required.")
|
||||
stencil_size = overridable_property("stencil_size", "Number of bits per stencil buffer element.")
|
||||
accum_buffer = overridable_property("accum_buffer", "True if an accumulation buffer is required.")
|
||||
accum_size = overridable_property("accum_size", "Number of bits per accumulation buffer component.")
|
||||
multisample = overridable_property("multisample", "True if a multisampled context is required.")
|
||||
samples_per_pixel = overridable_property("samples_per_pixel", "Number of samples per multisampled pixel.")
|
||||
|
||||
_double_buffer = True
|
||||
_alpha = True
|
||||
_color_size = 8
|
||||
_alpha_size = 8
|
||||
_stereo = False
|
||||
_aux_buffers = 0
|
||||
_depth_buffer = True
|
||||
_depth_size = 32
|
||||
_stencil_buffer = False
|
||||
_stencil_size = 8
|
||||
_accum_buffer = False
|
||||
_accum_size = 8
|
||||
_multisample = False
|
||||
_samples_per_pixel = False
|
||||
|
||||
_pixel_format_attribute_names = (
|
||||
'double_buffer', 'alpha', 'color_size', 'alpha_size',
|
||||
'stereo', 'aux_buffers', 'depth_buffer', 'depth_size',
|
||||
'stencil_buffer', 'stencil_size', 'accum_buffer', 'accum_size',
|
||||
'multisample', 'samples_per_pixel',
|
||||
)
|
||||
|
||||
def _from_args(cls, pixel_format, kwds):
|
||||
# Extract pixel format arguments from arguments of GLView.__init__
|
||||
# or GLPixmap.__init__ and return a GLPixelFormat. Used keyword
|
||||
# arguments are removed from kwds.
|
||||
pf_kwds = {}
|
||||
for name in cls._pixel_format_attribute_names:
|
||||
if name in kwds:
|
||||
pf_kwds[name] = kwds.pop(name)
|
||||
if pixel_format and pf_kwds:
|
||||
raise TypeError("Explicit pixel_format cannot be used with other pixel format keyword arguments")
|
||||
if not pixel_format:
|
||||
pixel_format = cls(**pf_kwds)
|
||||
return pixel_format
|
||||
|
||||
_from_args = classmethod(_from_args)
|
||||
|
||||
def get_double_buffer(self):
|
||||
return self._double_buffer
|
||||
|
||||
def set_double_buffer(self, x):
|
||||
self._double_buffer = x
|
||||
|
||||
def get_alpha(self):
|
||||
return self._alpha
|
||||
|
||||
def set_alpha(self, x):
|
||||
self._alpha = x
|
||||
|
||||
def get_color_size(self):
|
||||
return self._color_size
|
||||
|
||||
def set_color_size(self, x):
|
||||
self._color_size = x
|
||||
|
||||
def get_alpha_size(self):
|
||||
return self._alpha_size
|
||||
|
||||
def set_alpha_size(self, x):
|
||||
self._alpha_size = x
|
||||
|
||||
def get_stereo(self):
|
||||
return self._stereo
|
||||
|
||||
def set_stereo(self, x):
|
||||
self._stereo = x
|
||||
|
||||
def get_aux_buffers(self):
|
||||
return self._aux_buffers
|
||||
|
||||
def set_aux_buffers(self, x):
|
||||
self._aux_buffers = x
|
||||
|
||||
def get_depth_buffer(self):
|
||||
return self._depth_buffer
|
||||
|
||||
def set_depth_buffer(self, x):
|
||||
self._depth_buffer = x
|
||||
|
||||
def get_depth_size(self):
|
||||
return self._depth_size
|
||||
|
||||
def set_depth_size(self, x):
|
||||
self._depth_size = x
|
||||
|
||||
def get_stencil_buffer(self):
|
||||
return self._stencil_buffer
|
||||
|
||||
def set_stencil_buffer(self, x):
|
||||
self._stencil_buffer = x
|
||||
|
||||
def get_stencil_size(self):
|
||||
return self._stencil_size
|
||||
|
||||
def set_stencil_size(self, x):
|
||||
self._stencil_size = x
|
||||
|
||||
def get_accum_buffer(self):
|
||||
return self._accum_buffer
|
||||
|
||||
def set_accum_buffer(self, x):
|
||||
self._accum_buffer = x
|
||||
|
||||
def get_accum_size(self):
|
||||
return self._accum_size
|
||||
|
||||
def set_accum_size(self, x):
|
||||
self._accum_size = x
|
||||
|
||||
def get_multisample(self):
|
||||
return self._multisample
|
||||
|
||||
def set_multisample(self, x):
|
||||
self._multisample = x
|
||||
|
||||
def get_samples_per_pixel(self):
|
||||
return self._samples_per_pixel
|
||||
|
||||
def set_samples_per_pixel(self, x):
|
||||
self._samples_per_pixel = x
|
||||
|
||||
def supported(self):
|
||||
"""Determine whether the combination of attributes requested by this pixel format
|
||||
can be satisfied. If successful, a new GLPixelFormat object is returned whose
|
||||
attributes reflect those actually allocated. Otherwise, a GLPixelFormatError is
|
||||
raised."""
|
||||
raise NotImplementedError
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class GLPixelFormatError(ValueError):
|
||||
|
||||
def __init__(self):
|
||||
ValueError.__init__(self,
|
||||
"OpenGL pixel format attribute request cannot be satisfied")
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Pixmap - Generic
|
||||
#
|
||||
|
||||
from OpenGL.GL import glViewport
|
||||
from GUI import ImageBase
|
||||
from GUI.GLContexts import GLContext
|
||||
|
||||
class GLPixmap(ImageBase, GLContext):
|
||||
"""An offscreen OpenGL drawing area.
|
||||
|
||||
Constructors:
|
||||
GLPixmap(width, height, share = None, config_attr = value...)
|
||||
GLPixmap(width, height, config, share = None)
|
||||
"""
|
||||
|
||||
def destroy(self):
|
||||
GLContext.destroy(self)
|
||||
|
||||
def _init_context(self):
|
||||
width, height = self.size
|
||||
glViewport(0, 0, int(width), int(height))
|
||||
self.init_context()
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Textures - Generic
|
||||
#
|
||||
|
||||
from weakref import WeakKeyDictionary
|
||||
from OpenGL.GL import glGenTextures, glBindTexture, glDeleteTextures, \
|
||||
glTexImage2D, GL_TEXTURE_2D, GL_RGBA
|
||||
from OpenGL.GLU import gluBuild2DMipmaps
|
||||
from GUI.GGLContexts import current_share_group
|
||||
from GUI.GLDisplayLists import call_when_not_compiling_display_list
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class TextureIdMap(WeakKeyDictionary):
|
||||
|
||||
def __del__(self):
|
||||
#print "GL.TextureIdMap.__del__:", self ###
|
||||
def free_texture():
|
||||
glDeleteTextures([gl_id])
|
||||
for share_group, gl_id in self.items():
|
||||
context = share_group._some_member()
|
||||
if context:
|
||||
#print "...freeing texture id", gl_id, "for", share_group, "using", context ###
|
||||
context.with_context(free_texture)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class Texture(object):
|
||||
"""This class encapsulates an OpenGL texture and maintains a
|
||||
representation of it for each OpenGL context with which it is used.
|
||||
Allocation and maintentance of texture numbers is handled automatically.
|
||||
|
||||
Constructor:
|
||||
Texture(texture_type)
|
||||
where texture_type is the appropriate GL constant for the type
|
||||
of texture (GL_TEXTURE_2D etc.)
|
||||
"""
|
||||
#
|
||||
# _gl_type int GL_TEXTURE_2D, etc.
|
||||
# _gl_id ShareGroup -> int Mapping from OpenGL share group to texture number
|
||||
|
||||
def __init__(self, texture_type):
|
||||
self._gl_type = texture_type
|
||||
self._gl_id = TextureIdMap()
|
||||
|
||||
def deallocate(self):
|
||||
"""Deallocate any OpenGL resources that have been allocated for this
|
||||
texture in any context."""
|
||||
self._gl_id.__del__()
|
||||
|
||||
def bind(self):
|
||||
"""Makes this texture the current texture for the current context
|
||||
by calling glBindTexture. If this texture has not previously been
|
||||
used with the current context, do_setup() is called to allocate
|
||||
and initialise a representation of the texture."""
|
||||
gl_id = self.gl_id()
|
||||
glBindTexture(self._gl_type, gl_id)
|
||||
|
||||
def gl_id(self):
|
||||
"""Returns the OpenGL texture number corresponding to this texture
|
||||
in the current context. May trigger allocation of a new texture and
|
||||
a call to do_setup(). Does not bind the texture, unless a new texture
|
||||
is allocated, in which case the current texture binding may be changed
|
||||
as a side effect."""
|
||||
share_group = current_share_group()
|
||||
gl_id = self._gl_id.get(share_group)
|
||||
if gl_id is None:
|
||||
gl_id = glGenTextures(1)
|
||||
#print "GLTexture: assigned id %d for %s in share group %s" % (
|
||||
# gl_id, self, share_group) ###
|
||||
self._gl_id[share_group] = gl_id
|
||||
call_when_not_compiling_display_list(lambda: self._setup(gl_id))
|
||||
return gl_id
|
||||
|
||||
def _setup(self, gl_id):
|
||||
glBindTexture(self._gl_type, gl_id)
|
||||
self.do_setup()
|
||||
|
||||
def do_setup(self):
|
||||
"""Subclasses should override this to make the necessary OpenGL
|
||||
calls to initialise the texture, assuming that glBindTexture has
|
||||
already been called."""
|
||||
raise NotImplementedError
|
||||
|
||||
def gl_tex_image_2d(self, image, target = GL_TEXTURE_2D, internal_format = GL_RGBA,
|
||||
border = False, with_mipmaps = False):
|
||||
"""Load the currently bound texture with data from an image, with automatic
|
||||
scaling to power-of-2 size and optional mipmap generation."""
|
||||
border = bool(border)
|
||||
if border and with_mipmaps:
|
||||
raise ValueError("Bordered texture cannot have mipmaps")
|
||||
b2 = 2 * border
|
||||
width, height = image.size
|
||||
twidth = pow2up(width - b2) + b2
|
||||
theight = pow2up(height - b2) + b2
|
||||
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: before scaling: size =", (width, height) ###
|
||||
if width <> twidth or height <> theight:
|
||||
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: scaling image to size", (twidth, theight) ###
|
||||
from Pixmaps import Pixmap
|
||||
image2 = Pixmap(twidth, theight)
|
||||
def scale(canvas):
|
||||
image.draw(canvas, (0, 0, width, height), (0, 0, twidth, theight))
|
||||
image2.with_canvas(scale)
|
||||
image = image2
|
||||
format, type, data = self._gl_get_texture_data(image)
|
||||
if with_mipmaps:
|
||||
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: loading mipmaps" ###
|
||||
gluBuild2DMipmaps(target, internal_format, twidth, theight,
|
||||
format, type, data)
|
||||
else:
|
||||
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: loading texture" ###
|
||||
glTexImage2D(target, 0, internal_format, twidth, theight, border,
|
||||
format, type, data)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
def pow2up(size):
|
||||
# Round size up to a power of 2
|
||||
psize = 1
|
||||
while psize < size:
|
||||
psize <<= 1
|
||||
return psize
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL View - Generic
|
||||
#
|
||||
|
||||
from OpenGL.GL import glViewport, glMatrixMode, glLoadIdentity, \
|
||||
GL_PROJECTION, GL_MODELVIEW
|
||||
from GUI import Component
|
||||
from GUI import ViewBase
|
||||
from GUI.GLContexts import GLContext
|
||||
|
||||
class GLError(StandardError):
|
||||
pass
|
||||
|
||||
class GLView(ViewBase, Component, GLContext):
|
||||
"""A GLView is a Component providing an OpenGL 3D display area.
|
||||
|
||||
Constructors:
|
||||
GLView(config_attr = value..., share = None)
|
||||
GLView(config, share = None)
|
||||
"""
|
||||
|
||||
_default_size = (100, 100)
|
||||
|
||||
def __init__(self, **kwds):
|
||||
Component.__init__(self, **kwds)
|
||||
ViewBase.__init__(self)
|
||||
|
||||
def destroy(self):
|
||||
ViewBase.destroy(self)
|
||||
Component.destroy(self)
|
||||
|
||||
def _render(self):
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glLoadIdentity()
|
||||
self.render()
|
||||
|
||||
def render(self):
|
||||
"""This method is called when the contents of the view needs to
|
||||
be redrawn, with the view's OpenGL context as the current context.
|
||||
The modelview matrix has been selected as the current matrix and
|
||||
set to an identity matrix. After calling this method, buffers will
|
||||
be automatically swapped or drawing flushed as appropriate."""
|
||||
pass
|
||||
|
||||
def viewport_changed(self):
|
||||
"""This method is called when the view's size has changed, with
|
||||
the view's OpenGL context as the current context, and the OpenGL
|
||||
viewport set to (0, 0, width, height). The default implementation
|
||||
loads an identity projection matrix and calls init_projection()."""
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
self.init_projection()
|
||||
|
||||
def init_projection(self):
|
||||
"""This method is called to establish the projection whenever the
|
||||
viewport changes. The projection matrix has been selected as the
|
||||
current matrix and set to an identity matrix."""
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
"""Redraws the contents of the view immediately, without waiting
|
||||
for a return to the event loop."""
|
||||
self.with_context(self.render, flush = True)
|
||||
|
||||
def _init_context(self):
|
||||
self.init_context()
|
||||
self._update_viewport()
|
||||
|
||||
def _update_viewport(self):
|
||||
width, height = self.size
|
||||
glViewport(0, 0, int(width), int(height))
|
||||
self.viewport_changed()
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#
|
||||
# Python GUI - Point and rectangle utilities - Generic
|
||||
#
|
||||
|
||||
def add_pt((x1, y1), (x2, y2)):
|
||||
return (x1 + x2), (y1 + y2)
|
||||
|
||||
def sub_pt((x1, y1), (x2, y2)):
|
||||
return (x1 - x2), (y1 - y2)
|
||||
|
||||
def rect_sized((l, t), (w, h)):
|
||||
return (l, t, l + w, t + h)
|
||||
|
||||
def rect_left(r):
|
||||
return r[0]
|
||||
|
||||
def rect_top(r):
|
||||
return r[1]
|
||||
|
||||
def rect_right(r):
|
||||
return r[2]
|
||||
|
||||
def rect_bottom(r):
|
||||
return r[3]
|
||||
|
||||
def rect_width(r):
|
||||
return r[2] - r[0]
|
||||
|
||||
def rect_height(r):
|
||||
return r[3] - r[1]
|
||||
|
||||
def rect_topleft(r):
|
||||
return r[:2]
|
||||
|
||||
def rect_botright(r):
|
||||
return r[2:]
|
||||
|
||||
def rect_center((l, t, r, b)):
|
||||
return ((l + r) // 2, (t + b) // 2)
|
||||
|
||||
def rect_size((l, t, r, b)):
|
||||
return (r - l, b - t)
|
||||
|
||||
def union_rect((l1, t1, r1, b1), (l2, t2, r2, b2)):
|
||||
return (min(l1, l2), min(t1, t2), max(r1, r2), max(b1, b2))
|
||||
|
||||
def sect_rect((l1, t1, r1, b1), (l2, t2, r2, b2)):
|
||||
return (max(l1, l2), max(t1, t2), min(r1, r2), min(b1, b2))
|
||||
|
||||
def inset_rect((l, t, r, b), (dx, dy)):
|
||||
return (l + dx, t + dy, r - dx, b - dy)
|
||||
|
||||
def offset_rect((l, t, r, b), (dx, dy)):
|
||||
return (l + dx, t + dy, r + dx, b + dy)
|
||||
|
||||
def offset_rect_neg((l, t, r, b), (dx, dy)):
|
||||
return (l - dx, t - dy, r - dx, b - dy)
|
||||
|
||||
def empty_rect((l, t, r, b)):
|
||||
return r <= l or b <= t
|
||||
|
||||
def pt_in_rect((x, y), (l, t, r, b)):
|
||||
return l <= x < r and t <= y < b
|
||||
|
||||
def rects_intersect((l1, t1, r1, b1), (l2, t2, r2, b2)):
|
||||
return l1 < r2 and l2 < r1 and t1 < b2 and t2 < b1
|
||||
|
||||
def rect_with_center((l, t, r, b), (x, y)):
|
||||
w = r - l
|
||||
h = b - t
|
||||
rl = x - w // 2
|
||||
rt = y - h // 2
|
||||
return (rl, rt, rl + w, rt + h)
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#
|
||||
# Python GUI - Image Base - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
from GUI.Geometry import rect_sized
|
||||
|
||||
class ImageBase(Properties):
|
||||
"""Abstract base class for Image, Pixmap and GLPixmap."""
|
||||
|
||||
width = overridable_property('width', "Width of the image in pixels (read only).")
|
||||
height = overridable_property('height', "Height of the image in pixels (read only).")
|
||||
size = overridable_property('size', "Size of the image in pixels (read only).")
|
||||
bounds = overridable_property('bounds', "Bounding rectangle of the image in pixels (read only).")
|
||||
|
||||
def get_size(self):
|
||||
return (self.width, self.height)
|
||||
|
||||
def get_bounds(self):
|
||||
return rect_sized((0, 0), self.size)
|
||||
|
||||
def draw(self, canvas, src_rect, dst_rect):
|
||||
"""Draw the part of the image specified by src_rect on the given canvas,
|
||||
scaled to fit within dst_rect."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#
|
||||
# Python GUI - Images - Generic
|
||||
#
|
||||
|
||||
from GUI.Files import FileRef
|
||||
from GUI.Resources import get_resource
|
||||
from GUI import ImageBase
|
||||
|
||||
class Image(ImageBase):
|
||||
"""Class Image represents an RGB or RGBA image.
|
||||
|
||||
Constructors:
|
||||
Image(file = FileRef or pathname)
|
||||
"""
|
||||
|
||||
def from_resource(cls, name, **kwds):
|
||||
return get_resource(cls, name, **kwds)
|
||||
|
||||
from_resource = classmethod(from_resource)
|
||||
|
||||
def __init__(self, file):
|
||||
if isinstance(file, FileRef):
|
||||
file = file.path
|
||||
self._init_from_file(file)
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Display Lists - Generic
|
||||
#
|
||||
|
||||
from weakref import WeakKeyDictionary
|
||||
from OpenGL.GL import glGenLists, glNewList, glEndList, glCallList, \
|
||||
glDeleteLists, GL_COMPILE
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
from GUI.GGLContexts import current_share_group
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class DisplayListIdMap(WeakKeyDictionary):
|
||||
|
||||
def __del__(self):
|
||||
# Delete any display lists that have been allocated for this map.
|
||||
#print "GL.DisplayListIdMap.__del__:", self ###
|
||||
def free_display_list():
|
||||
glDeleteLists(gl_id, 1)
|
||||
for share_group, gl_id in self.items():
|
||||
context = share_group._some_member()
|
||||
if context:
|
||||
#print "...freeing display list id", gl_id, "for", share_group, "using", context ###
|
||||
context.with_context(free_display_list)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class DisplayList(Properties):
|
||||
"""This class encapsulates an OpenGL display list and maintains a
|
||||
representation of it for each OpenGL context with which it is used.
|
||||
Allocation and maintentance of display list numbers is handled
|
||||
automatically."""
|
||||
#
|
||||
# _gl_id ShareGroup -> int Mapping from OpenGL share group to
|
||||
# display list number
|
||||
|
||||
setup = overridable_property('setup',
|
||||
"""Function to set up the display list by making the necessary
|
||||
OpenGL calls, excluding glNewList and glEndList.""")
|
||||
|
||||
def __init__(self, setup = None):
|
||||
self._gl_id = DisplayListIdMap()
|
||||
self._setup = setup
|
||||
|
||||
def deallocate(self):
|
||||
"""Deallocate any OpenGL resources that have been allocated for this
|
||||
display list in any context."""
|
||||
self._gl_id.__del__()
|
||||
|
||||
def get_setup(self):
|
||||
return self._setup
|
||||
|
||||
def set_setup(self, x):
|
||||
self._setup = x
|
||||
|
||||
def call(self):
|
||||
"""Calls the display list using glCallList(). If this display list
|
||||
has not previously been used with the current context, allocates
|
||||
a display list number and arranges for do_setup() to be called
|
||||
to compile a representation of the display list."""
|
||||
share_group = current_share_group()
|
||||
gl_id = self._gl_id.get(share_group)
|
||||
if gl_id is None:
|
||||
gl_id = glGenLists(1)
|
||||
#print "GLDisplayList: assigned id %d for %s in share group %s" % (
|
||||
# gl_id, self, share_group) ###
|
||||
self._gl_id[share_group] = gl_id
|
||||
call_when_not_compiling_display_list(lambda: self._compile(gl_id))
|
||||
glCallList(gl_id)
|
||||
|
||||
def _compile(self, gl_id):
|
||||
global compiling_display_list
|
||||
compiling_display_list = True
|
||||
glNewList(gl_id, GL_COMPILE)
|
||||
try:
|
||||
self.do_setup()
|
||||
finally:
|
||||
glEndList()
|
||||
compiling_display_list = False
|
||||
|
||||
def do_setup(self):
|
||||
"""Make all the necessary OpenGL calls to compile the display list,
|
||||
except for glNewList() and glEndList() which will be called automatically
|
||||
before and after. The default implementation calls the 'setup' property."""
|
||||
setup = self._setup
|
||||
if setup:
|
||||
setup()
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"No setup function or do_setup method for GL.DisplayList")
|
||||
|
||||
|
||||
compiling_display_list = False
|
||||
pending_functions = []
|
||||
|
||||
def call_when_not_compiling_display_list(func):
|
||||
#print "GLDisplayLists: entering call_when_not_compiling_display_list" ###
|
||||
if compiling_display_list:
|
||||
#print "GLDisplayLists: deferring", func ###
|
||||
pending_functions.append(func)
|
||||
else:
|
||||
#print "GLDisplayLists: immediately calling", func ###
|
||||
func()
|
||||
while pending_functions:
|
||||
#print "GLDisplayLists: calling deferred", func ###
|
||||
pending_functions.pop()()
|
||||
#print "GLDisplayLists: exiting call_when_not_compiling_display_list" ###
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#
|
||||
# PyGUI - OpenGL Context Sharing - Generic
|
||||
#
|
||||
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
class ShareGroup(object):
|
||||
"""Object representing a shared texture and display list
|
||||
namespace for OpenGL contexts."""
|
||||
|
||||
def __init__(self):
|
||||
self.contexts = WeakKeyDictionary()
|
||||
|
||||
def __contains__(self, context):
|
||||
"Test whether a GLView or GLPixmap is a member of this share group."
|
||||
return context in self.contexts
|
||||
|
||||
def __iter__(self):
|
||||
"Return an iterator over the members of this share group."
|
||||
return iter(self.contexts)
|
||||
|
||||
def _add(self, context):
|
||||
self.contexts[context] = 1
|
||||
|
||||
def _some_member(self, exclude = None):
|
||||
for member in self.contexts:
|
||||
if member is not exclude:
|
||||
return member
|
||||
return None
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#
|
||||
# Python GUI - Labels - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Control
|
||||
|
||||
class Label(Control):
|
||||
"""A piece of static text for labelling items in a window."""
|
||||
|
||||
_default_tab_stop = False
|
||||
|
||||
text = overridable_property('text')
|
||||
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#--------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Pop-up list control - Generic
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Actions import Action
|
||||
from GUI import Control, application
|
||||
|
||||
class ListButton(Control, Action):
|
||||
"""A button that displays a value and provides a pop-up or
|
||||
pull-down list of choices."""
|
||||
|
||||
titles = overridable_property('titles',
|
||||
"List of item title strings")
|
||||
|
||||
values = overridable_property('values',
|
||||
"List of values corresponding to tiles, or None to use item index as value")
|
||||
|
||||
def _extract_initial_items(self, kwds):
|
||||
titles = kwds.pop('titles', None) or []
|
||||
values = kwds.pop('values', None)
|
||||
return titles, values
|
||||
|
||||
def get_titles(self):
|
||||
return self._titles
|
||||
|
||||
def set_titles(self, x):
|
||||
self._titles = x
|
||||
self._update_items()
|
||||
|
||||
def get_values(self):
|
||||
return self._values
|
||||
|
||||
def set_values(self, x):
|
||||
self._values = x
|
||||
|
||||
def get_value(self):
|
||||
i = self._get_selected_index()
|
||||
if i >= 0:
|
||||
values = self.values
|
||||
if values:
|
||||
return values[i]
|
||||
else:
|
||||
return i
|
||||
|
||||
def set_value(self, value):
|
||||
values = self.values
|
||||
if values:
|
||||
try:
|
||||
i = values.index(value)
|
||||
except ValueError:
|
||||
i = -1
|
||||
else:
|
||||
if value is None:
|
||||
i = -1
|
||||
else:
|
||||
i = value
|
||||
self._set_selected_index(i)
|
||||
|
||||
def do_action(self):
|
||||
try:
|
||||
Action.do_action(self)
|
||||
except:
|
||||
application().report_error()
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
#----------------------------------------------------------------------
|
||||
#
|
||||
# Python GUI - Menus - Generic
|
||||
#
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
from GUI import Globals
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
def search_list_for_key(items, char, shift, option):
|
||||
for i in xrange(len(items)-1, -1, -1):
|
||||
result = items[i]._search_for_key(char, shift, option)
|
||||
if result:
|
||||
return result
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class Menu(Properties):
|
||||
"""Pull-down or pop-up menu class.
|
||||
|
||||
Menu(title, item_descriptors)
|
||||
constructs a menu with the given title and items. Each
|
||||
item_descriptor is of the form
|
||||
|
||||
"-"
|
||||
|
||||
for a separator,
|
||||
|
||||
("text/key", 'command_name')
|
||||
|
||||
for a single menu item, or
|
||||
|
||||
(["text/key", ...], 'command_name')
|
||||
|
||||
for an indexed item group. An indexed group is a group
|
||||
of items sharing the same command name and distinguished
|
||||
by an integer index. Items can be added to and removed
|
||||
from the group dynamically, to implement e.g. a font
|
||||
menu or windows menu.
|
||||
|
||||
The "key" part of the item descriptor (which is optional)
|
||||
specifies the keyboard equivalent. It should consist of
|
||||
a single character together with the following optional
|
||||
modifiers:
|
||||
|
||||
^ representing the Shift key
|
||||
@ representing the Alt or Option key
|
||||
"""
|
||||
|
||||
title = overridable_property('title', "Title string appearing in menu bar")
|
||||
special = overridable_property('special', "Menu appears at right end of menu bar")
|
||||
|
||||
_flat_items = None
|
||||
|
||||
def __init__(self, title, items, special = False, substitutions = {}, **kwds):
|
||||
self._title = title
|
||||
self._items = []
|
||||
self._special = special
|
||||
Properties.__init__(self, **kwds)
|
||||
self.extend(items, substitutions)
|
||||
|
||||
def get_title(self):
|
||||
return self._title
|
||||
|
||||
def get_special(self):
|
||||
return self._special
|
||||
|
||||
def item_with_command(self, cmd):
|
||||
for item in self._items:
|
||||
if item._command_name == cmd:
|
||||
return item
|
||||
return None
|
||||
|
||||
def append(self, item, substitutions = {}):
|
||||
items = self._items
|
||||
item = self._make_item(item, substitutions)
|
||||
if not (items and isinstance(item, MenuSeparator)
|
||||
and isinstance(items[-1], MenuSeparator)):
|
||||
items.append(item)
|
||||
|
||||
def extend(self, items, substitutions = {}):
|
||||
for item in items:
|
||||
self.append(item, substitutions)
|
||||
|
||||
def _make_item(self, item, substitutions):
|
||||
if isinstance(item, MenuItem):
|
||||
return item
|
||||
elif item == "-":
|
||||
return _menu_separator
|
||||
else:
|
||||
(text, cmd) = item
|
||||
if isinstance(text, basestring):
|
||||
return SingleMenuItem(text, cmd, substitutions)
|
||||
else:
|
||||
return MenuItemGroup(text, cmd)
|
||||
|
||||
def _command_and_args_for_item(self, item_num):
|
||||
i = 1
|
||||
for item in self._items:
|
||||
n = item._num_subitems()
|
||||
if item_num < i + n:
|
||||
return item._command_and_args_for_subitem(item_num - i)
|
||||
i += n
|
||||
return '', ()
|
||||
|
||||
def _update_platform_menu(self):
|
||||
# Called during menu setup after items have been enabled/checked.
|
||||
# Generic implementation rebuilds the whole menu from scratch.
|
||||
# Implementations may override this to be more elegant.
|
||||
self._rebuild_platform_menu()
|
||||
|
||||
def _rebuild_platform_menu(self):
|
||||
self._clear_platform_menu()
|
||||
for item in self._items:
|
||||
item._add_to_platform_menu(self)
|
||||
|
||||
def _search_for_key(self, char, shift, option):
|
||||
return search_list_for_key(self._items, char, shift, option)
|
||||
|
||||
def _get_flat_items(self):
|
||||
flat = self._flat_items
|
||||
if flat is None:
|
||||
flat = []
|
||||
for item in self._items:
|
||||
item._collect_flat_items(flat)
|
||||
self._flat_items = flat
|
||||
return flat
|
||||
|
||||
def _get_flat_item(self, i):
|
||||
return self._get_flat_items()[i]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class MenuItem(Properties):
|
||||
# Internal class representing a menu item, group or separator.
|
||||
#
|
||||
# _command_name string Internal command name
|
||||
|
||||
def _num_subitems(self):
|
||||
return 1
|
||||
|
||||
def _split_text(self, text):
|
||||
# Split menu text into label and key combination.
|
||||
if "/" in text:
|
||||
return text.split("/")
|
||||
else:
|
||||
return text, ""
|
||||
|
||||
def _name(self):
|
||||
return self._label.replace("<app>", Globals.application_name)
|
||||
|
||||
def _collect_flat_items(self, result):
|
||||
result.append(self)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class MenuSeparator(MenuItem):
|
||||
# Internal class representing a menu separator.
|
||||
|
||||
_command_name = ''
|
||||
|
||||
def _add_to_platform_menu(self, menu):
|
||||
menu._add_separator_to_platform_menu()
|
||||
|
||||
def _search_for_key(self, char, shift, option):
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class SingleMenuItem(MenuItem):
|
||||
"""Class representing a menu item.
|
||||
|
||||
Properties:
|
||||
enabled boolean
|
||||
checked boolean
|
||||
"""
|
||||
|
||||
enabled = 0
|
||||
checked = 0
|
||||
_key = None
|
||||
_shift = 0
|
||||
_option = 0
|
||||
#_index = None
|
||||
|
||||
def __init__(self, text, cmd, substitutions = {}):
|
||||
label1, keycomb1 = self._split_text(text)
|
||||
label2, keycomb2 = self._split_text(substitutions.get(cmd, ""))
|
||||
self._label = label2 or label1
|
||||
keycomb = keycomb2 or keycomb1
|
||||
for c in keycomb:
|
||||
if c == '^':
|
||||
self._shift = 1
|
||||
elif c == '@':
|
||||
self._option = 1
|
||||
else:
|
||||
self._key = c.upper()
|
||||
self._command_name = cmd
|
||||
|
||||
def __str__(self):
|
||||
return "<SingleMenuItem %r %r Sh:%s Op:%s En:%s>" % (
|
||||
self._label, self._key, self._shift, self._option, self.enabled)
|
||||
|
||||
def _add_to_platform_menu(self, menu):
|
||||
menu._add_item_to_platform_menu(self, self._name(), self._command_name)
|
||||
|
||||
def _command_and_args_for_subitem(self, i):
|
||||
return self._command_name, ()
|
||||
|
||||
def _search_for_key(self, char, shift, option):
|
||||
if self._matches_key(char, shift, option):
|
||||
return self._command_name
|
||||
|
||||
def _matches_key(self, char, shift, option):
|
||||
return self._key == char and self._shift == shift \
|
||||
and self._option == option and self.enabled
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class MenuItemGroup(MenuItem):
|
||||
"""Class representing a menu item group.
|
||||
|
||||
Properties:
|
||||
enabled <- boolean Assigning to these changes the corresponding
|
||||
checked <- boolean property of all the group's items.
|
||||
|
||||
Operators:
|
||||
group[index] -> MenuItem
|
||||
|
||||
Methods:
|
||||
set_items(["text/key", ...])
|
||||
Replaces all the items in the group by the specified items.
|
||||
"""
|
||||
|
||||
enabled = overridable_property('enabled')
|
||||
checked = overridable_property('checked')
|
||||
|
||||
def __init__(self, text_list, cmd):
|
||||
self.set_items(text_list)
|
||||
self._command_name = cmd
|
||||
|
||||
def _num_subitems(self):
|
||||
return len(self._items)
|
||||
|
||||
def _command_and_args_for_subitem(self, i):
|
||||
return self._command_name, (i,)
|
||||
|
||||
def get_enabled(self):
|
||||
raise AttributeError("'enabled' property of MenuItemGroup is write-only")
|
||||
|
||||
def set_enabled(self, state):
|
||||
for item in self._items:
|
||||
item.enabled = state
|
||||
|
||||
def get_checked(self):
|
||||
raise AttributeError("'checked' property of MenuItemGroup is write-only")
|
||||
|
||||
def set_checked(self, state):
|
||||
for item in self._items:
|
||||
item.checked = state
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self._items[index]
|
||||
|
||||
def set_items(self, text_list):
|
||||
self._items = [SingleMenuItem(text, '') for text in text_list]
|
||||
|
||||
def _add_to_platform_menu(self, menu):
|
||||
#for item in self._items:
|
||||
# item._add_to_platform_menu(menu)
|
||||
cmd = self._command_name
|
||||
for index, item in enumerate(self._items):
|
||||
menu._add_item_to_platform_menu(item, item._name(), cmd, index)
|
||||
|
||||
def _search_for_key(self, char, shift, option):
|
||||
items = self._items
|
||||
for i in xrange(len(items)-1, -1, -1):
|
||||
if items[i]._matches_key(char, shift, option):
|
||||
return (self._command_name, i)
|
||||
|
||||
def _collect_flat_items(self, result):
|
||||
for item in self._items:
|
||||
item._collect_flat_items(result)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
_menu_separator = MenuSeparator()
|
||||
_dummy_menu_item = SingleMenuItem("", '')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class MenuState:
|
||||
"""A MenuState object is used to enable/disable and check/uncheck
|
||||
menu items, and to add or remove items of indexed groups,
|
||||
during the menu setup phase of menu command handling.
|
||||
|
||||
Each single menu item or item group appears as an attribute of
|
||||
the MenuState object, with the command name as the attribute name,
|
||||
allowing operations such as
|
||||
|
||||
menu_state.copy_cmd.enabled = 1
|
||||
menu_state.font_cmd[current_font].checked = 1
|
||||
|
||||
The command name may also be used as a mapping index.
|
||||
|
||||
Operators:
|
||||
menu_state.command_name -> MenuItem
|
||||
menu_state['command_name'] -> MenuItem
|
||||
"""
|
||||
|
||||
def __init__(self, menu_list):
|
||||
mapping = {}
|
||||
for menu in menu_list:
|
||||
for item in menu._items:
|
||||
cmd = item._command_name
|
||||
if cmd:
|
||||
mapping[cmd] = item
|
||||
self._mapping = mapping
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self._mapping[name]
|
||||
except KeyError:
|
||||
if name.startswith("__"):
|
||||
raise AttributeError, name
|
||||
return _dummy_menu_item
|
||||
|
||||
__getitem__ = __getattr__
|
||||
|
||||
def reset(self):
|
||||
"""Disable and uncheck all items."""
|
||||
for item in self._mapping.values():
|
||||
item.enabled = 0
|
||||
item.checked = None
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#
|
||||
# Python GUI - Mouse trackers - Generic
|
||||
#
|
||||
|
||||
from GUI import application
|
||||
|
||||
class MouseTracker(object):
|
||||
"""Iterator used to track movements of the mouse following a mouse_down
|
||||
event in a Views. Each call to the next() method returns a mouse_drag
|
||||
event, except for the last one, which returns a mouse_up event."""
|
||||
|
||||
def __init__(self, view):
|
||||
self._view = view
|
||||
self._finished = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if not self._finished:
|
||||
event = self._next_mouse_event()
|
||||
event.position = event.position_in(self._view)
|
||||
if event.kind == 'mouse_up':
|
||||
self._finished = 1
|
||||
return event
|
||||
else:
|
||||
raise StopIteration
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#
|
||||
# Python GUI - Pixmap - Generic
|
||||
#
|
||||
|
||||
from GUI import ImageBase
|
||||
|
||||
class Pixmap(ImageBase):
|
||||
"""A Pixmap is an offscreen area that can be used both as a
|
||||
destination for drawing and a source of image data for drawing
|
||||
in a View or another Pixmap.
|
||||
|
||||
Constructor:
|
||||
Pixmap(width, height)
|
||||
"""
|
||||
|
||||
def with_canvas(self, proc):
|
||||
"""Call the given procedure with a canvas suitable for drawing on
|
||||
this Pixmap. The canvas is valid only for the duration of the call,
|
||||
and should not be retained beyond it."""
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Printing - Generic
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
from math import ceil
|
||||
import cPickle as pickle
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import application
|
||||
|
||||
class PageSetup(object):
|
||||
"""Holder of information specified by the "Page Setup" command."""
|
||||
|
||||
paper_name = overridable_property('paper_name')
|
||||
paper_width = overridable_property('paper_width')
|
||||
paper_height = overridable_property('paper_height')
|
||||
left_margin = overridable_property('left_margin')
|
||||
top_margin = overridable_property('top_margin')
|
||||
right_margin = overridable_property('right_margin')
|
||||
bottom_margin = overridable_property('bottom_margin')
|
||||
orientation = overridable_property('orientation')
|
||||
|
||||
paper_size = overridable_property('paper_size')
|
||||
margins = overridable_property('margins')
|
||||
page_width = overridable_property('page_width')
|
||||
page_height = overridable_property('page_height')
|
||||
page_size = overridable_property('page_size')
|
||||
page_rect = overridable_property('page_rect')
|
||||
printable_rect = overridable_property('printable_rect') # May not work
|
||||
printer_name = overridable_property('printer_name')
|
||||
|
||||
_pickle_attributes = ['paper_name', 'paper_size', 'margins',
|
||||
'printer_name', 'orientation']
|
||||
|
||||
def __getstate__(self):
|
||||
state = {}
|
||||
for name in self._pickle_attributes:
|
||||
state[name] = getattr(self, name)
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
for name, value in state.iteritems():
|
||||
setattr(self, name, value)
|
||||
|
||||
def from_string(s):
|
||||
"""Restore a pickled PageSetup object from a string."""
|
||||
return pickle.loads(s)
|
||||
|
||||
from_string = staticmethod(from_string)
|
||||
|
||||
def to_string(self):
|
||||
"""Pickle the PageSetup object and return it as a string."""
|
||||
return pickle.dumps(self, 2)
|
||||
|
||||
def get_paper_size(self):
|
||||
return self.paper_width, self.paper_height
|
||||
|
||||
def set_paper_size(self, x):
|
||||
self.paper_width, self.paper_height = x
|
||||
|
||||
def get_margins(self):
|
||||
return self.left_margin, self.top_margin, self.right_margin, self.bottom_margin
|
||||
|
||||
def set_margins(self, x):
|
||||
self.left_margin, self.top_margin, self.right_margin, self.bottom_margin = x
|
||||
|
||||
def get_page_width(self):
|
||||
return self.paper_width - self.left_margin - self.right_margin
|
||||
|
||||
def get_page_height(self):
|
||||
return self.paper_height - self.top_margin - self.bottom_margin
|
||||
|
||||
def get_page_size(self):
|
||||
return (self.page_width, self.page_height)
|
||||
|
||||
def get_page_rect(self):
|
||||
lm, tm, rm, bm = self.margins
|
||||
pw, ph = self.paper_size
|
||||
return (lm, tm, pw - rm, ph - bm)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Printable(object):
|
||||
"""Mixin class for components implementing the "Print" command."""
|
||||
|
||||
printable = overridable_property('printable', "Whether this component should handle the 'Print' command.")
|
||||
page_setup = overridable_property('page_setup', "The PageSetup object to use for printing.")
|
||||
print_title = overridable_property('print_title', "Title for print job.")
|
||||
|
||||
_printable = True
|
||||
|
||||
def get_printable(self):
|
||||
return self._printable
|
||||
|
||||
def set_printable(self, x):
|
||||
self._printable = x
|
||||
|
||||
def get_print_title(self):
|
||||
window = self.window
|
||||
if window:
|
||||
return window.title
|
||||
else:
|
||||
return ""
|
||||
|
||||
def get_page_setup(self):
|
||||
result = None
|
||||
model = getattr(self, 'model', None)
|
||||
if model:
|
||||
result = getattr(model, 'page_setup', None)
|
||||
if not result:
|
||||
result = application().page_setup
|
||||
return result
|
||||
|
||||
def setup_menus(self, m):
|
||||
if self.printable:
|
||||
m.print_cmd.enabled = True
|
||||
|
||||
def print_cmd(self):
|
||||
if self.printable:
|
||||
page_setup = self.page_setup
|
||||
if page_setup:
|
||||
self.print_view(page_setup)
|
||||
else:
|
||||
self.pass_to_next_handler('print_cmd')
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class Paginator(object):
|
||||
"""Used internally. A generic pagination algorithm for printing."""
|
||||
|
||||
def __init__(self, view, page_setup):
|
||||
self.view = view
|
||||
extent_width, extent_height = view.get_print_extent()
|
||||
#paper_width, paper_height = page_setup.paper_size
|
||||
#lm, tm, rm, bm = page_setup.margins
|
||||
#page_width = int(paper_width - lm - rm)
|
||||
#page_height = int(paper_height - tm - bm)
|
||||
page_width, page_height = page_setup.page_size
|
||||
if page_width <= 0 or page_height <= 0:
|
||||
from AlertFunctions import stop_alert
|
||||
stop_alert("Margins are too large for the page size.")
|
||||
return
|
||||
self.extent_rect = (0, 0, extent_width, extent_height)
|
||||
self.page_width = page_width
|
||||
self.page_height = page_height
|
||||
self.left_margin = page_setup.left_margin
|
||||
self.top_margin = page_setup.top_margin
|
||||
self.pages_wide = int(ceil(extent_width / page_width))
|
||||
self.pages_high = int(ceil(extent_height / page_height))
|
||||
self.num_pages = self.pages_wide * self.pages_high
|
||||
|
||||
def draw_page(self, canvas, page_num):
|
||||
row, col = divmod(page_num, self.pages_wide)
|
||||
view_left = col * self.page_width
|
||||
view_top = row * self.page_height
|
||||
view_right = view_left + self.page_width
|
||||
view_bottom = view_top + self.page_height
|
||||
view_rect = (view_left, view_top, view_right, view_bottom)
|
||||
dx = self.left_margin - view_left
|
||||
dy = self.top_margin - view_top
|
||||
canvas.translate(dx, dy)
|
||||
canvas.rectclip(self.extent_rect)
|
||||
canvas.rectclip(view_rect)
|
||||
canvas._printing = True
|
||||
self.view.draw(canvas, view_rect)
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#
|
||||
# Python GUI - Radio buttons - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Control
|
||||
|
||||
class RadioButton(Control):
|
||||
"""RadioButtons are used in groups to represent a 1-of-N
|
||||
choice. A group of RadioButtons is coordinated by a
|
||||
RadioGroup object. The 'group' property indicates the
|
||||
RadioGroup to which it belongs, and the 'value' property
|
||||
is the value to which the RadioGroup's value is set
|
||||
when this RadioButton is selected."""
|
||||
|
||||
group = overridable_property('group', """The RadioGroup to
|
||||
which this radio button belongs.""")
|
||||
|
||||
value = overridable_property('value', """The value to which
|
||||
the associated radio group's 'value' property should be
|
||||
set when this radio button is selected.""")
|
||||
|
||||
_group = None
|
||||
_value = None
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
def get_group(self):
|
||||
return self._group
|
||||
|
||||
def set_group(self, new_group):
|
||||
old_group = self._group
|
||||
if new_group is not old_group:
|
||||
if old_group:
|
||||
old_group._remove_item(self)
|
||||
self._group = new_group
|
||||
if new_group:
|
||||
new_group._add_item(self)
|
||||
|
||||
def get_value(self):
|
||||
return self._value
|
||||
|
||||
def set_value(self, new_value):
|
||||
old_value = self._value
|
||||
if new_value != old_value:
|
||||
self._value = new_value
|
||||
self._value_changed()
|
||||
|
||||
def _value_changed(self):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
#
|
||||
# Python GUI - Radio groups - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
from GUI.Actions import Action
|
||||
|
||||
class RadioGroup(Properties, Action):
|
||||
"""A RadioGroup coordinates a group of RadioButtons.
|
||||
It has a 'value' property which is equal to the value
|
||||
of the currently selected RadioButton. It may be given
|
||||
an action procedure to execute when its value changes.
|
||||
|
||||
Operations:
|
||||
iter(group)
|
||||
Returns an iterator over the items of the group.
|
||||
"""
|
||||
|
||||
value = overridable_property('value', """The value of the currently
|
||||
selected radio button.""")
|
||||
|
||||
_items = None
|
||||
_value = None
|
||||
|
||||
def __init__(self, items = [], **kwds):
|
||||
Properties.__init__(self, **kwds)
|
||||
self._items = []
|
||||
self.add_items(items)
|
||||
|
||||
#
|
||||
# Operations
|
||||
#
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._items)
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
|
||||
def get_value(self):
|
||||
return self._value
|
||||
|
||||
def set_value(self, x):
|
||||
if self._value <> x:
|
||||
self._value = x
|
||||
self._value_changed()
|
||||
self.do_action()
|
||||
|
||||
#
|
||||
# Adding and removing items
|
||||
#
|
||||
|
||||
def add_items(self, items):
|
||||
"Add a sequence of RadioButtons to this group."
|
||||
for item in items:
|
||||
self.add_item(item)
|
||||
|
||||
def add_item(self, item):
|
||||
"Add a RadioButton to this group."
|
||||
item.group = self
|
||||
|
||||
def remove_items(self, items):
|
||||
"Remove a sequence of RadioButtons from this group."
|
||||
for item in items:
|
||||
item.group = None
|
||||
|
||||
def remove_item(self, item):
|
||||
"Remove a RadioButton from this group."
|
||||
item.group = None
|
||||
|
||||
def _add_item(self, item):
|
||||
self._items.append(item)
|
||||
self._item_added(item)
|
||||
|
||||
def _remove_item(self, item):
|
||||
self._items.remove(item)
|
||||
self._item_removed(item)
|
||||
|
||||
def _item_added(self, item):
|
||||
raise NotImplementedError
|
||||
|
||||
def _item_removed(self, item):
|
||||
raise NotImplementedError
|
||||
|
||||
def _value_changed(self):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
#
|
||||
# Python GUI - Scrollable Views - Generic
|
||||
#
|
||||
|
||||
from GUI.Geometry import rect_sized, add_pt, sub_pt
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Geometry import sect_rect
|
||||
from GUI import DrawableContainer
|
||||
|
||||
default_extent = (300, 300)
|
||||
default_line_scroll_amount = (16, 16)
|
||||
default_scrolling = 'hv'
|
||||
|
||||
class ScrollableView(DrawableContainer):
|
||||
"""A ScrollableView is a 2D drawing area having its own coordinate
|
||||
system and clipping area, with support for scrolling."""
|
||||
|
||||
scrolling = overridable_property('scrolling',
|
||||
"String containing 'h' for horizontal and 'v' for vertical scrolling.")
|
||||
|
||||
hscrolling = overridable_property('hscrolling',
|
||||
"True if horizontal scrolling is enabled.")
|
||||
|
||||
vscrolling = overridable_property('vscrolling',
|
||||
"True if vertical scrolling is enabled.")
|
||||
|
||||
extent = overridable_property('extent',
|
||||
"Size of scrollable area in local coordinates.")
|
||||
|
||||
scroll_offset = overridable_property('scroll_offset',
|
||||
"Current scrolling position.")
|
||||
|
||||
line_scroll_amount = overridable_property('line_scroll_amount',
|
||||
"Tuple specifying horizontal and vertical line scrolling increments.")
|
||||
|
||||
background_color = overridable_property('background_color',
|
||||
"Color with which to fill areas outside the extent, or None")
|
||||
|
||||
#scroll_bars = overridable_property('scroll_bars',
|
||||
# "Attached ScrollBar instances.")
|
||||
#
|
||||
## _scroll_bars [ScrollBar]
|
||||
|
||||
def set(self, **kwds):
|
||||
if 'scrolling' in kwds:
|
||||
self.scrolling = kwds.pop('scrolling')
|
||||
DrawableContainer.set(self, **kwds)
|
||||
|
||||
def get_scrolling(self):
|
||||
chars = []
|
||||
if self.hscrolling:
|
||||
chars.append('h')
|
||||
if self.vscrolling:
|
||||
chars.append('v')
|
||||
return ''.join(chars)
|
||||
|
||||
def set_scrolling(self, value):
|
||||
self.hscrolling = 'h' in value
|
||||
self.vscrolling = 'v' in value
|
||||
|
||||
def viewed_rect(self):
|
||||
"""Return the rectangle in local coordinates bounding the currently
|
||||
visible part of the extent."""
|
||||
return rect_sized(self.scroll_offset, self.size)
|
||||
|
||||
def get_print_extent(self):
|
||||
return self.extent
|
||||
|
||||
def get_background_color(self):
|
||||
return self._background_color
|
||||
|
||||
def set_background_color(self, x):
|
||||
self._background_color = x
|
||||
self.invalidate()
|
||||
|
||||
#
|
||||
# Coordinate transformation
|
||||
#
|
||||
|
||||
def local_to_container_offset(self):
|
||||
return sub_pt(self.position, self.scroll_offset)
|
||||
|
||||
#
|
||||
# Scrolling
|
||||
#
|
||||
|
||||
def h_line_scroll_amount(self):
|
||||
"""Return the horizontal line scroll increment."""
|
||||
return self.line_scroll_amount[0]
|
||||
|
||||
def v_line_scroll_amount(self):
|
||||
"""Return the vertical line scroll increment."""
|
||||
return self.line_scroll_amount[1]
|
||||
|
||||
def h_page_scroll_amount(self):
|
||||
"""Return the horizontal page scroll increment."""
|
||||
return self.width - self.h_line_scroll_amount()
|
||||
|
||||
def v_page_scroll_amount(self):
|
||||
"""Return the vertical page scroll increment."""
|
||||
return self.height - self.v_line_scroll_amount()
|
||||
|
||||
def scroll_by(self, dx, dy):
|
||||
"""Scroll by the given amount horizontally and vertically."""
|
||||
self.scroll_offset = add_pt(self.scroll_offset, (dx, dy))
|
||||
|
||||
def scroll_line_left(self):
|
||||
"""Called by horizontal scroll bar to scroll left by one line."""
|
||||
self.scroll_by(-self.h_line_scroll_amount(), 0)
|
||||
|
||||
def scroll_line_right(self):
|
||||
"""Called by horizontal scroll bar to scroll right by one line."""
|
||||
self.scroll_by(self.h_line_scroll_amount(), 0)
|
||||
|
||||
def scroll_line_up(self):
|
||||
"""Called by vertical scroll bar to scroll up by one line."""
|
||||
self.scroll_by(0, -self.v_line_scroll_amount())
|
||||
|
||||
def scroll_line_down(self):
|
||||
"""Called by vertical scroll bar to scroll down by one line."""
|
||||
self.scroll_by(0, self.v_line_scroll_amount())
|
||||
|
||||
def scroll_page_left(self):
|
||||
"""Called by horizontal scroll bar to scroll left by one page."""
|
||||
self.scroll_by(-self.h_page_scroll_amount(), 0)
|
||||
|
||||
def scroll_page_right(self):
|
||||
"""Called by horizontal scroll bar to scroll right by one page."""
|
||||
self.scroll_by(self.h_page_scroll_amount(), 0)
|
||||
|
||||
def scroll_page_up(self):
|
||||
"""Called by vertical scroll bar to scroll up by one page."""
|
||||
self.scroll_by(0, -self.v_page_scroll_amount())
|
||||
|
||||
def scroll_page_down(self):
|
||||
"""Called by vertical scroll bar to scroll down by one page."""
|
||||
self.scroll_by(0, self.v_page_scroll_amount())
|
||||
|
||||
#
|
||||
# Background drawing
|
||||
#
|
||||
|
||||
def _draw_background(self, canvas, clip_rect):
|
||||
# If the view has a background color, uses it to fill the parts of the
|
||||
# clip_rect that are outside the view's extent and returns the remaining
|
||||
# rectangle. Otherwise, returns the clip_rect unchanged.
|
||||
color = self._background_color
|
||||
if color:
|
||||
vl, vt, vr, vb = clip_rect
|
||||
ew, eh = self.extent
|
||||
if vr > ew or vb > eh:
|
||||
#if getattr(self, "_debug_bg", False): ###
|
||||
# print "ScrollableView: old backcolor =", canvas.backcolor ###
|
||||
canvas.gsave()
|
||||
canvas.backcolor = color
|
||||
if ew < vr:
|
||||
#if getattr(self, "_debug_bg", False): ###
|
||||
# print "ScrollableView: erasing", (ew, vt, vr, vb) ###
|
||||
canvas.erase_rect((ew, vt, vr, vb))
|
||||
if eh < vb:
|
||||
if getattr(self, "_debug_bg", False): ###
|
||||
print "ScrollableView: erasing", (vl, eh, ew, vb) ###
|
||||
canvas.erase_rect((vl, eh, ew, vb))
|
||||
canvas.grestore()
|
||||
#if getattr(self, "_debug_bg", False): ###
|
||||
# print "ScrollableView: restored backcolor =", canvas.backcolor ###
|
||||
return sect_rect(clip_rect, (0, 0, ew, eh))
|
||||
return clip_rect
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#
|
||||
# Python GUI - Slider - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI.Actions import Action
|
||||
from GUI import Control
|
||||
|
||||
class Slider(Control, Action):
|
||||
"""A control for entering a value by moving a knob along a scale.
|
||||
|
||||
Constructor:
|
||||
Slider(orient)
|
||||
where orient = 'h' for horizontal or 'v' for vertical.
|
||||
"""
|
||||
|
||||
_default_length = 100
|
||||
|
||||
value = overridable_property('value', "The current value of the control")
|
||||
min_value = overridable_property('min_value', "Minimum value of the control")
|
||||
max_value = overridable_property('max_value', "Maximum value of the control")
|
||||
range = overridable_property('range', "Tuple (min_value, max_value)")
|
||||
ticks = overridable_property('ticks', "Number of tick marks")
|
||||
discrete = overridable_property('discrete', "Whether to constrain value to ticks")
|
||||
live = overridable_property('live', "Whether to invoke action continuously while dragging")
|
||||
|
||||
def get_range(self):
|
||||
return (self.min_value, self.max_value)
|
||||
|
||||
def set_range(self, x):
|
||||
self.min_value = x[0]
|
||||
self.max_value = x[1]
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# PyGUI - Standard Menus - Generic
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
from GUI.Compatibility import set
|
||||
from GUI import Menu
|
||||
from GUI import MenuList
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class CommandSet(set):
|
||||
"""A set of menu command names.
|
||||
|
||||
Constructors:
|
||||
CommandSet(string)
|
||||
CommandSet(sequence of strings)
|
||||
|
||||
Operations:
|
||||
string in CommandSet
|
||||
CommandSet + x
|
||||
CommmandSet - x
|
||||
x + CommandSet
|
||||
x - CommandSet
|
||||
CommandSet += x
|
||||
CommandSet -= x
|
||||
where x is a CommandSet, a string or a sequence of strings
|
||||
"""
|
||||
|
||||
def __init__(self, arg = None):
|
||||
if arg:
|
||||
if isinstance(arg, basestring):
|
||||
arg = [arg]
|
||||
set.__init__(self, arg)
|
||||
|
||||
def __or__(self, other):
|
||||
return set.__or__(self, as_command_set(other))
|
||||
|
||||
__ror__ = __add__ = __radd__ = __or__
|
||||
|
||||
def __ior__(self, other):
|
||||
return set.__ior__(self, as_command_set(other))
|
||||
|
||||
__iadd__ = __ior__
|
||||
|
||||
def __sub__(self, other):
|
||||
return set.__sub__(self, as_command_set(other))
|
||||
|
||||
def __rsub__(self, other):
|
||||
return as_command_set(other) - self
|
||||
|
||||
def __isub__(self, other):
|
||||
return set.__isub__(self, as_command_set(other))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def as_command_set(x):
|
||||
if not isinstance(x, CommandSet):
|
||||
if isinstance(x, basestring):
|
||||
x = [x]
|
||||
x = CommandSet(x)
|
||||
return x
|
||||
|
||||
def filter_menu_items(items, include):
|
||||
result = []
|
||||
sep = False
|
||||
for item in items:
|
||||
if item == "-":
|
||||
sep = True
|
||||
elif item[1] in include:
|
||||
if sep:
|
||||
result.append("-")
|
||||
sep = False
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
def build_menus(spec_list, substitutions = {}, include = None, exclude = None):
|
||||
if include is None:
|
||||
include = sum(default_includes)
|
||||
include = include + sum(always_include)
|
||||
if exclude is not None:
|
||||
include = include - exclude
|
||||
menus = []
|
||||
for title, items, special in spec_list:
|
||||
items = filter_menu_items(items, include)
|
||||
if items:
|
||||
menus.append(Menu(title, items, special = special, substitutions = substitutions))
|
||||
return MenuList(menus)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
fundamental_cmds = CommandSet(['quit_cmd'])
|
||||
help_cmds = CommandSet(['about_cmd', 'help_cmd'])
|
||||
pref_cmds = CommandSet(['preferences_cmd'])
|
||||
file_cmds = CommandSet(['new_cmd', 'open_cmd', 'close_cmd', 'save_cmd', 'save_as_cmd', 'revert_cmd'])
|
||||
print_cmds = CommandSet(['page_setup_cmd', 'print_cmd'])
|
||||
edit_cmds = CommandSet(['undo_cmd', 'redo_cmd', 'cut_cmd', 'copy_cmd', 'paste_cmd', 'clear_cmd', 'select_all_cmd'])
|
||||
|
||||
always_include = [fundamental_cmds, edit_cmds]
|
||||
default_includes = [help_cmds, pref_cmds, file_cmds, print_cmds]
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
if __name__ == "__main__":
|
||||
s1 = CommandSet('a')
|
||||
print "s1 =", s1
|
||||
s2 = CommandSet(['a', 'b'])
|
||||
print "s2 =", s2
|
||||
s3 = s2 + 'c'
|
||||
print "s3 =", s3
|
||||
s4 = 'd' + s3
|
||||
print "s4 =", s4
|
||||
s5 = s4 - 'b'
|
||||
print "s5 =", s5
|
||||
s6 = ['a', 'b', 'c', 'd', 'e', 'f'] - s5
|
||||
print "s6 =", s6
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#
|
||||
# PyGUI - Tasks - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import Properties, overridable_property
|
||||
|
||||
class Task(Properties):
|
||||
"""A Task represents an action to be performed after a specified
|
||||
time interval, either once or repeatedly.
|
||||
|
||||
Constructor:
|
||||
Task(proc, interval, repeat = False, start = True)
|
||||
Creates a task to call the given proc, which should be
|
||||
a callable object of no arguments, after the specified
|
||||
interval in seconds from the time the task is scheduled.
|
||||
If repeat is true, the task will be automatically re-scheduled
|
||||
each time the proc is called. If start is true, the task will be
|
||||
automatically scheduled upon creation; otherwise the start()
|
||||
method must be called to schedule the task.
|
||||
"""
|
||||
|
||||
interval = overridable_property('interval', "Time in seconds between firings")
|
||||
repeat = overridable_property('repeat', "Whether to fire repeatedly or once only")
|
||||
|
||||
def __del__(self):
|
||||
self.stop()
|
||||
|
||||
scheduled = overridable_property('scheduled',
|
||||
"True if the task is currently scheduled. Read-only.")
|
||||
|
||||
def start(self):
|
||||
"""Schedule the task if it is not already scheduled."""
|
||||
raise NotImplementedError("GUI.Task.start")
|
||||
|
||||
def stop(self):
|
||||
"""Unschedules the task if it is currently scheduled."""
|
||||
raise NotImplementedError("GUI.Task.stop")
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#
|
||||
# Python GUI - Text Editor - Generic
|
||||
#
|
||||
|
||||
from GUI.Properties import overridable_property
|
||||
from GUI import Component
|
||||
from GUI import EditCmdHandler
|
||||
from GUI.Printing import Printable
|
||||
|
||||
class TextEditor(Component, EditCmdHandler, Printable):
|
||||
"""A component for editing substantial amounts of text. The text is
|
||||
kept internally to the component and cannot be shared between views."""
|
||||
|
||||
text = overridable_property('text', "The contents as a string.")
|
||||
text_length = overridable_property('text_length', "Number of characters in the text.")
|
||||
selection = overridable_property('selection', "Range of text selected.")
|
||||
font = overridable_property('font')
|
||||
tab_spacing = overridable_property('tab_spacing', "Distance between tab stops")
|
||||
|
||||
def setup_menus(self, m):
|
||||
Component.setup_menus(self, m)
|
||||
EditCmdHandler.setup_menus(self, m)
|
||||
Printable.setup_menus(self, m)
|
||||
|
||||
def key_down(self, e):
|
||||
if e.key == 'enter':
|
||||
self.pass_to_next_handler('key_down', e)
|
||||
else:
|
||||
Component.key_down(self, e)
|
||||
|
||||
def print_view(self, page_setup):
|
||||
from TextEditorPrinting import TextEditorPrintView
|
||||
view = TextEditorPrintView(self, page_setup)
|
||||
view.print_view(page_setup)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue