查找和删除侦听器

时间:2018-01-19 16:16:51

标签: matlab unit-testing

我正在尝试为可以在窗口模式或无头模式下运行的MATLAB应用程序设计单元测试。测试以无头模式运行程序,并尝试检测在处理期间是否打开任何窗口。

我的想法是将一个监听器附加到groot属性CurrentFigure,然后编写一个PostSet回调函数,该回调函数会增加一个计数器,以跟踪打开多少个窗口。最后,测试确保该值为0.对于记录,这似乎不起作用。即使我有ShowHiddenHandles'on',它似乎也没有捕获模态窗口。但是,这不是我的问题。

我的问题是,我在创建监听器的地方和我delete的地方之间的测试代码中有错误。现在,有一个监听器附加到groot,但监听器句柄变量被清除,所以每次我尝试打开一个窗口时都会产生非常奇怪的行为。测试对象打开,然后在侦听器回调时抛出错误。

如果已经从工作区中删除了对它的所有原始引用,我如何找到并删除附加到groot的侦听器?到目前为止,唯一有效的方法是重启MATLAB,但这似乎是一种低效的调试方法。

要重现错误,请创建以下测试类:

classdef MCVtest < matlab.unittest.TestCase
    % Minimal, Complete, Verifiable example

    properties
        numOfFiguresCreated = 0;
    end

    methods
        function figureCreatedListener(testCase)
            testCase.numOfFiguresCreated = testCase.numOfFiguresCreated + 1;
        end
    end

    methods (Test)
        function testFiles(testCase)
            %Create Listener for this particular input file.
            listener = addlistener(groot, 'CurrentFigure', 'PostSet', @testCase.figureCreatedListener); %#ok<NASGU>

            error('Well, this sucks...')

            % delete Listener for this input file
            delete(listener) %#ok<UNRCH>

            % Verify That no graphics objects were created at all.
            testCase.verifyEqual(testCase.numOfFiguresCreated, 0);

        end
    end

end

从命令行:

>> suite = matlab.unittest.TestSuite.fromClass(?MCVtest)
>> results = suite.run

错误之后:

>> figure
Error using MCVtest/figureCreatedListener
Too many input arguments.

Error in MCVtest>@(varargin)testCase.figureCreatedListener(varargin{:}) (line 17)
            listener = addlistener(groot, 'CurrentFigure', 'PostSet', @testCase.figureCreatedListener); %#ok<NASGU>

当然,listener已不存在,因此我无法将其删除。我已按此顺序尝试clearclear allclear classes,并且听众仍然存在。清除它的唯一方法(就我发现而言)是重启MATLAB。

2 个答案:

答案 0 :(得分:1)

您可能需要考虑使用较低级别的界面来创建和管理您的侦听器,这样您就不必再次解决此搁置的侦听器状态问题。您可以将对侦听器的唯一引用绑定到所需的函数或对象生命周期范围,这样您就不必显式删除侦听器,而是可以依赖引用计数在最后一个引用被销毁后删除侦听器。 / p>

https://www.mathworks.com/help/matlab/ref/event.listener.html

https://www.mathworks.com/help/matlab/ref/event.proplistener.html

这些接口允许您在创建后将侦听器对象作为LHS参数接收。然后,您可以决定是否存储侦听器,以便您可以根据需要管理其生命周期。

如果使用此接口,则可以避免显式使用delete,因为您可以将唯一且唯一的侦听器句柄的生命周期与您希望它存在的作用域相关联。

因此,具体而言,在同一个例子中:

lsnr = event.proplistener(groot,findprop(groot,'CurrentFigure'),'PostSet',@(hobj,evt) disp('fire'));

当lsnr超出范围时,对侦听器对象的最后一个引用将丢失,并且lsnr将自动删除。当然,您也可以明确地调用delete。

答案 1 :(得分:1)

您应该能够通过访问'AutoListeners__'(或任何其他图形)对象的未记录的Root属性来查找侦听器,该对象包含侦听器的单元数组。

例如:

a = addlistener(groot, 'CurrentFigure', 'PostSet', @(s,e)disp('hi'));
tmp = groot;
listeners = tmp.AutoListeners__;

这给了我们:

>> a == listeners{1}

ans =

  logical

   1

所以我们可以这样做:

for ii = 1:numel(listeners)
    delete(listeners{ii});
end

删除所有悬空的听众。

请注意,如果没有该对象的侦听器,则AutoListeners__不存在。

另请注意,MATLAB具有2 different listener implementationsaddlistener,它将侦听器绑定到对象,并在对象超出范围时被删除,{{3} },我们解除绑定,当侦听器超出范围时将被删除。

通过在单元测试中使用listener而不是addlistener,如果由于某种原因没有运行清理,则可以避免使用悬空侦听器。在这种情况下,由于错误。