更新 QAction 的启用状态

updating enabled-status of QAction

几年前 Delphi 引入动作时,最大的优势之一是动作的状态 (enabled/disabled) 在事件循环中处理,而不是程序员考虑根据应用程序的所有状态更改更新这些状态。例如:

procedure TMyForm.SaveActionUpdate(Sender: TObject) begin (sender as TAction).enabled := self.contentHasChanged(); end;

而不是

procedure TMyForm.onContentChanging(sender: TObject) begin SaveAction.enabled := True; SaveAsAction.enabled := True; RevertAction ... etc. end;

现在我正在尝试使用 Qt 在 Python 中重建一些旧的 Delphi 程序,但到目前为止我还没有弄清楚如何启用 QActions 而不求助于显式设置它启用每当我的内容改变时。这样做的有效方法是什么?

在 C++ 中:

void MyForm::onSaveActionUpdateSlot() // must be a slot
{
    QAction* pAction = qobject_cast<QAction*>( sender() );
    if (pAction)  // if sent by QAction
       pAction->setEnbaled( this->contentHasChanged() );
}

我深入研究了 VCL,看看它是如何在那里实现的。我犹豫这样做是否是个好主意,因为这个框架的概念可能与 Qt 有很大不同,让我走上了错误的轨道而不是提供帮助。然而,对这个问题缺乏回应向我表明,Qt 操作可能无法以这种方式工作。可惜了。

Delphi 的 VCL 操作将自己注册到中央列表中的构造中,该列表在应用程序的空闲时间在事件循环中进行评估。这至少我发现可以通过使用以超时 0 启动的计时器在 Qt 中进行模拟。因此我得出了这个解决方法:

class HFClient(QMainWindow):

    def updateAction(self, action):
        action.setEnabled(False)

class ActionUpdater(QTimer):

    def __init__(self):
        super(ActionUpdater, self).__init__()
        self.members = {}
        self.timeout.connect(self.timerEvent)

    def add(self, action, updateProc):
        self.members[action] = updateProc

    def remove(self, action):
        del self.members[action] 

    def timerEvent(self, unused):
        # prevent being the last one to keep the object alive:
        done = [action for action in self.members if sys.getrefcount(action) < 5]
        for action in done:
            self.remove(action)

        # call registered method:
        for action, updateProc in self.members.items():
                updateProc(action)




if __name__ == '__main__':

    app = QApplication(sys.argv)
    w   = HFClient()
    w.show()

    updater = ActionUpdater()
    updater.start()

    a = QAction("save as", app)
    updater.add(a, w.updateAction)
#     del a        
    app.exec()

内存管理编程语言有点难以在注册对象完成后再次注销对象。对象很容易保持活动状态,因为它在寄存器中仍然有一个引用。我试图通过检查更新程序实例中的引用计数与引用数量来避免这种情况。不过,我会看重一个更好的方法。

可以创建一个 QAction 后代来注册自己(也可能是一个用于更新的信号槽组合)。

更新

要使用传统的信号槽机制,请从 Action like

class UpdatedAction(QAction):
    update = pyqtSignal(QAction)

    def getUpdated(self):
        self.update.emit(self)

弱引用解决了上面代码中的引用计数问题:

class ActionUpdater(QTimer):

    def __init__(self):
        super(ActionUpdater, self).__init__()
        self.members = []
        self.timeout.connect(self.timerEvent)

    def add(self, action):
        self.members.append(weakref.ref(action, self.remove))

    def remove(self, action):
        self.members.remove(action) 

    def timerEvent(self, unused):
        # call registered method:
        for action in self.members:
            action().getUpdated()