Qt dynamic stylesheet with User Interface Compiler - c++

I'm working with a Qt application that uses an XML file to generate the user interface, via the Qt User Interface Compiler.
I don't have access to the code that holds each widget (I do but the Qt UI Compiler re-generates it each time), so I'm not able to add another method to the class that it generates.
I'm trying to do a setStyleSheet on one of the QLineEdit widgets, but it gives me a QPixmap: It is not safe to use pixmaps outside the GUI thread warning and then eventually seg faults. After resigning myself to not being able to go this route, I decided to test having two copies of each widget, each with the stylesheet values needed. I would then trigger a QLineEdit::hide() and QLineEdit::show() on the widgets as needed, which I thought would work.
It didn't. The program now spits out QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread whenever run.
What can I do to fix this? I need to change the stylesheet of the widget dynamically, but seem unable to do so in any manner.

You can't be invoking any QWidget methods from threads other than the main thread. It's fairly easy to indirectly invoke such methods safely from any thread, though. See this answer for details.
For example, suppose you wanted to call setStyleSheet on a widget, from code that runs in some other thread:
template <typename F>
static void postToMainThread(F && fun, QObject * object) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, object, std::forward(fun));
}
void threadCode(QWidget * widget) {
postToMainThread([widget]{
widget->setStyleSheet("color: black");
}, widget);
}

Related

how to access/modify QObject, how to interoperate between Qt5 and a library

