我在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);
}
...
答案 0 :(得分: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"
答案 1 :(得分:1)
您可以通过设置QWidget::focusPolicy = Qt::NoFocus来阻止窗口小部件获得焦点。
但是,这里有两个混合概念 - 聚焦控件(每个窗口)和活动窗口(每个桌面)。我认为在您正在描述的场景(一个撕下的弹出窗口)中,即使Qt没有设置聚焦控件,OS窗口管理器仍可能仍然会更改活动的顶级窗口。这将导致没有人有键盘焦点(这是一个有效的状态!)。
所以我认为对你的问题的完整答案将涉及一些非便携式位。我不知道你正在使用什么GUI环境,但我知道Win32的一些答案,所以我会坚持下去并希望它有用:
文章Win32 Activation and Focus中对MSDN上的Win32状态跟踪进行了很好的讨论。我不知道Qt做了什么来包装这个级别,所以你必须使用QWidget::nativeEvent或QCoreApplication::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 true
),同时保留其他所有事件(包括点击,因为我们没有使用MA_NOACTIVATEANDEAT
来阻止它)通常被处理成QEvents和Qt信号(最后是return false
)。
如果您需要进一步的低级访问权限(虽然我认为您不会这样做),另请参阅QWidget::effectiveWinId()和QWidget::windowHandle
答案 2 :(得分:0)
非常感谢Martin Gräßlin获得答案!
我的建议:查看KDE Plasma中的虚拟键盘代码:http://quickgit.kde.org/?p=kdeplasma-addons.git&a=blob&h=5628d6325afe57f85917dad865a07d4116335726&hb=a658d1e257cfca2a43c12714d026ec26f1fdb755&f=applets%2Fplasmaboard%2Fwidget.cpp
看起来键是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();
}
}
答案 3 :(得分:-1)
我想我找到了更好的方法!
只需使用this->setAttribute(Qt::WA_X11DoNotAcceptFocus);
并瞧!
示例:
MyVirtualKeyboard::MyVirtualKeyboard(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::MyVirtualKeyboard)
{
ui->setupUi(this);
this->setAttribute(Qt::WA_X11DoNotAcceptFocus);
}