Skip to content

Instantly share code, notes, and snippets.

@ryanbugden
Last active January 23, 2024 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanbugden/b91b13c35dee797fabb6b29d3c9cd8ca to your computer and use it in GitHub Desktop.
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.
# 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