How to programmatically click a QPushButton - c++

I am using Qt. I have a button in the page added via the Qt Creator. It is connected to the method void MyPage::on_startButton_clicked().
I want to programmatically click this button. I tried ui->startButton->clicked(), it gives,
error C2248: 'QAbstractButton::clicked' : cannot access protected member declared in class 'QAbstractButton'
Please help. Thanks!

Use QAbstractButton::animateClick():
QPushButton* startButton = new QPushButton("Start");
startButton->animateClick();

RA's answer provides the way to do it so that it's visible that the button was clicked. If all you wish for is to emit the signal, what you're doing is correct in Qt 5, where the signals are public.
The error you're facing indicates that you're using Qt 4, where the signals are not public. You can work around this by invoking the signal indirectly:
QMetaObject::invokeMethod(ui->startButton, "clicked");
This calls the method immediately, i.e. the signal will be dispatched and the slots called by the time invokeMethod returns. Alas, most code (mostly your code!) assumes that the signal is emitted from event processing code close to the event loop - i.e. it's not reentrant, rather than from your own code. Thus you should defer the signal emission to the event loop:
// Qt 5.4 & up
QTimer::singleShot(0, ui->startButton, [this]{ ui->startButton->clicked(); });
// Qt 4/5
QTimer::singleShot(0, ui->startButton, "clicked");
The following is a complete example for Qt 5.4 & up:
#include <QtWidgets>
int main(int argc, char ** argv) {
bool clicked = {};
QApplication app{argc, argv};
QPushButton b{"Click Me"};
QObject::connect(&b, &QPushButton::clicked, [&]{ clicked = true; qDebug() << "clicked"; });
Q_ASSERT(!clicked);
b.clicked(); // immediate call
Q_ASSERT(clicked);
clicked = {};
// will be invoked second - i.e. in connect order
QObject::connect(&b, &QPushButton::clicked, &app, &QApplication::quit);
QTimer::singleShot(0, &b, [&]{ b.clicked(); }); // deferred call
Q_ASSERT(!clicked);
app.exec();
Q_ASSERT(clicked);
}

If you do not want the animation stuff, you can also just call the method:
on_startButton_clicked();

Related

Not able to connect signals and slots

I wrote a basic C++ program for simulating the signal and slot process. I made a push button "button", a QVBoxLayout "layout". I added the button in the layout. Everything is fine till now. But when push button is made to connect by signal and slot, there's are two problem/errors.
"no instance of overloaded function "QObject::connect matches the argument list"."
'QObject::connect': none of the 3 overloads could convert all the argument types.
Question:
I am sure that there's something missing, which I am not able to decipher. Here, the push button "button" has to invoke the function "connectFunc". Instead it gives me above two errors. How to make the button invoke the function?
This is my code.
#include "signalsslots.h"
#include <QtWidgets/QApplication>
#include<qpushbutton.h>
#include<qboxlayout.h>
#include<iostream>
using std::cout;
using std::endl;
void connectFunc()
{
cout << endl << "connected " << endl;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton* button = new QPushButton("Press Here");
QAction* bAction = new QAction;
button->addAction(bAction);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(button);
//layout->SetFixedSize(200, 150);
QObject::connect(bAction, SIGNAL(QPushButton::clicked()), &a,(connectFunc()));
QWidget w;
w.setLayout(layout);
w.setWindowTitle("Signal and Slot Example !!!");
w.show();
return a.exec();
}
FYI, I am using VS 2019 for writing Qt widget applications.
The QObject::connect() function that you use, connects a signal (i.e. a method) of an object to a slot (i.e. a method) of another object.
What you are telling QT is to connect the clicked() method of your QAction object, to the connectFunc() of your QApplication object. Both methods do not exist on those objects!
What you probably want is to connect the clicked() method of your QButton button to the free function connectFunc(). Like this:
QObject::connect(button, &QPushButton::clicked, &connectFunc);
Note that this requires QT5. (See Is it possible to connect a signal to a static slot without a receiver instance?)

