Last active
January 23, 2024 16:11
-
-
Save ryanbugden/b91b13c35dee797fabb6b29d3c9cd8ca to your computer and use it in GitHub Desktop.
A Subscriber-based version of eventObserver. Useful for tracking internal notifications and using it for tool-building.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# menuTitle: Event Subscriber | |
''' | |
A Subscriber-based version of eventObserver. | |
Useful for tracking internal notifications and using it for tool-building. | |
2024.01.22 — Ryan Bugden | |
2024.01.23 — Made a lot faster by Tal Leming. Thanks! | |
''' | |
from AppKit import * | |
import re | |
from pprint import pformat | |
import weakref | |
import ezui | |
from mojo.subscriber import * | |
# Info text styling | |
KEY_POINT_SIZE = 18 | |
VALUE_POINT_SIZE = 10 | |
PARAGRAPH_STYLE = NSParagraphStyle.defaultParagraphStyle().mutableCopy() | |
PARAGRAPH_STYLE.setLineSpacing_(KEY_POINT_SIZE) | |
KEY_ATTRIBUTES = {NSFontAttributeName : NSFont.fontWithName_size_("Menlo-Bold", KEY_POINT_SIZE), NSForegroundColorAttributeName : NSColor.blueColor(), NSParagraphStyleAttributeName: PARAGRAPH_STYLE.copy()} | |
PARAGRAPH_STYLE.setLineSpacing_(VALUE_POINT_SIZE) | |
VALUE_ATTRIBUTES = {NSFontAttributeName : NSFont.fontWithName_size_("Menlo-Regular", VALUE_POINT_SIZE), NSParagraphStyleAttributeName: PARAGRAPH_STYLE.copy(), NSForegroundColorAttributeName : NSColor.darkGrayColor()} | |
def format_info(info_dict): | |
keys = sorted(info_dict.keys()) | |
text = NSMutableAttributedString.alloc().init() | |
for key in keys: | |
attributedString = NSMutableAttributedString.alloc().initWithString_attributes_(key, KEY_ATTRIBUTES) | |
text.appendAttributedString_(attributedString) | |
value = "\n%s\n\n" % str(info_dict[key]) | |
attributedString = NSMutableAttributedString.alloc().initWithString_attributes_(value, VALUE_ATTRIBUTES) | |
text.appendAttributedString_(attributedString) | |
return text | |
# Set up the 6 basic Subscribers as empty classes. | |
events = [ | |
info["methodName"] for event, info in getRegisteredSubscriberEvents().items() | |
] | |
class PassSubscriber(Subscriber): | |
debug = True | |
window_controller = None | |
def __getattr__(self, attr): | |
if attr in events: | |
return self.relay_method | |
return super().__getattr__(attr) | |
def relay_method(self, info={}): | |
if self.window_controller is not None: | |
window_controller = self.window_controller() | |
if window_controller: | |
window_controller.log_subscriber_event(self, info) | |
class RoboFont(PassSubscriber): pass | |
class FontOverview(PassSubscriber): pass | |
class GlyphEditor(PassSubscriber): pass | |
class SpaceCenter(PassSubscriber): pass | |
class CurrentFont(PassSubscriber): pass | |
class CurrentGlyph(PassSubscriber): pass | |
subscribers = { | |
RoboFont: (registerRoboFontSubscriber, unregisterRoboFontSubscriber), | |
FontOverview: (registerFontOverviewSubscriber, unregisterFontOverviewSubscriber), | |
GlyphEditor: (registerGlyphEditorSubscriber, unregisterGlyphEditorSubscriber), | |
SpaceCenter: (registerSpaceCenterSubscriber, unregisterSpaceCenterSubscriber), | |
CurrentFont: (registerCurrentFontSubscriber, unregisterCurrentFontSubscriber), | |
CurrentGlyph: (registerCurrentGlyphSubscriber, unregisterCurrentGlyphSubscriber) | |
} | |
class EventSubscriber(ezui.WindowController): | |
def build(self): | |
content = """ | |
* HorizontalStack @mainHorzStack | |
> * VerticalStack | |
>> * TwoColumnForm @form | |
>>> : Subscriber Type: | |
>>> ( ) RoboFont @radios | |
>>> ( ) FontOverview | |
>>> (X) GlyphEditor | |
>>> ( ) SpaceCenter | |
>>> ( ) CurrentFont | |
>>> ( ) CurrentGlyph | |
>>> : Ignore Event Names: | |
>>> [_ _] @ignoreText | |
>> |-------------------------| @table | |
>> | Dispatcher | Event Name | | |
>> |------------|------------| | |
>> | | | | |
>> |-------------------------| | |
> [[_ _]] @textEditor | |
""" | |
window_width = 800 | |
padding = 20 | |
title_column_width = 132 | |
item_column_width = (window_width / 2) - (padding * 1.5) - title_column_width | |
descriptionData = dict( | |
mainHorzStack=dict( | |
distribution="fillEqually", | |
), | |
form=dict( | |
titleColumnWidth=title_column_width, | |
itemColumnWidth=item_column_width | |
), | |
table=dict( | |
width='fill', | |
height='fill', | |
items=[ | |
dict( | |
dispatcher="", | |
event_name="", | |
), | |
], | |
columnDescriptions=[ | |
dict( | |
identifier="dispatcher", | |
title="Dispatcher", | |
width=130, | |
), | |
dict( | |
identifier="event_name", | |
title="Event Name", | |
), | |
], | |
allowsMultipleSelection=False, | |
), | |
textEditor=dict( | |
width='fill', | |
height='fill', | |
value='Select an event. Info will be displayed here.', | |
editable=False | |
) | |
) | |
self.w = ezui.EZPanel( | |
content=content, | |
title="Event Subscriber", | |
descriptionData=descriptionData, | |
controller=self, | |
minSize=(window_width,350), | |
maxSize=(window_width,900), | |
size=(window_width,400), | |
) | |
self.all_info = [] | |
self.w.getNSWindow().setTitlebarAppearsTransparent_(True) | |
self.ignore = ["glyphEditorDidMouseMove"] | |
self.w.getItem("ignoreText").set(" ".join(self.ignore)) | |
self.table = self.w.getItem("table") | |
self.text_editor = self.w.getItem("textEditor") | |
self.radiosCallback(self.w.getItem("radios")) | |
def reset_table(self): | |
'''Clear the table and the internal database.''' | |
self.table.set({}) | |
self.all_info = [] | |
def started(self): | |
self.w.open() | |
self.reset_table() | |
def destroy(self): | |
for class_name, (registrar, unregistrar) in subscribers.items(): | |
unregistrar(class_name) | |
def log_subscriber_event(self, obj, info): | |
if "subscriberEventName" not in info: | |
return | |
event_name = info['subscriberEventName'] | |
dispatcher = str(obj.__class__.__name__) | |
table_item = { | |
"dispatcher": dispatcher, | |
"event_name": event_name | |
} | |
if not event_name in self.ignore: | |
self.table.appendItems([table_item]) | |
self.all_info.append(info) | |
# Keep table scrolled to the end | |
length = len(self.table.get()) | |
self.table.scrollToIndex(length-1) | |
def radiosCallback(self, sender): | |
subs_list = list(subscribers.keys()) | |
selected_class = subs_list[sender.get()] | |
for subscriber_class, (registrar, unregistrar) in subscribers.items(): | |
if subscriber_class == selected_class: | |
subscriber_class.window_controller = weakref.ref(self) | |
registrar(subscriber_class) | |
else: | |
subscriber_class.window_controller = None | |
unregistrar(subscriber_class) | |
self.reset_table() | |
def tableSelectionCallback(self, sender): | |
formatted_info = format_info(self.all_info[sender.getSelectedIndexes()[0]]) | |
self.text_editor.getNSTextView().textStorage().setAttributedString_(formatted_info) | |
def ignoreTextCallback(self, sender): | |
self.ignore = sender.get().split(" ") | |
EventSubscriber() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment