QT-How to utilize QWidget with QThread? - qt

I'm making some GUI through QT.
I almost complete my work but I have a hard time dealing with Qthread.
My goal is to measure the position of the motor (it moves) and display it on the Qtextbrowser while working another function in the main thread. When I wrote codes like below, people said I can't use QTextBrowser(Qwidget) directly in the thread, so I'm searching how to return location value to the main thread. Can you do me a favor?
MDCE is a class in another header and the codes I attach are some parts of my first code.
void MotorPort::StartThread(MDCE* com, QTextBrowser* browser)
{
thread1 = QThread::create(std::bind(&MotorPort::MeasureLocation,this,com,browser));
thread1 -> start();
}
void MotorPort::MeasureLocation(MDCE* com, QTextBrowser* browser)
{
double location;
while(1)
{
location = CurrentLocation(com); \\return current position value
browser->setText(QString::number(location));
if (QThread::currentThread()->isInterruptionRequested()) return ;
}
}
void MotorPort::stopMeasure()
{
thread1->requestInterruption();
if (!thread1->wait(3000))
{
thread1->terminate();
thread1->wait();
}
thread1 = nullptr;
}

You should use the Qt signal/slot mechanism for iter-thread notification such as this. Firstly change your MotorPort class definition to declare a signal location_changed...
class MotorPort: public QObject {
Q_OBJECT;
signals:
void location_changed(QString location);
...
}
Now, rather than MotorPort::MeasureLocation invoking QTextBrowser::setText directly it should emit the location_changed signal...
void MotorPort::MeasureLocation (MDCE *com, QTextBrowser *browser)
{
while (true) {
double location = CurrentLocation(com);
/*
* Emit signal to notify of location update.
*/
emit location_changed(QString::number(location));
if (QThread::currentThread()->isInterruptionRequested())
return ;
}
}
Finally, update MotorPort::StartThread to connect the signal to the browser's setText slot...
void MotorPort::StartThread (MDCE *com, QTextBrowser *browser)
{
connect(this, &MotorPort::location_changed, browser, &QTextBrowser::setText);
thread1 = QThread::create(std::bind(&MotorPort::MeasureLocation, this, com, browser));
thread1->start();
}

Related

connect multiple signal to a slot

I've the follwoing issue.
There are two signals :
void buttonChanged(int);
void pulseWidthValue(int);
buttonChanged is emitted from a slot nextBtn:
void Program::nextBtn()
{
m_currentBtn++;
if(m_currentBtn > btnGrp->buttons().size())
{
m_currentBtn = 0;
phaseOver = true;
saveToXMLFile();
}
emit buttonChanged(m_currentBtn);
}
it's connected as follows:
connect(ui->btn_nextPhase, &QPushButton::clicked, this, &Program::nextBtn);
connect(this, &Program::buttonChanged, this, &Program::paintBtn);
the signal, buttonChanged is used in another slot to paint button:
void Program::paintBtn(int id) // how do I change the def of this function to receive to signal?
{
if(id==1)
{
ui->btn1->setStyleSheet(StyleSheetOn);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOff);
}
else if(id==2)
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOn);
ui->btn3->setStyleSheet(StyleSheetOff);
}
else if(id==3)
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOn);
}
else
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOff);
}
ui->label_7->setText(QString::number(pw_value)); // this pw_value is from the other signal pulseWidthValue
}
the basic idea is , there are 3 phase buttons , clicking Next will switch between these buttons and change its color. Now I need to use the other signal pulseWidthValue inside paintBtn
Now I come to the question:
How do I connect two signals, buttonChanged and pulseWidthValue, (both signals are coming from different functions) to the paintBtn slot?
Qt allows you to connect multiple times even to the same slots. It even allows you to connect between signals.
Check this example, is not meant to be functional but descriptive enought to see many options and accesability.
class A:public QObject
{
Q_OBJECT
...
signals:
void signalA1();
void signalA2(const QString &);
}
class B:public QObject
{
Q_OBJECT...
signals:
void signalB();
public slots:
void slotB();
}
class C:public QObject
{
...
public:
C(QObject *parent):QObject(parent)
{
a=new A(this);
b=new B(this);
}
void connectionTest()
{
connect (a,&A::signalA1,b,&B::slotB) ; // one connection to slot B::slotB
connect (this,&C::signalC,b,&B::slotB); // another connection to B::slotB
connect (a,&A::signalA1,this,&C::slotC) ; // another connection from A::signalA, slotC is private so only I can connect
connect (b,&B::signalB,this,&C::signalC); // connection from signal to signal
connect (a,&A::signalA2,this,&C::slotC); //connection from A::signalA2 to C::slotC
//with different argument count, but compatible as slotC doesn't need an argument
connect (a,&A::signalA1,b,&B::slotB) ; // duplicate connection to slot B::slotB (signalA1 will trigger slotB two times)
}
signals:
void signalC();
private slots:
void slotC();
private:
class A *a;
class B *b;
}

