2

我正在 QDockWidget 中创建一个简单的虚拟键盘...
当小部件停靠到 QMainWindow 时,选定的小部件(例如 qdoublespinbox)被突出显示,如果我单击虚拟键盘 clearFocus() 工作...

当 QDockWidget 浮动在窗口上方并且我单击一个按钮时,clearFocus 不起作用,我在 QMainWindow 中看不到焦点小部件...

如何强制 QDockWidget 根本没有任何焦点?

谢谢 :-)

这是代码:

// class MyVirtualKeyboard : public QDockWidget

void MyVirtualKeyboard::sendKey(Qt::Key key, Qt::KeyboardModifier mod)
{
    this->clearFocus();

    QMainWindow *w = dynamic_cast<QMainWindow *>(this->parent());
    if(w == NULL) return;

    QWidget *widget = w->focusWidget();

    QString repr = QKeySequence(key).toString();

    QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, key, mod, repr);
    QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, key, mod, repr);

    qDebug("%s", pressEvent->text().toAscii().data());

    MyApplication *app = MyApplication::myInstance();

    app->postEvent(widget, pressEvent);
    app->postEvent(widget, releaseEvent);
}

void MyVirtualKeyboard::on_BTN_1_clicked()
{
    sendKey(Qt::Key_1);
}

...
4

4 回答 4

2

clearFocus()通话应该是不必要的。您的停靠小部件及其所有小部件都必须具有该Qt::NoFocus策略。

下面的代码显示了您可以如何做到这一点。

截屏

// https://github.com/KubaO/stackoverflown/tree/master/questions/vkb-focus-18558664
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class Keyboard : public QDockWidget {
   Q_OBJECT
   QWidget m_widget;
   QGridLayout m_layout{&m_widget};
   QToolButton m_buttons[10];
   void sendKey(Qt::Key key, Qt::KeyboardModifier mod)
   {
      if (! parentWidget()) return;
      auto target = parentWidget()->focusWidget();
      if (! target) return;

      auto repr = QKeySequence(key).toString();
      auto pressEvent = new QKeyEvent(QEvent::KeyPress, key, mod, repr);
      auto releaseEvent = new QKeyEvent(QEvent::KeyRelease, key, mod, repr);
      qApp->postEvent(target, pressEvent);
      qApp->postEvent(target, releaseEvent);
      qDebug() << repr;
   }
   Q_SLOT void clicked() {
      auto key = sender()->property("key");
      if (key.isValid()) sendKey((Qt::Key)key.toInt(), Qt::NoModifier);
   }
public:
   explicit Keyboard(const QString & title, QWidget *parent = nullptr) : Keyboard(parent) {
      setWindowTitle(title);
   }
   explicit Keyboard(QWidget *parent = nullptr) : QDockWidget(parent) {
      int i{};
      for (auto & btn : m_buttons) {
         btn.setText(QString::number(i));
         btn.setProperty("key", Qt::Key_0 + i);
         m_layout.addWidget(&btn, 0, i, 1, 1);
         connect(&btn, SIGNAL(clicked()), SLOT(clicked()));
         btn.setFocusPolicy(Qt::NoFocus);
         ++i;
      }
      setWidget(&m_widget);
      setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
   }
};

int main(int argc, char ** argv)
{
   QApplication a(argc, argv);
   QMainWindow w;
   w.setCentralWidget(new QLineEdit);
   w.addDockWidget(Qt::TopDockWidgetArea, new Keyboard("Keyboard", &w));
   w.show();
   return a.exec();
}

#include "main.moc"
于 2013-09-02T16:29:36.387 回答
1

您可以通过设置QWidget::focusPolicy = Qt::NoFocus来防止小部件获得焦点。

但是,您在这里混合了两个概念 - 聚焦控件(每个窗口)和活动窗口(每个桌面)。我认为在您描述的场景中(一个撕掉的弹出窗口),即使 Qt 没有设置焦点控件,操作系统窗口管理器也可能仍会更改活动的顶级窗口。这将导致没有人拥有键盘焦点(这是一个有效状态!)。

所以我认为你的问题的完整答案将涉及一些非便携式位。我不知道你在什么 GUI 环境下工作,但我知道 Win32 的一些答案,所以我会继续下去,希望对你有用:

Win32

在 MSDN 上的Win32 Activation and Focus文章中对 Win32 的状态跟踪进行了很好的讨论。我不知道 Qt 做了什么来包装这个级别,所以你必须使用QWidget::nativeEventQCoreApplication::installNativeEventFilter来获取低级别事件。如果您可以对窗口进行子类化,我更喜欢前者,因为它更加独立。

bool FooWidget::nativeEvent(const QByteArray & eventType, void * message, long * result)
{
#ifdef Q_OS_WIN
    if(eventType == "windows_generic_MSG") {
        const MSG *msg = reinterpret_cast<MSG *>(message);
        if(msg->message == WM_MOUSEACTIVATE) {
            *result = MA_NOACTIVATE;
            return true;
        }
    }
#else
    #error Need platform-specific code to suppress click-activation
#endif
    return false;
}

应该阻止点击激活窗口MA_NOACTIVATE(Qt 信号正常(最后)。return trueMA_NOACTIVATEANDEATreturn false

如果您需要进一步的低级访问(尽管我认为您不会),另请参阅QWidget::effectiveWinId()QWidget::windowHandle

于 2013-09-02T16:37:20.523 回答
0

非常感谢Martin Graßlin的回答!

我的建议:查看 KDE Plasma 中的虚拟键盘代码:http: //quickgit.kde.org/ ?p=kdeplasma-addons.git&a=blob&h=5628d6325afe57f85917dad865a07d4116335726&hb=a658d1e257cfca2a43c12714d026ec26f1fdbapp%2fwigetlets.%2f1fdbapp755&fwigetlets.

看起来关键是 setWindowFlags(Qt::X11BypassWindowManagerHint) 和 setFocusPolicy(Qt::NoFocus)

MyVirtualKeyboard::MyVirtualKeyboard(QWidget *parent) :
    QDockWidget(parent),
    ui(new Ui::MyVirtualKeyboard)
{
    ui->setupUi(this);

    this->connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(topLevelChanged()));
}

void MyVirtualKeyboard::topLevelChanged()
{
    if(this->isWindow())
    {
        this->setWindowFlags(Qt::Popup | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
        this->setFocusPolicy(Qt::NoFocus);
        this->show();
    }
}
于 2013-09-03T07:42:26.510 回答
0

我想我找到了更好的方法!只需使用this->setAttribute(Qt::WA_X11DoNotAcceptFocus);,瞧!

例子:

MyVirtualKeyboard::MyVirtualKeyboard(QWidget *parent) :
    QDockWidget(parent),
    ui(new Ui::MyVirtualKeyboard)
{
    ui->setupUi(this);
    this->setAttribute(Qt::WA_X11DoNotAcceptFocus);
}
于 2013-09-05T10:06:36.453 回答