如何在处理所有其他信号之前阻止信号?

时间:2018-02-01 09:04:53

标签: python qt pyqt

我正在开发一个提供图表网格的Qt小部件(每个都是一个QWidget)。我想在所有情节上同步“keepAspectRatio”政策。

如果我调用sigKeepAspectRatioChanged方法广播政策(setKeepDataAspectRatio(bool)True)及其False,则每个单独的地图都会发出(row, column)信号网格中的坐标。

我的复合小部件会侦听其所有图,当其中一个更改其宽高比策略(单击工具按钮)时,它需要将其传播到所有其他图。

这是我尝试过的,但它会导致无数RuntimeError: maximum recursion depth exceeded in cmp个错误:

    def _onKeepAspectRatioChanged(self, isKeepAspectRatio, row, col):
       """If any plot changes its keepAspectRatio policy,
        apply it to all other plots."""
        print("received sigKeepAspectRatioChanged from plot %d, %d" % (row, col))
        self.blockSignals(True)
        for r, c in self._plots:
            if not (r, c) == (row, col):
                self._plots[(r, c)].plot.setKeepDataAspectRatio(isKeepAspectRatio)
        qt.QApplication.instance().processEvents()
        self.blockSignals(False)

有关如何正确执行此操作的任何想法?

顺便说一下,对每个单独情节的引用都保存在字典中(self. _plots),而dict键是(r, c)坐标元组。

2 个答案:

答案 0 :(得分:2)

您真正关心的是该方法未重新输入。信号和插槽只是它发生的渠道。因此:保护相关方法免于递归。它比断开和恢复信号槽连接效率高出一个数量级。基本上:

def _method(self, ...):
  if self.in_method:
    return
  try:
    self.in_method = True
    ...
  finally:
    self.in_method = False

这当然可以包装在装饰者中:

@norecursion
def _onKeepAspectRatioChanged(self, isKeepAspectRatio, row, col):
  ...

来自this blog post的装饰者:

def norecursion(default=None):
  '''Prevents recursion into the wrapped function.'''
  def entangle(f):
    def inner(*args, **kwds):
      if not hasattr(f, 'callcount'):
        f.callcount = 0
      if f.callcount >= 1:
        return default
      else:
        f.callcount += 1
        x = f(*args, **kwds)
        f.callcount -= 1
        return x
    return inner
  return entangle

答案 1 :(得分:0)

我找到了一个有效的解决方案,但它可能不是最有效的解决方案:我断开所有单独的信号,然后在设置所有策略后重新连接。

def _onKeepAspectRatioChanged(self, isKeepAspectRatio, row, col):
    with self._disconnectAllAspectRatioSignals():
        for r, c in self._plots:
            self._plots[(r, c)].plot.setKeepDataAspectRatio(isKeepAspectRatio)

@contextlib.contextmanager
def _disconnectAllAspectRatioSignals(self):
    for r, c in self._plots:
        self._plots[(r, c)].sigKeepAspectRatioChanged.disconnect(
            self._onKeepAspectRatioChanged)
    yield
    for r, c in self._plots:
        self._plots[(r, c)].sigKeepAspectRatioChanged.connect(
            self._onKeepAspectRatioChanged)