Interaction between QMainWindow class and QDialogue class

I am developing a GUI app. I have a main window(QMainWindow) which pops up when the app is executed.
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This window has some buttons. When I click a button here, another window which is of QDialog is popped up. The logic of the button is
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
lib.setModal(false);
lib.exec();
}
Question: I have a socket in the QMainWindow class, whenever I receive a message there I need to send it to the QDialogue window and display it in a QLineEdit. With the above logic, even after I set QDialogue to nonmodal, I am not able to interact with the QMainWindow when the QDialogue window is open. I tried lib.show() instead of lib.exec(), but with this, when I click the button the QMainWindow class the QDialogue window does not pop up.
Please advice me what is the best method to communicate from the background window the the foreground window?
Your code can looks like
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( letsChangeLineEdit( QString ) ) ) );
lib.setModal(false);
lib.exec();
}
Of course you should implement your signal and slot. QString in signal slot arguments is target string to show in dialog.
Hope that will clear for you.
In your MainWindow::on_InsertButton_pressed()slot you are creating a local variable libinsert lib which gets destroyed when it gets out of scope, i.e. when the slot execution finishes. If you use lib.exec() the execution waits for the dialog execution to return so you see the dialog, but can't interact with the main window until you close it. When you use lib.show(), your dialog is shown, execution continues and exits the slot right away, at which point the lib gets destroyed and you don't see it any more (depending on your configuration this may be fast enough that it looks like it newer gets shown in the first place).
One solution is to have a pointer to your dialog as a member of MainWindow class (add libinsert* lib; in MainWindow.h), and initialize it in your Mainwindow constructor like this:
`MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
lib = new libinsert(this);
lib->setModal(false);
}`
Then in MainWindow::on_InsertButton_pressed() you only need to call QWidget::show() method, i.e. like this:
void MainWindow::on_InsertButton_pressed()
{
lib->show();
}
Now, if what you are trying to achieve is to have a dialog pop-up every time the message is received , and display the message, first you will need to have a public method(or slot) in your libinsert class which updates the text in your QLineEdit, for example
void libinsert::onMessageReceived(QString message)
{
// Update the text and show the dialog to avoid having to call show()
// explicitly from MainWindow.
ui->lineEdit->setText(message);
show();
}
and then either call this method directly, when you receive the message, with:
lib->onMessageReceived(message);
or connect a signal, emitted when message is received, to it with something like this (in MainWindow constructor):
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( onMessageReceived( QString ) ) ) );

How do we pass the value to all the elements of GUI in this QT code

