Make a floating QDockWidget unfocusable - qt

I'm creating a simple virtual keyboard in a QDockWidget...
When the widget is docked into the QMainWindow, the selected widget (for example a qdoublespinbox) is highlighted and if I click on the virtual keyboard clearFocus() works...
When the QDockWidget is floating above the window and I click a button, clearFocus doesn't work and I can't see the focused widget in QMainWindow...
How can I force the QDockWidget to not have any focus at all?
Thanks :-)
This is the code:
// 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);
}
...

The clearFocus() call should be unnecessary. Your dock widget and all of its widgets must have the Qt::NoFocus policy.
The code below shows how you might do it.
// 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"

You can prevent a widget from taking focus by setting QWidget::focusPolicy = Qt::NoFocus.
However, there are two concepts here that you're mixing - the focused control (per window), and the active window (per desktop). I think in the scenario you're describing (a torn-off popup window), the OS window manager is likely to still change the active top-level window even if Qt doesn't set a focused control. That will result in nobody having keyboard focus (which is a valid state!).
So I think a full answer to your question will involve some non-portable bits. I don't know what GUI environment you're working in, but I know some of the answer for Win32, so I'll keep going and hope that's useful:
Win32
There's a pretty good discussion of the state tracking for Win32 on MSDN in the article Win32 Activation and Focus. I'm not aware that Qt does anything to wrap this level, so you'd have to use QWidget::nativeEvent or QCoreApplication::installNativeEventFilter to get at the low-level event. If you can subclass the window, I'd prefer the former, since it's more self-contained.
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;
}
This should block the click from activating the window (MA_NOACTIVATE), and block Qt from processing it further (return true), while leaving other all events (including the the click, since we didn't use MA_NOACTIVATEANDEAT to block it too) to be processed into QEvents and Qt signals normally (return false at the end).
If you need further low-level access (though I don't think you will), see also QWidget::effectiveWinId() and QWidget::windowHandle

Thanks a lot to Martin Gräßlin for the answer!
My recommendation: check out the virtual keyboard code in KDE Plasma: http://quickgit.kde.org/?p=kdeplasma-addons.git&a=blob&h=5628d6325afe57f85917dad865a07d4116335726&hb=a658d1e257cfca2a43c12714d026ec26f1fdb755&f=applets%2Fplasmaboard%2Fwidget.cpp
Looks like the key is setWindowFlags(Qt::X11BypassWindowManagerHint) and 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();
}
}

I think I've found a better way to do it!
Just use this->setAttribute(Qt::WA_X11DoNotAcceptFocus); and voila!
Example:
MyVirtualKeyboard::MyVirtualKeyboard(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::MyVirtualKeyboard)
{
ui->setupUi(this);
this->setAttribute(Qt::WA_X11DoNotAcceptFocus);
}

Related

Global shortcut in QML

