showEvent() called more than once - Intentional? - qt

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.

Related

End Game Function

I'm trying to display "Game Over" once the player runs out of lives. However, it's never displayed after the game ends. Here is some example code.
void Game::end()
{
QGraphicsTextItem *text = new QGraphicsTextItem("GameOver");
text->setPos(400, 500);
scene->addItem(text);
sleep(2);
QApplication::quit();
}
You dont get the desired result because the drawing occurs after the function it's been called inside returns, and it never returns since you are quiting the application before. As drescherjm pointed out, try to delay the quit by using QTimer::SingleShot. Like this:
QTimer::singleShot(1000, [](){
QApplication::exit();
});
Thus QApplication::exit() is called in given interval, by which time Game::end() should return.

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.

Forward value from signals within a QStateMachine

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.

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.

How to stop an Loading function on button click?

I have a problem, I have to stop one Loading class on button click. I already checked some forums related to this. But didn't find an exact solution.
For example:
Public Sub LoadDropDown()
Dim it As Integer
For it = 0 To 1000000
DropDownList1.Items.Add(it)
Next
End Sub
I have to load the DropDown on Load button click and I have to cancel that on cancel button click.
Since populating the control happens on the server, I can't imagine way to interrupt your method from the client. The whole control is being populated, rendered, and only then sent to the client. You might interrupt the callback using ajax, but then the control wouldn't be returned at all.
An alternative could be to load the contents in chunks with ajax and append them to the control on the client-side.
There is no formal way to do what you're asking, but you should be able to achieve the same result if you refactor your code. If certain content shouldn't be loaded for certain users, do that logic in your code behind before it renders to the page.
Per your response to the other answers...
You could consider doing multiple my_ddl.items.add() calls on a timer. Would involve multiple, separate postbacks / ajax calls. For example:
1) add records for 2 seconds (instead of a fixed number of records at a time)
2) check for session("continue") = "true"
3) add more records for 2 more seconds
4) check session("continue")
...
At some point, user clicks cancel, which assigns "false" to session("continue"). Next time your loop checks session("continue"), it will see that it's false and exit.
This would give you a partially-loaded data control. You might want other code to wipe-out the partial update.
I think you could accomplish this with a Session Variable. Forgive me, but I'll have to provide the example in C#, but I'm sure you can get the general idea of this.
private bool CancelRequested
{
get
{
if (Session["CancelRequested"] == null)
return false;
else
return (bool)Session["CancelRequested"];
}
set
{
Session["CancelRequested"] = value;
}
}
public void LoadDropDown()
{
for (int it = 0; it <= 1000000; it++)
{
if (CancelRequested)
{
CancelRequested = false;
break;
}
//Your logic here
}
}
protected void btnCancelRequest_Click(object sender, EventArgs e)
{
CancelRequested = true;
}
The idea here is that the inital loop checks a Session variable to see if it should continue or break out of the loop. If you have a button on the page that will allow the user to set this Session variable to "true", they can essential communicate to the inital request and cause it to break out of the loop. I'm not sure if this would fully accomplish what you're looking to achieve, but hopefully it helps.

Resources