How to connect callback with QPlainTextEdit change? - c++

I am listening to a topic and want to display and update the received value every time it changes.
This function creates the logging part of the GUI
QGroupBox *Window::startLoggingGroup()
{
QGroupBox *groupBox = new QGroupBox(tr("Logging"));
log_value = new QPlainTextEdit;
log_value->setReadOnly(true);
log_value->setPlaceholderText("Value will appear here \n");
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(log_carrot);
groupBox->setLayout(hbox);
return groupBox;
}
This is the code executed on every value changed.
void EFISWindow::callback_value(const geometry_msgs::PoseStamped& msg){
QString qst = QString::number(msg.pose.position.z);
log_value->setPlainText(qst);
}
It works at first, I can see the GUI and some values, but after several messages like the ones I show now it crashes:
QObject::connect: Cannot queue arguments of type 'QTextBlock' (Make
sure 'QTextBlock' is registered using qRegisterMetaType().) QObject:
Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x227e580), parent's thread is
QThread(0x1f9db50), current thread is QThread(0x7f4ae40011d0)
How can I solve this threading issue? Maybe using a signal-slot design? I don't really understand why this is not working.

You should not access a GUI element from another thread.
Maybe using a signal-slot design?
Yes, Your worker object should have a signal that you emit at some point and you should connect that signal to update the "log_value" value.

Related

Qt 5.11, QWidget and creating a window and connecting to signals

