Why doesn't my QDeclarativeItem get any mouse/keyboard events? - c++

This is a reduced down, minimum complete example demonstrating my problem:
I have an application which hosts a QDeclarativeView; file events.cpp:
#include <QApplication>
#include <QDeclarativeView>
#include "TestItem.h"
int main(int argc,char* argv[]) {
QApplication app(argc,argv);
qmlRegisterType<TestItem>("Testing",1,0,"Tester");
QDeclarativeView page;
page.setSource(QUrl("page.qml"));
Q_ASSERT(page.status()==QDeclarativeView::Ready);
page.show();
return app.exec();
}
That TestItem is a subclassed QDeclarativeItem defined in file TestItem.h:
#ifndef _TestItem_h_
#define _TestItem_h_
#include <iostream>
#include <QDeclarativeItem>
#include <QPainter>
class TestItem : public QDeclarativeItem {
Q_OBJECT
public:
TestItem() {
setFlag(QGraphicsItem::ItemHasNoContents,false);
std::cerr << "[TestItem created]";
}
void paint(QPainter* painter,const QStyleOptionGraphicsItem*,QWidget*) {
painter->drawLine(0,0,width(),height());
painter->drawLine(0,height(),width(),0);
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*) {
std::cerr << "[TestItem::mousePressEvent]";
}
void keyPressEvent(QKeyEvent*) {
std::cerr << "[TestItem::keyPressEvent]";
}
};
#endif
and the page.qml file loaded into the QDeclarativeView is simply:
import QtQuick 1.0
import Testing 1.0
Tester {
width: 200
height: 200
}
It's all built (with Qt 4.8 on Debian-Wheezy amd64) with qmake file
CONFIG += debug
QT += core gui declarative
TARGET = events
TEMPLATE = app
SOURCES += events.cpp
HEADERS += TestItem.h
and, once built, when I run ./events I get a window displaying the Tester's painted 'X', as expected:
and a [TestItem created] logged to the console. However, clicking within the window or pressing keys completely fails to invoke either of the mouse or key event handlers.
I'm completely mystified. Is some extra magic (in the C++ or QML domains) needed to get mouse/keyboard events routed to these sort of "plugin" QDeclarativeItem classes? I certainly don't have any problems defining a MouseArea in the QML file and having it do stuff to QML state, and the code this is reduced from has no problems with signals and slots interoperating between the C++ item and QML code... but when it comes to mouse/keyboard events, there's just no sign of them on the C++ side.

To get (left) mouse events, all that is needed is to add
setAcceptedMouseButtons(Qt::LeftButton);
in the TestItem constructor. This is a little surprising as the documentation for the inherited QGraphicsItem::setAcceptedMouseButtons says "By default, all mouse buttons are accepted", but maybe something else in the setup messes with the state.
To get keyboard events, setFocus(true) just needs to be invoked. The documentation seems to imply setFlag(QGraphicsItem::ItemIsFocusable,true) should also be called, but it doesn't actually seem to be necessary in my test.

Related

Removing titlebar from Qt window while keeping the default window borders

I'm trying to remove the default Windows titlebar from my Qt window (QT version 5.12.2 and using C++) while still keeping the window borders. I've more or less achieved this using the flag Qt::CustomizeWindowHint. However, this changes the window borders to white lines instead of the default borders.
Example of how the borders look AFTER I've applied the Qt::CustomizeWindowHint flag:
As you can see, these are not the normal Windows window borders.
How can I change/edit these boders (i.e. change their color) or how am I able to keep the default Windows window borders while removing the titlebar?
Here's a minimal reproducible example:
main.cpp:
int main(int argc, char* argv[]) {
QApplication application(argc, argv);
launcher mainWindow;
//debugChecks();
mainWindow.setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::MSWindowsFixedSizeDialogHint);
mainWindow.setWindowTitle("Test");
mainWindow.show();
return application.exec();
}
launcher.h
#pragma once
#include <QtWidgets/QMainWindow>
#include <QMouseEvent>
#include <QPoint>
#include "ui_launcher.h"
class launcher : public QMainWindow {
Q_OBJECT
public:
launcher(QWidget* parent = Q_NULLPTR);
private:
Ui::launcherClass ui;
void mousePressEvent(QMouseEvent* eventVar);
void mouseMoveEvent(QMouseEvent* eventVar);
int mouseClickX = 0;
int mouseClickY = 0;
};
launcher.cpp
#include "launcher.h"
launcher::launcher(QWidget* parent) : QMainWindow(parent) {
ui.setupUi(this);
}
void launcher::mousePressEvent(QMouseEvent* eventVar) {
mouseClickX = eventVar->x();
mouseClickY = eventVar->y();
}
void launcher::mouseMoveEvent(QMouseEvent* eventVar) {
move(eventVar->globalX() - mouseClickX, eventVar->globalY() - mouseClickY);
}
Your description isn't really clear. One should not be using a QMainWindow for a logon/in dialog. Having said that I created an application on Ubuntu 20.04 (should build fine for you as I used qmake). You can download the project zip here. When the application starts it looks like this:
After clicking on "golden" it looks like this:
After Green it looks like this:
After clicking on Freaky it looks like this:
Please note that FramelessWindowHint not only removes the title bar and system menu along with window frame, it also removes your ability to resize/drag/move the window.
My apologies for not taking out the StackOver1.pro.user file. Didn't think about it until just now. You will need to delete that or it could hose your build.
QPalette and QStyle for Border
Palette Can Change window styles and colors of the Application. and for Reference
https://doc.qt.io/archives/qt-5.7/qtwidgets-widgets-styles-example.html
You can do something like this
self.setWindowTitle("‎") # Python
‎
mainWindow.setWindowTitle(""); # C/C++
but instead of passing an empty string, pass an empty character. This one worked for me: https://emptycharacter.com/

