Make one qthread check the state of the other - c++

I am doing an exercise qt console application on threading, here is the code:
// make two thread, one checking on the state of the other
//////////////////////////////////////
// main.cpp
#include "mytimer.h"
#include "mythread.h"
#include "checkthread.h"
#include <QCoreApplication>
#include <QString>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread mThread1;
mThread1.name = "thread 1";
mThread1.start(QThread::HighestPriority);
CheckThread mCheck(&mThread1);
mCheck.start();
return a.exec();
}
///////////////////////////////////////
// mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtCore>
class MyThread : public QThread
{
public:
MyThread();
void run();
QString name;
bool stop;
int sum;
};
#endif // MYTHREAD_H
//////////////////////////////////////
// mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
sum = 0;
}
void MyThread::run()
{
qDebug() << this->name << " running...";
for(int i=0; i<1000; i++) {
this->sum += i;
qDebug() << this->name << " counting " << sum;
this->sleep(1);
if(this->stop) {
break;
}
}
}
//////////////////////////////////////
// checkthread.h
#ifndef CHECKTHREAD_H
#define CHECKTHREAD_H
#include <QThread>
#include "mythread.h"
class CheckThread : public QThread
{
Q_OBJECT
public:
explicit CheckThread(QObject *parent = 0);
explicit CheckThread(MyThread *tocheck);
void run();
MyThread *tocheck_;
};
#endif // CHECKTHREAD_H
//////////////////////////////////////
// checkthread.cpp
#include "checkthread.h"
CheckThread::CheckThread(QObject *parent) :
QThread(parent)
{
}
CheckThread::CheckThread(MyThread *tocheck) :
tocheck_(tocheck)
{
}
void CheckThread::run() {
while(true) {
this->sleep(1);
if(tocheck_->sum > 15) {
tocheck_->stop = true;
}
}
}
The expected behavior is that mThread1 shoud count to 15 and then stop,
but instead it is stuck at 0.
Interestingly, if I add the following code into the main.cpp file, then it runs
ok:
void Write(QString Filename)
{
QFile fh(Filename);
if(!fh.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << "Could not open file for writing";
return;
}
QTextStream out(&fh);
out << "hi world";
fh.flush();
fh.close();
}
void Read(QString Filename)
{
QFile fh(Filename);
if(!fh.open(QFile::ReadOnly | QFile::Text))
{
qDebug() << "Could not open file for writing";
return;
}
QTextStream in(&fh);
QString mtext = in.readAll();
qDebug() << mtext;
}
I am using qt 4.8 on a kubuntu 13.10 machine, and the ide is qt creator 3.0.1

The problem is the member var stop in mythread.h was not initialized.
But this does not explain why the Read and Write functions in main.cpp solves the problem. Very puzzling.

Related

Redirect qDebug to an emitted signal