I'm reading a book on QT4 and here's an example from the book:
QApplication a(argc, argv);
QWidget window;
QVBoxLayout* mainLayout = new QVBoxLayout(&window);
QLabel* label = new QLabel("0");
QSpinBox* spinBox = new QSpinBox;
QSlider* slider = new QSlider(Qt::Horizontal);
mainLayout->addWidget(label);
mainLayout->addWidget(spinBox);
mainLayout->addWidget(slider);
QObject::connect(spinBox, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)));
QObject::connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
QObject::connect(slider, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)));
QObject::connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));
window.show();
As the book and compilation show, changing the value of one of the widgets leads to changing the values of the other elements.
What I don't understand is how it happens. How do we pass that value from one widget to the rest? There's no variable that gets changed by means of one widget and gets passed to the others.
PS Conceptually, I do understand the idea of slots and signals and 'connect'. It's how the value is passed from one widget to the rest is the problem.
A signal is a C++ method with code generated by a utility called moc (meta object compiler). A slot is regular C++ method, with code under your control. A connection is a way of letting the signal know what slots to call. When the signal gets emitted, it really means that you call the machine-generated method that iterates the connection list.
Conceptually, the valueChanged signal implementation looks like this:
void valueChanged(int value) {
for (slot : this->slots)
(slot.object->*slot.method)(value);
}
Thus, when the slider "emits" its signal, it calls each slot with a given value. After the connections are made, you should think of the spinbox's valueChanged signal as doing the following:
void SpinBox::valueChanged(int value) {
// 1st connection
label->setNum(value);
// 2nd connection
slider->setVale(value);
}
There's no "variable" that gets changed because the signal-slot mechanism is, at its core, an easier-to-use way of doing indirect method calls (via method pointers and instance pointers).
In modern code (Qt5/C++11), that example would be (this is complete code):
#include <QtWidgets>
int main(int argc, char** argv) {
QApplication a{argc, argv};
QWidget window;
QVBoxLayout mainLayout{&window};
QLabel label{"0"};
QSpinBox spinBox;
QSlider slider{Qt::Horizontal};
mainLayout.addWidget(&label);
mainLayout.addWidget(&spinBox);
mainLayout.addWidget(&slider);
QObject::connect(&spinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
&label, static_cast<void(QLabel::*)(int)>(&QLabel::setNum));
QObject::connect(&spinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
&slider, static_cast<void(QSlider::*)(int)>(&QSlider::setValue));
QObject::connect(&slider, static_cast<void(QSlider::*)(int)>(&QSlider::valueChanged),
&label, static_cast<void(QLabel::*)(int)>(&QLabel::setNum));
QObject::connect(&slider, &QSlider::valueChanged, &spinBox, &QSpinBox::setValue);
window.show();
return a.exec();
}

Repaint splash screen from thread / disable assertion

Problem
I want to use a QTimer to update a derived QSplashScreen that draws a progress bar (using paint commands, not a widget) to estimate when the program will start running.
By necessity, this happens prior to the exec call of the QCoreApplication. I've gotten this to work (in release mode only) on both X11 and Windows, by putting a timer in a second thread, and calling a function in the splash screen which updates the progress and repaints the widget. However, this doesn't work in debug mode as it produces the following error:
"ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread."
I'm not really worried about this assertion as the code doesn't crash in release and it's just a splash screen, however I need to be able to run the program in debug so I'd like to either a) refactor the code so it doesn't trigger the assertion, or b) dissable this particular assertion.
Tried:
Using update() instead of repaint(). This doesn't cause an assertion, but it also doesn't repaint because the main thread is too busy loading in the shared libraries, etc, and the timer events don't get processed until I'm ready to call finish on the splash screen.
starting QTimer in main loop. Same result as above.
Using a QT::QueuedConnection. Same result.
Main
#include <QApplication>
#include <QtGui>
#include <QTimer>
#include <QThread>
#include "mySplashScreen.h"
#include "myMainWindow.h" // contains a configure function which takes a
// LONG time to load.
int main( int argc, char* argv[] )
{
// estimate the load time
int previousLoadTime_ms = 10000;
QApplication a(argc, argv);
MySplashScreen* splash = new MySplashScreen(QPixmap(":/splashScreen"));
// progress timer. Has to be in a different thread since the
// qApplication isn't started.
QThread* timerThread = new QThread;
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(previousLoadTime_ms / 100.0);
timer->moveToThread(timerThread);
QObject::connect(timer, &QTimer::timeout, [&]
{
qApp->processEvents(); splash->incrementProgress(1);
});
QObject::connect(timerThread, SIGNAL(started()), timer, SLOT(start()));
timerThread->start();
splash->show();
a.processEvents();
myMainWindow w;
QTimer::singleShot(0, [&]
{
// This will be called as soon as the exec() loop starts.
w.configure(); // this is a really slow initialization function
w.show();
splash->finish(&w);
timerThread->quit();
});
return a.exec();
}
Splash Screen
#include <QSplashScreen>
class MySplashScreen : public QSplashScreen
{
Q_OBJECT
public:
MySplashScreen(const QPixmap& pixmap = QPixmap(), Qt::WindowFlags f = 0)
: QSplashScreen(pixmap, f)
{
m_pixmap = pixmap;
}
virtual void drawContents(QPainter *painter) override
{
QSplashScreen::drawContents(painter);
// draw progress bar
}
public slots:
virtual void incrementProgress(int percentage)
{
m_progress += percentage;
repaint();
}
protected:
int m_progress = 0;
private:
QPixmap m_pixmap;
};
MyMainWindow
#include <QMainWindow>
class myMainWindow : public QMainWindow
{
public:
void configure()
{
// Create and configure a bunch of widgets.
// This takes a long time.
}
}
The problems are because the design is backwards. The GUI thread should not be doing any loading. The general approach to GUI threads is: do no work in the GUI thread. You should spawn a worker thread to load what you need loaded. It can post events (or invoke slots using a queued connection) to the GUI thread and its splash screen.
Of course, the worker thread should not create any GUI objects - it can't instantiate anything deriving from QWidget. It can, though, instantiate other things, so if you need any expensive-to-obtain data, prepare it in the worker thread, and then cheaply build a QWidget in the GUI thread once that data is available.
If your delays are due to library loading, then do load all the libraries in a worker thread, explicitly, and ensure that all of their pages are resident in memory - for example by reading the entire .DLL after you're loaded it as a library.
The MyMainWindow::configure() could be called in a worker thread, as long as it doesn't invoke any QWidget methods nor constructors. It can do GUI work, just not visible on screen. For example, you can load QImage instances from disk, or do painting on QImages.
This answer provides several approaches to executing a functor in a different thread, GCD-style.
If you are constructing widgets that are expensive to construct, or construct many of them, it's possible to make sure that the event loop can run between the instantiation of each widget. For example:
class MainWindow : public QMainWindow {
Q_OBJECT
QTimer m_configureTimer;
int m_configureState = 0;
Q_SLOT void configure() {
switch (m_configureState ++) {
case 0:
// instantiate one widget from library A
break;
case 1:
// instantiate one widget from library B
...
break;
case 2:
// instantiate more widgets from A and B
...
break;
default:
m_configureTimer.stop();
break;
}
}
public:
MainWindow(QWidget * parent = 0) : QMainWindow(parent) {
connect(&m_configureTimer, SIGNAL(timeout()), SLOT(configure()));
m_configureTimer.start(0);
//...
}
};