can't close app from dialog box (Qt app)

Getting right to it I have a MainWindow and a dialog window which is executed if a condition is met but the problem is I can't get the app to quit if the cancel button from the dialog window is clicked. I've tried putting qApp->quit() in the slot function for the cancel button. I've tried connecting the cancel button slot to the predefined close slot for the MainWindow object via a clickme() signal from the dialog class. (as shown below)
qt application does not quit I read the answer to this question which I think got me close because it made me realize that I can't quit the app before showing the MainWindow but making that change didn't solve the problem. I even tried to explicitly emit the clickme() signal from cancel button slot but that actually caused the OS to throw a signal which threw an error at me saying "the inferior stopped because it received a signal from the operating system signal name: SIGSEGV
signal meaning: segmentation fault"
Here's my code:
Notice warning; // this is the object for the dialog window also all of this code is in main.cpp
warning.setModal(true);
QObject::connect(&warning, SIGNAL(clickme()), &warning, SLOT(on_Cancel_button_clicked()));
QObject::connect(&warning, SIGNAL(clickme()), &w, SLOT(close()));
warning.exec();
Also before that code is
MainWindow w;
w.show();
Also while writing this question I tried this
QObject::connect(&warning, SIGNAL(clickme()), qApp, SLOT(quit()));
But that still didn't work. If you need more info just let me know.
Update: I'm starting to think that the reason I'm having so much trouble with this connect signal/slot function is because it's not designed to connect two windows of two different classes and I should rework my app to do everything from the MainWindow class which is a shame because when I picture a GUI program I picture multiple windows connected to each other regardless of whether or not the object representing each window is from the same class as the others yet I have such a hard time trying do that with the QT framework when it comes to trying to connect objects of different classes.
Update: please forgive me. I assume that the code that I originally thought was the answer would work and took a break from working on the program before actually testing out that code. Going back to it now I discovered that it doesn't work. The code I'm referring to is the following
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
QObject::connect(&msg, &QMessageBox::rejected,
&app, &QApplication::quit); // this line doesn't work for me and I don't know why
QObject::connect(&msg, &QMessageBox::accepted, [&dlg]{
(new QLabel("I'm running")).show();
});
QApp->quit(); should work. Remove warning.setModal(true); The dialog becomes modal when you call exec(). SetModal(true) should be used with show() according to Qt docs. So this may be causing your problem.
I think I've found the problem.
Probably, you're calling exec() twice:
To enter the QApplicationevent loop
To execute the dialog.
Use show() instead of exec() for the dialog. You have an example below where you can check the signal/slot works well. In your application, you need the slot to close the window, but:
With the line of code dialog.exec();, the app keeps running. This is your issue.
With the line of code dialog.show();, the app stops.
By the way, I saw your last question update, but it is not correct. In fact, of course you can connect two different classes.
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QApplication>
#include <QMainWindow>
#include <QAbstractButton>
#include <QDebug>
#include "dialog.h"
class Window : public QMainWindow
{
Q_OBJECT
public:
Window()
{
dialog = new Dialog();
dialog->setText("Continue?");
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
auto onClick = [this]() {
auto role = dialog->buttonRole(dialog->clickedButton());
if (role == QMessageBox::NoRole) {
qDebug() << "QMessageBox::NoRole";
QApplication::quit();
}
if (role == QMessageBox::YesRole) {
qDebug() << "QMessageBox::YesRole";
}
};
QObject::connect(dialog, &QMessageBox::buttonClicked, onClick);
dialog->show(); // this must be show(), not exec()
}
virtual ~Window() { delete dialog; }
private:
Dialog *dialog;
public slots:
void windowSlot() { qDebug() << Q_FUNC_INFO;
close();
}
};
#endif // WINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QMessageBox>
class Dialog : public QMessageBox
{
Q_OBJECT
public:
Dialog() {}
virtual ~Dialog() {}
};
#endif // DIALOG_H
main.cpp
#include <QApplication>
#include <QtGui>
#include "window.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.setWindowTitle("window");
window.show();
return app.exec();
}
Update #1: a very interesting post.

