/GUI/Cocoa/Applications.py
https://bitbucket.org/alsh/pygui-mirror · Python · 403 lines · 230 code · 60 blank · 113 comment · 47 complexity · a92d134962343b896822d85cf3fbb27f MD5 · raw file
- #------------------------------------------------------------------------------
- #
- # Python GUI - Application class - PyObjC
- #
- #------------------------------------------------------------------------------
- import os, sys, traceback
- import objc
- #from ExceptionHandling import \
- # NSExceptionHandler, NSLogUncaughtExceptionMask, NSLogAndHandleEveryExceptionMask
- from Foundation import NSObject, NSBundle, NSDefaultRunLoopMode
- import AppKit
- from AppKit import NSApplication, NSResponder, NSScreen, NSMenu, NSMenuItem, \
- NSKeyDown, NSKeyUp, NSMouseMoved, NSLeftMouseDown, NSSystemDefined, \
- NSCommandKeyMask, NSPasteboard, NSStringPboardType, NSModalPanelRunLoopMode, \
- NSAnyEventMask
- import Globals
- import GApplications
- from Globals import application
- from GApplications import Application as GApplication
- from Events import Event
- #from Exceptions import Cancel, Quit
- from Menus import _ns_standard_actions
- #------------------------------------------------------------------------------
- Globals.ns_screen_height = None
- Globals.ns_last_mouse_moved_event = None
- ns_application = None
- #------------------------------------------------------------------------------
- #def test_menus(app):
- # print "Testing menus"
- # foomenu = NSMenu.alloc().initWithTitle_("Foo")
- # foomenu.addItemWithTitle_action_keyEquivalent_("Blarg", None, "B")
- # feemenu = NSMenu.alloc().initWithTitle_("Fee")
- # feemenu.addItemWithTitle_action_keyEquivalent_("Blork", None, "K")
- # menubar = NSMenu.alloc().initWithTitle_("Main")
- # fooitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Foo", "", "")
- # menubar.addItem_(fooitem)
- # menubar.setSubmenu_forItem_(foomenu, fooitem)
- # feeitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Fee", "", "")
- # menubar.addItem_(feeitem)
- # menubar.setSubmenu_forItem_(feemenu, feeitem)
- # app.setMainMenu_(menubar)
- # app.setAppleMenu_(foomenu)
- # #app.run()
- # print "Finished testing menus"
- #------------------------------------------------------------------------------
- class Application(GApplication):
- # _ns_app _PyGui_NSApplication
- # _ns_pasteboard NSPasteboard
- # _ns_key_window Window
-
- _ns_menubar_update_pending = False
- def __init__(self, **kwds):
- #print "Application.__init__: argv =", sys.argv ###
- create_ns_application()
- self._ns_app = ns_application
- self._ns_app.pygui_app = self
- self._ns_init_standard_menu_items()
- self._ns_pasteboard = NSPasteboard.generalPasteboard()
- self._ns_key_window = None
- GApplication.__init__(self, **kwds)
- ns_application.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)
- ns_app_menu = menu_list[0]._ns_menu
- ns_app.setAppleMenu_(ns_app_menu)
- # def handle_events(self):
- # #print "Application.handle_events: entering NS run loop" ###
- # #try:
- # self._ns_app.run()
- # #finally:
- # #print "Application.handle_events: exiting NS run loop" ###
- def handle_next_event(self, modal_window = None):
- #print "Application.handle_next_event" ###
- 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, None, 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):
- raise NotImplementedError("TODO: Application.get_clipboard")
-
- def set_clipboard(self, data):
- raise NotImplementedError("TODO: Application.set_clipboard")
-
- def _ns_init_standard_menu_items(self):
- self._ns_standard_menu_items = {}
- for (cmd_name, ns_selector) in _ns_standard_actions.iteritems():
- self._ns_standard_menu_items[cmd_name] = NSMenuItem.alloc().\
- initWithTitle_action_keyEquivalent_("", ns_selector, "")
-
- def _dispatch_menu_setup(self, m):
- for (cmd_name, ns_menu_item) in self._ns_standard_menu_items.iteritems():
- ns_selector = ns_menu_item.action()
- target = self._ns_app.targetForAction_(ns_selector)
- if target and target.respondsToSelector_('validateMenuItem:'):
- valid = target.validateMenuItem_(ns_menu_item)
- m[cmd_name].enabled = valid
- GApplication._dispatch_menu_setup(self, m)
-
- def setup_menus(self, m):
- 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_app.using_clargs = True
- GApplication.process_args(self, args)
- def run(self, fast_exit = True):
- #print "Application.run" ###
- 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):
- #print "Application.event_loop" ###
- self._ns_app.run()
- #print "Exit Application.event_loop" ###
-
- # def _exit_event_loop(self):
- # self._ns_app.stop_(self)
- def _quit(self):
- #print "Application._quit" ###
- self._quit_flag = True
- self._ns_app.stop_(self._ns_app)
- #print "Exit Application._quit" ###
-
- #------------------------------------------------------------------------------
- _ns_key_event_mask = AppKit.NSKeyDownMask | AppKit.NSKeyUpMask
- #------------------------------------------------------------------------------
- class _PyGui_NSApplication(NSApplication):
- pygui_app = None
- files_opened = False
- using_clargs = False
- pygui_menus_updated = False
- 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 pending_exception:
- raise_pending_exception()
- ns_type = ns_event.type()
- #print "sendEvent_:", ns_event ###
- self.pygui_menus_updated = False
- if (1 << ns_type) & _ns_key_event_mask:
- self.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 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.
- #print "_PyGui_NSApplication.process_key_event:", ns_event ###
- if ns_event.modifierFlags() & NSCommandKeyMask:
- #self.perform_menu_setup()
- NSApplication.sendEvent_(self, ns_event)
- else:
- ns_window = ns_event.window()
- #print "_PyGui_NSApplication.process_key_event: ns_window =", ns_window ###
- if ns_window:
- ns_window.sendEvent_(ns_event)
- else:
- self.pass_event_to_application(ns_event)
-
- def menuNeedsUpdate_(self, ns_menu):
- #print "_PyGui_NSApplication.menuNeedsUpdate_:", object.__repr__(ns_menu) ###
- if not self.pygui_menus_updated:
- #print "...updating menus" ###
- self.perform_menu_setup()
- self.pygui_menus_updated = True
- def perform_menu_setup(self):
- #print "_PyGui_NSApplication.perform_menu_setup" ###
- app = self.pygui_app
- if app:
- app._perform_menu_setup()
-
- def pass_event_to_application(self, ns_event):
- app = self.pygui_app
- if app:
- event = Event(ns_event)
- app.handle(event.kind, event)
- # def sendAction_to_from_(self, action, target, sender):
- # print "_PyGui_NSApplication.sendAction_to_from_", action, target, sender ###
- # return NSApplication.sendAction_to_from_(self, action, target, sender)
- def menuSelection_(self, ns_menu_item):
- command = ns_menu_item.representedObject()
- index = ns_menu_item.tag()
- if index >= 0:
- self.dispatch_to_app(command, index)
- else:
- self.dispatch_to_app(command)
-
- def dispatch_to_app(self, *args):
- app = self.pygui_app
- if app:
- app.dispatch(*args)
-
- def validateMenuItem_(self, item):
- return False
-
- def undo_(self, sender):
- self.dispatch_to_app('undo_cmd')
- def redo_(self, sender):
- self.dispatch_to_app('redo_cmd')
- def cut_(self, sender):
- self.dispatch_to_app('cut_cmd')
- def copy_(self, sender):
- self.dispatch_to_app('copy_cmd')
- def paste_(self, sender):
- self.dispatch_to_app('paste_cmd')
- def clear_(self, sender):
- self.dispatch_to_app('clear_cmd')
- def select_all_(self, sender):
- self.dispatch_to_app('select_all_cmd')
- def 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 ns_info:
- if ns_info['CFBundleName'] == "Python":
- ns_info['CFBundleName'] = Globals.application_name
- return
- #raise RuntimeError("No bundle information found. "
- # "Perhaps the application was not run with pythonw?")
-
- def application_openFile_(self, ns_app, path):
- if self.using_clargs:
- return True
- #print "PyGUI_NSApplication.application_openFile_:", path ###
- # 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
- self.files_opened = True
- try:
- self.pygui_app.open_path(path)
- return True
- except Exception, e:
- self.pygui_app.report_error()
- return False
-
- def applicationDidFinishLaunching_(self, notification):
- if self.using_clargs:
- return
- #print "PyGUI_NSApplication.applicationDidFinishLaunching_"
- try:
- if not self.files_opened:
- self.pygui_app.open_app()
- except Exception, e:
- self.pygui_app.report_error()
- return False
- #------------------------------------------------------------------------------
- pending_exception = None
- def raise_pending_exception():
- global pending_exception
- exc_type, exc_value, exc_tb = pending_exception
- pending_exception = None
- raise exc_type, exc_value, exc_tb
- def create_ns_application():
- global ns_application
- ns_application = _PyGui_NSApplication.sharedApplication()
- ns_application.setDelegate_(ns_application)
- Globals.ns_screen_height = NSScreen.mainScreen().frame().size.height
- #------------------------------------------------------------------------------
- # 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()