From 8ed3e9c53163fe743caf9e82ff2d3aafd67f663a Mon Sep 17 00:00:00 2001 From: Tim McNamara Date: Sun, 15 Aug 2010 00:47:18 +0000 Subject: Refactored widgetIdentifier.py. Closes #4 Moved the widgetIdentifier.wigdetIdentifer.identifiers attribute to its own variable. This sits better with its purpose as a collection of widget identifiers. Also modified sbgui.py to change references to the original reference. Grep indicates that this the only place where the class attribute was being referenced. --- diff --git a/Experior.Activity/sbgui.py b/Experior.Activity/sbgui.py index 28541b0..3cf2c77 100755 --- a/Experior.Activity/sbgui.py +++ b/Experior.Activity/sbgui.py @@ -28,294 +28,294 @@ import gtk import logging import sugar -from gobject import idle_add -from gtk import gdk -from gtk.gdk import event_handler_set -from sbexecutionengine import sbExecutionEngine -from sugar import graphics +from gobject import idle_add +from gtk import gdk +from gtk.gdk import event_handler_set +from sbexecutionengine import sbExecutionEngine +from sugar import graphics from sugar.graphics.toolbutton import Palette -from widgetIdentifier import * - -class sbGUI(gobject.GObject): - """ - Responsible for tracking all identifiable* widgets instantiated by GTK. - - *Identifiable widgets are widgets for which a valid identifier can be - found, via any of the identifiers enumerated by widgetIdentifier.identifiers. - """ - def __init__(self,sugarbotInstance,rpcServer): - """ - Initialize internal variables, and register self as an event handler. - """ - # Necessary, due to inheritance - gobject.GObject.__init__(self) - - # Get the RPC server connection and the sugarbot instance, as well - # as the execution engine. - self.sugarbot = sugarbotInstance - self.rpc = rpcServer - self.engine = sbExecutionEngine(self,self.rpc) - self.log = logging.getLogger('sbGUI') - - # Keep track of the amount of idle time... - self.idletime = 0 - self.idletimeout= 6 - - # In order to keep track of all of the windows, we set up a - # dict of Windows. For each window, the key is its id (as provided - # by id(theWindowObject). The value of each dictionary entry is a - # dictionary of Widgets, set up the same way -- the id() of the - # widget is the key, and the - self.trackedWidgets = {} # Maps id(object) -> widget - self.names = {} # Maps identifier -> widget - - # Register one of our methods to intercept all GTK events - self.registerEventHandler() - - - def eventHandler(self,event=None): - """ Intercepts all GDK events. We then send them off to a separate - handler method (to keep things clean), and then have GTK execute - whatever the event is supposed to do. - """ - if event is not None: - gtk.main_do_event(event) - self.handleEvent(event) - - if event.type is not gtk.gdk.EXPOSE: - self.idletime = 0 - - return True - - - def getWidgetIdentifier(self,widget): - """ - Returns the Widget's unique identifier (for example, a button's - label, or a gkt.Entry's name), or None if it does not have one. - Also, this function filters out many uninitialized identifiers, - such as "GtkToolbar" or "GtkButton" - @param widget - The widget whose identifier is to be retrieved. - """ - if not isinstance(widget,gtk.Widget): - raise "_getWidgetName must take a gtk.Widget object as its argument" - - # ---- GENERIC APPROACH ---- - # Assuming that the widget was named explicitly by the developers, - # getting the name should be very straightforward, with no specialized - # cases or special name-detecting. - widId = widgetIdentifier(widget) - ident = widId.getIdentifier() - - if ident is not None: - return ident - - # ---- SPECIALIZED APPROACH ---- - # Check to see if we have an identifier for the specific type - # of widget before we iterate through all of the different identifiers - # hoping to get a hit. - if widgetIdentifier.identifiers.has_key(widget.__class__): - widId = widgetIdentifier.identifiers[widget.__class__](widget) - ident = widId.getIdentifier() - - if ident is not None: - return ident - - # ---- BRUTE FORCE ---- - # The widget was not named explicitly by the developers, and we do - # not have a case for the specific widget class. - # Iterate through all of our potential identifiers, since we - # very likely have a widget class that it inherits from. - # This method is undesirable, since the identifiers in the dictionary - # widgetIdentifiers may be in a different order each time the - # program is run, due to the non-ordered nature of dictionaries. - for identifier in widgetIdentifier.identifiers: - if isinstance(widget, identifier): - widId = widgetIdentifier.identifiers[identifier](widget) - ident = widId.getIdentifier() - - if ident is not None: - break - - # At this point, we either have a valid widget identifier, or None. - return ident - - - def addWidget(self,widget): - """ - Add a widget to be tracked internally. Widgets are tracked by their - id(). Additionally, add it to the names{} dictionary, so that we - can quickly look up widgets by the name. - """ - - # Don't do anything if we already have this widget by ID - if self.trackedWidgets.has_key(id(widget)): - return - - # Make sure we are working on a widget - if not isinstance(widget, gtk.Widget): - return - - # Containers might have children. Check them all. - if isinstance(widget, gtk.Container): # gtk.Container can have - for child in widget.get_children(): # any number of children - self.addWidget(child) - - if isinstance(widget, gtk.Bin): # gtk.Bin can only have - self.addWidget(widget.get_child()) # one child. - - if isinstance(widget, gtk.Notebook): # gtk.Notebook can have - numPages = widget.get_n_pages() # many children. - for count in range(0, numPages): - page = widget.get_nth_page(count) - self.addWidget(page) - return - - - # Get the widget's identifier & id. If the widget cannot be reliably - # identified, there is no use in tracking it. - identifier = self.getWidgetIdentifier(widget) - _id = id(widget) - if identifier is None: - return - - # Simply keep track of the widgets by ID - self.trackedWidgets[_id]=widget - - # Keep track of the widgets by identifier. - # Check to see if it's already being tracked. - if self.names.has_key(identifier): - raise KeyError, "Already tracking a widget by identifier %s" \ - % identifier - - # Track the little bugger - else: - self.log.debug("Tracking widget id %i by identifier %s" % (_id, identifier)) - self.names[identifier] = widget - - - def delWidget(self,widget): - """ - Remove a widget from internal tracking. Widgets can be removed by - their id() or by the gtk.Widget object. - - TODO: I don't think this function ever gets called. - """ - if not isinstance(widget, gtk.Widget): - raise "Called delWidget on non-Widget object" - return - - # Get id(widget) - identifier = self.getWidgetIdentifier(widget) - _id = id(widget) - - # Remove the widget from the trackedWidgets dict if it is tracked. - if self.trackedWidgets.has_key(_id): - del self.trackedWidgets[_id] - del self.names[identifier] - - else: # Tried to call delWidget on non-tracked Widget - return - - def getWidgetByName(self,widgetName): - """ - Checks to see if we are tracking a Widget with a given name. - If so, return the Widget. If not, return None. - """ - if widgetName in self.names: - return self.names[widgetName] - return None - - def handleEvent_firehose(self,event): - """ - For exploratory testing. Don't bother trying to figure out what this - does, it will change without notification. - Pretty much just outputs a firehose of events. Useful for exploratory - testing, and that's pretty much it. - """ - if not hasattr(self,'classes'): - self.classes = [] - - eventType = event.type - print "----------------------------------------" - print event.type - if event.window: - # print event.window - try: - widget = event.window.get_user_data() - print widget.__class__ - self.classes[widget.__class__] = 1 - except: - raise - print "----------------------------------------" - print "" - print "" - - def handleEvent(self,event): - """ - Handles all GDK events. We first filter them so that we know - they pertain to a window, and then we further drill down based on the - type of action. This method is used to achieve two goals: - [1] Build a database of all windows and widgets - [2] Allow us to see actions as they happen. This could lead to - recording functionality in the future. - """ - try: - # Does the event have a Window that it belongs to? - if (not event.window): - return - - # Get some information on the widget. If it doesn't work, just - # gracefully fail. Exceptions happen with the following events: - # (maybe more, but these are what I've observed): - # - GDK_OWNER_CHANGE - widget = event.window.get_user_data() - eventType = event.type - - # -------- HANDLE WIDGET INSTANTIATION -------- - # MAP events are generated when a widget is initially displayed - # on the screen. In most cases, any naming or configuration that - # is going to be performed *has been* performed. - if eventType == gdk.MAP: - self.addWidget(widget) - - # -------- HANDLE WIDGET DESTRUCTION -------- - # UNMAP events are generated when a widget - # is being taken off the screen. Generally, this will happen at - # the end of an application's execution. However, to maintain - # flexibility (e.g. the possibility of dialog windows), handle - # the UNMAP event here. - elif eventType == gdk.UNMAP: - self.delWidget(widget) - except ValueError: - pass - except: # Oops! - raise - - def idleHandler(self, event=None): - - if self.engine.isComplete(): - return False - - if self.idletime is 0: - self.idletime = time.time() - self.lasttime = self.idletime - - if self.lasttime + 1 < time.time(): - self.lasttime += 1 - - if self.idletime + self.idletimeout < time.time(): - self.engine.executePy() - self.idletime = 0 - - return True - - def registerEventHandler(self): - """ - Registers the method self.eventHandler as the function that - will receive all GDK events. This allows us to snoop on GDK. - """ - if not event_handler_set: - raise NotImplementedError - else: - gobject.idle_add(self.idleHandler) - event_handler_set(self.eventHandler) +from widgetIdentifier import * + +class sbGUI(gobject.GObject): + """ + Responsible for tracking all identifiable* widgets instantiated by GTK. + + *Identifiable widgets are widgets for which a valid identifier can be + found, via any of the identifiers enumerated by widgetIdentifier.identifiers. + """ + def __init__(self,sugarbotInstance,rpcServer): + """ + Initialize internal variables, and register self as an event handler. + """ + # Necessary, due to inheritance + gobject.GObject.__init__(self) + + # Get the RPC server connection and the sugarbot instance, as well + # as the execution engine. + self.sugarbot = sugarbotInstance + self.rpc = rpcServer + self.engine = sbExecutionEngine(self,self.rpc) + self.log = logging.getLogger('sbGUI') + + # Keep track of the amount of idle time... + self.idletime = 0 + self.idletimeout= 6 + + # In order to keep track of all of the windows, we set up a + # dict of Windows. For each window, the key is its id (as provided + # by id(theWindowObject). The value of each dictionary entry is a + # dictionary of Widgets, set up the same way -- the id() of the + # widget is the key, and the + self.trackedWidgets = {} # Maps id(object) -> widget + self.names = {} # Maps identifier -> widget + + # Register one of our methods to intercept all GTK events + self.registerEventHandler() + + + def eventHandler(self,event=None): + """ Intercepts all GDK events. We then send them off to a separate + handler method (to keep things clean), and then have GTK execute + whatever the event is supposed to do. + """ + if event is not None: + gtk.main_do_event(event) + self.handleEvent(event) + + if event.type is not gtk.gdk.EXPOSE: + self.idletime = 0 + + return True + + + def getWidgetIdentifier(self,widget): + """ + Returns the Widget's unique identifier (for example, a button's + label, or a gkt.Entry's name), or None if it does not have one. + Also, this function filters out many uninitialized identifiers, + such as "GtkToolbar" or "GtkButton" + @param widget - The widget whose identifier is to be retrieved. + """ + if not isinstance(widget,gtk.Widget): + raise "_getWidgetName must take a gtk.Widget object as its argument" + + # ---- GENERIC APPROACH ---- + # Assuming that the widget was named explicitly by the developers, + # getting the name should be very straightforward, with no specialized + # cases or special name-detecting. + widId = widgetIdentifier(widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- SPECIALIZED APPROACH ---- + # Check to see if we have an identifier for the specific type + # of widget before we iterate through all of the different identifiers + # hoping to get a hit. + if identifiers.has_key(widget.__class__): + widId = identifiers[widget.__class__](widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- BRUTE FORCE ---- + # The widget was not named explicitly by the developers, and we do + # not have a case for the specific widget class. + # Iterate through all of our potential identifiers, since we + # very likely have a widget class that it inherits from. + # This method is undesirable, since the identifiers in the dictionary + # widgetIdentifiers may be in a different order each time the + # program is run, due to the non-ordered nature of dictionaries. + for identifier in identifiers: + if isinstance(widget, identifier): + widId = identifiers[identifier](widget) + ident = widId.getIdentifier() + + if ident is not None: + break + + # At this point, we either have a valid widget identifier, or None. + return ident + + + def addWidget(self,widget): + """ + Add a widget to be tracked internally. Widgets are tracked by their + id(). Additionally, add it to the names{} dictionary, so that we + can quickly look up widgets by the name. + """ + + # Don't do anything if we already have this widget by ID + if self.trackedWidgets.has_key(id(widget)): + return + + # Make sure we are working on a widget + if not isinstance(widget, gtk.Widget): + return + + # Containers might have children. Check them all. + if isinstance(widget, gtk.Container): # gtk.Container can have + for child in widget.get_children(): # any number of children + self.addWidget(child) + + if isinstance(widget, gtk.Bin): # gtk.Bin can only have + self.addWidget(widget.get_child()) # one child. + + if isinstance(widget, gtk.Notebook): # gtk.Notebook can have + numPages = widget.get_n_pages() # many children. + for count in range(0, numPages): + page = widget.get_nth_page(count) + self.addWidget(page) + return + + + # Get the widget's identifier & id. If the widget cannot be reliably + # identified, there is no use in tracking it. + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + if identifier is None: + return + + # Simply keep track of the widgets by ID + self.trackedWidgets[_id]=widget + + # Keep track of the widgets by identifier. + # Check to see if it's already being tracked. + if self.names.has_key(identifier): + raise KeyError, "Already tracking a widget by identifier %s" \ + % identifier + + # Track the little bugger + else: + self.log.debug("Tracking widget id %i by identifier %s" % (_id, identifier)) + self.names[identifier] = widget + + + def delWidget(self,widget): + """ + Remove a widget from internal tracking. Widgets can be removed by + their id() or by the gtk.Widget object. + + TODO: I don't think this function ever gets called. + """ + if not isinstance(widget, gtk.Widget): + raise "Called delWidget on non-Widget object" + return + + # Get id(widget) + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + + # Remove the widget from the trackedWidgets dict if it is tracked. + if self.trackedWidgets.has_key(_id): + del self.trackedWidgets[_id] + del self.names[identifier] + + else: # Tried to call delWidget on non-tracked Widget + return + + def getWidgetByName(self,widgetName): + """ + Checks to see if we are tracking a Widget with a given name. + If so, return the Widget. If not, return None. + """ + if widgetName in self.names: + return self.names[widgetName] + return None + + def handleEvent_firehose(self,event): + """ + For exploratory testing. Don't bother trying to figure out what this + does, it will change without notification. + Pretty much just outputs a firehose of events. Useful for exploratory + testing, and that's pretty much it. + """ + if not hasattr(self,'classes'): + self.classes = [] + + eventType = event.type + print "----------------------------------------" + print event.type + if event.window: + # print event.window + try: + widget = event.window.get_user_data() + print widget.__class__ + self.classes[widget.__class__] = 1 + except: + raise + print "----------------------------------------" + print "" + print "" + + def handleEvent(self,event): + """ + Handles all GDK events. We first filter them so that we know + they pertain to a window, and then we further drill down based on the + type of action. This method is used to achieve two goals: + [1] Build a database of all windows and widgets + [2] Allow us to see actions as they happen. This could lead to + recording functionality in the future. + """ + try: + # Does the event have a Window that it belongs to? + if (not event.window): + return + + # Get some information on the widget. If it doesn't work, just + # gracefully fail. Exceptions happen with the following events: + # (maybe more, but these are what I've observed): + # - GDK_OWNER_CHANGE + widget = event.window.get_user_data() + eventType = event.type + + # -------- HANDLE WIDGET INSTANTIATION -------- + # MAP events are generated when a widget is initially displayed + # on the screen. In most cases, any naming or configuration that + # is going to be performed *has been* performed. + if eventType == gdk.MAP: + self.addWidget(widget) + + # -------- HANDLE WIDGET DESTRUCTION -------- + # UNMAP events are generated when a widget + # is being taken off the screen. Generally, this will happen at + # the end of an application's execution. However, to maintain + # flexibility (e.g. the possibility of dialog windows), handle + # the UNMAP event here. + elif eventType == gdk.UNMAP: + self.delWidget(widget) + except ValueError: + pass + except: # Oops! + raise + + def idleHandler(self, event=None): + + if self.engine.isComplete(): + return False + + if self.idletime is 0: + self.idletime = time.time() + self.lasttime = self.idletime + + if self.lasttime + 1 < time.time(): + self.lasttime += 1 + + if self.idletime + self.idletimeout < time.time(): + self.engine.executePy() + self.idletime = 0 + + return True + + def registerEventHandler(self): + """ + Registers the method self.eventHandler as the function that + will receive all GDK events. This allows us to snoop on GDK. + """ + if not event_handler_set: + raise NotImplementedError + else: + gobject.idle_add(self.idleHandler) + event_handler_set(self.eventHandler) diff --git a/Experior.Activity/widgetIdentifier.py b/Experior.Activity/widgetIdentifier.py index 7d00024..47caf1a 100755 --- a/Experior.Activity/widgetIdentifier.py +++ b/Experior.Activity/widgetIdentifier.py @@ -45,7 +45,6 @@ class widgetIdentifier: This class provides some of the functionality for identifying widgets, such as a list of strings that are default Widget names (e.g. 'GtkButton'). """ - identifiers = {} def __init__(self, widget): self.widgetAttribute = "sugarbotWidgetIdentifier" @@ -148,7 +147,6 @@ class widgetIdentifier: setattr(self._widget, self.widgetAttribute, ident) return ident return None -widgetIdentifier.identifiers[gtk.Widget] = widgetIdentifier class buttonIdentifier(widgetIdentifier): @@ -160,7 +158,6 @@ class buttonIdentifier(widgetIdentifier): ident = widget.get_label() return ident -widgetIdentifier.identifiers[gtk.Button] = buttonIdentifier # class toolButtonIdentifier(widgetIdentifier): class toolButtonIdentifier(buttonIdentifier): @@ -186,7 +183,6 @@ class toolButtonIdentifier(buttonIdentifier): ident = label.get_text() return ident -widgetIdentifier.identifiers[gtk.ToolButton] = toolButtonIdentifier class comboBoxIdentifier(widgetIdentifier): def getIdentifierSub(self): @@ -197,8 +193,6 @@ class comboBoxIdentifier(widgetIdentifier): ident = self._widget.get_title() return ident -widgetIdentifier.identifiers[gtk.ComboBox] = comboBoxIdentifier - class entryIdentifier(widgetIdentifier): def getIdentifierSub(self): @@ -210,7 +204,6 @@ class entryIdentifier(widgetIdentifier): ident = self._widget.get_text() return ident -widgetIdentifier.identifiers[gtk.Entry] = entryIdentifier class paletteIdentifier(widgetIdentifier): def getIdentifierSub(self): @@ -225,8 +218,6 @@ class paletteIdentifier(widgetIdentifier): ident = getattr(widget, "props.primary_text") return ident -widgetIdentifier.identifiers[Palette] = paletteIdentifier - class toolComboBoxIdentifier(widgetIdentifier): def getIdentifierSub(self): @@ -257,4 +248,12 @@ class toolComboBoxIdentifier(widgetIdentifier): # return ident -widgetIdentifier.identifiers[ToolComboBox] = toolComboBoxIdentifier + +identifiers = {} +identifiers[gtk.Entry] = entryIdentifier +identifiers[Palette] = paletteIdentifier +identifiers[ToolComboBox] = toolComboBoxIdentifier +identifiers[gtk.ComboBox] = comboBoxIdentifier +identifiers[gtk.ToolButton] = toolButtonIdentifier +identifiers[gtk.Button] = buttonIdentifier +identifiers[gtk.Widget] = widgetIdentifier -- cgit v0.9.1