I am dynamically creating new windows using:
QWidget* pobjWin = new QWidget();
pobjNode->setWidget(pobjWin);
pobjWin->resize(intWidth, intHeight);
pobjWin->move(intX, intY);
This works and in my application I have 3 windows, I want to connect to the various signals that according to the online documentation are:
activeChanged
contentOrientationChanged
focusObjectChanged
...
In my application as an example I connect to these signals with:
QObject::connect(pobjWin, SIGNAL(activeChanged(void))
,pobjSubNode
,SLOT(windowSlotActiveChanged(void)));
The pointer 'pobjSubNode' points to a class which has defined slots for each of the signals I am connecting to.
When I run this and the connects are executed I get:
2018-10-25 16:26:39.550030+0100 XMLMPAM[2048:219099] QObject::connect: No such signal QWidget::activeChanged(void) in ../XMLMPAM/clsMainWnd.cpp:733
I think the issue is because the pobjWin points to a QWidget and not a QWindow, but I couldn't find an example that doesn't create a window this way.
[Edit] Using the new connection method:
QObject::connect(pobjWin, &QWindow::activeChanged
,pobjSubNode, &clsXMLnode::windowSlotActiveChanged);
The prototypes for activeChanged and windowSlotActiveChanged are identical. But when I build with this implementation I get:
/Users/simonplatten/XMLMPAM/clsMainWnd.cpp:733: error: no matching function for call to 'connect'
QObject::connect(pobjWin, &QWindow::activeChanged
^~~~~~~~~~~~~~~~
[Edit2] I've changed the connect to:
QObject::connect(pobjWin->windowHandle(), &QWindow::activeChanged
,pobjSubNode, &clsXMLnode::windowSlotActiveChanged);
Now I get:
2018-10-25 17:37:22.299066+0100 XMLMPAM[2930:423194] QObject::connect: invalid null parameter
Having just assigned:
QWindow* pobjW2 = pobjWin->windowHandle();
I can see in the debugger that pobjW2 is NULL, which explains the error.
Changing the line:
QWidget* pobjWin = new QWidget();
To:
QWindow* pobjWin = new QWindow();
Has fixed the connection problems, once I fixed all the methods to use the QWindow versions instead of the QWidget versions, but now I don't get any visible windows...investigating. Its odd that the methods in QWindow have different names to those that do the exact same job in QWidget.
[Edit 3] After fixing the problems with help on correcting the connection and inserting a call to showNormal before creating the connections. The code is exactly as originally posted with a call to pobjWin->showNormal before any connects.
Fixed, I acknowledge the input from eylanesc, thank you.
I had to add a call to:
pobjWin->showNormal();
Before attempting the signal / slot connections, an example of a signal and slot connection now looks like:
QObject::connect(pobjWin->windowHandle(), &QWindow::activeChanged
,pobjSubNode, &clsXMLnode::windowSlotActiveChanged);

Connecting two signals with different arguments

I want to get a QTreeView widget to emit a clicked(const QModelIndex&) signal whenever a pushbutton is clicked. This is so that I can get a list of all the items that are selected within the QTreeView at the time of clicking the pushbutton. Now I thought I could connect two signals with distinct arguments (Qt Connect signals with different arguments), however when I try to call
connect(ui.pbAddVideo, SIGNAL(clicked()), ui.treeView_video, SIGNAL(clicked(const QModelIndex&)));
I get the error message:
QObject::connect: Incompatible sender/receiver arguments QPushButton::clicked() --> QTreeView::clicked(QModelIndex)
Have I misunderstood the whole signal forwarding concept?
As always, many thanks.
Firtsly, what index you must send by clicking a button from your tree?
Secondly, since c++11 standart you can do something like that:
connect(ui.pbAddVideo, &QPushButton::clicked, [=] { emit ui.treeView_video->clicked(ui.treeView_video->currentIndex()); });
I would solve your problem with the following approach:
First you have to handle the button click:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(onLoadVideo()));
In the slot you need to get the list of selected items from the tree view and do something with them:
void MyClass::onLoadVideo()
{
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex *video, selectedVideos) {
// Play the video.
}
}
You are connecting one SIGNAL() to another SIGNAL() which is perfectly ok but their parameters should match. In your case the second signal has a parameter(i.e QModelIndex) that the first signal does not have.
Have I misunderstood the whole signal forwarding concept?
Yes.
When a signal is emitted, Qt takes the signal's arguments and passes them to the connected slot/signal.
Valid signal-slot connection
For example, suppose you connect the QSlider::valueChanged(int) signal to the QSpinBox::setValue(int) slot. When the valueChanged() signal is emitted, this is effectively how the slot gets called:
// "Emitting a signal" == "Calling the signal function"
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
receiver->setValue(sliderSignalValue);
}
Valid signal-signal connections
Similarly, if you connect the QSlider::valueChanged(int) signal to the QSpinBox::valueChanged(int) signal, the code behaves like this:
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
emit receiver->valueChanged(sliderSignalValue);
}
Now, if you want to connect in the opposite direction (connect(ui.treeView_video, SIGNAL(clicked(const QModelIndex&)), ui.pbAddVideo, SIGNAL(clicked()));, it's perfectly fine:
QTreeView::clicked(const QModelIndex& viewSignalValue)
{
QPushButton *receiver = findReceiver();
emit receiver->clicked(); // No problem. The viewSignalValue is simply ignored.
}
Invalid signal-slot connection
However, for the connection that you wanted to make, the code will need to behave like this:
QPushButton::clicked()
{
QTreeView *receiver = findReceiver();
emit receiver->clicked(/*???*/); // ERROR: You need to pass a QModelIndex value!
}
You've got a parameter mismatch. QTreeView::clicked() requires a QModelIndex value, but QPushButton::clicked() cannot provide this. Therefore, you cannot connect these two together.
Does this make sense?
Many thanks to #vahancho whose answer I have adopted. There is no point in using the "clicked()" signal from the qTreeView since I do not need to wait for this to access the data inside. Hence:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(addVideo()));
void VigilWidget::addVideo() {
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex video, selectedVideos) {
qDebug().nospace() << video.data(0);
}
}
As to my question about how signal to signal connections work, thanks to everyone for taking the time to explain this :)

Refer to the Sender Object in Qt