Qt and dead keys in a custom widget

Issue
I'm unable to make dead keys work in my Qt program, while on the same system Qt applications (konsole or kmail for instance) are correctly processing them.
How to reproduce
testcase.pro
TEMPLATE = app
TARGET = testcase
INCLUDEPATH += .
QT += core widgets gui
HEADERS += testcase.hpp
SOURCES += testcase.cpp
testcase.hpp
#include <QWidget>
class TestWindow: public QWidget
{
Q_OBJECT
public:
TestWindow(QWidget* parent=0, Qt::WindowFlags flags=0);
void keyPressEvent(QKeyEvent* event);
};
testcase.cpp
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>
#include "testcase.hpp"
TestWindow::TestWindow(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_KeyCompression);
}
void TestWindow::keyPressEvent(QKeyEvent* event)
{
qDebug() << event;
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
TestWindow mainWin;
mainWin.show();
return app.exec();
}
Compile the above program (qmake; make), launch it. Dead keys give for instance:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 45, 0, ""e"", false, 1)
I was expecting
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
This would also be acceptable:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
What I've tried
I'm using a Ubuntu 14.10 system with locale fr_FR.UTF-8
I've tried
with Qt 5.3.0 and Qt 4.8.6 as provided on the system.
unseting XMODIFIERS (the default value is #im=ibus is being reported an issue by some)
changing the locale (again, google find reports were the part after the dot is an issue, I've tried the 4 variants UTF-8, utf-8, UTF8 and utf8)
with and without the setAttribute(Qt::WA_KeyCompression); in the constructor.
None changed my observable behavior.
Searching the web show mainly (only?) system related issues. As written above, I've tried the proposed solution, that doesn't solve my problem and the fact that other Qt applications I've tried are able to process the dead key makes me think I miss something in my code, especially that with a slightly more complex example, I'm able to use dead keys with Qt provided widgets (for instance a QLineEdit).
This is a partial answer, but as nobody answered that could be useful for someone later.
Dead keys are handled by input methods which are also handling the compose key and the way Chinese characters and others may be entered.
The widget must signify that it handles input method:
setAttribute(Qt::WA_InputMethodEnabled, true);
Then it has to overwrite two virtual members:
void inputMethodEvent(QInputMethodEvent*);
QVariant inputMethodQuery(Qt::InputMethodQuery) const;
To handle the dead keys and compose, it seems to be enough to have
void TestWindow::inputMethodEvent(QInputMethodEvent* event)
{
if (!event->commitString().isEmpty()) {
QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier,
event->commitString());
keyPressEvent(&keyEvent);
}
event->accept();
}
QVariant TestWindow::inputMethodQuery(Qt::InputMethodQuery) const
{
return QVariant();
}
but that's were my answer is incomplete:
I'm not sure it is really enough even for that
I'm sure it is not enough for more complex writing system and I lack the prerequisite to understand the documentation I've found.

How to detect global key sequence press in Qt?

I want to detect whether a key sequence is pressed and want to perform certain task on that event in Qt. Currently I can detect keypresses for certain widgets but how to detect global keypresses. By global I mean that even if the application is minimized or hidden , it should detect key press.
I tried making an eventFilter for the application, by first overloading QObject::eventFilter like this:
bool GlobalEventFilter::eventFilter(QObject *Object, QEvent *Event)
{
if (Event->type() == QEvent::KeyPress)
{
QKeyEvent *KeyEvent = (QKeyEvent*)Event;
switch(KeyEvent->key())
{
case Qt::Key_F1:
cout<<"F1 press detected"<<endl;
return true;
default:
break;
}
}
return QObject::eventFilter(Object,Event);
}
and then installing that object as the eventFilter for my application:
QApplication a(argc,argv);
a.installEventFilter(new GlobalEventFilter());
and I also tried the doing this:
QCoreApplication::instance()->installEventFilter(new GlobalEventFilter());
In both the cases I am able to detect key presses when my application window is open but it fails when window is minimized or hidden. How to solve this?
See the documentation of QKeyEvent:
Key events are sent to the widget with keyboard input focus when keys are pressed or released.
That means, if you wish to use QKeyEvent, you need to have the keyboard focus. Filtering those events does not change this conceptually either. I am not sure where you got the filtering idea from.
You might want to look into alternative solutions, for instance how it is implemented in kwin, etc. In general, be careful with this use case. It could do weird things without the end users noticing it.
You could take a look at this class:
QxtGlobalShortcut Class Reference
You could write something like this then:
main.cpp
#include <QxtGlobalShortcut>
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
class MyGlobalShortcutHandler : public QObject
{
Q_OBJECT
public:
explicit MyGlobalShortcutHandler(QObject *parent = 0)
: QObject(parent)
{
m_globalShortcut.setShortcut(QKeySequence("Ctrl+Shift+X"));
m_globalShortcut.setEnabled(true);
connect(&m_globalShortcut, SIGNAL(activated()), SLOT(handleGlobalShortcut()));
}
public slots:
void handleGlobalShortcut()
{
qDebug() << "Global shortcut handled";
}
private:
QxtGlobalShortcut m_globalShortcut;
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
QMainWindow mainWindow;
MyGlobalShortcutHandler myGlobalShortcut();
mainWindow.show();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += qxt
QXT = core gui
SOURCES += main.cpp
Build and Run
qmake-qt4 && make && ./main
This code should be working with Qt 4, at least. It prints out the debug statement for me. You could easily verify this on your end.
To get the libqxt library, please visit the following link:
http://dev.libqxt.org/libqxt/wiki/Home
This feature is not implemented in Qt. You can use Qxt. Qxt is an extension library for Qt providing a suite of cross-platform utility classes to add functionality not readily available in Qt. It has Global Shortcut (hot keys) which detects key presses even if the application is minimized.
After compiling and linking your application to Qxt, you can include QxtGlobalShortcut:
#include <QxtGlobalShortcut>
Example usage:
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));

