I'm new to QT and am making a widget that interfaces with a pre-existing gui. I'd like to have my widget continuously output one signal while the user has a pushbutton pressed and then continuously output another when it is released.
By enabling autorepeat I can have the widget output my signal while the user is pressing the pushbutton, however, the output signal switches between pressed() and released(). E.G.
<>
Outputs:
* pressed signal
* released signal
* pressed signal
* released signal
I've seen this question been asked about keyPressEvents but I'm not sure how to access isAutoRepeat() for PushButtons. Can someone give me advice on this?
One way is you can use timer object to achieve this. Below is the example, that will run the 2 slot's when button pressed and released. The code comment will explain in detail. when button pressed & released a text box will show the continuous time in Milli-seconds. Timer is an object that will emit the timeout() signal in a given interval. We need to stop and start the alternate timers in button pressed / released signal. This application created using the QT Creator "QT Widgets Application" wizard.
Hope this help.
//Header File
class MainWindow : public QMainWindow{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
//Button slots
void on_pushButton_pressed(); //Continuous press
void on_pushButton_released(); //Continuous release
void on_pushButton_2_clicked(); //stop both the timer
//QTimer timeout actions
void timer1_action();
void timer2_action();
private:
Ui::MainWindow *ui;
//Timer object
QTimer *t1, *t2;
//Date time object for testing
QDateTime dt1,dt2;
};
//CPP file
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
ui->setupUi(this);
//Parent object will take care of the deallocation of the 2 timer objects
t1 = new QTimer(this);
t2 = new QTimer(this);
//Interval to the timer object
t1->setInterval(10);
t2->setInterval(10);
//Signal slot for the timer
this->connect(t1,SIGNAL(timeout()),this,SLOT(timer1_action()));
this->connect(t2,SIGNAL(timeout()),this,SLOT(timer2_action()));
}
MainWindow::~MainWindow(){
delete ui;
}
void MainWindow::on_pushButton_pressed(){
//starting and stoping the timer
t2->stop();
t1->start();
//date time when pressed
dt1 = QDateTime::currentDateTime();
}
void MainWindow::on_pushButton_released(){
//starting and stoping the timer
t1->stop();
t2->start();
//date time when pressed
dt2 = QDateTime::currentDateTime();
}
void MainWindow::timer1_action(){
ui->txtTimer1->setPlainText("Button Pressed for " + QString::number(dt1.msecsTo(QDateTime::currentDateTime())) + " Milli Seconds");
}
void MainWindow::timer2_action(){
ui->txtTimer2->setPlainText("Button Released for " + QString::number(dt2.msecsTo(QDateTime::currentDateTime())) + " Milli Seconds");
}
void MainWindow::on_pushButton_2_clicked(){
//stoping both the timer
t1->stop();
t2->stop();
}
Related
I have a dialog in Qt5(C++). You can see that the dialog just creates some widgets (VisGLWidget from QOpenGLWidget) and keeps updating it.
#include "visualizationdlg.h"
#include "ui_visualizationdlg.h"
VisualizationDlg::VisualizationDlg(QWidget *parent, NewtonSpace *data) :
QDialog(parent),
ui(new Ui::VisualizationDlg)
{
ui->setupUi(this);
this->setFixedSize(600,600);
this->data = data;
this->visualizationGL = new VisGLWidget(this, this->data);
this->visualizationGL->setObjectName(QString::fromUtf8("visualizationGL"));
this->visualizationGL->setGeometry(QRect(10, 50, 581, 541));
this->visualizationGL->repaint();
this->rePaintTimer = startTimer(1);
}
VisualizationDlg::~VisualizationDlg()
{
delete ui;
}
void VisualizationDlg::timerEvent(QTimerEvent *event)
{
if(event->timerId() == this->rePaintTimer) {
this->data->update(ui->GInput->value(), ui->updateSpdInput->value());
this->visualizationGL->repaint();
this->rePaintTimer = startTimer(1);
}
}
The question is, I can quit this dialog by clicking the cross on MacOS, but not on Windows. What's wrong with it?
In your VisualizationDlg constructor you have...
this->rePaintTimer = startTimer(1);
That will result in a QTimerEvent being sent to your VisualizationDlg instance once every millisecond (potentially).
Now consider the code that handles timer events...
void VisualizationDlg::timerEvent (QTimerEvent *event)
{
if(event->timerId() == this->rePaintTimer) {
this->data->update(ui->GInput->value(), ui->updateSpdInput->value());
this->visualizationGL->repaint();
this->rePaintTimer = startTimer(1);
}
}
The first time this is invoked the id associated with the timer event will match rePaintTimer. Hence repaint will be called. But you then start a new timer and assign its id to rePaintTimer without stopping the original timer. You now have two timers each generating events at 1KHz but your event handler only ever acts on events from the most recently created timer.
Now think what this will become after even a few seconds. You could potentially have thousands of timers each generating events at 1KHz.
Remove the line...
this->rePaintTimer = startTimer(1);
from your event handler to make it...
void VisualizationDlg::timerEvent (QTimerEvent *event)
{
if(event->timerId() == this->rePaintTimer) {
this->data->update(ui->GInput->value(), ui->updateSpdInput->value());
this->visualizationGL->repaint();
}
}
This is my first time to ask on Stack Overflow. If any suggestion about asking question, please let me know.
I am very new to Qt, and have some problems while using event. Hope someone can provide any thoughts.
Background:
I have my custom pushButton, which is rxPushButton in rx.h and rx.cpp. I use on_rxPushButton_clicked to change the image and it works pretty well.
In MainWindow, I need to use some rx so I include the class rx and I want to detect if I press left button of the mouse, I need to know which rx has been pressed and record its id in int rxId in MainWindow.
Problem:
I tried two ways to achieve my goal, including mousePressEvent and eventFilter. I found that I can't detect the mouse pressed signal on any rx, but I can detect it outside rx in other places in Mainwindow. I wonder if the events will conflict, but when I comment on_rxPushButton_clicked in rx.cpp, MainWindow still doesn't work for the problem. So I presume that maybe the space in the screen occupied by rx will not be in control of MainWindow (I can get Debug message "test1" but not "test2" in my code, check below).
How should I do to solve this problem if I need both things (change image in rx and modify one variable in MainWindow)? Or maybe it's just something wrong with my code and how to modify?
I hope to separate them if possible because I still need to include many objects in MainWindow in the future.
Here are some of my related codes:
rx.cpp
void rx::on_rxPushButton_clicked(void)
{
startLoading();
}
void rx::startLoading(void)
{
state = 1;
connect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
timer->start(LOADING_INTERVAL);
}
void rx::loading1(void)
{
if(state == 1)
{
state = 2;
ui->rxPushButton->setStyleSheet("border-image: url(:/images/Rx/Rxloading1.png);");
disconnect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
}
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
}
//rxList[0]->installEventFilter(this);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "test1";
if(rxList[0]->rect().contains(event->x(), event->y()))
qDebug() << "test2";
}
}
Change image in rx
Use QSignalMapper class that bundles signals from QWidgets (class rx) and re-emits them with widget (class rx) parameters corresponding to the object that sent the signal.
We connect each button's clicked() signal to the signal mapper's map() slot, and create a mapping in the signal mapper from each button to the button itself.
Finally we connect the signal mapper's mapped() signal to the custom widget's(rx object) clicked() signal. When the user clicks a button, the custom widget(rx object) will emit a single clicked() signal whose argument is the button (rx object) the user clicked.
Handle the button clicked event in MainWindow::slotButtonsClicked(QWidget *p) function.
Modify n variable in MainWindow:
You can update variable of MainWindow in mousePressEvent function
class MainWindow : public QWidget
{
public :
MainWindow (QWidget *parent);
QSignalMapper * mapper;
slots:
void slotButtonsClicked(QWidget *);
// ...
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mapper = new QSignalMapper(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
QObject::connect(rxList[i], SIGNAL(clicked()),mapper,SLOT(map()));
//Adds a mapping so that when map() is signalled from the sender, the signal mapped(widget ) is emitted.
mapper->setMapping(rxList[i], rxList[i]);
}
QObject::connect(mapper,SIGNAL(mapped(QWidget *)),this,SLOT(slotButtonsClicked(QWidget *)));
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "Clicked outside the button";
//Now you can modify n variable in MainWindow
}
MainWindow::mousePressEvent(event);
}
void MainWindow::slotButtonsClicked(QWidget *p)
{
rx *button = dynamic_cast<rx *>(p);
button->on_rxPushButton_clicked();
qDebug() << button <<" clicked on button";
//Now you can change image in rx
}
Hope this works :)
Event propagating prevent you to do it. When mouse is over Button, event sends to button, not to the MainWindow, thats why you never see test2 in debug output.
Since I don't know what do you need it's hard to say what you should do. You can process event in Button and send some signals or whatever, or set event filter and check if target object is your button, and so on.
Any way, read Qt event system docs for better understanding.
I am coding a fairly simple application that uses Qt with OpenCV. I have single window that contains a widget which displays a video feed captured from a webcam. The webcam video capture is running in an infinite loop in a separate thread so as not to consume the UI thread.
When I close the window (using the normal "X" button on the top right of the window - this app is being developed in Windows 7), it doesn't seem to be shutting down the program correctly. The window does close visibly, but I put a breakpoint in the destructor of the main window, and the breakpoint never gets hit. Additionally, the thread which does video capture continues to run (I know this because the thread outputs to stdout periodically). Only when I click "stop debugging" in the Qt development environment does it cause everything to completely shut down.
Here is my worker object (not subclassing from QThread):
class Worker : public QObject
{
Q_OBJECT
private:
VideoCapture *cap;
bool finished;
QMutex mutex;
public:
Worker ()
{
cap = new VideoCapture(0);
finished = false;
}
bool isFinished ()
{
QMutexLocker locker (&mutex);
return finished;
}
public slots:
void doWork ()
{
Mat frame;
while(!isFinished())
{
// ...some code that outputs to stdout deleted for clarity...
(*cap) >> frame;
emit resultReady(frame);
}
}
void setFinished (bool f)
{
QMutexLocker locker (&mutex);
finished = f;
}
signals:
void resultReady (Mat frame);
};
Here is the header file for my main window:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
CVImageWidget* imageWidget;
Worker *worker;
QThread workerThread;
public slots:
void handleResults (Mat frame);
signals:
void operate ();
void finishSignal (bool f);
private:
Ui::MainWindow *ui;
};
And the class implementation:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//Create a widget for this window.
QWidget *wdg = new QWidget(this);
QGridLayout *grid = new QGridLayout(wdg);
// ...code creating widgets deleted for clarity...
this->setCentralWidget(wdg);
//Start video capture
qRegisterMetaType<Mat>("Mat");
worker = new Worker();
worker->moveToThread(&workerThread);
connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate()), worker, SLOT(doWork()));
connect(worker, SIGNAL(resultReady(Mat)), this, SLOT(handleResults(Mat)));
connect(this, SIGNAL(finishSignal(bool)), worker, SLOT(setFinished(bool)));
workerThread.start();
emit operate();
}
MainWindow::~MainWindow()
{
emit finishSignal(true);
workerThread.quit();
workerThread.wait();
delete ui;
}
void MainWindow::handleResults(Mat frame)
{
imageWidget->showImage(frame);
}
Any help understanding why the program doesn't shut down properly would be appreciated. Thanks!
I wanted to know what would be the best approach of adding a countdown timer to a QMessageBox ? For instance when a message box is displayed the countdown timer starts for say 5 seconds. If the user doesn't respond to the Message box the message box picks up a default choice.
How about something like this:
#include <QMessageBox>
#include <QPushButton>
#include <QTimer>
class TimedMessageBox : public QMessageBox
{
Q_OBJECT
public:
TimedMessageBox(int timeoutSeconds, const QString & title, const QString & text, Icon icon, int button0, int button1, int button2, QWidget * parent, WindowFlags flags = (WindowFlags)Dialog|MSWindowsFixedSizeDialogHint)
: QMessageBox(title, text, icon, button0, button1, button2, parent, flags)
, _timeoutSeconds(timeoutSeconds+1)
, _text(text)
{
connect(&_timer, SIGNAL(timeout()), this, SLOT(Tick()));
_timer.setInterval(1000);
}
virtual void showEvent(QShowEvent * e)
{
QMessageBox::showEvent(e);
Tick();
_timer.start();
}
private slots:
void Tick()
{
if (--_timeoutSeconds >= 0) setText(_text.arg(_timeoutSeconds));
else
{
_timer.stop();
defaultButton()->animateClick();
}
}
private:
QString _text;
int _timeoutSeconds;
QTimer _timer;
};
[...]
TimedMessageBox * tmb = new TimedMessageBox(10, tr("Timed Message Box"), tr("%1 seconds to go..."), QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Default, QMessageBox::Cancel, QMessageBox::NoButton, this);
int ret = tmb->exec();
delete tmb;
printf("ret=%i\n", ret);
Use QTimer::singleShot with either close(), accept() or reject() slots if you don't need to display the timeout. If you need, then subclass QMessageBox or QDialog and reimplement methods as you want them to be, e.g. reimplement QObject::timerEvent to make text update.
If you want the message box to display the timer value I think you're better off making your own QDialog subclass. Otherwise it sounds simple - display your message with show, start the timer, connect to the timeout slot and manipulate your dialog.
I have a simple setup to change a label on a timed interval, for testing purposes. It seems that the signal does not ever get emitted. I'm using Visual Studio 2010 with the Qt add-in. Here is my setup...
Window::Window(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
my_label = new QLabel();
timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(nextFrame()));
}
void Window::nextFrame()
{
static int i = 0;
std::stringstream ss;
ss << "C:/files/" << i << ".txt";
QString qstr = QString::fromStdString(ss.str());
ui.label->setText(qstr);
ss.str("");
i++;
repaint();
}
And in the header file,
public:
Window(QWidget *parent = 0, Qt::WFlags flags = 0);
~Window();
public slots:
void nextFrame();
private:
Ui::TrackerClass ui;
QTimer *timer;
};
Why is the slot nextFrame() never being triggered?
There is nothing in this code which calls start() and so based on this code nextFrame() would never be triggered by timeout().
Couple of things I think I can help with:
nextFrame() is a SLOT. SLOTs don't emit. They receive. SIGNALS emit. Not trying to be rude, just want to be clear on this as it's an important distinction. (in this case the signal is timeout() )
you need to have a start() to begin the timer. something like the line below would seem to do the trick:
Hope this helps clear up some confusion.
timer->start(1000);