I'm having some trouble, I'm fairly new to Qt and C++ and was testing the waters so to say. Ill try to describe my problem as follows. I have a LineEdit QLineEdit and this edit has a connection which looks like this:
connect(my_lineedit, SIGNAL (textEdited(QString)),this, SLOT (handleEdits()));
The handleEdits() method gets called and does the following:
Disconnect the previous Signal from my_lineedit
Create a new QLineEdit which gets a new signal and calls handleAddedEdits()
Adds the newely created Edit to my layout.
The stated above works fine Im just telling you this so you get the picture.
Now in the new method which I called handleAddedEdits() I want kinda the same procedure to happen again.
Disconnect the Signal which calls handleAddedEdits() from the Edit which invoked this method in the first place.
Create a fresh QLineEdit
Add this to my layout.
The problem is: in the first case my_lineedit is declared in my class so I can freely refer to it and and remove the signal as I wish. In the second case I have a QLineEdit which was created dynamically in the handleEdits() method and is the "Sender". My Question is, how can I refer to the "Sender Object" ro remove the Signal from the dynamically created edit?
You need to use QObject::sender() method in your receiver's slot:
For cases where you may require information on the sender of the
signal, Qt provides the QObject::sender() function, which returns a
pointer to the object that sent the signal.
handleAddedEdits()
{
QObject* obj = sender();
disconnect( obj, SIGNAL(textEdited(QString)), 0, 0 );
//...
}

QThread, creating GUI widget element on a thread

I've been trying that for a while and seems it's not something common as I didn't find much information about.
I have a QTree in which I put item, on my Item i have a check box on the first column.
Problem: Checkbox is not optimised to be treated as so and takes quite some time as soon as there is several elements.
So i'm using a thread to create the checkbox before putting in my list, but seems impossible to get the checkbox back on the GUI thread.
void CheckItemThread::run()
{
setPriority(QThread::IdlePriority);
QCheckBox *m_check;
m_check = new QCheckBox();
emit done(m_check);
}
My main thread:
myCheckItem::myCheckItem(QTreeWidget *parent, QStringList columnNames ):
myWidgetItem(parent)
{
m_parent = parent;
m_columnNames = columnNames;
connect(&TheThread,SIGNAL(done(QCheckBox *)), this, SLOT(retThread(QCheckBox *)));
connect(&TheThread,SIGNAL(terminated()), this, SLOT(endThread()));
TheThread.setdata(columnNames,parent, this);
TheThread.start(); //run thread
}
void myCheckItem::endThread()
{
m_check->setParent(m_parent);
connect(m_check, SIGNAL(stateChanged(int)), this, SLOT(onCheckBox(int)));
}
void myCheckItem::retThread(QCheckBox *check)
{
m_check = check;
}
Maybe I'm missing something or it's simple not possible to reattach the thread ?
Thanks
You must not create, edit or work with UI elements in other threads. UI elements must be worked with in the main thread (UI thread). If you have time-consuming prerequisites before "drawing" a checkbox, do your work in a thread (eventually QtConcurrent) and send a signal to the main thread for creating the corresponding checkbox.
You can change GUI elements only in main thread. How many checkboxes do you have? Maybe you should create a limited count of checkboxes and reuse them when needed?

Qt - updating main window with second thread