I created a command-line program. I now want to add a Qt5 GUI wrapper onto it. (I am very new to Qt5 C++ programming) Previously the program simply displays command-line outputs, now I want it to display outputs on the Qt5 window. (I have converted the core code of the CLI program into a library)
My Qt5 program (a class inheriting QMainWindow) starts my library in a new STD thread. (Is using STD thread correct?) While my library is being executed (and it can take hours), it returns outputs in callback functions. In those callback functions I want to display those outputs in a QTextEdit instance; And so my Qt5 crashes all the time (and the console always says "Segmentation fault (core dumped)"), as this must be the wrong way to modify QObject.
How should I do it? What is the correct way of accessing/modifying QObject instances in a threaded function? (I want to run my library in another thread (under multithreading) because I don't want to freeze my Qt5 window) Do I need to lock a mutex? Perhaps I should use a queue structure and collect any outputs with the queue and read the queue to update my QTextEdit instance within QMainWindow (but now I need a loop function)? I hope, a kind expert can advise me.
I myself have found the solution.
One must use QThread. I think, STD thread can never access/modify QObject instances?
https://doc.qt.io/qt-5/qthread.html
the important lines:
-initialize:
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
-invoke:
workerThread->start();
The callback functions must be able to see the thread instance and call this "emit" function. This "emit" function is automatically defined in the .moc file. (Notice: the token "emit" is a macro.)
in the documentation page
emit resultReady(result);
In my case my callback functions call this function with the thread variable.
emit workerThread->resultReady(result);
So whenever this "resultReady" is called, the "handleResults" function gets called. In my case the "handleResults" function is defined inside my QMainWindow-inheriting "myMainWindow" class.
void QMainWindow::handleResults(const QString &s){
// Finally I can access/modify the QTextLine instances XDDD
}
One difficulty is, I must use a global variable to pass parameters into my library code whenever I invoke this "workerThread->start();" line.
Thanks anyway. XD

How to execute QML slot from C++ without a signal?

I got a QML object in the QObject shape. With ->setProperty(..., ...) I can change properties, but how can I execute slots without signals?
Currently I declare a signal in my C++ class:
signals:
void testSignal();
and signal/slot in QML object:
signal executeTestFunc(); onExecuteTestFunc: {
testAnimation.start();
}
Then I connect these two:
QObject::connect(this, SIGNAL(testSignal()),
QmlObject, SIGNAL(executeTestFunc()));
But this is not clean, as I can see:
De facto this is strange SIGNAL/SIGNAL connection.
I do not want to use SIGNAL/SLOT mechanism, unless I have to, due to performance and long code.
Is there a way to execute QML onExecuteTestFunc(); from QObject directly?
You can create a C++ class that will emit a signal. That signal will be caught in QML. No explicit connection with SIGNAL/SLOT is needed. Example:
C++:
class Presenter : public QObject
{
Q_OBJECT
public:
explicit Presenter()
{
QTimer *timer = new QTimer();
timer->setInterval(500);
connect(timer, &QTimer::timeout,
this, &Presenter::timeout);
timer->start();
}
Q_SIGNAL void timeout();
};
QML:
Window {
...
Presenter {
onTimeout: {
console.log("called from c++")
}
}
}
Result:
qml: called from c++
qml: called from c++
qml: called from c++
qml: called from c++
...
#Thomenson has already given a good answer on how you can connect to a signal from C++ and act on it in QML. His solution works if you can create the C++ from QML, which may not always be the case.
Connections element
There are two other options you might consider. First, if you have an object that was not created from QML but was put into it in some other way (QML context, static object, returned from an invokable...) you may use the Connections element:
Connections {
target: theObjectWithTheSignal
onSignalName: {doSomething();}
}
This is a flexible way to react to signals from object that you did not create in QML, or even objects that were created elsewhere in QML.
Callback using JSValue
If you really insist on avoiding signal/slot (though I don't understand why; Qt and QML are build around it and trying to avoid it is fighting against the framework instead of using its strenghts), there is another way. You can, on the C++ object that is exposed to QML, create a property of type QJSValue. Then, in C++ on setting, check that whatever was set is callable (using QJSValue::isCallable()). Then as the point you wish to trigger whatever you want to execute in your QML, call it using QJSValue::call.
On the QML side, you can simply assign or bind something callable, just like you'd do for a signal handler.
Anti pattern: QMetaObject::invokeMethod
There is another way, which I will only include as a warning against an anti-pattern. You can call into the QML from C++ using Qt's introspection mechanism. You can find an object by it's set objectName and call any (invokable) method on it using QMetaObject::invokeMethod, read and write any property and listen to all signals. That includes calling methods you defined in QML. Using this, you can manipulate your QML from your C++. Don't do this. It leads to inflexible and unmaintainable code.

Why do I get "QMetaObject::connectSlotsByName: No matching signal" with a custom dialog?

I am using Qt5.
I have a very simple dialog class that inherits from QDialog.
I have a class that uses this dialog, and also a QFileDialog:
NameDlg m_name_dlg;
QFileDialog m_file_dlg;
This class also has some slots to handle dialog closing:
private slots:
void on_dlgName_accepted();
void on_FileDlgClosed(int result);
In the constructor I do some connecting:
QObject::connect(&m_file_dlg, SIGNAL(finished(int)),
this, SLOT(on_FileDlgClosed(int)));
QObject::connect(&m_name_dlg, SIGNAL(accepted()),
this, SLOT(on_dlgName_accepted()));
The first call to connect is fine, but the second call generates the output:
QMetaObject::connectSlotsByName: No matching signal for on_dlgName_accepted()
Curiously, my slot is correctly called when the name dialog is accepted!
The documentation (and header file) for QDialog says:
Signals
void accepted()
So it isn't a parameter mismatch.
As I said above, the signal is correctly called so my code all works fine, I just would really really like to understand why I get this warning (as an educational exercise) and also get rid of it (for peace of mind).
I cannot post full code, but I do believe there should be enough for anyone to understand the problem.
Things I have tried:
Using finished() instead.
Casting &m_name_dialog to a QDialog *.
Changing the signal to QDialog::accepted().
Thanks in advance.
In some part of your code you are using the method connectSlotsByName, if you have created a design (.ui) this usually calls it since compiling generates a file ui_somefile.h, and this file is used.
According to the docs:
void QMetaObject::connectSlotsByName(QObject *object)
Searches recursively for all child objects of the given object, and
connects matching signals from them to slots of object that follow the
following form:
void on_<object name>_<signal name>(<signal parameters>);
From the above it is observed that this method will try to connect the slots that have that format, and in your case the second slot fulfills it, when trying to connect it looks for the objects and signals but in your case it does not find it since the object does not exist dlgName and generates the warning you see.
This method is created by the .ui file because through the design you can create slots by right clicking on the widget and selecting go to slot, choosing the signal and finally creating the slot.
Note:
If you are going to create your own slot, avoid using the underscores as this could cause you problems because Qt would try to connect it and if the objects do not exist it will send you several warnings.

Loading defaults of all widgets in Qt Creator

I would like to use a button_clicked slot to reset all the widgets used in the application. I tried calling the function used in the application constructor, which is present by default when you create an ui application project in Qt.
ui->setup(this).
I called the same in the pushbutton_clicked() slot function. For example -
void MyMainWindow::on_pushButtonLdDefaults_clicked()
{
ui->setupUi(this);
}
This gives me the expected result, all the widgets get reset. But after this, all the buttons and widgets become unresponsive. No events are heard after this.
Am i doing something wrong by calling ui->setup(this)?
Also, If there is any other way to easily reset the widgets, please share.

Signals/slots on non widgets in Qt 5.0

As far as the GUI designer, I understand how certain signals affect certain slots and invoke code. Other than that method, I am unsure about how to invoke a slot from a signal.
Take this example:
void QFileDialog::directoryEntered ( const QString & directory ) [signal]
This is a signal. When the directory is entered, I want this to populate a widget QColumnView with the contents of the directory.
How does a non widget signal invoke a slot of a UI widget.
I assume you use connect but the example provided uses two separate objects.
Signals and slots are features of QObject. It works well even for non-GUI code.
Connecting a signal to a slot is always done through the connect function:
connect(myDialog, SIGNAL(directoryEntered(QString)),
this, SLOT(updateColumn(QString)));
here assuming that you have updateColumn() slot in your main object handling the actual UI update of that QColumnView.