Lightningbeam/PyGUI-2.5.3/build/lib/GUI/Application.py

357 lines
12 KiB
Python

#------------------------------------------------------------------------------
#
# 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()