How to render programmatically a vtk item in qml?

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.

How can I avoid infinite loop when modifying textboxes (QLineEdit) that change related item info?

I have several fields in a widget, that each can affect the behavior of an item, and changing some of them will change others.
I read somewhere that the editingFinished() signal of a line edit is triggered only by user actions - and not by code changes... Is that true ?
connect(m_lineEdit1, SIGNAL(editingFinished()), this, SLOT(m_lineEdit1Changed()));
connect(m_lineEdit2, SIGNAL(editingFinished()), this, SLOT(m_lineEdit2Changed()));
connect(this, SIGNAL(someSignal()), this, SLOT(updateData()));
void m_lineEdit1Changed()
{
changedata1();
emit someSignal();
}
void m_lineEdit2Changed()
{
changedata2();
emit someSignal();
}
void updateData()
{
m_lineEdit1.setText(fromdata);
m_lineEdit2.setText(fromdata);
}
If I change m_lineEdit1, and update the entire widget (which changes, through code, m_lineEdit2), I hit a breakpoint in m_lineEdit2Changed()
This leads to an infinite loop of updates...
What can I do to get around it ?
Blocking signals is a bit of a sledgehammer of an approach. You can use a sentinel class to explicitly prevent recursion:
#define SENTINEL_STRINGIFY(x) #x
#define SENTINEL_TOSTRING(x) SENTINEL_STRINGIFY(x)
#define SENTINEL_AT __FILE__ ":" SENTINEL_TOSTRING(__LINE__)
class Sentinel {
Q_DISABLE_COPY(Sentinel);
static QMutex m_mutex;
static QSet<QString> m_sentinels;
QString const m_sentinel;
bool const m_ok;
static bool checkAndSet(const QString & sentinel) {
QMutexLocker lock(&m_mutex);
if (m_sentinels.contains(sentinel)) return false;
m_sentinels.insert(sentinel);
return true;
}
public:
explicit Sentinel(const char * sentinel) :
m_sentinel(sentinel), m_ok(checkAndSet(m_sentinel)) {}
~Sentinel() {
if (!m_ok) return;
QMutexLocker lock(&m_mutex);
m_sentinels.remove(m_sentinel);
}
bool operator()() const { return m_ok; }
};
QMutex Sentinel::m_mutex;
QSet<QString> Sentinel::m_sentinels;
...
void Foo::m_lineEdit1Changed()
{
Sentinel s(SENTINEL_AT);
if (!s) return; // exit if this method is on the call stack
...
changedata1();
emit someSignal();
}
This is thread-safe and can be used from any thread.
A technique to avoid this problem is to use the QObject::blockSignals() function.
In your example you would do:
void updateData()
{
m_lineEdit1.blockSignals(true);
m_lineEdit1.setText(fromdata);
m_lineEdit1.setText(fromdata);
m_lineEdit1.blockSignals(false);
}
The blockSignals() call prevents the object sending any signals while you are changing the data in the line edit.

How to monitor changes to an arbitrary widget?