I am trying to make an application in QML (Qt 5.5) that is always running and shows up when the user presses alt+space.
I have tried using the Action class in QML, but it only works when the window has the focus, not when the window is not visible.
I've also tried QShortcut (which is not ideal, since my application is not based on QWidget) but I had no results.
Also using eventFilter on QApplication doesn't seem to work when the window is not visible.
Is there a way of doing it?
I've never implemented an app in QML but I think what you are looking for is the Global Shortcut Module (http://libqxt.bitbucket.org/doc/tip/qxtglobalshortcut.html).
It is a "A global shortcut triggers even if the application is not active."
If a system wide global shortcut is what you need, I don't recall Qt having anything to offer out of the box, much less QML.
You will have to resort to the platform specific APIs to get this thing done. Like for example on windows that would be the BOOL WINAPI RegisterHotKey() function.
I have resorted to use XGrab and create a subclass of QThread (because of the separate event loop) to integrate it with the Qt signals.
shortcutactivator.h
#ifndef SHORTCUTACTIVATOR_H
#define SHORTCUTACTIVATOR_H
#include <QThread>
class ShortcutActivator : public QThread
{
Q_OBJECT
public:
void run();
signals:
void activated();
public slots:
void end();
private:
bool terminate = false;
};
#endif // SHORTCUTACTIVATOR_H
shortcutactivator.cpp
#include "shortcutactivator.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
void ShortcutActivator::end() {
this->terminate = true;
}
void ShortcutActivator::run() {
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = Mod1Mask; // AnyModifier; // ControlMask | ShiftMask | AnyModifier;
int keycode = XKeysymToKeycode(dpy,XK_space);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode, keyboard_mode);
XGrabKey(dpy, keycode, modifiers | Mod2Mask , grab_window, owner_events, pointer_mode, keyboard_mode);
XGrabKey(dpy, keycode, modifiers | LockMask, grab_window, owner_events, pointer_mode, keyboard_mode);
XGrabKey(dpy, keycode, modifiers | LockMask | Mod2Mask, grab_window, owner_events, pointer_mode, keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
{
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
printf("Key pressed\n");
emit this->activated();
default:
break;
}
if(this->terminate)
break;
}
XCloseDisplay(dpy);
XUngrabKey(dpy,keycode,modifiers,grab_window);
XUngrabKey(dpy,keycode,modifiers | Mod2Mask,grab_window);
XUngrabKey(dpy,keycode,modifiers| LockMask,grab_window);
XUngrabKey(dpy,keycode,modifiers | LockMask | Mod2Mask,grab_window);
}

How to monitor changes to an arbitrary widget?

