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.
Related
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.
At the moment I am developing a Windows DLL with Qt 5.9.2 (MSVC 2015 compiler), which should be loaded by an existing, commercial MFC application. Upon request of this application a modal instance of QDialog should be displayed.
Since QApplication::exec() would block the entire application, I "simulate" the event loop using the following code:
void Core::createQApplicationInstance()
{
// Check, if there's already a 'QApplication' instance running (unlikely)
if (!QApplication::instance())
{
int argc = 1;
// Create a new 'QApplication' instance
m_app = new QApplication(argc, nullptr);
// Create a 'QTimer' instance to call 'processEvents' periodically:
// We can't run 'm_app->exec()' because it would block everything,
// so we'll use this 'hacky-whacky' method here
m_timer = new QTimer;
// Connect the timer's timeout to the app's 'processEvents' via a lambda
QObject::connect(
m_timer,
&QTimer::timeout,
[&]()
{
m_app->processEvents();
}
);
// Start the timer with the fixed 'message' interval
m_timer->start(kMsgInterval);
}
}
If my DLL should now display a modal dialog, it works (partially) with the following code:
{...}
case eUserIGeneral:
{
qDebug() << "<< eUserIGeneral";
QDialog w;
w.setModal(true);
w.exec();
// --> Code here is executed AFTER the dialog has been closed
}
break;
//-------------------------------------------------------------------
{...}
The code after w.exec() will actually be executed AFTER the dialog was closed (as intended). However, the main application still remains responsive and is not affected by the modality of my dialog, which Is not as I expected it to behave.
How can I make sure that inputs in the main application are locked when calling a modal DLL dialog?
Although I don't have a real answer to your question, there too much stuff wrong with your code to be properly explained in a comment. Therefore I am writing this as an answer.
QApplication::exec(): I strongly recommend revising the decision against it. If you want the window to be modal, why would it be wrong to "block the entire application" until it is closed? Note that you will not block the Qt part of the application, only the one that calls exec.
QTimer: A timer can only run inside an event loop. So the m_app->processEvents() either never executes, or you already have an event loop running. Either way, there is no use for the timer.
w.setModal(): If what this does is not correct for you, check out setWindowModality().
w.exec(): Ignores the value of setModal(). Read the documentation of setModal() and exec() to find out more.
w.exec(): Executes an event loop. If this somewhat does what you want, QApplication::exec() should work too. Just make sure to exit the main event loop when done.
w.exec(): Is not executed after the dialog was closed. It is executes while the dialog is shown. It blocks until the dialog is closed. So you will start executing it, show the dialog, close the dialog, and then return from it. Read the documentation of exec() to find out more.
I am investigating QTest for GUI testing. It appears that there is no mechanism in QTest to asynchronously test a signal callback. Am I misunderstanding how to use QTest, or misunderstanding the intended functionality provided by QTest?
For example, I am attempting to test a signal which launches a modal QMessageBox popup in response to clicking a QPushButton. I want to test events and state between the clicking of the button and clicking 'OK' on the QMessageBox. I have tried using QSignalSpy.wait(), QTestEventList.simulate(), QTest::mouseClick(), QTest::mouseEvent(), and QTRY_VERIFY(), all of which, when called from the testing code, do not return until after clicking 'OK' on the QMessageBox popup. Therefore, to test anything before all event handlers return, I would have to do something like install an event filter on the object under test, and write my own asynchronous testing code.
This is all the same code we would have to write if we weren't using a test library and we can do all of this without using QTest. For example, we can manually get a handle to the object under test, connect signals for testing, invoke an event to trigger the event handler or invoke the signal manually, and also write the installed test handlers that interact with the test environment before returning execution to the point at which the event was launched. What does QTest gain us here?
(Also posted on Qt forums)
Working with synchronous events using qtestlib is a bit tricky. If you look into the sources of qtestlib, you can find that event simulation are pretty straightforward. So, qtestlib doesn't provide any methods to handle synchronous events. Anyway, it's possible to handle Qt modal windows which are spawned by your app.
Main note to this question is that GUI objects can't be accessed from others threads except GUI thread. And moreover GUI could be created only in thread where QApplication was created. So some tricks like spawning a new thread to press OK button in QMessageBox will be unsuccessful with error like this object can not be accessed from other thread somewhere in QWidget sources.
To avoid this case async event could be triggered with Qt slots mechanism. First of all you should define a Helper object with some slot, for example
class Helper {
Helper() {}
public slots:
doSmth();
}
Further you should create an instance of this object in the testcase
void BlahblahTest::testCase1() {
Helper h;
...
And before you invoke some synchronous event with, for example, QTest::mouseClick, set a delayed action with
QTimer::singleShot(delay, &h, SLOT(doSmth));
Depends on your needs the implementation of doSmth could be like that
void Helper::doSmth() {
QList<QWidget *> tlw = qApp()->topLevelWidgets();
foreach (QWidget *w, tlw) {
if (...) { // w is a messagebox
Qt::keyClick(w, Qt::Key_Enter);
}
}
}
I've problem with qt signal-slot system.
First I've created a class which is called System in Singleton pattern, so I can access it's instance where I want. System has a signal SelectionChanged.
I've a list widget and I am connecting it's itemSelectionChanged signal to my custom slot which is called onSelectionChanged. In onSelectionChanged slot, I am emitting System's SelectionChanged signal. There is no problem yet.
In my software design, a selection of object(s) can be used by many GUI widgets or custom classes and System's SelectionChanged signal can be emited by widgets other then the list widget.
So I am creating a slot called OnSystemSelectionChanged in the list widget then connect it to the System's SelectionChanged signal. The OnSystemSelectionChangedSlot is like this.
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this) return;
// Then I want to get a list of selected objects and set them as selection of this widget like this:
this->SetSelection(System::Instance()->GetSelectedObjects());
}
But the problem is when I start to set the list widget's selected items, it is going to emit itemSelectionChanged signal and my onSelectionChanged slot will be called. Then the slot will emit System's SelectionChanged signal and then OnSystemSelectionChanged will be called too. It will stop through sender parameter but there is no method for setting list widget's selected items at once.
How can I figure this problem out.
I hope I did explain my problem well. Thanks in advance.
Edit: Spelling and grammer errors are corrected.
There are a few ways of dealing with this in Qt.
Idioms
Use multiple views with one underlying model. This handles propagation of changes to multiple view controls automatically and you don't need to do anything extra. You can use QDataWidgetMapper to link "plain old" widgets to the data elements in a model. I'd say that this should be the preferred way of doing things. Having an underlying model for all of your UI is a step in the direction of good software design anyway.
When propagating changes between data models, implement both a DisplayRole and an EditRole. The views will nominally modify the models using one of the roles (say, the EditRole), while you can, programmatically, modify the models using the other role (say, the DisplayRole). You handle the dataChanged signals from the model in your own slot, properly dealing with the roles, and call setData on the other models with the other role. This prevents the loops.
For controls that are not QAbstractItemViews, implement two signals: one emitted on any change, another one emitted only on changes based on keyboard/mouse input. This is the interface exposed by QAbstractButton, for example: the toggled(bool) signal is the former, the clicked() is the latter. You then only connect to the input-based signals.
Your own code must propagate programmatic changes to all the interlinked controls, since changing one control from your code won't modify the others. This should not be a problem, since well designed code should encapsulate the implementation details of UI controls from rest of the code. Your dialog/window class will thus expose its properties in a way that's not coupled to the number of controls showing a particular property.
Hackish Let's-Hope-They-Won't-Become Idioms
Use a flag inhibiting signal emission (Bartosz's answer).
Break the signal/slot connections for the duration of the change (Bartosz's answer).
Use QObject::blockSignals().
There are two possible solutions I can think of:
add a flag which makes possible to ignore particular signals:
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this || inhibitSelectionChanged)
return;
this->inhibitSelectionChanged = true;
this->SetSelection(System::Instance()->GetSelectedObjects());
this->inhibitSelectionChanged = false;
}
disconnect the slot from the signal, and reconnect it after changing the selection:
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this)
return;
this->disconnect(SIGNAL(SelectionChanged()));
this->SetSelection(System::Instance()->GetSelectedObjects());
this->connect(
this, SIGNAL(SelectionChanged()),
this, SLOT(OnSystemSelectionChanged(QObject*)));
}
I found my solution in QObject::blockSignals() method. It will prevent emitting signals from the list widget while I am setting selected items.
Thanks for all the answers and solutions especialy for BartoszKP's. This solution is looks like the official way of his first solution.
The problem: you've tried to cut corners and created a singleton. Not a classic case for singleton.
Signals and slots are used for notifications, each object notifies interested objects about what it did or to reflect its new state.
I'm suggesting changing the design as follows:
No singleton signal.
Each Object has its own signal and slot for a relevant event (e.g. selection change).
The application or a higher level object (that created the widgets/objects) performs the signal to slot connection. If those widgets are placed in a list, this is very simple.
I'm attempting to implement a simple, lightweight system for recording Qt GUI events and playing them back from a script. I thought this would be fairly straightforward using the magic of Qt's event system, but I'm running into a problem I don't understand.
Here's quick summary of what I'm doing:
RECORDING:
I use QApplication.instance().eventFilter() to capture all GUI events I'm interested in* and save them to a Python script, in which each step looks something like this:
obj = get_named_object('MainWindow.my_menubar')
recorded_event = QMouseEvent(2, PyQt4.QtCore.QPoint(45, 8), 1, Qt.MouseButtons(0x1), Qt.KeyboardModifiers(0x0))
post_event(obj, recorded_event)
PLAYBACK:
I simply execute the script above, in a worker (non-GUI) thread. (I can't use the GUI thread because I want to keep sending scripted events to the application, even if the 'main' eventloop is blocked while a modal dialog eventloop is running.)
The important stuff happens in my post_event() function, which needs to do two things:
First, call QApplication.postEvent(obj, recorded_event)
Wait for all events to finish processing:**
Post a special event to the same eventloop that obj is running in.
When the special event is handled:
Call QApplication.processEvents()
Set a flag that tells the playback thread it's okay to continue
After the second part is complete, my expectation is that all effects of the first part (the recorded event) have completed, since the special event was queued after the recorded event.
The whole system mostly seems to work just fine for mouse events, key events, etc. But I'm having a problem with QAction handlers when I attempt to playback events for my main QMenuBar.
No matter what I try, it seems that I can't force my playback thread to block for the completion of all QAction.triggered handlers that result from clicking on my QMenu items. As far as I can tell, QApplication.processEvents() is returning before the QAction handler is complete.
Is there something special about QMenu widgets or QAction signals that breaks the normal rules for QApplication.postEvent() and/or QApplication.processEvents()? I need a way to block for the completion of my QMenu's QAction handlers.
[*] Not every event is recorded. I only record spontaneous() events, and I also filter out a few other types (e.g. Paint events and ordinary mouse movements).
[**] This is important because the next event in the script might refer to a widget that was created by the previous event.
I think your problem might best be served by using QFuture and QFutureWatcher (that is, if you're using the QtConcurrent namespace for threads, and not QThreads). Basically, the Qt Event handling system does NOT necessarily handle events in the order they're posted. If you need to block until a certain action is completed, and you're doing that action in a separate thread, you can use the QFuture object returned by QtConcurrent::run() with a QFutureWatcher to block until that particular thread finishes its processing.
Something else to consider is the way you handle events. When you use QApplication.postEvent(), the event you create gets added to the receiver's event queue to be handled later. Behind the scenes, Qt can reorder and compress these events to save processor time. I suspect this is more your problem.
In your function which handles playback, consider using QCoreApplication::processEvents(), which will not return until all events have finished processing. Documentation for QCoreApplication is here.
QMenu widgets and QAction signals are a special case. QMenu has an exec() function, normally used for popups. I suspect (but I don't know for sure) that QMenuBar would use this mechanism when it opens a regular pull-down menu. The docs are not clear about this, but Menus act a lot like dialog boxes in that they block all other user activity - how would Qt do this except by giving menus their own event loop? I can't fill in all the blanks from the information in your post, but I don't see how your playback thread would cope with a new event loop.