I am starting a QT5 application with a rather complex design based on Qt Widgets. It runs on Beagleboard with a touchscreen. I will have a rather weird local invention instead of the LCD display. It's a laser painting on acrylic plate. It has no driver yet. To actually update a screen I must create a screenshot of the window as bitmap, turn it to grayscale and feed to a proprietary library, which will handle the laser. It should look cute, when ready. Unfortunately, the laser blinks on update, so I cannot just make screenshots on timer, or it will be jerky like hell.
I need to run a function every time a meaningful update of GUI happens, while preferably ignore things like button being pressed and released. Is there some way to create a hook without subclassing every single Qt Widget I will use? The only way to do this I know is to override paintEvent of everything. I want a simpler solution.
Possible assumptions are: the application will be running under X server with dummy display, will be the only GUI app running. Some updates happen without user input.
The code below does it. It doesn't dig too deeply into the internals of Qt, it merely leverages the fact that backing store devices are usually QImages. It could be modified to accommodate OpenGL-based backing stores as well.
The WidgetMonitor class is used to monitor the widgets for content changes. An entire top-level window is monitored no matter which particular widget is passed to the monitor(QWidget*) method. You only need to call the monitor method for one widget in the window you intend to monitor - any widget will do. The changes are sent out as a QImage of window contents.
The implementation installs itself as an event filter in the target window widget and all of its children, and monitors the repaint events. It attempts to coalesce the repaint notifications by using the zero-length timer. The additions and removals of children are tracked automagically.
When you run the example, it creates two windows: a source window, and a destination window. They may be overlapped so you need to separate them. As you resize the source window, the size of the destination's rendition of it will also change appropriately. Any changes to the source children (time label, button state) propagate automatically to the destination.
In your application, the destination could be an object that takes the QImage contents, converts them to grayscale, resizes appropriately, and passes them to your device.
I do not quite understand how your laser device works if it can't gracefully handle updates. I presume that it is a raster-scanning laser that runs continuously in a loop that looks roughly like this:
while (1) {
for (line = 0; line < nLines; ++line) {
drawLine();
}
}
You need to modify this loop so that it works as follows:
newImage = true;
QImage localImage;
while (1) {
if (newImage) localImage = newImage;
for (line = 0; line < localImage.height(); ++line) {
drawLine(line, localImage);
}
}
You'd be flipping the newImage flag from the notification slot connected to the WidgetMonitor. You may well find out that leveraging QImage, and Qt's functionality in general, in your device driver code, will make it much easier to develop. Qt provides portable timers, threads, collections, etc. I presume that your "driver" is completely userspace, and communicates via a serial port or ethernet to the micro controller that actually controls the laser device.
If you will be writing a kernel driver for the laser device, then the interface would be probably very similar, except that you end up writing the image bitmap to an open device handle.
// https://github.com/KubaO/stackoverflown/tree/master/questions/surface-20737882
#include <QtWidgets>
#include <array>
const char kFiltered[] = "WidgetMonitor_filtered";
class WidgetMonitor : public QObject {
Q_OBJECT
QVector<QPointer<QWidget>> m_awake;
QBasicTimer m_timer;
int m_counter = 0;
void queue(QWidget *window) {
Q_ASSERT(window && window->isWindow());
if (!m_awake.contains(window)) m_awake << window;
if (!m_timer.isActive()) m_timer.start(0, this);
}
void filter(QObject *obj) {
if (obj->isWidgetType() && !obj->property(kFiltered).toBool()) {
obj->installEventFilter(this);
obj->setProperty(kFiltered, true);
}
}
void unfilter(QObject *obj) {
if (obj->isWidgetType() && obj->property(kFiltered).toBool()) {
obj->removeEventFilter(this);
obj->setProperty(kFiltered, false);
}
}
bool eventFilter(QObject *obj, QEvent *ev) override {
switch (ev->type()) {
case QEvent::Paint: {
if (!obj->isWidgetType()) break;
if (auto *window = static_cast<QWidget *>(obj)->window()) queue(window);
break;
}
case QEvent::ChildAdded: {
auto *cev = static_cast<QChildEvent *>(ev);
if (auto *child = qobject_cast<QWidget *>(cev->child())) monitor(child);
break;
}
default:
break;
}
return false;
}
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != m_timer.timerId()) return;
qDebug() << "painting: " << m_counter++ << m_awake;
for (auto w : m_awake)
if (auto *img = dynamic_cast<QImage *>(w->backingStore()->paintDevice()))
emit newContents(*img, w);
m_awake.clear();
m_timer.stop();
}
public:
explicit WidgetMonitor(QObject *parent = nullptr) : QObject{parent} {}
explicit WidgetMonitor(QWidget *w, QObject *parent = nullptr) : QObject{parent} {
monitor(w);
}
Q_SLOT void monitor(QWidget *w) {
w = w->window();
if (!w) return;
filter(w);
for (auto *obj : w->findChildren<QWidget *>()) filter(obj);
queue(w);
}
Q_SLOT void unMonitor(QWidget *w) {
w = w->window();
if (!w) return;
unfilter(w);
for (auto *obj : w->findChildren<QWidget *>()) unfilter(obj);
m_awake.removeAll(w);
}
Q_SIGNAL void newContents(const QImage &, QWidget *w);
};
class TestWidget : public QWidget {
QVBoxLayout m_layout{this};
QLabel m_time;
QBasicTimer m_timer;
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != m_timer.timerId()) return;
m_time.setText(QTime::currentTime().toString());
}
public:
explicit TestWidget(QWidget *parent = nullptr) : QWidget{parent} {
m_layout.addWidget(&m_time);
m_layout.addWidget(new QLabel{"Static Label"});
m_layout.addWidget(new QPushButton{"A Button"});
m_timer.start(1000, this);
}
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
TestWidget src;
QLabel dst;
dst.setFrameShape(QFrame::Box);
for (auto *w : std::array<QWidget *, 2>{&dst, &src}) {
w->show();
w->raise();
}
QMetaObject::invokeMethod(&dst, [&] { dst.move(src.frameGeometry().topRight()); },
Qt::QueuedConnection);
WidgetMonitor mon(&src);
src.setWindowTitle("Source");
dst.setWindowTitle("Destination");
QObject::connect(&mon, &WidgetMonitor::newContents, [&](const QImage &img) {
dst.resize(img.size());
dst.setPixmap(QPixmap::fromImage(img));
});
return app.exec();
}
#include "main.moc"

QT EventTransition implementation

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.

Resources