Open QDialog and run the QProcess simultaneously - qt

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.

Related

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.

Text display on GUI

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);
}

Can't write to Qt5 textEdit box with ui->control->setText(message)

I am trying to put the data passed to a mainwindow.cpp function on the screen with the typical ui->control->setText(message) without success. The same line works if it is in a timer loop or a button function but not from display_that_data function
"MainWindow::display_that_data()" is called from myudp.cpp with the following lines
MainWindow show_tlm;
show_tlm.display_that_data(data_source, buf_copy);
mainwindow.cpp (updated to include emitting a signal for a SIGNAL/SLOT connection)
The SIGNAL/SLOT connection is set up in the constructor with the following line
connect (this, SIGNAL (showdata_signal()), this, SLOT(showdata_slot()));
void MainWindow::display_that_data(QByteArray data_source, QByteArray tlmBuf){
QString msg ;
msg = " in display_that_data";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
//generate a signal which will trigger showdata_slot
emit showdata_signal();
msg = " in display_that_data after emit showdata_signal()";
qDebug() << msg ;
}
void MainWindow::showdata_slot() {
QString msg = "showdata_slot called";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
}
Runtime debug messages show that code is making it to the showdata_slot but it is still not writing to the ui->tlm_vals
" in display_that_data"
"showdata_slot called"
" in display_that_data after emit showdata_signal()"
but.... neither one of the ui->tlm_vals->setText(msg) lines are putting text on the ui
"MainWindow::realtimeDataSlot()" is called by at timer timout signal as follows:
void MainWindow::setupRealtimeDataDemo(QCustomPlot *customPlot) {
// setup a timer that repeatedly calls MainWindow::realtimeDataSlot
connect(&dataTimer, SIGNAL(timeout()), this, SLOT(realtimeDataSlot()));
dataTimer.start(1000);
}
void MainWindow::realtimeDataSlot(){
QString temp = QString("%1").arg(epochTime, 10, 10, QChar('0'));
ui->tlm_vals->setText(temp);
}
And this works perfectly (of course I have to disable it to see if showdata_slot is writing to the ui)
I thought the problem was a needed SIGNAL and SLOT connection to trigger the write to the ui but generating a SIGNAL/SLOT connection (which debug shows as working) still does not write to the ui from the slot function.
For completeness mainwindow.h contains the following
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setupRealtimeDataDemo(QCustomPlot *customPlot);
void display_that_data (QByteArray data_source, QByteArray tlmBuf);
private slots:
void realtimeDataSlot();
void showdata_slot();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
What am I missing/doing wrong?
Somewhere deeply embedded in Qt there is a difference between gui mouse click generated event and code generated event and this somehow caues Qt behavior to vary with the same lines of code. I will repost a more more "root level" question on this topic.

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"

How to pass data from one form to another in Qt?

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
}

Resources