Forward value from signals within a QStateMachine - qt

I am currently having trouble with a QStateMachine in QT. Basically I want to use it to control the flow between my screens, e.g. to switch from a master page to a detail page.
What I basically have is this:
// Overview
QState* overviewState = new QState();
overviewState->assignProperty(ui->stackedWidget, "currentIndex", 0);
stateMachine->addState(overviewState);
// Editor
QState* editorState = new QState();
editorState ->assignProperty(ui->stackedWidget, "currentIndex", 1);
stateMachine->addState(editorState);
// Overview -> Editor
overviewState->addTransition(overview, SIGNAL(onEditor()), editorState);
So far this is working as expected. The overview emits onEditor and the editor is shown. However, now I want to forward a value from the signal. The editor can be called with a selected value (meaning the user wants to edit an entry) or without a value (meaning the user starts with an empty editor). The overview would then have two signals: onEditor and onEditor(long). The editor would have two slots: performInitialize and performInitialize(long).
connect(editorState, SIGNAL(entered()), editor, SLOT(performInitialize()));
This is the point where I am stuck. (How) can I forward the signal from onEditor(long) to performInitialize(long)? The signal from the QState surely has no knowledge of this additional parameter. How can I solve this?
Thanks a lot in advance.

You can split the initialization into setup and performance stages:
void Editor::performInitialize(long val); // current implementation - keep it
void Editor::setupInitialize(long val);
m_initVal = val;
}
void Editor::performInitialize() {
performInitialize(m_initVal);
}
connect(overview, SIGNAL(onEditor(long)), editor, SLOT(setupInitialize(long)));
overviewState->addTransition(overview, SIGNAL(onEditor(long)), editorState);
addTransition sets up an internal connection to the signal. Qt guarantees that slots are executed in the order of connection. Thus the state transition is guaranteed never to precede setupInitialize call.

Related

Qt - start process with click event and stop it again with the second click event

I found time to investigate a bit into QT, and it is very interesting for me. However, right now I am encountering a problem that I am not aware about how to solve it. My aim is actually simple. I have a QCheckBox that I want to activate. If it is activated, I am starting a process (I am opening a file, reading it, taking some values out and change different labels accordingly). This process is repeated until the user is deactivating the QCheckBox. Some small code example to get a better idea of what I am going to do.
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
qDebug() << "Start data analysis";
// Infinity loop to get the data and display it
while true
{
// Open file and extract data
const actualTemperature = getData();
// Change any label or do something with the data
ui->anyLabel->setText(actualTemperature);
// Some break
QThread::sleep(1);
// Leave the loop if user deactivate the QCheckBox
// Something like on_actualTemperature_stateChange == 0
}
}
// Stop reading the data
else
{
qDebug() << "Stop data analysis";
}
}
It is obvious that after activating the QCheckBox, the loop will not finish at all and the GUI will not recognize anything anymore. Hence, I guess I have to start some new thread and have to kill it. However, I have no idea how to proceed here. An idea would be:
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
// Start reading the file and updating the label using some other thread
startThread(XY);
}
// Stop reading the data
else
{
// Kill thread 1234
killThread(XY);
}
}
Any hint is warmly welcomed and I hope this question is not too basic for you. Thank you for reading, Tobi.
I think killing a running thread is not a decent behavior. Let's be gentle to our threads with a loop control variable. In this example it named keepLoop. Set keepLoop when checkbox checked. Then start thread if it is not running. We are using QtConcurrent::run, and monitoring it by a QFuture in this case.
connect(ui->checkBox, &QCheckBox::toggled,
[&](const bool checked) {
analyzer->keepLoop = checked;
if (checked && !future.isRunning())
future = QtConcurrent::run(analyzer, &Analyzer::on_actualTemperature_stateChanged);
}
);
Don't call user interface slots directly, instead connect them to signals. Connections will be queued connection when signals emitted from another thread. It means slots will be called in event loop of main thread and changes will be shown when the next frame painted.
connect(analyzer, &Analyzer::temperatureCalculated, ui->anyLabel, &QLabel::setText);
Our asynchronous function does not forced to die immediately when user toggle checkbox. Instead we letting it to finish the iteration it already on halfway through.
Analyzer::on_actualTemperature_stateChanged() {
while (keepLoop) {
// Open file and extract data
const QString& actualTemperature = getData();
// send data
emit temperatureCalculated(actualTemperature);
}
}
You can use atomic bool if you want a more precise loop control.
Bonus:
If you don't want to mess with threads, you can avoid GUI freezing by using QTimer to run your loop periodically in main thread.

