我今天从最后一个稳定版本(我认为是3143)更新到Sublime Text版本3170。我一直在使用sublime_plugin.EventListener来执行与.scpt文件等相关的任务。为此,我创建了一个具有查看二进制或编码文件的基本功能的类。要使用该类,我会将其子类化并继承sublime_plugin.EventListener,以便sublime将调用适当的方法(on_modified,on_load等)。
但是,我希望在更新后更改为sublime_plugin.ViewEventListener(扩展了ViewEventListener的api),因为这样可以让我在插件中完成更多的工作。
问题是我的超类方法没有被sublime调用。下面是一些有希望解释我的问题的代码。在此先感谢。
#this does work
class test:
def on_modified(self, view):
print("mod")
class test2(test, sublime_plugin.EventListener):
pass
当对视图进行更改时将调用on_modified并打印" mod"每一次。
#this does not work
class test:
def on_modified(self):
print("mod")
class test2(test, sublime_plugin.ViewEventListener):
pass
另一方面,这将无法正常工作。 " MOD"永远不会打印。我搜索了谷歌并尝试调试。我可以确认正在创建一个ViewEventListener实例,如果on_modified是在test2中那么" mod"打印。
任何建议将不胜感激。再次感谢。
答案 0 :(得分:1)
这是一个有趣的小问题,因为从表面上看,这似乎应该可行。如果你在Sublime插件系统的内部进行了足够的跟踪,那么原因就更明显了。
出于本答案的目的,我使用的插件与您问题中的非工作示例匹配,我将其命名为test_plugin.py
:
import sublime
import sublime_plugin
class test:
def on_modified(self):
print("mod")
class test2(test, sublime_plugin.ViewEventListener):
pass
首先,Sublime使用EventListener
和ViewEventListener
的方式在插件方面具有不同的内部机制。
EventListener
事件同样适用于所有地方的所有视图,因此当Sublime加载插件并找到EventListener
类时,它会立即创建一个实例,然后检查该实例的事件支持。相关代码位于第142行的sublime_plugin.py
方法中reload_plugin()
:
if issubclass(t, EventListener):
obj = t()
for p in all_callbacks.items():
if p[0] in dir(obj):
p[1].append(obj)
all_callbacks
是一个字典,其中键是事件的名称,值是数组;因此,这将检查事件监听器实例的dir()
是否包含事件,如果是,则将其添加到支持该事件的类列表中。
另一方面,ViewEventListener
仅适用于基于is_applicable
和applies_to_primary_view_only
类方法的特定视图。这意味着它必须存储类而不是实例,以便在创建新视图时可以创建特定于该视图的实例。
相关代码正好在上面的sublime_plugin.py
文件的第156行:
if issubclass(t, ViewEventListener):
view_event_listener_classes.append(t)
module_view_event_listener_classes.append(t)
现在让我们看一下需要引发事件时会发生什么。在我们的示例中,我们正在查看on_modified
事件,该事件由on_modified
第566行的sublime_plugin.py
模块函数处理(但所有事件的工作方式类似):
def on_modified(view_id):
v = sublime.View(view_id)
for callback in all_callbacks['on_modified']:
run_callback('on_modified', callback, lambda: callback.on_modified(v))
run_view_listener_callback(v, 'on_modified')
第一部分是常规EventListener
;它找到之前找到的所有具有on_modified
处理程序的对象实例,然后直接在它们上调用on_modified()
(run_callback()
包装器负责为您的分析执行计时可以在Tools > Developer > Profile Plugins
)中看到。
ViewEventListener
的处理发生在run_view_listener_callback()
,sublime_plugin.py
中的第480行:
def run_view_listener_callback(view, name):
for vel in event_listeners_for_view(view):
if name in vel.__class__.__dict__:
run_callback(name, vel, lambda: vel.__class__.__dict__[name](vel))
如果您已经在示例中定义了类,那么这就是事物开始变形的部分,因为它特别询问__dict__
属性是否包含要调用的事件,并且仅调用它如果是的话。
根据上面的示例插件,从Sublime控制台注意以下内容:
>>> from User.test_plugin import test, test2
>>> "on_modified" in test.__dict__
True
>>> "on_modified" in test2.__dict__
False
特别是,test2
并不直接包含on_modified
方法,因此它不在__dict__
中。在常规Python程序中,如果您尝试调用on_modified
,它会注意到它不在对象的__dict__
中,然后开始搜索层次结构,在test
中找到它并且一切都是肉汁。
在EventListener
实例的情况下,这就是发生的事情; dir()
函数知道其中一个超类包含on_modified
并返回它,调用它的调用会上升到链中,一切都按预期工作。
由于对run_view_listener_callback
的调用没有直接尝试调用该方法,因此它在直接__dict__
查找中找不到它,因此它什么也没做,因为它认为该类没有处理该事件,即使它确实如此。
因此,所有这些文本墙的结果都是ViewEventListener
,事件必须直接存在于子类ViewEventListener
的类中,否则它不起作用。
在您的示例中,一种方法是重新构建代码并直接在test2
内而不是test
内定义这些方法。
另一种方法是通过在子类中定义方法来“代理”它们,但让主体遵循超类版本。这将适当的方法放在类的__dict__
中,但仍然允许实现出现在另一个类中。
class test2(test, sublime_plugin.ViewEventListener):
def on_modified(self):
super().on_modified()
我不是一个Python大师,但这个对我来说似乎并不过分Pythonic(至少对于布局的例子而言)并且可能还有其他方法可以达到同样的目的。