Click default QPushButton when Enter SpinBox

I need to raise the click signal of my QPushButton when I press Enter on QSpinBox (actually on every input box), but even if my button is the default button, the following code doesn't work.
#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSpinBox>
#include <QMessageBox>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QWidget* window = new QWidget();
QSpinBox* spinbox = new QSpinBox();
QPushButton* button = new QPushButton("Ok");
button->setDefault(true);
QObject::connect(button, &QPushButton::clicked, []()
{
QMessageBox::question(nullptr, "Test", "Quit?", QMessageBox::Yes|QMessageBox::No);
});
QHBoxLayout* layout = new QHBoxLayout();
layout->addWidget(spinbox);
layout->addWidget(button);
window->setLayout(layout);
window->show();
return app.exec();
}
How can I fix it?
You can see in the Qt documentation about QPushButton :
The default button behavior is provided only in dialogs
You are using a QWidget and expect the default button behavior to work. Just use a QDialog instead of QWidget :
QDialog * window = new QDialog();
...
you can use Event
The QObject::installEventFilter() function enables this by setting up
an event filter, causing a nominated filter object to receive the
events for a target object in its QObject::eventFilter() function. An
event filter gets to process events before the target object does,
allowing it to inspect and discard the events as required.
void QObject::installEventFilter ( QObject * filterObj )
Installs an event filter filterObj on this object. For example:
monitoredObj->installEventFilter(filterObj);
An event filter is an object that receives all events that are sent to
this object. The filter can either stop the event or forward it to
this object. The event filter filterObj receives events via its
eventFilter() function. The eventFilter() function must return true if
the event should be filtered, (i.e. stopped); otherwise it must return
false.