【问题标题】:Sublime Text 3.1 Build 3170 ViewEventListener Super Class methodsSublime Text 3.1 搭建3170个ViewEventListener超类方法
【发布时间】:2018-05-07 23:47:45
【问题描述】:

我今天从我认为是 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”。
任何建议将不胜感激。再次感谢。

【问题讨论】:

    标签: python-3.x sublimetext3 sublime-text-plugin


    【解决方案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 使用 EventListenerViewEventListener 的方式在插件方面有不同的内部机制。

    EventListener 事件同样适用于任何地方的所有视图,因此当 Sublime 加载插件并找到 EventListener 类时,它会立即创建它的一个实例,然后检查该实例支持的事件。与此相关的代码在第 142 行附近的 reload_plugin() 方法中的 sublime_plugin.py 中:

    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_applicableapplies_to_primary_view_only 类方法的某些视图。这意味着它必须存储 class 而不是 instance,以便在创建新视图时,它可以创建特定于该视图的实例。

    相关代码就在上面,在sublime_plugin.py文件的第156行:

    if issubclass(t, ViewEventListener):
        view_event_listener_classes.append(t)
        module_view_event_listener_classes.append(t)
    

    现在让我们看看需要引发事件时会发生什么。在我们的示例中,我们正在查看 on_modified 事件,该事件由第 566 行的 sublime_plugin.py 中的 on_modified 模块函数处理(但所有事件的工作方式相似):

    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(至少对于所展示的示例而言),并且可能还有其他方法可以实现相同的目标。

    【讨论】:

    • 哇。谢谢你的详细解释。对此,我真的非常感激。我想我应该开始阅读 sublime_plugin.py。就像您指出的那样,似乎没有一个很好的解决方案。我将不得不考虑这一点。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2012-02-26
    • 2020-06-06
    • 2017-05-12
    • 2016-01-07
    • 1970-01-01
    • 2017-05-24
    • 2013-02-02
    • 1970-01-01
    相关资源
    最近更新 更多