I am new to QT.
How can I press and release a button in Qt ?
In java I do the below program to control key events ?
Robot r = new Robot();
r.keyPress(KeyEvent.VK_ENTER);
r.keyRelease(KeyEvent.VK_ENTER);
I want to press a key in keyboard programatically.
But , How can I do the same thing in QT ?
You can either create QKeyEvent and send them to the application using QApplication::sendEvent(). Or if you want higher level API, you can build your application with QtTest module and use keyClick functions. See https://doc.qt.io/qt-6/qtest.html
In Qt, key presses are handled by the Event System. Like other languages/frameworks events are encapsulated in an object, in Qt's case, QEvent. All subclasses of QObject have a virtual QObject::event(QEvent *e) method to handle event objects sent to it. This method does not directly handle the event, but calls the object's appropriate event handler based on the QEvent::Type enum. In the case of key presses, the QEvent::type() method returns QEvent::KeyPress.
While most events are handled internally without programmer intervention, you may send events manually using the QCoreApplication class or its subclass QGuiApplication. An instance of one of these classes is typically instantiated in the boilerplate main.cpp file created when you generate a new project with Qt Creator. These classes have access to the methods QCoreApplication::sendEvent(QObject *receiver, QEvent *event), which sends an event directly to receiver, and QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority), which sends the event to Qt's event queue to be processed later.
I've created a project to demonstrate this functionality. This app just displays a plain rectangle which can be either red or blue. The rectangle only switches colors when it receives a QKeyEvent indicating that the C key was pressed. Below the rectangle is a button which programmatically produces this event and sends it to the rectangle's widget. The project went on a bit long and is a bit messy, but I hope it helps some.
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Here I modify the boilerplate code to allow MainWindow w to have access
// to QApplication a so that a widget in MainWindow w can use postEvent()
MainWindow w(nullptr, &a);
w.show();
return a.exec();
}
mainwindow.h
#include <QCoreApplication>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr, QCoreApplication* app = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QLabel>
#include "keypressacceptor.h"
#include "keypressgenerator.h"
MainWindow::MainWindow(QWidget *parent, QCoreApplication *app)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGridLayout* layout = new QGridLayout(this);
KeyPressAcceptor* kpa = new KeyPressAcceptor(this);
KeyPressGenerator* kpg = new KeyPressGenerator();
kpg->registerReceiver(kpa);
kpg->registerApp(app);
layout->addWidget(kpa);
layout->addWidget(kpg);
centralWidget()->setLayout(layout);
}
MainWindow::~MainWindow()
{
delete ui;
}
keypressacceptor.h
#include <QWidget>
class KeyPressAcceptor : public QWidget
{
Q_OBJECT
public:
explicit KeyPressAcceptor(QWidget *parent = nullptr);
bool handleKeyPress(const int &key);
protected:
bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
private:
QColor m_color;
};
keypressacceptor.cpp
#include "keypressacceptor.h"
#include <QEvent>
#include <QKeyEvent>
#include <QPainter>
KeyPressAcceptor::KeyPressAcceptor(QWidget *parent)
: QWidget{parent}
, m_color(QColor(220, 20, 20))
{
// Setting focus policy so that the widget can accept focus.
// This is necessary to process key press events.
setFocusPolicy(Qt::StrongFocus);
}
bool KeyPressAcceptor::handleKeyPress(const int &key)
{
// This method performs some arbitrary action, in this case changing a
// color, to indicate that a key press has been processed.
switch (key) {
case Qt::Key_C:
// If the "C" key was pressed, switch m_color
if (m_color == QColor(220, 20, 20)) {
m_color = QColor(20, 20, 220);
} else {
m_color = QColor(220, 20, 20);
}
// Call update() to tell Qt to repaint this widget once Qt has entered
// the main event loop
update();
return true;
default:
return false;
}
}
bool KeyPressAcceptor::event(QEvent *event)
{
switch (event->type()) {
case QEvent::KeyPress:
// If the received event is of type QEvent::KeyPress, then cast the
// variable event to type QKeyEvent*, then use the event's key()
// method to pass as an argument to this class's handleKeyPress()
// method.
return handleKeyPress(static_cast<QKeyEvent*>(event)->key());
// Note! This overrides QWidget's default behavior upon receiving a
// QKeyEvent event
default:
// Otherwise, be sure to use the class's superclass to process any
// other events.
return QWidget::event(event);
}
}
void KeyPressAcceptor::paintEvent(QPaintEvent *event)
{
// Don't need to use the event parameter in this implementation.
Q_UNUSED(event)
// Want to draw a rectangle centered in the widget whose height is half
// the widget's height and whose width is half the widget's width.
// The color of the rectangle is determined by m_color.
// First define the rectangle using the height and width properties of
// QWidget to determine the rectangle's height, width, and coordinates of
// top left corner.
QRect rect(width() / 4, height() / 4, // Coordinates of top left corner
width() / 2, height() / 2); // Width and height
// Create a QPainter object to paint with
QPainter painter(this);
// Set pen and brush for rectangle's outline and fill respectively.
painter.setPen(QColor(0,0,0)); // Black 1px pen
painter.setBrush(QBrush(m_color)); // Solid fill of color m_color
// Draw the rectangle
painter.drawRect(rect);
}
keypressgenerator.h
#include <QCoreApplication>
#include <QPushButton>
#include <QObject>
class KeyPressGenerator : public QPushButton
{
Q_OBJECT
public:
explicit KeyPressGenerator(QWidget *parent = nullptr);
void registerApp(QCoreApplication* app);
void registerReceiver(QObject* receiver);
public slots:
void generateKeyPress();
private:
QCoreApplication* m_app;
QObject* m_receiver;
};
keypressgenerator.cpp
#include "keypressgenerator.h"
#include <QCoreApplication>
#include <QKeyEvent>
KeyPressGenerator::KeyPressGenerator(QWidget *parent)
: QPushButton{parent}
, m_app(nullptr)
, m_receiver(nullptr)
{
setText("Push Button to Send C Key Press");
// Connect clicked signal to generateKeyPress so when button is clicked
// a programmatically generated keypress is sent to m_receiver
connect(this, &KeyPressGenerator::clicked,
this, &KeyPressGenerator::generateKeyPress);
}
void KeyPressGenerator::registerApp(QCoreApplication *app)
{
m_app = app;
}
void KeyPressGenerator::registerReceiver(QObject *receiver)
{
m_receiver = receiver;
}
void KeyPressGenerator::generateKeyPress()
{
if (m_app == nullptr || m_receiver == nullptr) return;
// Generate the key press event. Check documentation for an explanation of
// the constructor's parameters.
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_C, Qt::NoModifier);
m_app->postEvent(m_receiver, event);
}
Related
I'm working on a simple video looping app and would like to know if there is a way to add a volume slider control to the fullscreen mode of QVideoWidget.
I want the app to show a small QVideoWidget at startup. Then after double clicking the video widget, I want the video to be shown at fullscreen with an additional volume slider.
Currently I have a code based on the QMultimedia example which looks like this:
In videowidget.h
#ifndef VIDEOWIDGET_H
#define VIDEOWIDGET_H
#include <QVideoWidget>
class VideoWidget : public QVideoWidget
{
Q_OBJECT
public:
VideoWidget(QWidget *parent = 0);
protected:
void keyPressEvent(QKeyEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
};
#endif // VIDEOWIDGET_H
the videowidget.cpp
void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
setFullScreen(!isFullScreen());
event->accept();
}
One solution I could think of is to create a base widget to draw another videowidget and a slider and load it in the double click event, but I have difficulty implementing it. I don't have a clue how to transfer the current playing states of the media player to the new video widget.
#include <QApplication>
#include <QMouseEvent>
#include <QVideoWidget>
#include <QSlider>
#include <QMediaPlayer>
#include <QFileDialog>
/**
* #brief The VideoWidget class
*
* VideoWidget contains QSlider & QVideoWidget
*/
class VideoWidget : public QWidget
{
Q_OBJECT
QVideoWidget* videoWidget;
QSlider* slider;
public:
VideoWidget() :
videoWidget(new QVideoWidget(this)),
slider(new QSlider(Qt::Horizontal, this))
{
slider->hide();
slider->move(10, 10); // Put slider where you want.
slider->setRange(0, 100);
const QMediaContent meida(QFileDialog::getOpenFileUrl());
QMediaPlayer* player = new QMediaPlayer(this);
player->setVideoOutput(videoWidget);
player->setMedia(meida);
player->play();
slider->setValue(player->volume());
connect(slider, &QSlider::valueChanged, player, &QMediaPlayer::setVolume);
}
virtual ~VideoWidget() {}
protected:
/**
* #brief changeEvent
* #param event
*
* Listen to window state change event,
* and show the slider only when the state is WindowFullScreen.
*/
void changeEvent(QEvent* event) {
if(event->type() == QEvent::WindowStateChange)
slider->setVisible(windowState() == Qt::WindowFullScreen);
QWidget::changeEvent(event);
}
/**
* #brief resizeEvent
* #param event
*
* Make videoWidget fill the parent widget.
*/
void resizeEvent(QResizeEvent* event) {
videoWidget->resize(size());
event->accept();
}
void mouseDoubleClickEvent(QMouseEvent *event) {
if(!isFullScreen())
showFullScreen();
else
showNormal();
event->accept();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
VideoWidget w;
w.resize(500, 400);
w.show();
return a.exec();
}
#include "main.moc"
//oneLed.h
#pragma once
#include<QPushButton>
class oneLed :public QPushButton
{
Q_OBJECT
public:
oneLed(QWidget* parent = 0);
protected:
void doPainting();
};
#include"oneLed.h"
#include<QPainter>
oneLed::oneLed(QWidget* parent)
:QPushButton(parent)
{
connect(this, &QPushButton::clicked, this, &oneLed::doPainting);
}
void oneLed::doPainting()
{
QPainter painter(this);
//painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(QBrush("#888"), 1));
painter.setBrush(QBrush(QColor("#888")));
painter.drawEllipse(0, 0, this->width(), this->height());
//painter.drawEllipse(0, 0, 30, 30);
}
//main.cpp
#include"oneLed.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
oneLed w;
w.resize(100, 500);
w.show();
return a.exec();
}
I want to achieve the following effect:
When I clicked on the oneLed object, A circle appears at the position of the oneled object. When I click on the oneLed object again, the circle disappears.
But in fact when I click on the oneLed object, the circle doesn't appear.
I guess you got it wrong. What happens in your code is:
the button is clicked and your doPainting slot is called
you do your custom painting
the actual button paint event is triggered by Qt main event loop and overwrites your painting
You need to override the paintEvent method.
In your custom slot, raise a boolean flag that indicates the button has been pressed.
void oneLed::slotClicked()
{
m_clicked = !m_clicked;
}
Then do something like this:
void oneLed::paintEvent(QPaintEvent *event)
{
// first render the Qt button
QPushButton::paintEvent(event);
// afterward, do custom painting over it
if (m_clicked)
{
QPainter painter(this);
painter.setPen(QPen(QBrush("#888"), 1));
painter.setBrush(QBrush(QColor("#888")));
painter.drawEllipse(0, 0, this->width(), this->height());
}
}
The method that you implement is paintEvent, in the slot that doPainting you must change a flag and call the update() method.
Important: The update method calls paintEvent.
oneLed.h
#ifndef ONELED_H
#define ONELED_H
#include <QPushButton>
class oneLed : public QPushButton
{
Q_OBJECT
public:
oneLed(QWidget* parent = 0);
protected:
void paintEvent(QPaintEvent * event);
private slots:
void doPainting();
private:
bool state;
};
#endif // ONELED_H
oneLed.cpp
#include "oneled.h"
#include <QPainter>
oneLed::oneLed(QWidget *parent):QPushButton(parent)
{
state = false;
connect(this, &QPushButton::clicked, this, &oneLed::doPainting);
}
void oneLed::paintEvent(QPaintEvent *event)
{
QPushButton::paintEvent(event);
if(state){
QPainter painter(this);
//painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(QBrush("#888"), 1));
painter.setBrush(QBrush(QColor("#888")));
painter.drawEllipse(0, 0, width(), height());
}
}
void oneLed::doPainting()
{
state = !state;
update();
}
I have QGraphicsView, QGraphicsScene and QGraphicsRectItem.
QGraphicsRectItem in the QGraphicsScene and the last one in the QGraphicsView. I want to move QGraphicsRectItem with mouse by clicking on it only! But in my implementation it moves if I click on any position on my QGraphicsScene. Whether it is my QGraphicsRectItem or some other place. And the second issue. The item has been moved to the center of the scene. Clicking on it again it starts to move from the home location.
void Steer::mousePressEvent(QMouseEvent *click)
{
offset = click->pos();
}
void Steer::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
p1->setPos(event->localPos() - offset); //p1 movable item
}
}
What do I do wrong?
UPDATE:
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Steer w;
w.show();
return a.exec();
}
widget.h
#ifndef STEER_H
#define STEER_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QPoint>
#include <QGraphicsRectItem>
class Steer : public QGraphicsView
{
Q_OBJECT
private:
QGraphicsScene *scene;
QGraphicsRectItem *p1;
QPoint offset;
public:
explicit Steer(QGraphicsView *parent = 0);
~Steer(){}
public slots:
void mousePressEvent(QMouseEvent * click);
void mouseMoveEvent(QMouseEvent * event);
};
#endif // STEER_H
widget.cpp
#include "widget.h"
#include <QBrush>
Steer::Steer(QGraphicsView *parent)
: QGraphicsView(parent)
{
scene = new QGraphicsScene;
p1 = new QGraphicsRectItem;
//add player
p1->setRect(760, 160, 10, 80);
//add scene
scene->setSceneRect(0, 0, 800, 400);
//add moveable item
scene->addItem(p1);
//set scene
this->setScene(scene);
this->show();
}
void Steer::mousePressEvent(QMouseEvent *click)
{
offset = click->pos();
}
void Steer::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
p1->setPos(event->localPos() - offset);
}
}
I'd try a different approach that is a little easier to understand:
#include <QtWidgets>
class Steer : public QGraphicsView
{
public:
Steer()
{
scene = new QGraphicsScene;
p1 = new QGraphicsRectItem;
//add player
p1->setRect(0, 0, 10, 80);
p1->setX(760);
p1->setY(160);
//add scene
scene->setSceneRect(0, 0, 800, 400);
//add moveable item
scene->addItem(p1);
//set scene
this->setScene(scene);
this->show();
}
protected:
void mousePressEvent(QMouseEvent * click)
{
if (p1->contains(p1->mapFromScene(click->localPos()))) {
lastMousePos = click->pos();
} else {
lastMousePos = QPoint(-1, -1);
}
}
void mouseMoveEvent(QMouseEvent * event)
{
if(!(event->buttons() & Qt::LeftButton)) {
return;
}
if (lastMousePos == QPoint(-1, -1)) {
return;
}
p1->setPos(p1->pos() + (event->localPos() - lastMousePos));
lastMousePos = event->pos();
}
private:
QGraphicsScene *scene;
QGraphicsRectItem *p1;
QPoint lastMousePos;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Steer w;
w.show();
return a.exec();
}
There are a few things to point out here:
Don't use setRect() to set the position of a QGraphicsRectItem. It doesn't work the way you think it might. Always use setPos() to change the position of an item.
Rename offset to something more descriptive. I chose lastMousePos. Instead of just updating it once when the mouse is pressed, also update it whenever the mouse is moved. Then, it's simply a matter of getting the difference between the two points and adding that to the position of the item.
Check if the mouse is actually over the item before reacting to move events. If the mouse isn't over the item, you need some way of knowing that, hence the QPoint(-1, -1). You may want to use a separate boolean flag for this purpose. This solves the problem that you saw, where it was possible to click anywhere in the scene to get the item to move.
Also, note the mapFromScene() call: the contains() function works in local coordinates, so we must map the mouse position which is in scene coordinates before testing if it's over the item.
The event functions are not slots, they're virtual, protected functions.
You could also consider handling these events in the items themselves. You don't need to do it from within QGraphicsView, especially if you have more than one of these items that need to be dragged with the mouse.
I am using the QWidget::setCursor method, but after a QMessageBox popup, in some cases it temporarily reverts to the old cursor (until I cause it to "refresh" and load the override cursor again).
The pattern seems to be that if you exit the message box with the cursor outside of its frame (for example, use the Escape key), then the cursor is gone. On the other hand, if you exit the message box with the cursor inside its frame, then the override cursor returns.
I have tried to debug it myself by subclassing QWidget and overriding enterEvent and leaveEvent. Interestingly, when the dialog box appears, a leaveEvent is triggered on the main widget, but the next enterEvent is triggered only if the cursor was inside the message box frame when closing the dialog. Otherwise, no enterEvent is seen.
How can I get that the override cursor is always returned?
Header file:
#include <QtWidgets>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0);
void enterEvent(QEvent *);
void leaveEvent(QEvent *);
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void popup();
};
Source file:
#include <QtWidgets>
#include "main.h"
Widget::Widget(QWidget *parent) : QWidget(parent) { }
void Widget::enterEvent(QEvent * e) {
puts("Widget::enterEvent"); QWidget::enterEvent(e);
}
void Widget::leaveEvent(QEvent * e) {
puts("Widget::leaveEvent"); QWidget::leaveEvent(e);
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
Widget * widget = new Widget(this);
widget->setMinimumSize(500, 500);
widget->setCursor(Qt::CrossCursor);
setCentralWidget(widget);
connect(new QShortcut(QKeySequence("A"),this),SIGNAL(activated()),
this,SLOT(popup()));
}
void MainWindow::popup() {
QMessageBox::question(
this, "Test", "Test", QMessageBox::Yes|QMessageBox::No);
}
int main(int argc, char *argv[]) {
QApplication app(argc,argv);
MainWindow thewindow;
thewindow.show();
int re = app.exec();
return re;
}
I am a novice to c++, but I am doing my best to learn.
I am getting two errors and I don't know why, these are:
In constructor 'MyLabel::MyLabel(QWidget*)':
Qualified-id in declaration before '(' token -line 7
Qualified-id in declaration before '(' token -line20
My code is as follows:
mylabel.cpp:
#include "mylabel.h"
#include "ui_mainwindow.h"
MyLabel::MyLabel(QWidget *parent) :
QWidget(parent)
{
void MyLabel::MyLabel()
{
this->setAlignment(Qt::AlignCenter);
//Default Label Value
this->setText("No Value");
//set MouseTracking true to capture mouse event even its key is not pressed
this->setMouseTracking(true);
}
void MyLabel::mouseMoveEvent(QMouseEvent * event)
{
//Show x and y coordinate values of mouse cursor here
this->setText("X:" + QString::number(event->x()) + "-- Y:" + QString::number(event->y()));
}
}
mylabel.h:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QObject>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
class MyLabel : public QWidget
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
~MyLabel();
void mouseMoveEvent(QMouseEvent * event);
signals:
};
#endif // MYLABEL_H
main.cpp
#include "mainwindow.h"
#include "mylabel.h"
#include <QApplication>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow *window = new QMainWindow();
window->setWindowTitle(QString::fromUtf8("QT - Capture Mouse Move"));
window->resize(300, 250);
QWidget *centralWidget = new QWidget(window);
QHBoxLayout* layout = new QHBoxLayout(centralWidget);
MyLabel* CoordinateLabel = new MyLabel();
layout->addWidget(CoordinateLabel);
window->setCentralWidget(centralWidget);
window->show();
return app.exec();
}
mainwindow.cpp is blank
You are getting error are you are trying to define functions inside your constructor. MyLabel::MyLabel(QWidget *parent) so be
MyLabel::MyLabel(QWidget *parent) : QWidget(parent)
{
this->setAlignment(Qt::AlignCenter);
//Default Label Value
this->setText("No Value");
//set MouseTracking true to capture mouse event even its key is not pressed
this->setMouseTracking(true);
}
And then the definition for mouseMoveEvent should follow after the constructor
void MyLabel::mouseMoveEvent(QMouseEvent * event)
{
//Show x and y coordinate values of mouse cursor here
this->setText("X:" + QString::number(event->x()) + "-- Y:" + QString::number(event->y()));
}
EDIT:
As pointed out in the comments setAlignment and setText are not members of QWidget so if they are not members of MyLable then you will need to remove those otherwise it will not compile.
In order to implement your custom label you have to derive your class from Qt's standard QLabel class as:
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
~MyLabel();
protected:
void mouseMoveEvent(QMouseEvent * event);
};
Unfortunately in C++ you can not define a function inside another function as you did in MyLabel::MyLabel() constructor. Just write it in the following way:
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
setAlignment(Qt::AlignCenter);
//Default Label Value
setText("No Value");
//set MouseTracking true to capture mouse event even its key is not pressed
setMouseTracking(true);
}
UPDATE
I would implement the handling of the mouse move event in this way:
void MyLabel::mouseMoveEvent(QMouseEvent * event)
{
// Show x and y coordinate values of mouse cursor here
QString txt = QString("X:%1 -- Y:%2").arg(event->x()).arg(event->y());
setText(txt);
}