i have an multithreaded qt application. when i am doing some processes in mainwindow.cpp, at the same time, i want to update mainwindow.ui from other thread.
i have mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include "mainwindow.h"
class mythread : public QThread
{
public:
void run();
mythread( MainWindow* ana );
MainWindow* ana;
private:
};
#endif // MYTHREAD_H
mythread.cpp
mythread::mythread(MainWindow* a)
{
cout << "thread created" << endl;
ana = a;
}
void mythread::run()
{
QPixmap i1 (":/notes/pic/4mdodiyez.jpg");
QLabel *label = new QLabel();
label->setPixmap(i1);
ana->ui->horizontalLayout_4->addWidget(label);
}
but the problem is that, i cannot reach the ana->ui->horizontalLayout_4->addWidget(label);
how can i do that?
but the problem is that, i cannot reach the
ana->ui->horizontalLayout_4->addWidget(label);
Put your UI modifications in a slot in your main window, and connect a thread signal to that slot, chances are it will work. I think only the main thread has access to the UI in Qt. Thus if you want GUI functionality, it must be there, and can be only signaled from other threads.
OK, here is a simple example. BTW, your scenario doesn't really require to extend QThread - so you are better off not doing it, unless you really have to. That is why in this example I will use a normal QThread with a QObject based worker instead, but the concept is the same if you subclass QThread:
The main UI:
class MainUI : public QWidget
{
Q_OBJECT
public:
explicit MainUI(QWidget *parent = 0): QWidget(parent) {
layout = new QHBoxLayout(this);
setLayout(layout);
QThread *thread = new QThread(this);
GUIUpdater *updater = new GUIUpdater();
updater->moveToThread(thread);
connect(updater, SIGNAL(requestNewLabel(QString)), this, SLOT(createLabel(QString)));
connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));
updater->newLabel("h:/test.png");
}
public slots:
void createLabel(const QString &imgSource) {
QPixmap i1(imgSource);
QLabel *label = new QLabel(this);
label->setPixmap(i1);
layout->addWidget(label);
}
private:
QHBoxLayout *layout;
};
... and the worker object:
class GUIUpdater : public QObject {
Q_OBJECT
public:
explicit GUIUpdater(QObject *parent = 0) : QObject(parent) {}
void newLabel(const QString &image) { emit requestNewLabel(image); }
signals:
void requestNewLabel(const QString &);
};
The worker object is created and moved to another thread, then connected to the slot that creates the labels, then its newLabel method is invoked, which is just a wrapper to emit the requestNewLabel signal and pass the path to the image. The signal is then passed from the worker object/thread to the main UI slot along with the image path parameter and a new label is added to the layout.
Since the worker object is created without parent in order to be able to move it to another thread, we also connect the thread destroyed signal to the worker deleteLater() slot.
First and foremost, "you're doing it wrong". Normally you want to create a class derived from a QObject and move that class to a new thread object instead of deriving your class from a Qthread
Now to get onto the specifics of your question, you're not able to directly modify the ui elements of your main GUI thread from a separate thread. You have to connect a signal from your 2nd thread to a slot in your main thread. You can pass any data that you need through this signal/slot connection but you're unable to directly modify the ui element (which in all honestly you probably do not want to if you intend to keep the frontend of your app separate from the backend). Checkout Qt's signal and slot documentation for a whole lot more information
how can i do that?
You've already got the answers to what you should be doing, but not a why, so I'm going to add a why.
The reason you don't modify GUI elements from another thread is because GUI elements are usually not thread-safe. This means that if both your main GUI thread and your worker thread update the UI, you cannot be certain of the order of what happened when.
For reading data generally this can sometimes be fine (e.g. checking a condition) but generally you do not want this to be case. For writing data, this is almost always the source of very, very stressful bugs which occur "at random".
Another answer has remarked on good design principles - not only does constraining your GUI logic to one thread and firing signals to talk to it get rid of your race condition issues, but it also forces you to compartmentalize your code nicely. Presentation logic (the display bit) and data processing logic can then be cleanly separated out, which makes maintaining the two much easier.
At this stage you might think: heck, this threads business is farrrrrr too much work! I'll just avoid that. To see why this is a bad idea, implement a file copy program in a single thread with a simple progress bar telling you how far along the copy is. Run it on a large file. On Windows, after a while, the application will "go white" (or on XP I think it goes gray) and will be "not responding". This is very literally what is happening.
GUI applications internally mostly work on the variation of "one big loop" processing and dispatching messages. Windows, for example, measures response time to those messages. If a message takes too long to get a response, Windows then decides it is dead, and takes over. This is documented in GetMessage().
So whilst it may seem like quite a bit of work, Signals/Slots (an event-driven model) is basically the way to go - another way to think of this is that it is totally acceptable for your threads to generate "events" for the UI too - such as progress updates and the like.