I am starting a QT5 application with a rather complex design based on Qt Widgets. It runs on Beagleboard with a touchscreen. I will have a rather weird local invention instead of the LCD display. It's a laser painting on acrylic plate. It has no driver yet. To actually update a screen I must create a screenshot of the window as bitmap, turn it to grayscale and feed to a proprietary library, which will handle the laser. It should look cute, when ready. Unfortunately, the laser blinks on update, so I cannot just make screenshots on timer, or it will be jerky like hell.
I need to run a function every time a meaningful update of GUI happens, while preferably ignore things like button being pressed and released. Is there some way to create a hook without subclassing every single Qt Widget I will use? The only way to do this I know is to override paintEvent of everything. I want a simpler solution.
Possible assumptions are: the application will be running under X server with dummy display, will be the only GUI app running. Some updates happen without user input.
The code below does it. It doesn't dig too deeply into the internals of Qt, it merely leverages the fact that backing store devices are usually QImages. It could be modified to accommodate OpenGL-based backing stores as well.
The WidgetMonitor class is used to monitor the widgets for content changes. An entire top-level window is monitored no matter which particular widget is passed to the monitor(QWidget*) method. You only need to call the monitor method for one widget in the window you intend to monitor - any widget will do. The changes are sent out as a QImage of window contents.
The implementation installs itself as an event filter in the target window widget and all of its children, and monitors the repaint events. It attempts to coalesce the repaint notifications by using the zero-length timer. The additions and removals of children are tracked automagically.
When you run the example, it creates two windows: a source window, and a destination window. They may be overlapped so you need to separate them. As you resize the source window, the size of the destination's rendition of it will also change appropriately. Any changes to the source children (time label, button state) propagate automatically to the destination.
In your application, the destination could be an object that takes the QImage contents, converts them to grayscale, resizes appropriately, and passes them to your device.
I do not quite understand how your laser device works if it can't gracefully handle updates. I presume that it is a raster-scanning laser that runs continuously in a loop that looks roughly like this:
while (1) {
for (line = 0; line < nLines; ++line) {
drawLine();
}
}
You need to modify this loop so that it works as follows:
newImage = true;
QImage localImage;
while (1) {
if (newImage) localImage = newImage;
for (line = 0; line < localImage.height(); ++line) {
drawLine(line, localImage);
}
}
You'd be flipping the newImage flag from the notification slot connected to the WidgetMonitor. You may well find out that leveraging QImage, and Qt's functionality in general, in your device driver code, will make it much easier to develop. Qt provides portable timers, threads, collections, etc. I presume that your "driver" is completely userspace, and communicates via a serial port or ethernet to the micro controller that actually controls the laser device.
If you will be writing a kernel driver for the laser device, then the interface would be probably very similar, except that you end up writing the image bitmap to an open device handle.
// https://github.com/KubaO/stackoverflown/tree/master/questions/surface-20737882
#include <QtWidgets>
#include <array>
const char kFiltered[] = "WidgetMonitor_filtered";
class WidgetMonitor : public QObject {
Q_OBJECT
QVector<QPointer<QWidget>> m_awake;
QBasicTimer m_timer;
int m_counter = 0;
void queue(QWidget *window) {
Q_ASSERT(window && window->isWindow());
if (!m_awake.contains(window)) m_awake << window;
if (!m_timer.isActive()) m_timer.start(0, this);
}
void filter(QObject *obj) {
if (obj->isWidgetType() && !obj->property(kFiltered).toBool()) {
obj->installEventFilter(this);
obj->setProperty(kFiltered, true);
}
}
void unfilter(QObject *obj) {
if (obj->isWidgetType() && obj->property(kFiltered).toBool()) {
obj->removeEventFilter(this);
obj->setProperty(kFiltered, false);
}
}
bool eventFilter(QObject *obj, QEvent *ev) override {
switch (ev->type()) {
case QEvent::Paint: {
if (!obj->isWidgetType()) break;
if (auto *window = static_cast<QWidget *>(obj)->window()) queue(window);
break;
}
case QEvent::ChildAdded: {
auto *cev = static_cast<QChildEvent *>(ev);
if (auto *child = qobject_cast<QWidget *>(cev->child())) monitor(child);
break;
}
default:
break;
}
return false;
}
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != m_timer.timerId()) return;
qDebug() << "painting: " << m_counter++ << m_awake;
for (auto w : m_awake)
if (auto *img = dynamic_cast<QImage *>(w->backingStore()->paintDevice()))
emit newContents(*img, w);
m_awake.clear();
m_timer.stop();
}
public:
explicit WidgetMonitor(QObject *parent = nullptr) : QObject{parent} {}
explicit WidgetMonitor(QWidget *w, QObject *parent = nullptr) : QObject{parent} {
monitor(w);
}
Q_SLOT void monitor(QWidget *w) {
w = w->window();
if (!w) return;
filter(w);
for (auto *obj : w->findChildren<QWidget *>()) filter(obj);
queue(w);
}
Q_SLOT void unMonitor(QWidget *w) {
w = w->window();
if (!w) return;
unfilter(w);
for (auto *obj : w->findChildren<QWidget *>()) unfilter(obj);
m_awake.removeAll(w);
}
Q_SIGNAL void newContents(const QImage &, QWidget *w);
};
class TestWidget : public QWidget {
QVBoxLayout m_layout{this};
QLabel m_time;
QBasicTimer m_timer;
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != m_timer.timerId()) return;
m_time.setText(QTime::currentTime().toString());
}
public:
explicit TestWidget(QWidget *parent = nullptr) : QWidget{parent} {
m_layout.addWidget(&m_time);
m_layout.addWidget(new QLabel{"Static Label"});
m_layout.addWidget(new QPushButton{"A Button"});
m_timer.start(1000, this);
}
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
TestWidget src;
QLabel dst;
dst.setFrameShape(QFrame::Box);
for (auto *w : std::array<QWidget *, 2>{&dst, &src}) {
w->show();
w->raise();
}
QMetaObject::invokeMethod(&dst, [&] { dst.move(src.frameGeometry().topRight()); },
Qt::QueuedConnection);
WidgetMonitor mon(&src);
src.setWindowTitle("Source");
dst.setWindowTitle("Destination");
QObject::connect(&mon, &WidgetMonitor::newContents, [&](const QImage &img) {
dst.resize(img.size());
dst.setPixmap(QPixmap::fromImage(img));
});
return app.exec();
}
#include "main.moc"

Sliding OSX Dock type "sidebar" rather embedded QDockWidget type?