Understanding signals, slot order, and parent widgets

So I have a situation where I can't get a couple slots to fire in the order that I would like them to.
The basic set-up is that I have a Mainwindow with a statusbar that needs to get updated based on a signal from a child widget (SearchWidget). When the "Go" button is clicked on the child widget, I would like it to update the status bar to say "Searching..." and then perform the actual database search. However, I can only get the updateStatusBar slot to trigger AFTER the search is complete and displayed in a tablewidget. I have tried re-arranging the connections to the appropriate order, I have tried a separate function that emits the signal for the statusbar and then the signal for the search, but nothing seems to work. The search always executes first and the statusbar doesn't change until after that is complete.
I'm a newbie a this, but I'm guessing maybe the issue has something to do with the parent-child relationship between the mainwindow and the widget? Perhaps slots within the same widget are prioritized in some way? See basic code below.
Mainwindow class:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
initMembers();
initUI();
connect(searchWidget, SIGNAL(searchingStatus(QString)), this, SLOT(updateStatusBar(QString)));
}
void MainWindow::initMembers()
{
tabWidget = new QTabWidget(this);
searchWidget = new SearchWidget(this);
saleWidget = new SaleWidget(this);
statusBar = new QStatusBar();
setCentralWidget(tabWidget);
setStatusBar(statusBar);
}
void MainWindow::initUI()
{
tabWidget->addTab(searchWidget, "Search");
tabWidget->addTab(saleWidget, "Sale Data");
}
void MainWindow::updateStatusBar(QString status)
{
statusBar->showMessage(status);
}
SearchWidget class:
SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent)
{
connect(goButton, SIGNAL(clicked), this, SLOT(buildQuery()));
}
void SearchWidget::buildQuery()
{
emit searchingStatus("Searching...");
//builds sql query
}
Any enlightening info would be much appreciated!
I suspect that it's not an ordering issue, but the fact that the SQL query is blocking the main GUI event loop: try inserting a call to QApplication::processEvents() after your emit searchStatus(...) call. If I'm correct, you should see the status bar update before the database search completes.
However, because you're still blocking the event loop, your GUI will still freeze while the DB call executes, which isn't great. You can eliminate this by running the query on a different thread (one of the simplest ways is via http://doc.qt.io/qt-5/qtconcurrent.html#run), but beware you then have to worry about concurrency issues (e.g., now you can click the go button lots of times in a row...).

showEvent() called more than once - Intentional?

What I want to do is display a set of pages setting up a test. Once all the details are correct the user presses Commit and the next wizard page is displayed that I want to immediately run a series of tests in. Displaying those to the user and once complete the user can then click Next.
I know to disable Next is simply a case of returning false on isComplete() and that is implemented okay. So, I want to use the function that is called just after the widget is displayed and so I used showEvent() which was indicated to me as the function to use.
At the moment my test is just displaying a progress bar as a test hence using a timer.
void RunTestWizardPage::showEvent(QShowEvent *event)
{
ui->statusEdit->setText("Running Tests");
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(100);
}
void RunTestWizardPage::update()
{
static int i = 10;
ui->statusEdit->append("Running Tests...");
ui->testProgress->setValue(i++);
if(i == 100)
{
i = 0;
timer->stop();
complete = true;
emit completeChanged();
}
}
However this function appears to be called twice (and I think before the widget display although that may be a trick of my debugging) and as such it causes issues with the timer I think as the timer never ends. I did read in the docs about spontaneous events but from what I can see both calls to the function are not spontaneous.
Is it being called twice intentional and if so how do I stop it or is there another function to use?
Thanks!
There is QWizardPage::initializePage() which is called just before showing the page.

Qt can not respond key Press event immediately

Environment: Ubuntu, Qt Creator
In my Qt app, I found that sometimes Qt doesn't respond to my key press event immediately, but if I wait a while, it eventually responds.
I think something is blocking the UI.
As I know, if a Qt's component (QWidget etc.) is being destroyed, the Qt UI will be blocked. I have checked my code, there is no component being destroyed at the time I'm pressing the up/down key.
I really want to know is there any other things can block Qt UI.
{
...
connect(webViewWidget, SIGNAL(loadfinished()), this, SLOT(addItem()));
...
}
void addItem()
{
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}
void keyPressEvent(QKeyEvent *event)
{
switch(event->key)
{
case UP_KEY:
scroll up;
break;
case DOWN_KEY:
scroll down;
break;
default:
break;
}
}
In general, your key press event will not be processed before all other events which were put into the application's event queue before pressing your key are processed.
Therefore it could be any kind of event which has not finished processing. Maybe you can figure out if there are any events, e.g. by using QApplication::hasPendingEvents or by inheriting from QApplication and adding debug output whenever an event is added or fully processed.
Destruction of objects is usually not a concern, unless you are doing a lot of work in the destructor. Destroying a webview may take long. You probably should not be destroying it like you do. Instrument that delete (see code below) and see how long it takes.
Your own code may be calling APIs that block. Are you calling any third party libraries? Are you calling any wait... methods in Qt's own API?
If you're unsure, you can instrument every slot and every reimplemented virtual method like xxxEvent(...). You'd need to instrument only slots and reimplemented QObject/QWidget methods, not every method in your code.
You may be producing an event storm, perhaps by posting lots of events in a loop, or by sending a lot of signals that are hooked up to slots connected via a Qt::QueuedConnection. Make sure you're not calling repaint() from within paintEvent() for example.
The instrumentation example below uses RAII and is very easy to apply. Alternatively, you can use a profiler.
#include <QElapsedTimer>
#define INSTRUMENT() Instrument instr__ument(__FUNCTION__)
#define INSTRUMENTLIM(lim) Instrument instr__ument(__FUNCTION__, (lim))
class Instrument {
QElapsedTimer timer;
int limit;
const char * function;
public:
Instrument(const char * name, int timeLimitMs = 20) :
function(name), limit(timeLimitMs) { timer.start(); }
~Instrument() {
if (timer.elapsed() > limit) {
qDebug("%s was slow, took %d ms", function, timer.elapsed());
}
}
}
void slot(...)
{
INSTRUMENT();
...
}
void addItem()
{
INSTRUMENT();
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}

Qt Modeless Dialog Destruction

From what I understand to make dialog Modeless you have to allocate it on the heap. By Doing something like this:
MyDialog* dlg = new MyDialog(this);
dlg->show();
dlg->raise();
Since exec() ignores Modal Property. However now there is a memory leak since nothing deallocates memory pointed to by dlg pointer until the application is closed. I found one solution here http://tinf2.vub.ac.be/~dvermeir/manuals/KDE20Development-html/ch08lev1sec3.html#ch08list09 at the end of the page and was wondering whether there was a less cumbersome way having Modeless dialog.
You can use the attribute Qt::WA_DeleteOnClose to destroy the window when it is closed/hidden, and QWeakPointer (or QPointer) with a static variable to track the existence of the window inside the slot/function which opens it:
void MyWindow::openDialog() {
static QWeakPointer<MyDialog> dlg_;
if (!dlg_)
dlg_ = new MyDialog(this);
MyDialog *dlg = dlg_.data();
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
dlg->raise();
dlg->activateWindow();
}
I'd schedule it for deletion at the time it's work is finished by using deleteLater:
void MyDialog::MyDialog(QWidget *parent) {
// ...
connect(this, SIGNAL(finished(int)), SLOT(deleteLater)));
}
This approach will preclude you from examining it after the finished signal has been emitted (unless you can guarantee that any accesses happen before everything gets back to the event loop when the deletion is actually performed).
Personally, I would choose between either using
dlg->setAttribute(Qt::WA_DeleteOnClose);
or making the dialog a -dynamically allocated- member i.e. creating it only once:
// constructor
: dialog_(0)
// member function
{
if (! dialog_)
dialog_ = new MyDialog(this);
dialog_->show();
dialog_->raise();
}
This way the dialog is deleted when the parent dies, and only needs to be constructed once.

Resources