In my Qt GUI application, there are 2 threads.
The non-GUI thread receives data on a serial port very frequently. This data needs to be displayed on the GUI which is the main thread. Scrolling also needs to be implemented.
How do I implement this? What Qt classes should be used?
You need to send a signal from the thread, containing the QString variable, and create a slot in the Widget containing the label to receive that data.
Documentation : http://doc.qt.io/qt-5.7/signalsandslots.html
Here you have a basic prototype for your needs :
In your customthread.h
signals:
portRead(QString text);
In your customthread.cpp
void process() //Your process function
{
QString text = readFromSerialPort(); // Your function that reads the SP
emit portRead(text)
}
In your mainwindow.h
slots:
void setLabelText(QString text);
In your mainwindow.cpp
Widget::Widget(QWidget *parent)
{
CustomThread *thread = new CustomThread();
//Some code
connect(thread,SIGNAL(portRead(QString)),this,SLOT(setLabelText(QString)));
}
void setLabelText(QString text)
{
this->label->setText(text);
}
Related
I am using Qt 4.6.0 (32 bit) under Windows 7 Ultimate. Consider the following QThread:
Interface
class ResultThread : public QThread
{
Q_OBJECT
QString _post_data;
QNetworkAccessManager _net_acc_mgr;
signals:
void onFinished(QNetworkReply* net_reply);
private slots:
void onReplyFinished(QNetworkReply* net_reply);
public:
ResultThread();
void run();
void setPostData(const QString& post_data);
};
Implementation
ResultThread::ResultThread() : _net_acc_mgr(this)
{
connect(&_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
}
void ResultThread::onReplyFinished(QNetworkReply* net_reply)
{
emit onFinished(net_reply);
}
void ResultThread::setPostData(const QString& post_data)
{
_post_data = post_data;
}
void ResultThread::run()
{
_net_acc_mgr.post(QNetworkRequest(QUrl("http://[omitted]")),
QByteArray(_post_data.toStdString().c_str()));
}
Whenever _net_acc_mgr.post() is executed in ResultThread::run(), I got the following Application Output in Qt Creator:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x22fe58), parent's thread is QThread(0x9284190), current thread is ResultThread(0x22fe48)
What does this mean? How to solve it?
The run() member function is executed in a different thread, rather than the thread where QNetworkRequestManager object was created.
This kind of different-thread problems happen all the time with Qt when you use multiple threads. The canonical way to solve this problem is to use signals and slots.
Create a slot in the object where QNetworkRequestManager belongs to, create a signal in ResultThread and connect both of the somewhere, the constructor of ResultThread would be a good place.
The code which is currently in ResultThread::run() goes to the new slot, and is replaced by a emit(yourSignal()). If necessary send a pointer to your ResultThread as a parameter with your emit function to gain access to member functions/variables.
I received this error message when I forgot to set the QNetworkRequestManager's parent.
nam = new QNetworkAccessManager(this);
So far, I understand that we have two threads in QML, our main application thread, and our "scene graph" thread : http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html
I've implemented my own vtkQmlItem with the help of this link : http://doc.qt.io/qt-5/qtquick-scenegraph-openglunderqml-example.html
and I've noticed that my vtkscene is only rendered when the afterrendering signal is emitted by the qml flow.
So far, everything is ok and works perfectly, I can see my vtk scene and can even interract with it.
But I would like to also programmatically render my vtk scene as well, since I want to do an animation by moving the camera around a vtk object.
Calling renderer->render() directly shows a lot of vtk error, and does not seem to be the good way to do this.
Calling this->window()->update() seems to put the event in the eventLoop, when I want it to be handled instantly. The only way I've managed to make it work instantly is by using QApplication::processEvents(), which is a hack I don't like and would love another solution.
So the pseudocode of the working solution that I don't like is the following :
for (int i = 0; i < 50; i++)
{
ChangeCameraPosition(i); // Change the position and orientation of the vtk camera
this->window()->update();
QApplication::processEvents(); // The hack I don't like
QThread::msleep(500);
}
For people looking for a solution for this using Qt QuickControls 2 and VTK 8, you can find a working example in this repository https://github.com/nicanor-romero/QtVtk with building instructions in the README.
the problem is actually a bit complicated and if nothing changed in the past few months, there is still no support for QtQuick in VTK, which means no simple few lines solution. You can find support classes for QtWidgets in VTK/GUISupport/QtOpenGL/ and use them as a template to derive support for qml. But mainly I recommend checking this thread for a discussion about this topic.
The key point is that QtQuick holds the openGL context for the qml window you are trying to render into in a dedicated thread and it won't let anything else get that context. So in order to render into it from VTK, you have to do it within that thread. This means:
1) Create your own vtkRenderWindow that overrides the Render() method such that it is ensured it happens in the qml's render thread.
2) Make that render window render into a framebuffer object provided by the qtquick (instance of QQuickFramebufferObject).
3) Interconnect vtk's rendering signals with the qt's rendering methods -> e.g. when the vtk render window calls makeCurrent, the qt's rendering thread "wakes up".
Here is my implementation based on Taylor Braun-Jones' template linked above. It might not be perfect, but it works for me (I have removed some parts specific to my app so it might not compile straight away, but it should put you on a path to some working solution):
qmlVtk.h:
#include <vtkEventQtSlotConnect.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkRenderer.h>
#include <QtQuick/QQuickFramebufferObject>
// Use the OpenGL API abstraction from Qt instead of from VTK because vtkgl.h
// and other Qt OpenGL-related headers do not play nice when included in the
// same compilation unit
#include <QOpenGLFunctions>
#include <qqmlapplicationengine.h>
class QVTKFramebufferObjectRenderer;
class QVTKInteractorAdapter;
class vtkInternalOpenGLRenderWindow;
class QVTKFramebufferObjectRenderer;
class QVTKFrameBufferObjectItem : public QQuickFramebufferObject
{
Q_OBJECT
public:
QVTKFrameBufferObjectItem(QQuickItem *parent = 0);
~QVTKFrameBufferObjectItem();
Renderer *createRenderer() const;
vtkSmartPointer<vtkInternalOpenGLRenderWindow> GetRenderWindow() const;
protected:
// Called once before the FBO is created for the first time. This method is
// called from render thread while the GUI thread is blocked.
virtual void init();
vtkSmartPointer<vtkInternalOpenGLRenderWindow> m_win;
QVTKInteractorAdapter* m_irenAdapter;
vtkSmartPointer<vtkEventQtSlotConnect> mConnect;
friend class QVTKFramebufferObjectRenderer;
// Convert the position of the event from openGL coordinate to native coordinate
QMouseEvent openGLToNative(QMouseEvent const& event);
virtual void mouseMoveEvent(QMouseEvent * event);
virtual void mousePressEvent(QMouseEvent * event);
virtual void mouseReleaseEvent(QMouseEvent * event);
virtual void mouseDoubleClickEvent(QMouseEvent * event);
virtual void wheelEvent(QWheelEvent *event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void focusInEvent(QFocusEvent * event);
virtual void focusOutEvent(QFocusEvent * event);
protected Q_SLOTS:
// slot to make this vtk render window current
virtual void MakeCurrent();
// slot called when vtk wants to know if the context is current
virtual void IsCurrent(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data);
// slot called when vtk wants to start the render
virtual void Start();
// slot called when vtk wants to end the render
virtual void End();
// slot called when vtk wants to know if a window is direct
virtual void IsDirect(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data);
// slot called when vtk wants to know if a window supports OpenGL
virtual void SupportsOpenGL(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data);
};
/// <summary>
/// An extension of vktGenericOpenGLRenderWindow to work with Qt.
/// Serves to write VTK-generated render calls to a framebuffer provided and maintained by Qt. It is meant to be used within Qt render loop, i.e. using Qt's render thread.
/// </summary>
/// <seealso cref="vtkGenericOpenGLRenderWindow" />
/// <seealso cref="QOpenGLFunctions" />
class vtkInternalOpenGLRenderWindow : public vtkGenericOpenGLRenderWindow, protected QOpenGLFunctions
{
public:
static vtkInternalOpenGLRenderWindow* New();
vtkTypeMacro(vtkInternalOpenGLRenderWindow, vtkGenericOpenGLRenderWindow)
virtual void OpenGLInitState();
// Override to use deferred rendering - Tell the QSG that we need to
// be rendered which will then, at the appropriate time, call
// InternalRender to do the actual OpenGL rendering.
virtual void Render();
// Do the actual OpenGL rendering
void InternalRender();
// Provides a convenient way to set the protected FBO ivars from an existing
// FBO that was created and owned by Qt's FBO abstraction class
// QOpenGLFramebufferObject
void SetFramebufferObject(QOpenGLFramebufferObject *fbo);
QVTKFramebufferObjectRenderer *QtParentRenderer;
protected:
vtkInternalOpenGLRenderWindow();
~vtkInternalOpenGLRenderWindow()
{
// Prevent superclass destructors from destroying the framebuffer object.
// QOpenGLFramebufferObject owns the FBO and manages it's lifecyle.
this->OffScreenRendering = 0;
}
};
qmlVtk.cpp:
#include "QVTKFramebufferObjectItem.h"
#include <QQuickFramebufferObject>
#include <QQuickWindow>
#include <QOpenGLFramebufferObject>
#include <QVTKInteractorAdapter.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkObjectFactory.h>
#include <vtkSmartPointer.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
#include <qglfunctions.h>
class QVTKFramebufferObjectRenderer : public QQuickFramebufferObject::Renderer
{
friend class vtkInternalOpenGLRenderWindow;
public:
QVTKFramebufferObjectRenderer(vtkSmartPointer<vtkInternalOpenGLRenderWindow> rw) :
m_framebufferObject(0)
{
m_vtkRenderWindow = rw;
m_vtkRenderWindow->QtParentRenderer = this;
}
~QVTKFramebufferObjectRenderer()
{
m_vtkRenderWindow->QtParentRenderer = 0;
glFrontFace(GL_CCW); // restore default settings
}
virtual void synchronize(QQuickFramebufferObject * item)
{
// the first synchronize call - right before the the framebufferObject
// is created for the first time
if (!m_framebufferObject)
{
QVTKFrameBufferObjectItem *vtkItem = static_cast<QVTKFrameBufferObjectItem*>(item);
vtkItem->init();
}
}
virtual void render()
{
m_vtkRenderWindow->InternalRender(); // vtkXOpenGLRenderWindow renders the scene to the FBO
}
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
{
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::Depth);
m_framebufferObject = new QOpenGLFramebufferObject(size, format);
m_vtkRenderWindow->SetFramebufferObject(m_framebufferObject);
return m_framebufferObject;
}
vtkSmartPointer<vtkInternalOpenGLRenderWindow> m_vtkRenderWindow;
QOpenGLFramebufferObject *m_framebufferObject;
};
vtkStandardNewMacro(vtkInternalOpenGLRenderWindow);
vtkInternalOpenGLRenderWindow::vtkInternalOpenGLRenderWindow() :
QtParentRenderer(0)
{
vtkOpenGLRenderWindow::OpenGLInitContext();
}
void vtkInternalOpenGLRenderWindow::OpenGLInitState()
{
this->MakeCurrent();
vtkOpenGLRenderWindow::OpenGLInitState();
// Before any of the gl* functions in QOpenGLFunctions are called for a
// given OpenGL context, an initialization must be run within that context
initializeOpenGLFunctions();
glFrontFace(GL_CW); // to compensate for the switched Y axis
}
void vtkInternalOpenGLRenderWindow::InternalRender()
{
vtkOpenGLRenderWindow::Render();
}
//
// vtkInternalOpenGLRenderWindow Definitions
//
void vtkInternalOpenGLRenderWindow::Render()
{
this->QtParentRenderer->update();
}
void vtkInternalOpenGLRenderWindow::SetFramebufferObject(QOpenGLFramebufferObject *fbo)
{
// QOpenGLFramebufferObject documentation states that "The color render
// buffer or texture will have the specified internal format, and will
// be bound to the GL_COLOR_ATTACHMENT0 attachment in the framebuffer
// object"
this->BackLeftBuffer = this->FrontLeftBuffer = this->BackBuffer = this->FrontBuffer =
static_cast<unsigned int>(GL_COLOR_ATTACHMENT0);
// Save GL objects by static casting to standard C types. GL* types
// are not allowed in VTK header files.
QSize fboSize = fbo->size();
this->Size[0] = fboSize.width();
this->Size[1] = fboSize.height();
this->NumberOfFrameBuffers = 1;
this->FrameBufferObject = static_cast<unsigned int>(fbo->handle());
this->DepthRenderBufferObject = 0; // static_cast<unsigned int>(depthRenderBufferObject);
this->TextureObjects[0] = static_cast<unsigned int>(fbo->texture());
this->OffScreenRendering = 1;
this->OffScreenUseFrameBuffer = 1;
this->Modified();
}
void QVTKFrameBufferObjectItem::Start()
{
m_win->OpenGLInitState();
}
void QVTKFrameBufferObjectItem::End()
{
}
void QVTKFrameBufferObjectItem::MakeCurrent()
{
this->window()->openglContext()->makeCurrent(this->window());
}
void QVTKFrameBufferObjectItem::IsCurrent(vtkObject*, unsigned long, void*, void* call_data)
{
bool* ptr = reinterpret_cast<bool*>(call_data);
*ptr = this->window()->openglContext();
}
void QVTKFrameBufferObjectItem::IsDirect(vtkObject*, unsigned long, void*, void* call_data)
{
int* ptr = reinterpret_cast<int*>(call_data);
*ptr = QGLFormat::fromSurfaceFormat(this->window()->openglContext()->format()).directRendering();
}
void QVTKFrameBufferObjectItem::SupportsOpenGL(vtkObject*, unsigned long, void*, void* call_data)
{
int* ptr = reinterpret_cast<int*>(call_data);
*ptr = QGLFormat::hasOpenGL();
}
QVTKFrameBufferObjectItem::QVTKFrameBufferObjectItem(QQuickItem *parent) : QQuickFramebufferObject(parent)
{
setAcceptedMouseButtons(Qt::AllButtons);
m_irenAdapter = new QVTKInteractorAdapter(this);
m_win = vtkSmartPointer<vtkInternalOpenGLRenderWindow>::New();
// make a connection between the vtk signals and qt slots so that an initialized and madeCurrent opengl context is given to the vtk
// we probably need only the Start(), MakeCurrent() and End() one, but just to be sure...
mConnect = vtkSmartPointer<vtkEventQtSlotConnect>::New();
mConnect->Connect(m_win, vtkCommand::WindowMakeCurrentEvent, this, SLOT(MakeCurrent()));
mConnect->Connect(m_win, vtkCommand::WindowIsCurrentEvent, this, SLOT(IsCurrent(vtkObject*, unsigned long, void*, void*)));
mConnect->Connect(m_win, vtkCommand::StartEvent, this, SLOT(Start()));
mConnect->Connect(m_win, vtkCommand::EndEvent, this, SLOT(End()));
mConnect->Connect(m_win, vtkCommand::WindowIsDirectEvent, this, SLOT(IsDirect(vtkObject*, unsigned long, void*, void*)));
mConnect->Connect(m_win, vtkCommand::WindowSupportsOpenGLEvent, this, SLOT(SupportsOpenGL(vtkObject*, unsigned long, void*, void*)));
}
QVTKFrameBufferObjectItem::~QVTKFrameBufferObjectItem()
{
mConnect->Disconnect(); // disconnect all slots
if (m_irenAdapter)
delete m_irenAdapter;
}
QQuickFramebufferObject::Renderer *QVTKFrameBufferObjectItem::createRenderer() const
{
return new QVTKFramebufferObjectRenderer(m_win);
}
vtkSmartPointer<vtkInternalOpenGLRenderWindow> QVTKFrameBufferObjectItem::GetRenderWindow() const
{
return m_win;
}
void QVTKFrameBufferObjectItem::init()
{
}
// theoretically not needed now - the Y is being flipped in render and devicePixelRatio will almost always be = 1 on a PC anyway...but lets keep it to be sure
QMouseEvent QVTKFrameBufferObjectItem::openGLToNative(QMouseEvent const& event)
{
QPointF localPos(event.localPos());
localPos.setX(localPos.x() * window()->devicePixelRatio());
localPos.setY(localPos.y() * window()->devicePixelRatio());
QMouseEvent nativeEvent(event.type(), localPos, event.button(), event.buttons(), event.modifiers());
return nativeEvent;
}
void QVTKFrameBufferObjectItem::mouseMoveEvent(QMouseEvent * event)
{
m_win->GetInteractor()->SetSize(this->width(), this->height());
QMouseEvent nativeEvent = openGLToNative(*event);
m_irenAdapter->ProcessEvent(&nativeEvent, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::mousePressEvent(QMouseEvent * event)
{
m_win->GetInteractor()->SetSize(this->width(), this->height());
QMouseEvent nativeEvent = openGLToNative(*event);
m_irenAdapter->ProcessEvent(&nativeEvent, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::mouseReleaseEvent(QMouseEvent * event)
{
m_win->GetInteractor()->SetSize(this->width(), this->height());
QMouseEvent nativeEvent = openGLToNative(*event);
m_irenAdapter->ProcessEvent(&nativeEvent, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::wheelEvent(QWheelEvent *event)
{
m_irenAdapter->ProcessEvent(event, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::keyPressEvent(QKeyEvent* event)
{
m_irenAdapter->ProcessEvent(event, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::keyReleaseEvent(QKeyEvent* event)
{
m_irenAdapter->ProcessEvent(event, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::focusInEvent(QFocusEvent * event)
{
m_irenAdapter->ProcessEvent(event, this->m_win->GetInteractor());
}
void QVTKFrameBufferObjectItem::focusOutEvent(QFocusEvent * event)
{
m_irenAdapter->ProcessEvent(event, this->m_win->GetInteractor());
}
To use it, define an instance of the framebuffer in your qml form and stretch it across the window you want to render into, e.g. like this (assuming you registered the QVTKFrameBufferObjectItem as a QVTKFrameBuffer in qml e.g. like this qmlRegisterType<QVTKFrameBufferObjectItem>("VtkQuick", 1, 0, "QVTKFrameBuffer");):
import VtkQuick 1.0
QVTKFrameBuffer
{
id: renderBuffer
anchors.fill : parent
Component.onCompleted :
{
myCppDisplay.framebuffer = renderBuffer // tell the c++ side of your app that this is the framebuffer into which it should render
}
}
You then use the vtkRenderWindow you get by myCppDisplay.framebuffer.GetRenderWindow() the same way you would use any other vtkRenderWindow if you were rendering into a vtk-managed window, i.e. you can assign vtkRenderer to it, assign actors to that renderer, call theWindow.Render() as you wish and it will all be rendered into the qml component to which you assigned the framebuffer.
Two notes: 1) the vtk and qt use different coordinate system, you need to flip the y-coordinate...I am doing it by assigning a scale transformation to the camera, but there is plenty of other ways to do it:
vtkSmartPointer<vtkTransform> scale = vtkSmartPointer<vtkTransform>::New();
scale->Scale(1, -1, 1);
renderer->GetActiveCamera()->SetUserTransform(scale);
2) things get quite tricky once you start using multiple threads - you have to make sure that you are not trying to render in two different threads, because they would compete for that one QtQuick's rendering thread. This does not mean only not calling renderWindow.Render() in parallel - that is easy to avoid - but you have to realize that that qt thread is used also for rendering the GUI, so you might get into trouble this way (updating GUI while doing VTK rendering).
I tried implementing this example and have successfully compiled and ran it as well but I am facing some challenges with respect to the code.
I am not able to figure out where these lines are defined as they are giving me runtime errors and are not getting recognized as well.
(Defined in qmlVtk.cpp -> SetFramebufferObject() function)
this->NumberOfFrameBuffers = 1;
this->FrameBufferObject = static_cast<unsigned int>(fbo->handle());
this->DepthRenderBufferObject = 0;
this->TextureObjects[0] = static_cast<unsigned int>(fbo->texture());
this->OffScreenRendering = 1;
this->OffScreenUseFrameBuffer = 1;
Also, can anyone guide me as how to integrate QML with OpenGLWidget.
This is my btconnect.h file
#ifndef BTCONNECT_H
#define BTCONNECT_H
#include "scandialog.h"
namespace Ui {
class BTConnect;
}
class BTConnect : public QWidget
{
Q_OBJECT
public:
explicit BTConnect(QWidget *parent = 0);
~BTConnect();
private slots:
void on_ScanButton_clicked();
void ScanBTDevices();
//some slots here
void ScanDialogShow();
void ScanDialogClose();
public slots:
//some slots here
private:
Ui::BTConnect *ui;
QProcess BTscan_Process;
scanDialog *scan;
};
#endif // BTCONNECT_H
btconnect.cpp
BTConnect::BTConnect(QWidget *parent) :
QWidget(parent),
ui(new Ui::BTConnect)
{
//set the userinterface as BTConnect.ui
ui->setupUi(this);
scan = new scanDialog(this);
}
void BTConnect::ScanDialogShow()
{
scan->show();
}
void BTConnect::ScanDialogClose()
{
scan->close();
}
void BTConnect::ScanBTDevices()
{
ScanDialogShow();
//Command to scan nearby bluetooth devices
//"hcitool scan"
QString cmd("hcitool scan");
//start the process
BTscan_Process.start(cmd);
//Wait for the processs to finish with a timeout of 20 seconds
if(BTscan_Process.waitForFinished(20000))
{
//Clear the list widget
this->ui->listWidget->clear();
//Read the command line output and store it in QString out
QString out(BTscan_Process.readAllStandardOutput());
//Split the QString every new line and save theve in a QStringList
QStringList OutSplit = out.split("\n");
//Parse the QStringList in btCellsParser
btCellsParser cp(OutSplit);
for(unsigned int i = 0; i<cp.count(); i++)
{
//writing in listwidget
}
}
ScanDialogClose();
}
void BTConnect::on_ScanButton_clicked()
{
//Scan for available nearby bluetooth devices
ScanBTDevices();
}
if I use the above code, the qdialog scandialog does open when the process begins and closes when the data is loaded in qlistwidget, but the contents of qdialog scandialog are not displayed. If I were to change show() to exec(), the contents will be shown but the QProcess does not run until the dialog is closed.
I want the dialog to open when the Qprocess starts and close when the qlistwidget is loaded with data from the scan. And I want the contents of scandialog to be displayed. It has two labels. One with .GIF file and another with text saying scanning.
Any help is appreciated.
you never return to the event loop when you do show (because of waitForFinished) and you never continue to the processing code when you do exec
instead of the waitForFinished you should connect to the finished signal and handle it there and use a single shot timer that will cancel it:
void BTConnect::on_BTscanFinished()//new slot
{
//Clear the list widget
this->ui->listWidget->clear();
//Read the command line output and store it in QString out
QString out(BTscan_Process.readAllStandardOutput());
//Split the QString every new line and save theve in a QStringList
QStringList OutSplit = out.split("\n");
//Parse the QStringList in btCellsParser
btCellsParser cp(OutSplit);
for(unsigned int i = 0; i<cp.count(); i++)
{
//writing in listwidget
}
ScanDialogClose();
}
void BTConnect::ScanBTDevices()
{
ScanDialogShow();
//Command to scan nearby bluetooth devices
//"hcitool scan"
QString cmd("hcitool scan");
//start the process
connect(BTscan_Process, SIGNAL(finished()), this, SLOT(on_BTscanFinished()));
BTscan_Process.start(cmd);
QTimer::singleShot(20000, scan, SLOT(close()));
}
The problem is that QDialog::exec and QProcess::waitForFinished functions block event loop. Never ever block event loop. So you just need to do things more asynchronously.
QProcess class can be handled asynchronously using signals like readReadStandardOutput. And QDialog can be shown asynchronously using open slot.
The example:
void ScanBTDevices() {
// Open dialog when process is started
connect(process, SIGNAL(started()), dialog, SLOT(open()));
// Read standard output asynchronously
connect(process, SIGNAL(readyReadStandardOutput()), SLOT(onReadyRead()));
// Start process asynchronously (for example I use recursive ls)
process->start("ls -R /");
}
void onReadyRead() {
// Write to list widget
dialog->appendText(QString(process->readAllStandardOutput()));
}
The data will be appended to the dialog during generating by process.
Also using QProcess::finished signal and you can close the dialog.
How can I pass data from one form to another in Qt?
I have created a QWidgetProgect -> QtGuiApplication, I have two forms currently. Now I want to pass data from one form to another.
How can I achieve that ?
Thanks.
Here are some options that you might want to try:
If one form owns the other, you can just make a method in the other and call it
You can use Qt's Signals and slots mechanism, make a signal in the form with the textbox, and connect it to a slot you make in the other form (you could also connect it with the textbox's textChanged or textEdited signal)
Example with Signals and Slots:
Let's assume that you have two windows: FirstForm and SecondForm. FirstForm has a QLineEdit on its UI, named myTextEdit and SecondForm has a QListWidget on its UI, named myListWidget.
I'm also assuming that you create both of the windows in the main() function of your application.
firstform.h:
class FistForm : public QMainWindow
{
...
private slots:
void onTextBoxReturnPressed();
signals:
void newTextEntered(const QString &text);
};
firstform.cpp
// Constructor:
FistForm::FirstForm()
{
// Connecting the textbox's returnPressed() signal so that
// we can react to it
connect(ui->myTextEdit, SIGNAL(returnPressed),
this, SIGNAL(onTextBoxReturnPressed()));
}
void FirstForm::onTextBoxReturnPressed()
{
// Emitting a signal with the new text
emit this->newTextEntered(ui->myTextEdit->text());
}
secondform.h
class SecondForm : public QMainWindow
{
...
public slots:
void onNewTextEntered(const QString &text);
};
secondform.cpp
void SecondForm::onNewTextEntered(const QString &text)
{
// Adding a new item to the list widget
ui->myListWidget->addItem(text);
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Instantiating the forms
FirstForm first;
SecondForm second;
// Connecting the signal we created in the first form
// with the slot created in the second form
QObject::connect(&first, SIGNAL(newTextEntered(const QString&)),
&second, SLOT(onNewTextEntered(const QString&)));
// Showing them
first.show();
second.show();
return app.exec();
}
You could also use pointers to access the QTextEdit (assuming that's what you're using) from the other form.
Following from Venemo's example (where FirstForm has the QTextEdit and SecondForm's the one you need to access the QTextEdit from):
firstform.h:
class FistForm : public QMainWindow
{
...
public:
QTextEdit* textEdit();
};
firstform.cpp:
QTextEdit* FirstForm::textEdit()
{
return ui->myTextEdit;
}
You can then access the QTextEdit's text in SecondForm with something like this (assuming your instance of FirstForm is called firstForm):
void SecondForm::processText()
{
QString text = firstForm->textEdit()->toPlainText();
// do something with the text
}
I am trying to build an QT State Maschine. I have some States, and for those States i need Transition that alter the Graphics on my gui.
The Problem i having and the only reason i am asking, i am Stuck and Point 1.
The compiler cant identifie the QTEventTransition. I have QT 4.6 wroking with QT Creator on Windows.
The compiler does not find Header #include < QtEventTransition >
This is what i did i never did this bevor but i think it should be correct, I have A Header File where i have my Transitions Declareted like this:
class validateBoatTransition : public QtEventTransition
{
public:
validateBoatTransition(Widget *widget,ServerSkeleton* server);
protected:
bool eventTest(QEvent *e);
void onTransition(QEvent *);
private:
Chart* ourChart;
Message current;
BarelySocket* myBarelySocket;
};
Than i have my Cpp File where i have this:
validateBoatTransition::validateBoatTransition(Widget *widget,ServerSkeleton* server)
{
}
void validateBoatTransition::onTransition(QEvent *e)
{
/*
My Logik should go here
*/
}
What i want is that if the Transition is activated by an Button (clicked) it should fire this transition!
I searched the net, but cant find an solution. Can i do that? I should i think.
Yours Thomas
Maybe you should take a look to signals/slot mechanism. I think this is what you need to achieve what you want.
Make your onTransition function a slot instead of an event handler and connect it to the signal clicked of the button.
class validateBoatTransition : public QtEventTransition
{
...
public slots:
void onTransition();
...
}
Somewhere in your code, connect the button to the slot:
QObject::connect(myButton, signal(clicked()), myValidateBoatTransition, slot(onTransition());
Each time the button will be clicked the execution will go through the onTransition function.
I think you're trying to use wrong classes/mechanisms to achieve your goals. If I understand you correctly, you have some GUI and after clicking some button you want to validate some stuff and if this validation is successful the state machine should change it's state. I'd write it this way:
Create some class to handle validation:
class BoatValidator : public QObject
{
Q_OBJECT
// boring stuff like constructor, etc.
public slots:
void validate()
{
if ( /*your validation logic goes here*/ ) {
emit boatTransition();
}
}
signals:
void boatTransition(); // emitted if validation is succesful
};
Then you connect your QPushButton::clicked() to BoatValidator::validate() and use BoatValidator::boatTransition() signal to drive the state machine:
QStateMachine machine;
QState *state1 = new QState(&machine);
QState *state2 = new QState(&machine);
// more state machine setup
// connect validator and button
QPushButton button;
BoatValidator validator;
connect(&button, SIGNAL(clicked()), &validator, SLOT(validate()));
// use validator to change states
state1->addTransition(&validator, SIGNAL(boatTransition()), state2);
Generally I'd use signal to drive state machine, unless some transitions are obviously event driven (for example some QEvent::Enter/QEvent::Leave on GUI widgets, etc.).
What i wanted to do is build a Qt State Machine. The Problem was that i could not trigger my own Transitions (let alone with my own Events). The answers given are good but would lead to a messy code. Why should i use a QT State Machine if i could not use the QT Transitions?
The First Problem above is solved, if you create a new Project. QT Creater is very annoying.
But here now my solution , may it help others.
First my State:
class ServerState : public QState
{
Q_OBJECT
public:
ServerState(QPushButton * pushButton);
~ServerState();
public slots:
void buttonWasClicked();
protected:
void onEntry(QEvent *e);
void onExit(QEvent *e);
private:
QPushButton * pushButton;
};
Normal, but you see i added an Slot. This slot enables me to connect a bottom signal or a Widget Mouse Press Signal to it !
Like this:
QStateMachine *machine = new QStateMachine(this);
ServerState *s1 = new ServerState(connectButton);
connect(connectButton, SIGNAL(clicked()), s1, SLOT(buttonWasClicked()));
machine->addState(s1);
s1->addTransition(connectTransition);
all i needed to to is now fire a declared Event like this one :
#define RegisterToServerEventIndex User+5
class ConnectToServerEvent : public QEvent
{
public:
ConnectToServerEvent() : QEvent(QEvent::Type(QEvent::ConnectToServerEventIndex))
{}
};
when the slot was called:
void ServerState::buttonWasClicked()
{
this->machine()->postEvent(new ConnectToServerEvent());
qDebug("ServerState::buttonWasClicked");
}
The QT State Machine would now call all the Transitions , link with this state:
ConnectToServerTransition::ConnectToServerTransition(QPushButton * pushButtonB,ServerSkeleton* serverSkeleton)
{
this->pushButtonB = pushButtonB;
this->pushButtonB->hide();
this->serverSkeleton = serverSkeleton;
qDebug("ConnectToServerTransition::ConnectToServerTransition");
}
bool ConnectToServerTransition::eventTest(QEvent *e)
{
return (e->type() == QEvent::ConnectToServerEventIndex);
}
void ConnectToServerTransition::onTransition(QEvent *e)
{
if (true == this->serverSkeleton->initalisieren())
{
this->pushButtonB->show();
}else{
qDebug("Conection to Server faild");
}
emit kill();
return;
}
Whats so great that i dare to post?
Well first you can link a Qt SM to a widget where a mouse press event , or somthing else, is called and process the raw data to a an level you need later in your program. All you then need to do is, to emit the singal:
void Widget::mousePressEvent(QMouseEvent *event){
Coordinates current;
current.line = 0;
current.row = A;
current.row = (Row) (event->x() / 30); // 30 = breite von einen Feld
current.line = event->y() / 30; // 30 = länge von einen Feld
emit this->clicked(current);
return;
}
Then this enhenced information (current) is passed to the slot at my state, where i chose to call the correct transition that does the work. You could link more transitions to it, if you need it.
But most importend you dont need to reprogramm the Transition, a think i realy disliked.
Thank you for your help , i could not done it alone.