I want to make a sliding "sidebar" similar to the functionality of the OSX "Dock" (e.g. mouse passes edge of screen and Dock slides out). I've been playing around with QDockWidget but since that is embedded in the window layout, it causes everything to shift when it becomes visible.
Can someone suggest a way to implement this?
Doesn't need to float (as a separate window/tool bar)
Should scale to window height (e.g. window can be fullscreen or default size)
Doesn't need to slide (animate) if that is complicated.
I'm new to Qt and so don't want to over-think this. Is this just a matter of a custom widget or should I be looking at a borderless window? The custom widget approach seems right but I don't know how to specify that it overlay other window content and also scale if the window scales.
QDockWidget has nothing to do with what you want - behaviorally. Just because it's called a Dock widget doesn't mean it's the same "Dock" concept as in OS X. It merely means that it docks somewhere. QDockWidget's documentation quite explicitly explains what is meant by the docking behavior.
The code below implements the behavior you seem to want. Whether it's good design or not is arguable. The reason the code is "convoluted" seems to hint that nobody is expected to come up with such a UI design. What's wrong with actually clicking a button somewhere to display the slider window?
The code works under both Qt 4.8 and 5.1.
Note: This begs to be implemented in Qt Quick 2. That's what it was designed for :) Of course Qt 4.6+ improved the behavior of the QWidget-moving animations, and Qt 5 does further tweaks, but really this code smells bad and there's a good reason it does: QWidget API, while powerful, ultimately encapsulates a set of APIs that date to 1984 when the original Macintosh was released. There's only so much you can do when you have to composite results from a bunch of stacked painters. In Qt Quick, the rendering is done by the GPU. The animation amounts to passing a couple of new floats to the GPU to update a single transformation matrix. That's it.
#include <QApplication>
#include <QWidget>
#include <QGridLayout>
#include <QLabel>
#include <QPainter>
#include <QGradient>
#include <QMouseEvent>
#include <QPropertyAnimation>
class Slider : public QWidget {
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
QPainter p(this);
QLinearGradient g(QPointF(0,0), QPointF(rect().bottomRight()));
g.setColorAt(0, Qt::blue);
g.setColorAt(1, Qt::gray);
p.setBackground(g);
p.eraseRect(rect());
p.setPen(Qt::yellow);
p.setFont(QFont("Helvetica", 48));
p.drawText(rect(), "Click Me To Hide");
}
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE {
hide();
}
public:
explicit Slider(QWidget *parent = 0) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
};
class Window : public QWidget {
QGridLayout m_layout;
Slider m_slider;
QLabel m_label;
QPropertyAnimation m_animation;
public:
explicit Window(QWidget *parent = 0, Qt::WindowFlags f = 0) :
QWidget(parent, f),
m_layout(this),
m_slider(this),
m_animation(&m_slider, "pos")
{
setMouseTracking(true);
m_layout.addWidget(&m_label);
m_slider.hide();
m_slider.setMouseTracking(false);
m_animation.setStartValue(QPoint(-width(), 0));
m_animation.setEndValue(QPoint(0, 0));
m_animation.setDuration(500);
m_animation.setEasingCurve(QEasingCurve::InCubic);
}
void leaveEvent(QEvent *) {
if (window() && QCursor::pos().x() <= window()->geometry().topLeft().x()) {
showSlider();
}
}
void childEvent(QChildEvent * ev) {
if (ev->added() && ev->child()->isWidgetType()) {
ev->child()->installEventFilter(this);
static_cast<QWidget*>(ev->child())->setMouseTracking(true);
}
}
bool event(QEvent * ev) {
eventFilter(this, ev);
return QWidget::event(ev);
}
bool eventFilter(QObject *, QEvent * ev) {
if (ev->type() == QEvent::MouseMove) {
auto pos = QCursor::pos();
if (window() && window()->isFullScreen()) {
if (pos.x() <= window()->geometry().topLeft().x()) {
showSlider();
}
}
m_label.setText(QString("%1, %2").arg(pos.x()).arg(pos.y()));
}
return false;
}
void resizeEvent(QResizeEvent *) {
m_slider.resize(size());
m_animation.setStartValue(QPoint(-width(), 0));
}
Q_SLOT void showSlider() {
if (m_slider.isVisible() || (window() && qApp->activeWindow() != window())) return;
m_slider.raise();
m_slider.show();
m_animation.start();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}

How to change a parent widget's background when a child widget has focus?

I would like to highlight a QFrame, if one of it's child widgets has focus (so the users know where to look for the cursor ;-)
using something along
ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");
highlights the QFrame when I click on it, but it loses its focus once one of its child widgets is selected.
Possible approaches:
I could connect() QApplication::focusChanged(old,now) and check each new object if it is a child of my QFrame, but this gets messy.
I could also subclass each child widget and reimplement focusInEvent()/focusOutEvent() and react on that, but with a lot of different widgets, this is also a lot of work.
Is there a more elegant solution?
Well, you can extend QFrame to make it listen on focus change of its children widgets.
Or you can also install an event filter on children widgets to catch QFocusEvent.
Here is an example:
MyFrame.h
#ifndef MYFRAME_H
#define MYFRAME_H
#include <QFrame>
class MyFrame : public QFrame
{
Q_OBJECT
public:
explicit MyFrame(QWidget* parent = 0, Qt::WindowFlags f = 0);
void hookChildrenWidgetsFocus();
protected:
bool eventFilter(QObject *object, QEvent *event);
private:
QString m_originalStyleSheet;
};
#endif // MYFRAME_H
MyFrame.cpp
#include <QEvent>
#include "MyFrame.h"
MyFrame::MyFrame(QWidget *parent, Qt::WindowFlags f)
: QFrame(parent, f)
{
m_originalStyleSheet = styleSheet();
}
void MyFrame::hookChildrenWidgetsFocus()
{
foreach (QObject *child, children()) {
if (child->isWidgetType()) {
child->installEventFilter(this);
}
}
}
bool MyFrame::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn) {
setStyleSheet("background-color: #FFFFCC;");
} else if (event->type() == QEvent::FocusOut) {
setStyleSheet(m_originalStyleSheet);
}
return QObject::eventFilter(object, event);
}
MainWindow.cpp
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLineEdit>
#include "MyFrame.h"
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle(tr("Test"));
MyFrame *frame1 = new MyFrame(this);
frame1->setLayout(new QVBoxLayout());
frame1->layout()->addWidget(new QLineEdit());
frame1->layout()->addWidget(new QLineEdit());
frame1->layout()->addWidget(new QLineEdit());
frame1->hookChildrenWidgetsFocus();
MyFrame *frame2 = new MyFrame(this);
frame2->setLayout(new QVBoxLayout());
frame2->layout()->addWidget(new QLineEdit());
frame2->layout()->addWidget(new QLineEdit());
frame2->layout()->addWidget(new QLineEdit());
frame2->hookChildrenWidgetsFocus();
QHBoxLayout *centralLayout = new QHBoxLayout();
centralLayout->addWidget(frame1);
centralLayout->addWidget(frame2);
QWidget *centralWidget = new QWidget();
centralWidget->setLayout(centralLayout);
setCentralWidget(centralWidget);
}
I believe the both answers you were given are wrong. They work for simple cases but are extremely fragile and clumsy. I believe that the best solution is what you actually suggested in your question. I would go for connecting to QApplication::focusChanged(from, to). You simply connect your main frame object to this signal and in the slot you check if the to object (the one which received focus) is a child of your frame object.
Frame::Frame(...)
{
// ...
connect(qApp, &QApplication::focusChanged, this, &Frame::onFocusChanged);
// ...
}
// a private method of your Frame object
void Frame::onFocusChanged(QWidget *from, QWidget *to)
{
auto w = to;
while (w != nullptr && w != this)
w = w->parentWidget();
if (w == this) // a child (or self) is focused
setStylesheet(highlightedStylesheet);
else // something else is focused
setStylesheet(normalStylesheet);
}
The advantage is obvious. This code is short and clean. You connect only one signal-slot, you do not need to catch and handle events. It responds well to any layout changes done after the object is created. And if you want to optimize away unnecessary redrawing, you should cache the information whether any child is focused and change the stylesheet only and only if this cached value gets changed. Then the solution would be prefect.
First, create a simple subclass of QFrame which reimplements the eventFilter(QObject*, QEvent*) virtual function:
class MyFrame : public QFrame {
Q_OBJECT
public:
MyFrame(QWidget *parent = 0, Qt::WindowFlags f = 0);
~MyFrame();
virtual bool eventFilter(QObject *watched, QEvent *event);
};
Use MyFrame instead of QFrame to contain your widgets. Then, somewhere in your code where you create the widgets contained in MyFrame, install an event filter on those widgets:
// ...
m_myFrame = new MyFrame(parentWidget);
QVBoxLayout *layout = new QVBoxLayout(myFrame);
m_button = new QPushButton("Widget 1", myFrame);
layout->addWidget(m_button);
m_button->installEventFilter(myFrame);
//...
At that point, MyFrame::eventFilter() will be called before any event is delivered to the widget, letting you act on it before the widget is aware of it. Within MyFrame::eventFilter(), return true if you want to filter the event out (i.e. you don't want the widget to process the event), or return false otherwise.
bool MyFrame::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_button) { // An event occured on m_button
switch (event -> type()) {
case QEvent::FocusIn:
// Change the stylesheet of the frame
break;
case QEvent::FocusOut:
// Change the stylesheet back
break;
default:
break;
}
}
return false; // We always want the event to propagate, so always return false
}