I've successfully redirected qDebug() output to a QTextEdit widget. For several reasons, I'd like every qDebug() message to be included in an emitted signal. One reason is that the object which shall receive the output isn't available. Another reason is that I want to redirect the output to different objects depending on which activity is active at the moment (using connect/disconnect of the signal to different slots).
I've made a working example code that redirects qDebug to a QTextEdit widget. Can someone please help me to get this code to emit a signal which includes the qDebug message?
I'm not sure if its possible to have Q_DebugStream emit a signal (I've tried and failed to make a Qt Class out of it).
It must be possible to pass a pointer to a function/slot instead of a pointer to a QTextEdit when calling Q_DebugStream, but I'm not sure how this is done.
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Message from a signal\n");
new Q_DebugStream(std::cout, logView); //Redirect Console output to QTextEdit
Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
qDebug() << "DEBUG MODE ACTIVE";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
q_debugstream.h
//As per forum:
//http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
//A couple of lines added to ensure newlines go between each call.
//Thanks, James!
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QTextEdit>
class Q_DebugStream : public std::basic_streambuf<char>
{
public:
Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
{
log_window = text_edit;
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~Q_DebugStream()
{
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler(){
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
std::cout << msg.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append("");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
QString str(p);
if(str.contains("\n")){
QStringList strSplitted = str.split("\n");
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++){
log_window->append(strSplitted.at(i));
log_window->append("\n");
}
}else{
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (str);
log_window->insertPlainText ("\n");
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get both messages in my QTextEdit:
"Message from a signal"
"DEBUG MODE ACTIVE"
(This answer extracted from an edit to the question - now rolled back).
Here's how I solved this:
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
void dbgMsg(QString);
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Now call Q_DebugStream");
//Redirect qDebug() output to dbgMsg(QString)
new Q_DebugStream(std::cout, this, &MainWindow::dbgMsg);
Q_DebugStream::registerQDebugMessageHandler();
qDebug() << "Debug message";
qWarning() << "Warning!";
qCritical() << "Critical issue!";
qInfo() << "Information";
qDebug() << "This\nis\na\nlong\none.";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
void MainWindow::dbgMsg(QString log) {
emit logSignal(log);
}
q_debugstream.h
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QString>
#include "mainwindow.h"
class Q_DebugStream : public std::basic_streambuf<char> {
public:
Q_DebugStream(std::ostream &stream, MainWindow* obj, void (MainWindow::*dbgMsgPtr)(QString log)): m_stream(stream) {
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
msgObj = obj;
msgHandler = dbgMsgPtr;
}
~Q_DebugStream() {
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler() {
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString message = msg;
switch (type) {
case QtDebugMsg:
message.prepend("qDbg(): ");
break;
case QtWarningMsg:
message.prepend("qWarn(): ");
break;
case QtCriticalMsg:
message.prepend("qCrit(): ");
break;
case QtInfoMsg:
message.prepend("qInfo(): ");
break;
case QtFatalMsg:
message.prepend("qFatal(): ");
abort();
break;
}
message.append(" (" + QString::fromUtf8(context.file) + ")");
message.append(" line: " + QString::number(context.line));
std::cout << message.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v) {
if (v == '\n') {
(msgObj->*msgHandler)("\n");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n) {
QString str(p);
if(str.contains("\n")) {
QStringList strSplitted = str.split("\n");
(msgObj->*msgHandler)(strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++) {
(msgObj->*msgHandler)("\\ " + strSplitted.at(i));
}
} else {
(msgObj->*msgHandler)(str);
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
MainWindow* msgObj;
void (MainWindow::*msgHandler)(QString);
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get these messages in my QTextEdit:
Now call Q_DebugStream
qDbg(): Debug message (..\qDebugFetch\mainwindow.cpp) line: 25
qWarn(): Warning! (..\qDebugFetch\mainwindow.cpp) line: 26
qCrit(): Critical issue! (..\qDebugFetch\mainwindow.cpp) line: 27
qInfo(): Information (..\qDebugFetch\mainwindow.cpp) line: 28
qDbg(): This
\ is
\ a
\ long
\ one. (..\qDebugFetch\mainwindow.cpp) line: 29

2 independent std threads in Qt Gui Application

I want to make my application multithreaded. When I added 2 separate independent threads I have got runtime error message. I can't find the solution. Perhaps someone may help.
Here is link to runtime error image https://postimg.org/image/aasqn2y7b/
threads.h
#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>
class Threads
{
public:
Threads() : m_threadOne(), m_threadTwo(), m_stopper(false) { }
~Threads() {
m_stopper.exchange(true);
if (m_threadOne.joinable()) m_threadOne.join();
if (m_threadTwo.joinable()) m_threadTwo.join();
}
void startThreadOne() {
m_threadOne = std::thread([this]() {
while (true) {
if (m_stopper.load()) break;
std::cout << "Thread 1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
void startThreadTwo() {
m_threadOne = std::thread([this]() {
while (true) {
if (m_stopper.load()) break;
std::cout << "Thread 2" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
private:
std::thread m_threadOne;
std::thread m_threadTwo;
std::atomic<bool> m_stopper;
};
mainwindow.h
#include "threads.h"
#include <QMainWindow>
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) : QMainWindow(parent), ui(new Ui::MainWindow), m_threads() {
ui->setupUi(this);
m_threads.startThreadOne();
m_threads.startThreadTwo();
}
~MainWindow() { delete ui; }
private:
Ui::MainWindow *ui;
Threads m_threads;
};
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Your start thread two is broken:
m_threadOne = std::thread([this]() { ... });
After starting thread one, m_thread_one gets another thread assigned. However, the thread one is not joined, hence the termination.

Qt QJsonModel isn't working at end of network request

So, after I make my QJsonModel, load the data, and set the treeView model, it will load the data normally into the treeView. Although, when I do this after a network request has been finished (serviceRequestFinished(QNetworkReply* reply)), it just doesn't woek. I can qDebug the Json data that is passed, but when it comes down to the Json loading and the model setting. It just fails to finish. Also, for some reason it won't set the Json from the network request, but if I just set it to set the json from a separate button, the json get added successfully. So that means that the Json is formatted correctly. The QJsonModel & QJsonItem are external classes not provided with Qt, that located here.
Here is my code:
Mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QItemSelection>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QTreeWidgetItem>
#include <QJsonDocument>
#include <QJsonObject>
#include "qjsonmodel.h"
#include <QClipboard>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
void setJson(QString json);
void getPlaylistList(QString accessToken);
QString disableStreams();
void appendEditValues(int currentRow);
void addVectorItems();
void streamCheck();
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_radioButtonNoStream_clicked()
{
streamCheck();
}
void on_radioButtonTwitchStream_clicked()
{
streamCheck();
}
void on_radioButtonYTStream_clicked()
{
streamCheck();
}
void on_pushButtonAddVid_clicked();
void on_pushButtonApplyAddVid_clicked();
void on_pushButtonDeleteSelection_clicked();
void on_pushButtonApplyAll_clicked();
public slots:
void serviceRequestFinished(QNetworkReply* reply);
void connectAPI(QString code);
signals:
void authenticate(QString accessCode);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "authdialog.h"
#include <qDebug>
#include <QVector>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QFile>
#include <QUrlQuery>
int val = 0;
int selected;
QVector<QString> vidTitles;
QVector<QString> vidUrls;
QVector<int> vidNumber;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
streamCheck();
ui->groupBoxEditVideo->setEnabled(false);
connect(this, SIGNAL(authenticate(QString)), SLOT(connectAPI(QString)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::streamCheck()
{
if (ui->radioButtonNoStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(false);
ui->groupBoxStreamSetup->setTitle("No Stream Selected!");
}
else if (ui->radioButtonTwitchStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(true);
ui->groupBoxStreamSetup->setTitle("Twitch Setup");
}
else if (ui->radioButtonYTStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(true);
ui->groupBoxStreamSetup->setTitle("YouTube Stream Setup");
}
}
void MainWindow::on_pushButtonAddVid_clicked()
{
AuthDialog *auth = new AuthDialog(this);
auth->setModal(true);
auth->exec();
delete auth;
}
void MainWindow::setJson(QString json)
{
qDebug() << json;
QJsonModel * model = new QJsonModel;
model->loadJson(json.toUtf8());
ui->treeView->setModel(model);
delete model;
}
void MainWindow::getPlaylistList(QString accessToken)
{
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
//
QNetworkRequest request(QUrl("https://www.googleapis.com/youtube/v3/search?part=snippet&forMine=true&order=viewCount&type=video&access_token=" + accessToken));
networkManager->get(request);
}
QString MainWindow::disableStreams()
{
QString line = "";
QString test = "";
QFile file;
file.setFileName("C:/WampStack/apache2/htdocs/index.html");
file.open(QIODevice::ReadWrite);
QTextStream in(&file);
while(!in.atEnd())
{
line += in.readAll();
}
if (line.contains("<!--t-->") && !line.contains("<!--y-->"))
{
//Twitch Stream is on, must turn it off
test = line.replace("<!--t-->", "<!--t");
test = test.replace("><!--tt-->", ">tt-->");
return test;
}
else if (line.contains("<!--y-->") && !line.contains("<!--t-->"))
{
//YouTube Stream is on, must turn it off
test = line.replace("<!--y-->", "<!--y");
test = test.replace("><!--yy-->", ">yy-->");
return test;
}
else if (line.contains("<!--y-->") && line.contains("<!--t-->"))
{
test = line.replace("<!--y-->", "<!--y");
test = test.replace("><!--yy-->", ">yy-->");
test = test.replace("<!--t-->", "<!--t");
test = test.replace("><!--tt-->", ">tt-->");
return test;
}
else
{
return line;
}
file.close();
return line;
}
void MainWindow::connectAPI(QString code)
{
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("code", code);
postData.addQueryItem("client_id", "CLIENT-ID");
postData.addQueryItem("client_secret", "CLIENT-SECRET");
postData.addQueryItem("redirect_uri", "REDIRECT-URI");
postData.addQueryItem("grant_type", "authorization_code");
QNetworkRequest request(QUrl("https://accounts.google.com/o/oauth2/token"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
networkManager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
}
void MainWindow::appendEditValues(int currentRow)
{
}
void MainWindow::addVectorItems()
{
}
void MainWindow::on_pushButtonApplyAddVid_clicked()
{
}
void MainWindow::on_pushButtonDeleteSelection_clicked()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Are you sure?", "Do you want to permanently delete this entry?", QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
}
addVectorItems();
}
void MainWindow::on_pushButtonApplyAll_clicked()
{
QString line = disableStreams();
QFile::remove("C:/WampStack/apache2/htdocs/index.html");
QFile file;
file.setFileName("C:/WampStack/apache2/htdocs/index.html");
file.open(QIODevice::ReadWrite);
//Start code for streaming
if (ui->radioButtonTwitchStream->isChecked() && line.contains("<!--t") && line.contains(">tt-->") && !line.contains("<!--t-->") && !line.contains("<!--tt-->"))
{
QString test = line.replace("<!--t", "<!--t-->");
test = line.replace(">tt-->", "><!--tt-->");
QTextStream stream( &file );
stream << test;
}
else if (ui->radioButtonYTStream->isChecked() && line.contains("<!--y") && line.contains(">yy-->") && !line.contains("<!--y-->") && !line.contains("<!--yy-->"))
{
QString test = line.replace("<!--y", "<!--y-->");
test = test.replace(">yy-->", "><!--yy-->");
QTextStream stream( &file );
stream << test;
}
else if (ui->radioButtonNoStream->isChecked())
{
QTextStream stream(&file);
stream << line;
}
//End code for streaming
file.close();
}
void MainWindow::serviceRequestFinished(QNetworkReply* reply)
{
QByteArray json = reply->readAll();
QString output = QString::fromUtf8(json);
QJsonDocument settdoc = QJsonDocument::fromJson(output.toUtf8());
QJsonObject sett2 = settdoc.object();
if (val == 0)
{
val++;
getPlaylistList(sett2.value(QString("access_token")).toString());
}
else if (val == 1)
{
setJson(output.toUtf8());
}
}
Main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
QJsonModel only works if the the JSON's root element is an object (see the bug report on github.com). I guess your JSON's root is an array.

VTK + QT project: error LNK1169: one or more multiply defined symbols found

My program is using VTK and QT to create a DICOM viewer inside a QT window. I'm trying to setup a custom interactor so I can override input functions. Upon creating this custom interactor class, I'm running into linker errors. My code is below:
FluoroViewer.h:
#ifndef FluoroViewer_H
#define FluoroViewer_H
#include <QMainWindow>
#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkDICOMImageReader.h>
#include <vtkImageViewer2.h>
#include "InteractorStyleImage.h"
namespace Ui {
class FluoroViewer;
}
class FluoroViewer : public QMainWindow {
Q_OBJECT
public:
explicit FluoroViewer(QWidget *parent = 0);
~FluoroViewer();
private slots:
void openDICOMFolder();
void on_loadImages_clicked();
private:
Ui::FluoroViewer *ui;
void drawDICOMSeries(std::string folderDICOM);
vtkSmartPointer<vtkDICOMImageReader> readerDICOMSeries;
vtkSmartPointer<vtkImageViewer2> imageViewerDICOMSeries;
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor;
vtkSmartPointer<InteractorStyleImage> interactorStyle;
};
#endif
FluoroViewer.cxx:
#include "FluoroViewer.h"
#include "ui_FluoroViewer.h"
#include <QFileDialog>
FluoroViewer::FluoroViewer(QWidget *parent) : QMainWindow(parent), ui(new Ui::FluoroViewer) {
ui->setupUi(this);
readerDICOMSeries = vtkSmartPointer<vtkDICOMImageReader>::New();
imageViewerDICOMSeries = vtkSmartPointer<vtkImageViewer2>::New();
renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
//interactorStyle = vtkSmartPointer<InteractorStyleImage>::New();
}
FluoroViewer::~FluoroViewer() {
delete ui;
}
void FluoroViewer::openDICOMFolder() {
QString folderNameDICOM = QFileDialog::getExistingDirectory(this, tr("Open DICOM Folder"), QDir::currentPath(), QFileDialog::ShowDirsOnly);
std::string folderName = folderNameDICOM.toUtf8().constData();
drawDICOMSeries(folderName);
}
void FluoroViewer::drawDICOMSeries(std::string folderDICOM) {
readerDICOMSeries->SetDirectoryName(folderDICOM.c_str());
readerDICOMSeries->Update();
imageViewerDICOMSeries->SetInputConnection(readerDICOMSeries->GetOutputPort());
ui->slider->setMinimum(imageViewerDICOMSeries->GetSliceMin());
ui->slider->setMaximum(imageViewerDICOMSeries->GetSliceMax());
//interactorStyle->SetImageViewer(imageViewerDICOMSeries);
imageViewerDICOMSeries->SetupInteractor(renderWindowInteractor);
//renderWindowInteractor->SetInteractorStyle(interactorStyle);
// SET FLUORO COLOR AND WINDOW LEVELS
imageViewerDICOMSeries->SetColorLevel(523);
imageViewerDICOMSeries->SetColorWindow(-1223);
imageViewerDICOMSeries->SetRenderWindow(ui->vtkRenderer->GetRenderWindow());
imageViewerDICOMSeries->Render();
renderWindowInteractor->Disable();
renderWindowInteractor->Start();
}
void FluoroViewer::on_loadImages_clicked() {
openDICOMFolder();
}
InteractorStyleImage.h:
#ifndef InteractorStyleImage_H
#define InteractorStyleImage_H
#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkImageViewer2.h>
#include <vtkDICOMImageReader.h>
#include <vtkInteractorStyleImage.h>
class InteractorStyleImage : public vtkInteractorStyleImage {
public:
static InteractorStyleImage* New();
vtkTypeMacro(InteractorStyleImage, vtkInteractorStyleImage);
protected:
vtkImageViewer2* imageViewer;
int currentSlice;
int minSlice;
int maxSlice;
public:
void SetImageViewer(vtkImageViewer2* imageViewerTemp);
void MoveSliceForward();
void MoveSliceBackward();
public:
virtual void OnKeyDown() {
std::string key = this->GetInteractor()->GetKeySym();
if (key.compare("Up") == 0) {
cout << "Up arrow key was pressed." << endl;
MoveSliceForward();
}
else if (key.compare("Down") == 0) {
cout << "Down arrow key was pressed." << endl;
MoveSliceBackward();
}
vtkInteractorStyleImage::OnKeyDown();
}
public:
virtual void OnMouseWheelForward() {
cout << "Scrolled mouse wheel forward." << endl;
MoveSliceForward();
//vtkInteractorStyleImage::OnMouseWheelForward();
}
public:
virtual void OnMouseWheelBackward() {
cout << "Scrolled mouse wheel backward." << endl;
if (currentSlice > minSlice) {
MoveSliceBackward();
}
//vtkInteractorStyleImage::OnMouseWheelBackward();
}
};
vtkStandardNewMacro(InteractorStyleImage);
#endif
InteractorStyleImage.cxx:
#include "InteractorStyleImage.h"
void InteractorStyleImage::SetImageViewer(vtkImageViewer2* imageViewerTemp) {
imageViewer = imageViewerTemp;
minSlice = imageViewer->GetSliceMin();
maxSlice = imageViewer->GetSliceMax();
currentSlice = minSlice;
cout << "Slicer: Min = " << minSlice << ", Max = " << maxSlice;
}
void InteractorStyleImage::MoveSliceForward() {
if(currentSlice < maxSlice) {
currentSlice += 1;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}
void InteractorStyleImage::MoveSliceBackward() {
if(currentSlice > minSlice) {
currentSlice -= 1;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}
FluoroViewerDriver.cxx:
#include <QApplication>
#include "FluoroViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
FluoroViewer fluoroViewer;
fluoroViewer.show();
return app.exec();
}
Any thoughts on the issue?
It's most likely due to vtkStandardNewMacro().
You don't show how you build this program, but I am pretty sure that FluoroViewer ends up in one translation unit and InteractorStyleImage in another. However, FluoroViewer #includes InteractorStyleImage.h, which defines the New function. So you'll end up with two same symbols in two separate objects, and the linker craps out. The solution is to move the vtkStandardNewMacro() invocation line into implementation (*.cpp file).
HTH,
Miro

QStateMachine not executing correctly when using QCoreApplication

I am trying to implement a simple state machine within a console application. The signals that are supposed to trigger the state transitions are being emitted, however, the state machine is not reacting to those signals.
This state machine works perfectly when running within a QApplication (i.e. a GUI application), however I am wanting to develop a console application. I suspect there is an issue in the way I have implemented the event loop, as the QStateMachine is not emitting the started() signal.
What is the correct way to execute the application in order for the state machine to function correctly?
main.cpp:
#include <QCoreApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QMetaObject::invokeMethod( &test, "Run", Qt::QueuedConnection );
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0) : QObject(parent) {}
public slots:
void Run();
signals:
void stateChanged();
void debugSignal();
private:
void buildStateMachine();
QStateMachine machine;
private slots:
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QDebug>
void Test::Run()
{
buildStateMachine();
QTextStream qin(stdin);
while (true)
{
QString line = qin.readLine();
qDebug() << "line: " << line;
if (line == "A")
{
qDebug() << "emit stateChanged signal";
emit stateChanged();
}
else if (line == "B")
{
qDebug() << "emit debugSignal";
emit debugSignal();
}
}
}
void Test::buildStateMachine()
{
connect(&machine, SIGNAL(started()), this, SLOT(debugSlot())); // doesn't seem to get triggered... (why is machine not starting?)
connect(this, SIGNAL(debugSignal()), this, SLOT(debugSlot())); // works as expected
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(stateChanged()), s2);
s2->addTransition(this, SIGNAL(stateChanged()), s3);
s3->addTransition(this, SIGNAL(stateChanged()), s1);
connect(s1, SIGNAL(entered()), this, SLOT(runS1())); // these are never triggered
connect(s2, SIGNAL(entered()), this, SLOT(runS2()));
connect(s3, SIGNAL(entered()), this, SLOT(runS3()));
s1->assignProperty(&machine, "state", 1);
s2->assignProperty(&machine, "state", 2);
s3->assignProperty(&machine, "state", 3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
qDebug() << "entered state S1";
}
void Test::runS2()
{
qDebug() << "entered state S2";
}
void Test::runS3()
{
qDebug() << "entered state S3";
}
void Test::debugSlot()
{
qDebug() << "slot was triggered!";
}
Solved my problem. The issue was caused by the infinite while loop. The slot can only be called once the Run function ends, which obviously never occurs.
Here is a working solution. The change to the Qt 5 style signals and slots syntax is optional. If main.cpp is kept as-is from the version shown in the question above, the application will not quit correctly.
main.cpp:
#include <QCoreApplication>
#include <QTimer>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QObject::connect(&test, &Test::finished, &a, &QCoreApplication::quit, Qt::QueuedConnection);
QTimer::singleShot(0, &test, &Test::Run);
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0);
signals:
void next_state();
void finished();
private:
void buildStateMachine();
QStateMachine machine;
public slots:
void Run();
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <iostream>
#include <QTextStream>
Test::Test(QObject * parent) : QObject(parent)
{
buildStateMachine();
}
void Test::Run()
{
QTextStream qin(stdin);
std::cout << "line: ";
QString line = qin.readLine();
if (line == "A")
{
std::cout << "emit stateChanged signal" << std::endl;
emit next_state();
}
else if (line == "q")
{
emit finished();
}
else
{
Run();
}
}
void Test::buildStateMachine()
{
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(next_state()), s2);
s2->addTransition(this, SIGNAL(next_state()), s3);
s3->addTransition(this, SIGNAL(next_state()), s1);
connect(s1, &QState::entered, this, &Test::runS1);
connect(s2, &QState::entered, this, &Test::runS2);
connect(s3, &QState::entered, this, &Test::runS3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
std::cout << "entered state S1" << std::endl;
Run();
}
void Test::runS2()
{
std::cout << "entered state S2" << std::endl;
Run();
}
void Test::runS3()
{
std::cout << "entered state S3" << std::endl;
Run();
}
void Test::debugSlot()
{
std::cout << "debug slot was triggered!" << std::endl;
Run();
}