5

我正在尝试设计一个带有用户可自定义热键的 Qt GUI 应用程序。我遇到的主要问题是如何在应用程序中同步热键,因为多个小部件/组件可能使用特定的热键(例如,复制)。

我目前的策略是使用一个引用类,它QKeySequence为每个不同的热键保存一个对象列表。每个小部件都必须有一种方法来引用这个主列表,并具有低级的自定义实现keyPressEvent,它将比较输入的键和热键。不过,我并不特别喜欢这种策略,因为它需要在每个小部件中进行大量的重新实现,并且感觉就像我正在尝试重新发明轮子。

我还尝试使用QAction可以在内部保存QKeySequence快捷方式的对象,然后使用它们来触发我可以使用插槽和信号处理的更高级别的事件。但是,我在这里遇到的主要问题是如何管理将信号路由到哪些插槽。

例如,假设我有 2 个打开的小部件,它们都可以接收copy动作信号。我可以将这两个的插槽连接到同一个信号,并利用单个更新点的快捷方式,但是事情变得混乱,因为只有活动的小部件应该作用于复制信号,而不是两个小部件。我可以重新实现focusOutEventandfocusInEvent处理程序以手动连接/断开插槽,但这似乎也遇到了上面我试图重新发明轮子并做比必要的更多工作的相同问题。

有没有更简单的方法解决这个问题?

4

1 回答 1

7

我认为这个问题没有特别简单/不乏味的解决方案,但是当我需要向我的应用程序添加用户可自定义的热键时,我是这样做的:

1) 从具有硬编码快捷键的应用程序开始,例如这样的代码:

QMenu * editMenu = new QMenu;
QAction * copyItem = menu->addAction(tr("Copy"), this, SLOT(CopyData()));
copyItem->setShortcut(tr("Ctrl+C"));

2) 创建一个如下所示的 GetKeySequence() 函数:

static QHash<QString, QKeySequence> _usersKeyPreferences;
static bool _usersKeyPreferencesLoaded = false;

QKeySequence GetKeySequence(const QString & keySequence, const QString & contextStr)
{
   if (_usersKeyPreferencesLoaded == false)
   {
      // Oops, time to load in the user's saved custom-key settings from a file somewhere
      _usersKeyPreferences = LoadUsersKeyPreferencesFromFile();
      _usersKeyPreferencesLoaded = true;  // so we'll only try to load the file once
   }
   if (_usersKeyPreferences.contains(contextStr)) 
   {
      return _usersKeyPreferences[contextStr];
   }
   else 
   {
      // No user preference specified?  Okay, fall back to using the 
      // hard-coded default key sequence instead.
      return QKeySequence(qApp->translate(contextStr, keySequence));
   }
}

3) 现在是乏味的部分:对所有代码以及任何你明确指定键序列的地方(如步骤 1 所示代码的第三行),用调用 GetKeySequence() 来包装它,像这样:

copyItem->setShortcut(GetKeySequence(tr("Ctrl+C"), tr("Edit_Menu|Copy")));

4)此时,您的程序的键序列将是可定制的;只需确保在 GUI 创建代码运行之前磁盘上存在密钥设置文件即可。这是我程序的键映射文件(我将其存储为简单的 ASCII 文本文件)的摘录:

Edit_Menu|Copy   = Ctrl+C
Edit_Menu|Cut    = Ctrl+X
Edit_Menu|Paste  = Ctrl+V
[... and so on for all other menu items, etc...]

...当然,这种方法的一个缺点是,一旦创建了 GUI,就不能“即时”修改键绑定(至少,在没有大量额外编码的情况下)。我的程序只需在用户单击“编辑键绑定”对话框中的“保存并应用”后关闭并重新创建所有窗口即可解决此问题。

5) 一个可选的进一步步骤(这是一些额外的工作,但从长远来看可以节省时间)是编写一个程序(或脚本),对程序代码库中的所有 .cpp 文件进行 greps 查找调用 GetKeySequence()代码。当它找到 GetKeySequence() 调用时,它会解析出调用的两个参数,并将它们打印为具有默认设置的键绑定文件中的一行。这很有用,因为您可以将此脚本作为自动构建的一部分,此后您将永远不必记住在向您添加新菜单项(或其他键序列说明符)时手动更新默认键设置文件程序。

无论如何,这对我来说效果很好。优点是您根本不必重构现有程序;您可以根据需要插入 GetKeySequence() ,同时保持程序的更大逻辑/结构完好无损。

于 2012-11-18T03:52:35.480 回答