Need help on Event handling to signalling in QTcreator

So basically what I am trying to do is the following:
I want to create a directional arrows pad on the screen. When the user presses the up or 8 key, the UI should react as if I clicked the up button. I have googled and searched all over, but as I just started using QTCreator (and C++), I am very inexperienced and any help would be appreciated.
So far I have
class GamePadWidget : public QWidget
{
public:
GamePadWidget(QWidget *parent = 0);
protected:
virtual void keyPressEvent(QKeyEvent *event);
};
GamePadWidget::GamePadWidget(QWidget *parent)
: QWidget(parent)
{
int buttonWidth = 75;
int buttonHeight = 75;
QPushButton *down = new QPushButton(("Y-"), this);;
down->setGeometry(100, 200, 100, 100);
QIcon downicon;
downicon.addFile(QString::fromUtf8("C:/arrows/Aiga_downarrow.png"), QSize(),QIcon::Normal, QIcon::Off);
down->setIcon(downicon);
down->setIconSize(QSize(buttonWidth,buttonHeight));
down->setFocusPolicy(Qt::NoFocus);
QPushButton *up = new QPushButton(("Y+"), this);;
up->setGeometry(100, 50, 100, 100);
QIcon upicon;
upicon.addFile(QString::fromUtf8("C:/arrows/Aiga_uparrow.png"), QSize(),QIcon::Normal, QIcon::Off);
up->setIcon(upicon);
up->setIconSize(QSize(buttonWidth,buttonHeight));
up->setFocusPolicy(Qt::NoFocus);
}
void GamePadWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_8 || event->key() == Qt::Key_Up ) {
printf("key event in board");
}
else if (event->key() == Qt::Key_9 || event->key() == Qt::Key_Down ) {
qApp->quit();
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
GamePadWidget widget;
widget.show();
return app.exec();
}
with my current code, if I press down or 2, the app exits as expected, yet here is part in which I am stuck at.
I want the same functionality as if I pressed the down (or up key), the pushbutton should light up briefly then shoot off a signal to who knows where
I realize it should have something to do with connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
but cannot quite wrap my mind / find it.
Thank you for your time.
You can call a slot on an object as if it was a normal method (which it is as far as C++ is concerned). Obv you'll need to make your pushButton a member though, so you have access to it outside of the constructor.
Then yes, just connect the button's clicked() signal to the app's quit() slot. The code below should work for you (not tested though):
GamePadWidget::GamePadWidget(QWidget *parent)
: QWidget(parent)
{
...
mDownButton = new QPushButton(("Y-"), this);;
...
connect(mDownButton, SIGNAL(clicked()), qApp, SLOT(quit()));
}
void GamePadWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Down ) {
qDebug() << "Down key pressed";
mDownButton.click();
}
}

Categories

Resources