I'm developing C++ application in QT with GUI. To make the GUI always respond, I create a thread for other blocking process. However, the application is waiting for the blocking process and therefore the GUI didn't respond.
Is creating a thread for blocking process is a wrong way?
Or it doesn't work in QT? If so, how to make the GUI respond? Please give me example.
This is a simple example of multithreaded application with responsive GUI:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QThread>
class Worker : public QThread
{
protected:
/// Wait 3s which simulates time demanding job.
void run() { sleep(3); }
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void doJob(bool);
void jobFinished();
private:
Worker worker;
QLineEdit *line;
QPushButton *button;
int counter;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
line(new QLineEdit()),
button(new QPushButton()),
counter(0)
{
line->setDisabled(true);
line->setAlignment(Qt::AlignRight);
line->setText(QString::number(counter));
button->setText("Push");
connect(button, SIGNAL(clicked(bool)), this, SLOT(doJob(bool)));
connect(&worker, SIGNAL(finished()), this, SLOT(jobFinished()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(line);
layout->addWidget(button);
QWidget *window = new QWidget();
window->setLayout(layout);
setCentralWidget(window);
}
void MainWindow::doJob(bool)
{
// Only one thread is running at a time.
// If you want a thread pool, the implementation is up to you :)
worker.start();
// Uncomment to wait. If waiting, GUI is not responsive.
//worker.wait();
}
void MainWindow::jobFinished()
{
++counter;
line->setText(QString::number(counter));
}
Qt has a very good multithreading support. You probably do something wrong and we can't help you if you don't provide any code. There are a lot of ways of implementing "responsive" GUI! (Including a lot of ways how to implement another thread!)
Related
In my App I have some problems with threads and GUI messages like QMessageBox or a new dialog. To reproduce I made a small app to show the problem:
mainwindow.cpp
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"
void ThreadAddTree::run() {
//mClass->addTreeEx();
bool b = false;
emit addTree(&b);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QString path = "";
mThreadAddTree = new ThreadAddTree(this, path);
connect(mThreadAddTree, SIGNAL(addTree(bool*)), this, SLOT(on_add_tree(bool*)), Qt::BlockingQueuedConnection);
//,Qt::DirectConnection
mThreadAddTree->start();
}
void MainWindow::on_add_tree(bool* newData) {
QMessageBox::information(this, tr("Information"),
tr("Button click!"));
*newData = true;
}
void MainWindow::addTreeEx()
{
QMessageBox::information(this, tr("Information"),
tr("Button click!"));
}
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWidgets>
#include <QThread>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class ThreadAddTree : public QThread
{
Q_OBJECT
public:
ThreadAddTree(class MainWindow *nClass, const QString &path) {
mPath = path;
mClass = nClass;
}
signals:
void addTree(bool*);
protected:
void run();
QString mPath;
class MainWindow *mClass;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
friend class ThreadAddTree;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void addTreeEx();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
protected:
ThreadAddTree *mThreadAddTree;
protected Q_SLOTS:
void on_add_tree(bool* newData);
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
If I use in Thread the call: mClass->addTreeEx(); the app will crash, in case of non main GUI thread. Understood.
So I uncouple the call with emit a message emit addTree(&b); works well. Messagebox is shown and no crash.
But now it becomes complicated for me. I need to call mClass->addTreeEx(); in my app because it will do a couple of operations. The same function is also used outside an additional thread.
But in one case, the mClass->addTreeEx(); that is running inside the thread need to call the Messagebox.
So my question is here, how to manage, that I can emit the emit addTree(&b); from the function mClass->addTreeEx(); if it was called from the thread and the app will not crash in case of no GUI thread?
Interpreting the code author's intent (to degree) and open to be corrected.
The signal parameter of type bool* in void addTree(bool*); makes not much sense especially for the case with signal sender having the boolean variable on the stack. Either make it void addTree(bool) and send immediate value or just void addTree() and handle the boolean atomic flag on the slot side as std::atomic_bool thread_safe_flag; so checking on that flag will be as actual as possible if (thread_safe_flag). But such handling requires even more to ensure the signal delivered at the right time in sync with the value. This is irrelevant to Qt, though: Why do I need to acquire a lock to modify a shared "atomic" variable before notifying condition_variable and can be done either with or without Qt.
The problem of message box on UI thread preventing the other message box from appearing (this is again an interpretation of the author's problem with the code). We obviously need to have a handle operate the message box, say, dismiss it, in case if it is open already:
QMessageBox* m_msgBoxPtr{nullptr};
std::atomic_bool m_threadSafeFlag;
void UI_Class::mySlotToHandleMsgBox()
{
if (m_msgBoxPtr != nullptr) {
m_msgBoxPtr->close();
m_msgBoxPtr->deleteLater();
m_msgBoxPtr = nullptr;
mySlotToHandleMsgBox();
}
else {
m_msgBoxPtr = new QMessageBox(QMessageBox::Information, title, message);
m_msgBoxPtr->exec(); // assuming we want modal dialog as QMessageBox::information()
// otherwise do m_msgBoxPtr->show()
// if this is set on UI thread only then and no
// "waits" for it on other threads then it being atomic is enough;
// then don't bother with any sync "complications"
m_threadSafeFlag = true;
}
}
mainworker.h
#ifndef MAINWORKER_H
#define MAINWORKER_H
#include <QObject>
class MainWorker : public QObject
{
Q_OBJECT
signals:
void completed(void);
public slots:
void run(void);
};
#endif // MAINWORKER_H
mainworker.cpp
#include "mainworker.h"
#include <QThread>
void MainWorker::run(void)
{
QThread::sleep(1);
emit completed();
}
mainthread.h
#ifndef MAINTHREAD_H
#define MAINTHREAD_H
#include <QThread>
class MainThread : public QThread
{
Q_OBJECT
public:
MainThread(void);
};
#endif // MAINTHREAD_H
mainthread.cpp
#include "mainthread.h"
MainThread::MainThread(void)
: QThread(nullptr)
{
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(void);
signals:
void runJob(void);
public slots:
void jobCompleted(void);
private:
QPushButton m_runButton;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(void)
: QMainWindow(nullptr),
m_runButton(this)
{
connect(&m_runButton, SIGNAL(released()), this, SIGNAL(runJob()));
m_runButton.setText("RUN!");
setCentralWidget(&m_runButton);
}
void MainWindow::jobCompleted(void)
{
QMessageBox::information(this, tr("Info"), tr("Job completed!"));
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
#include "mainthread.h"
#include "mainworker.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow window;
MainThread thread;
MainWorker worker;
worker.connect(&window, SIGNAL(runJob()), &worker, SLOT(run()));
window.connect(&worker, SIGNAL(completed()), &window, SLOT(jobCompleted()));
worker.moveToThread(&thread);
thread.start();
window.show();
int exitCode = a.exec();
thread.quit();
thread.wait();
return exitCode;
}
Of course you can add args to signals and slots and call jobCompleted() slot any time from GUI thread.
I cannot produce a very simple example to getting start with Qt multi-thread. I read a lot of posts and tutorials but still it doesn't work.
Goal
Have a background worker independent from the GUI. Oh, wow...
What I did
A simple example:
create the Engine class
that shows a QMainWindow
and starts a QTimer that prints a number
But if you left-click the title bar of the GUI, keeping pressed the mouse button (i.e. on the minimize button) the counter will stop! Even if it was created in a non-GUI environment and it was moved in another thread!
Why?
main.cpp
#include "engine.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Engine e;
return a.exec();
}
engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <QObject>
#include <QThread>
#include <QTimer>
#include "mainwindow.h"
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject *parent = 0);
private:
MainWindow mainWindow;
QThread *thread;
QTimer *timer;
private slots:
void foo();
};
#endif // ENGINE_H
engine.c
#include "engine.h"
#include <QDebug>
Engine::Engine(QObject *parent) : QObject(parent)
{
thread = new QThread(this);
timer = new QTimer();
timer->setInterval(100);
connect(timer, &QTimer::timeout, this, &Engine::foo);
connect(thread, &QThread::started, timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
timer->moveToThread(thread);
thread->start();
mainWindow.show();
}
void Engine::foo()
{
static int i;
qDebug() << ++i;
}
The QMainWindow contains no code.
Basically, Qt has one thread dealing with the GUI (typically the main thread). Any objects specific to this thread will be blocked by GUI work. You need to keep the GUI outside of your interacting partners.
To be more specific, your Engine object resides in the GUI/main thread. Even while your timer is sent to a worker thread, its signals are dispatched to the slot foo() in the main thread.
You need to de-mangle Engine and the main window such that Engine can reside in its own thread and process signals while the GUI is blocking.
Looks like you moved QTimer instance to your custom thread, but you didnt move Engine instance to this thread, therefore, foo slot of Engine class will be executed in main thread.
I can suggest you using additional helping QObject-derived class instance within Engine class and move it to your new thread in Engine constructor.
With following changes solution works fine even with minimize button pressed (I've commented places where i added or changed anything. Also added new QObject-derived class EngineWorker):
Engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <QObject>
#include <QThread>
#include <QTimer>
#include "mainwindow.h"
#include "engineworker.h"
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject *parent = 0);
private:
MainWindow mainWindow;
QThread *thread;
QTimer *timer;
//additional QObject-derived class
EngineWorker *worker;
private slots:
void foo();
};
#endif // ENGINE_H
Engine.cpp
#include "engine.h"
#include <QDebug>
Engine::Engine(QObject *parent) : QObject(parent)
{
thread = new QThread(this);
timer = new QTimer();
//Creating instance of Engine worker
worker = new EngineWorker();
timer->setInterval(100);
//Connecting Engine worker' foo slot to timer
connect(timer, &QTimer::timeout, worker, &EngineWorker::foo);
connect(thread, &QThread::started, timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
timer->moveToThread(thread);
//Moving worker to custom thread
worker->moveToThread(thread);
thread->start();
mainWindow.show();
}
void Engine::foo()
{
static int i;
qDebug() << ++i;
}
EngineWorker.h
#ifndef ENGINEWORKER_H
#define ENGINEWORKER_H
#include <QObject>
#include <QDebug>
class EngineWorker : public QObject
{
Q_OBJECT
public:
explicit EngineWorker(QObject *parent = 0);
signals:
public slots:
void foo();
};
#endif // ENGINEWORKER_H
EngineWorker.cpp
#include "engineworker.h"
EngineWorker::EngineWorker(QObject *parent) : QObject(parent)
{
}
//foo slot of EngineWorker class
void EngineWorker::foo()
{
static int j;
qDebug() <<"Worker: "<< ++j;
}
Before taking up the main subject, please mind that i`m a beginner of Qt.
I made a AddIm.cpp, and I want to set an image on QLabel in MainWindow.
here is my source in AddIm.cpp
void AddIm::on_pushButton_clicked()
{
MainWindow mainwindow;
mainwindow.setImage();
}
and here is MainWindow.cpp
void MainWindow::setImage()
{
QPixmap pix("./test.jpg");
ui->label->setPixmap(pix);
}
and MainWindow.h
class MainWindow : public QMainWindow
{
public:
void setImage();
~ some source ~
private:
Ui::MainWindow *ui;
};
it doesn't work at all. so I added a button in MainWindow for testing.
and when it clicked, setImage works. but when I execute setImage in AddIm.
it doesn't work. please let me know why
Your problem has nothing to do with your knowledge with Qt but rather your knowledge of c++.
In AddIm::on_pushButton_clicked(), you create a new MainWindow object on the stack, create the image, and then exit the function.
When a function exits, all local stack objects are destroyed. This means that your image is indeed being loaded but the window is being destroyed before you get a chance to see it. Even if it survived to live longer than the function allowed, you never show the window so it remains hidden.
UPDATE:
Change AddIm.cpp to be the following:
void AddIm::on_pushButton_clicked()
{
MainWindow *mainwindow = new MainWindow;
mainwindow->setAttribute(Qt::WA_DeleteOnClose, true);
mainwindow->setImage();
mainwindow->show();
}
Compare this with with your code to see where you might have gone wrong. I tried as much as possible to code it just like you did. I hardly use the designer, i like to code everything. It works as expected.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
#include <QLabel>
#include <QPixmap>
class MainWindow : public QWidget {
Q_OBJECT
public:
void setImage();
private:
QLabel *label;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
void MainWindow::setImage() {
QPixmap pix(":/test.jpg");
label = new QLabel;
label->setPixmap(pix);
label->show();
}
addim.h
#ifndef ADDIM_H
#define ADDIM_H
#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
#include "mainwindow.h"
class AddIm : public QMainWindow {
Q_OBJECT
public:
AddIm(QWidget *parent = 0);
~AddIm();
private slots:
void on_pushButton_clicked();
private:
QPushButton *button;
};
#endif // ADDIM_H
addim.cpp
#include "addim.h"
AddIm::AddIm(QWidget *parent) : QMainWindow(parent) {
button = new QPushButton("Show Image");
setCentralWidget(button);
connect(button, SIGNAL(clicked()), this, SLOT(on_pushButton_clicked()));
}
void AddIm::on_pushButton_clicked() {
MainWindow mainwindow;
mainwindow.setImage();
}
AddIm::~AddIm() {
}
main.cpp
#include "addim.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
AddIm window;
window.show();
return a.exec();
}
You did not shown the window.
First, you have to create a C++ Class not a single .cpp file. Then add a pointer to the window in your AddIm.h file:
private:
MainWindow* mainwindow;
Then in your AddIm.cpp file:
mainwindow = new MainWindow(this);
mainwindow->setAttribute(Qt::WA_DeleteOnClose, true); // prevent memory leak when closing window
mainwindow->setImage();
mainwindow->show();
And remember to include MainWIndow in AddIm.h
#include "mainwindow.h"
I want to run two threads concurrently. I successfully did the same when I ran a program as a QT console application.
Here's the working code of QT console application for multi threading:-
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
#include <QDebug>
#include <QThread>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);
void doSetup(QThread &cThread);
void doSetup2(QThread &cThread2);
signals:
public slots:
void doWork();
void doWork2();
};
#endif // MYOBJECT_H
main.cpp:
#include <QtCore/QCoreApplication>
#include <QThread>
#include <myobject.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread cThread, cThread2;
MyObject cObject, cObject2;
cObject.doSetup(cThread);
cObject.moveToThread(&cThread);
cObject2.doSetup2(cThread2);
cObject2.moveToThread(&cThread2);
cThread.start();
cThread2.start();
qDebug()<<"hello there ";
return a.exec();
}
my object.cpp
#include "myobject.h"
#include <QThread>
#include "tftpServer.h"
MyObject::MyObject(QObject *parent) :
QObject(parent)
{
}
void MyObject::doSetup(QThread &cThread)
{
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::doSetup2(QThread &cThread2)
{
connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MyObject::doWork()
{
for(int i=0; i<1000; i++)
{
qDebug()<<"******************Thread 1";
}
}
void MyObject::doWork2()
{
for(int i=1000; i<2000; i++)
{
qDebug()<<"Thread 2************************";
}
}
Here's the output:
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
..and so on
Now when I use this almost same code and run as a QT GUI application, the threads do not run concurrently, but run one after the other. Here's the code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void doSetup(QThread &cThread);
void doSetup2(QThread &cThread2);
private:
Ui::MainWindow *ui;
public slots:
void doWork();
void doWork2();
};
#endif // MAINWINDOW_H
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QThread>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QThread cThread, cThread2;
MainWindow cObject, cObject2;
cObject.doSetup(cThread);
cObject.moveToThread(&cThread);
cObject2.doSetup2(cThread2);
cObject2.moveToThread(&cThread2);
cThread.start();
cThread2.start();
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <qthread.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug()<<"gui running";
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::doSetup(QThread &cThread)
{
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MainWindow::doSetup2(QThread &cThread2)
{
connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MainWindow::doWork()
{
//QThread::sleep(100);
for(int i=0; i<1000; i++)
{
qDebug()<<"******************Thread 1";
// qDebug()<<i;
}
}
void MainWindow::doWork2()
{
for(int i=1000; i<2000; i++)
{
qDebug()<<"Thread 2************************";
// qDebug()<<i;
}
}
Here's the output:
gui running
gui running
QObject::moveToThread: Widgets cannot be moved to a new thread
QObject::moveToThread: Widgets cannot be moved to a new thread
gui running
******************Thread 1
******************Thread 1
******************Thread 1
******************Thread 1
(and so on...)
******************Thread 1
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
(and so on...)
Note that the only difference b/w console and gui application I made is changed the base class QObject to QMainWindow, and hence the thread objects so created are objects of QObject & QMainWindow respectively, for console and GUI applications.
Hoping I am sufficiently clear, can you please tell me what mistake am I committing, which is making the threads run one after the other and not concurrently, as it does for the console application?
Thank you.
Any class inherited from QWidget cannot reside in thread other than the main GUI thread. Actually this is what the output said. Your solution is a bad design. Separate the work objects (inherited from QObject, residing in another thread) and the visual objects (inherited from QWidget, residing in the GUI thread), to follow the "one class one responsibility" principle.
Another catch in multithreading in Qt is the fact that any QObject cannot reside in different thread than its parent. This is natural because the parent is the owner of the child object, most importantly it takes care of its destruction. To enable this, the child cannot be in other thread. Otherwise the parent might destroy an executing child, for example.
There are yet more catches, for example QPixmap cannot be in other thread than the main GUI etc.
I'm new to C++ and I'm just start to port a program that was originally in python/Qt to C++/Qt in order to take advantage of a better terminal widget that I can embed in my program. Right now I'm a bit stuck, I'm trying to setup where if a different item from a drop-down box is selected the currentIndex() of a tab widget is changed accordingly.
Heres my code so far:
//main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
heres the mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QTimer *timer;
void startMyTimer()
{
timer = new QTimer();
timer->setInterval(1);
timer->start();
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(changeIndex()));
}
private:
Ui::MainWindow *ui;
void changeIndex();
};
#endif // MAINWINDOW_H
And lastly heres the mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
changeIndex();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::changeIndex()
{
if (ui->comboBox->currentText() == "add-apt-repository")
{
ui->stackedWidget->setCurrentIndex(0);
ui->checkBox->setCheckState(Qt::Checked);
}
if (ui->comboBox->currentText() == "apt-get")
{
ui->stackedWidget->setCurrentIndex(1);
ui->checkBox->setCheckState(Qt::Checked);
}
if (ui->comboBox->currentText() == "aptitude")
{
ui->stackedWidget->setCurrentIndex(2);
ui->checkBox->setCheckState(Qt::Checked);
}
if (ui->comboBox->currentText() == "bzr")
{
ui->stackedWidget->setCurrentIndex(3);
ui->checkBox->setCheckState(Qt::Unchecked);
}
if (ui->comboBox->currentText() == "cd")
{
ui->stackedWidget->setCurrentIndex(4);
ui->checkBox->setCheckState(Qt::Unchecked);
}
if (ui->comboBox->currentText() == "chmod")
{
ui->stackedWidget->setCurrentIndex(5);
ui->checkBox->setCheckState(Qt::Checked);
}
}
I've looked at a bunch of QTimer examples but I'm at a loss.
I also tried doing if (ui->comboBox->changeEvent()) but I was probably using that wrong as well.
First, you probably have to mark changeIndex() as a slot, like this:
class MainWindow : public QMainWindow
{
Q_OBJECT
// ...
private slots:
void changeIndex();
private:
Ui::MainWindow *ui;
}
This also requires you to invoke the Qt meta object compiler. If you use qmake, that's already done for you. Otherwise, it depends on your build system.
Second, is there any particular reason for using the timer? You can also connect to one of the combo box's currentIndexChanged signals.
Drop the timer, it's of no use here.
Instead, make changeIndex() a slot by putting it into a "private slots:" section:
public slots:
void changeIndex();
Then connect the combobox's currentIndexChanged signal to your slot, in the MainWindow constructor:
connect( ui->combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeIndex()) );