Can't call slot or Q_INVOKABLE from QML in subclass of QQmlPropertyMap

I'm trying to test drive the QQmlPropertyMap class. It seems like it might work well for what I want, if I can subclass it. The documentation here even gives some rudimentary instructions on what to do for subclassing it. Said documentation also indicates that this class derives from QObject.
For what it's worth, I'm using QtCreator 2.6.1 on Qt 5.0.0 with QtQuick 2.0.
My main.qml:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
Text {
text: owner.field
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
owner.testFunc();
}
}
}
My main.cpp:
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "TestMap.h"
#include <QQmlContext>
int main(int argc, char *argv[])
{
TestMap* map = new TestMap();
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
QQmlContext* ctxt = viewer.rootContext();
ctxt->setContextProperty("owner", map);
viewer.setMainQmlFile(QStringLiteral("qml/TestMap/main.qml"));
viewer.showExpanded();
return app.exec();
}
My TestMap.h
#ifndef TESTMAP_H
#define TESTMAP_H
#include <QObject>
#include <QQmlPropertyMap>
#include <QDebug>
class TestMap: public QQmlPropertyMap // QObject
{
Q_OBJECT
public:
TestMap(QObject* parent = 0): QQmlPropertyMap(this, parent) // QObject(parent)
{
insert("field", "value"); // Comment this out
}
TestMap(const TestMap& value) { }
virtual ~TestMap() {}
public slots:
void testFunc()
{
qDebug() << "Success!";
}
};
Q_DECLARE_METATYPE(TestMap)
#endif
When I run, I get a window saying "value", as I'd expect. But when I click on the window, I get a console output saying
TypeError: Property 'testFunc' of object TestMap(0xaaa0b8) is not a function
I've looked for similar problems, but all the search results are about people that forgot to include the Q_OBJECT macro. It must be something I'm doing wrong in the code, because if I make all the changes indicated in the comments of the TestMap file (and leave the main.cpp and main.qml exactly as is), I get the qDebug message I expect.
I'm not sure whether I'm supposed to Q_DECLARE_METATYPE or not (I think the 2-arg protected constructor is supposed to do it for me), but it doesn't work either way.
For the record, the only things I have to change to get it to work are:
1) Derive from QObject instead of QQmlPropertyMap.
2) Change the constructor to:
TestMap(QObject* parent = 0): QObject(parent) {}
And that's it. Since it works when I don't change anything about the main.cpp, main.qml, or the slot itself, I have to conclude it's nothing wrong with those. Can anyone tell me what I'm doing wrong?
This is now fixed in Qt 5.1.0 and onwards. See the following codereview url for details:
https://codereview.qt-project.org/#change,57418