I have a button with function onClicked. There is a C++ class Middle with function search_connection connected via qmlRegisterType.
What I want to do is to change the text of searchButton while the C++ function is calculating the return value and also disable the button for that time.
What happens is nothing. The button is enabled for the whole time with text "Search". I believe that what actually happens is, that search_connection function is executed first and than all the rest happens so fast I can't notice the change from "Search" to "Searching..." and back again.
TextField {
id: startStop
}
TextField {
id: finishStop
}
Button {
id: searchButton
text: qsTr("Search")
onClicked: {
text = qsTr("Searching...")
enabled = false;
searchResult.text = middle.search_connection(startStop.text,finishStop.text)
enabled = true;
text = qsTr("Search")
}
}
Does anyone know how to make the Qt to call the function after it changes the text to "Searching..."?
Your UI will repaint certain areas with the next call of the event loop. This never happen in your case, because the slot (your function) is called directly and blocks the application until it is finished. There are a few ways to avoid that blocking:
Move your task to another thread (QThread) and wait until finish
Start your task with an timer (QML-Timer). You can redraw the button before starting the method, but your GUI will still block until the method has finished (the change of enabled will be useless).
Call processEvents() within your method multiple times (not recommended, can cause problems).
If the method needs some time, I would use a new thread. So your gui isn't blocking and your button wil still receive events (painting, mouse, ...).
Edit 1:
If your C++-Code is simple and can be translated to JavaScript-Code, you can also use WorkerScript. It's the QML-Version of QThread. But it has some limitations. You can only use JavaScript and you can't interact with the Worker while running.
Thanks to folibis for the suggestion.
Related
I want to create a menu whose content is determined dynamically (through some data received from the network). How would I go about implementing such a menu in Qt?
The network should be queried for the content to be shown whenever the menu is opened.
assuming you have a menu bar somewhere:
QMenu* m = new QMenu(this);
m->setTitle("click here");
m->setIcon(QIcon());
ui->menuBar->addMenu(m);
I think you are looking for QMenu::aboutToShow signal. This signal is emitted when the user opens the menu and it is the perfect moment to re-populate your menu.
Nevertheless, you have to take following things into consideration:
The signal is emitted synchronously respect the rest of the UI, it means that once the slot handling it finishes the menu will be shown. So you have to populate the menu before returning from that function.
This signal is emitted in the main thread (the one that handles the GUI), so the rest of the interface will be blocked until it finishes. This is important if your network connections depends on the events loop. You may use QApplication::processEvents to be sure events are consumed.
Basic example
The following example uses a timer instead of a network connection for the sake of simplicity, but illustrates my point:
In some initialization function (such as the constructor). The menu is assumed to already exists (similar case for context menu):
connect(ui.menuDynamicMenu, &QMenu::aboutToShow, this, &MainWindow::onMenuAboutToShow);
The slot (m_waiting should be an atomic boolean or similarly protected flag, in the case you want to handle the network connection using multi-threading):
void MainWindow::onMenuAboutToShow()
{
// Here your _synchronous_ network query
// Probably you'll have to use some kind of barrier
m_waiting = true;
QTimer::singleShot(2000, this, [this]() {
ui.menuDynamicMenu->clear();
ui.menuDynamicMenu->addAction("Some action from network query");
ui.menuDynamicMenu->addAction("Another action from network query");
m_waiting = false;
});
while (m_waiting) {
qApp->processEvents(QEventLoop::WaitForMoreEvents);
}
}
The full code of this example is available in GitHub.
I have an existing (legacy) SDI app, and I want to call some functions immediately after the UI is shown, i.e. when it would normally sit idle and wait for user input.
Normally I'd put the calls in CView::OnInitialUpdate, e.g.:
CMyView::OnInitialUpdate()
{
CView::OnInitialUpdate();
//...
LoadStartupData(); //<-- this is what I want to call
}
... but this gets executed before the UI is shown. For this I want to show some UI feedback while it runs (it takes a while and should probably be in its own thread, but that's a different problem); the function also shows a dialog at the end of it, so I do need the UI to be visible.
What's the "best practice" on how to do this?
Use PostMessage in your init code with a WM_APP+x message.
When the message pump runs your message is delivered to the window and you can do your lazy init.
Use a ON_MESSAGE(MY_WM_APP_INIT,OnMyInit) entry in the message map.
Other way would be to use CWinApp::OnIdle, but this isn't view specific.
Or use a "one time" Timer... start a timer handle it and kill it.
I'm writing simple application for WP 8.1 using C++/cx. My problem starts when I'm trying to do something in some event. For example if I create simple button event "tapped" and I want to do something inside for example change the color of the button background, it doesn't execute in the correct time. I mean that for the code below it will first execute Somefunction() and then change the color of the button.Same happens for example when I try to show message box using message dialog and ShowAsync function.
but->Background = ref new SolidColorBrush(Windows::UI::Colors::Red);
Somefunciton();
You have the background change and the function call in the same function and that function gets executed on one thread blocking it. This thread happens to be the UI thread which gets blocked for the time of your function execution. So you set the button background but the actual change will be applied only when the UI thread could run the render function and it will be able to do it only after your function call ends.
So in terms of program execution the button background gets updated before the call to Somefunciton();. But visual changes are delayed until after the function call is completed so you might think that Somefunciton(); gets called before the background is set which is not the case.
I've an application visual c++ written using vs2010,
I have two buttons: "start" and "stop",the first one calls a function that takes a lot of time to process, so in a certain moment I'd like to stop the computation pressing stop button. But in my application the button start seems still clicked (I think it's waiting for the return of the function) and all the other buttons appear to be disabled. Even if I had a wonderful stop function, I could not active because I'm not able to click on button stop. Solutions,ideas,using threads,easy example? Thanks.
You need to run your calculations in another thread. Otherwise your gui freezes until your calculations are done (because only one thing can be done at the moment).
If you are using some modern compiler look at std::thread.
other solutions are boost threads or even microsoft threads.
If your computation is a loop, it may be quite easy to check at each iteration if your User wants to stop the computation.
In the computation thread:
while(compute){
// one loop of computation
}
While your GUI thread can set computationto false through your stop button.
I hope it helps
Note: In c++ as in java Swing etc.., the GUI has it's own thread (not really visible to the developer) and you should never do heavy tasks in it. For instance, every callbacks for buttons should be as small as possible to keep your GUI responsive. This thread's job is just to drive your application.
Button 1 -> onClick =
{
start thread -> { do stuff here }
}
BUtton 2 -> onClick =
{
close thread
}
Be careful when forcibly closing a thread because you can leak memory !
http://msdn.microsoft.com/it-it/library/system.componentmodel.backgroundworker%28v=vs.110%29.aspx
BackgroundWorker is perfect for this use!
First you need to imports the namespace
using System.Threading;
then use the following code :
Thread T=new Thread(Your Code Goes method name);
your method name()
{
// your code goes here
}
T.Start();
then
T.Abort();
I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.