QMenu QThread Implementation error - qt

I am trying to move my QSystemTrayIcon module to a separate thread. I am getting the below error while running the application. The Initialize() method addAction causing the error.
QWidget: Must construct a QApplication before a QPaintDevice
My sample code snippets:
Declared the global variable
QMenu myMenu;
Member Variable in the header file
QAction* openFile;
In the constructor
openFile = new QAction(parent);
Initialize()
{
myMenu.addAction(openFile);
}
void myApp::run()
{
Initialize()
}

You must not use any GUI-related classes outside your main thread, i.e. the one QApplication was created in. Hence you cannot move your QSystemTrayIcon stuff in a separate thread. And no, there is no workaround.

You are approaching this problem backwards. The reason that "The TrayIcon menus are not displaying if some functionality is running at the background Menus will display after the process completion." is because you are blocking the GUI thread when you wait for things. Do not block the GUI thread by waiting on things. Most of Qt provides signals that fire when things get accomplished (when they succeed or fail). Those are the nonblocking APIs that you should be using.

Related

Run logic after window is shown

I have a small program which gets called by another program, downloads/renames some files and closes itself afterwards. I want to show the user the progress with some simple text. My problem is, that the logic of the code runs before the view is completely shown. The window is visible but the content (in this case the sample text) is not.
I have already tried setOnShown() (as seen in my example) or setOnShowning(). I also tried not using a fxml file for the layout. But nothing seems to work.
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
Text t = new Text();
t.setText("sample Text");
root.getChildren().add(t);
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("FXML Welcome");
primaryStage.setScene(scene);
primaryStage.setOnShown(e -> {
updaterLogic(); //do some work
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void updaterLogic(){
try {
Thread.sleep(10 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
I want the view to be completed before running the actual logic. How can i archieve this?
To execute something after a window has been shown you can:
Listen for a WindowEvent.WINDOW_SHOWN event.
See Window.onShown and Window.addEventHandler
Listen to the Window.showing property and react when it changes to true.
Put the code after the call to show().
But not showAndWait() as that method doesn't return until the window is closed (and is illegal to call on the primary stage).
However, how to do this is not your problem (you're already using onShown). Your problem is caused by how you execute the "updater logic". Inside your updaterLogic() method you are calling Thread.sleep (simulating work). Since event handlers are executed on the JavaFX Application Thread that causes said thread to block. While the FX thread is blocked it can't do anything related to the GUI (e.g. layout passes, trigger rendering pulses, process user events, etc...)—thus the frozen window with no content rendered.
Move the "updater logic" onto a background thread so that the FX thread remains free to do its work. For general information about concurrency in Java, see Lesson: Concurrency and the java.util.concurrent package. For JavaFX specifics, see Concurrency in JavaFX, the javafx.concurrent package, and Platform.runLater(Runnable).
Here are two golden rules of JavaFX:
You must never block, nor execute long running work on, the JavaFX Application Thread.
You must always access and modify a live scene graph on the JavaFX Application Thread.
This is documented by Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
Note: Some nodes, such as WebView, must be created on the FX thread; this will be documented as appropriate.

Qt shortcut for custom context menu

I have been reading though a couple of examples and post but I just cannot figure out how to add a shortcut to my custom context menu. My GUI has several elements. One of them is a treeView. For the elements in my treeView I would like to have a custom context menu.
My first approach was according to this tutorial here. The context menu itself worked but the shortcuts cannot work if you create the actin within the show function.
So my second approach was according to this tutorial. But still my shortcuts do not work and if I use the context menu all actions are called twice...
Since I did not find a tutorial or code example, which matches my case, I hope that someone here can explain to me how this is correctly done in theory. Adding a shortcut to an action for a custom context menu.
Where do I have to declare my action?
What needs to be the parent of the action?
On which widget do I need to call addAction?
Thanks for any hints.
Another way is to add your action also to the parent widget (or main window widget). As mentioned in this reply, adding the same action to multiple widgets is fine and it's the way QActions are supposed to be used.
Example with custom HtmlBrowser class deriving from QTextBrowser:
Ctrl+U shortcut works for this code:
HtmlBrowser::HtmlBrowser(QWidget * parent) : QTextBrowser(parent)
{
viewSourceAct = new QAction(tr("View/hide HTML so&urce"), this);
viewSourceAct->setShortcut(tr("Ctrl+U"));
viewSourceAct->setCheckable(true);
parent->addAction(viewSourceAct);
connect(viewSourceAct, &QAction::triggered, this, &HtmlBrowser::viewSourceToggle);
}
and Ctrl+U shortcut does not work with this code (same as above, but without parent->AddAction(...)):
HtmlBrowser::HtmlBrowser(QWidget * parent) : QTextBrowser(parent)
{
viewSourceAct = new QAction(tr("View/hide HTML so&urce"), this);
viewSourceAct->setShortcut(tr("Ctrl+U"));
viewSourceAct->setCheckable(true);
connect(viewSourceAct, &QAction::triggered, this, &HtmlBrowser::viewSourceToggle);
}
Curiously, parent in this case is another widget (tab widget), not MainWindow. Still, adding parent->addAction() helps. And, unlike your suggested answer, it works even when connecting action to simple methods, without slots. Works for me in Qt 5.15.0. Not quite sure why it works, though. Perhaps, the widget the action is added to must be permanent for shortcuts to work? Looks like a bug in Qt.
Thanks to Scheff's hint I got it working. I do not now if this is really the correct way but this works for me.
The action needs to be declared in the constructor of your GUI class (e.g. MainWindow):
actionDel = new QAction(tr("delete"), this);
actionDel->setShortcut(QKeySequence(Qt::Key_Delete));
connect(actionDel, SIGNAL(triggered()), this, SLOT(actionDel_triggered()));
The triggered signal needs to be connected to a slot. Hint: if you create the slot do not use on_ACTIONNAME_triggered, this will interfere with the designer and cause a connection error.
Next add the action to a custom menu
fileContextMenu = new QMenu(this);
fileContextMenu->addAction(actionDel);
And to the widget
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->treeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDirContextMenu(QPoint)));
ui->treeView->addAction(actionDel);
All in the constructor of your GUI class.
To show the context menu use the following code in slot used in the above connect:
QModelIndex index=ui->treeView->indexAt(pos);
// Here you can modify the menu e.g. disabling certain actions
QAction* selectedItem = fileContextMenu->exec(ui->treeView->viewport()->mapToGlobal(pos));
If you do not have a slot for an action, the action can be also handled in the context menu slot, but this does not work with shortcuts!
if(selectedItem == actionOpen){
on_treeView_doubleClicked(index);
}

Qt Form and main.cpp communication

i have a Qt project with mainwindow (GUI Application)
how can i communicate with my main via my form?
for example
increase variable (which there's in my main) while button a click in my form
i have make a project in C which its in console application. works fine, and now i make another project which contains ui form with same code. and i want to make communication between form and main function
You can use the Qt signal/slot functionality.
Example:
// on mainwindow.cpp. Delcare onButtonClicked as a slot() on the header
void onButtonClicked()
{
++myVar;
}
connect(myButton, SIGNAL(clicked()), this, SLOT(onButtonClicked());
Reference docs.

Using Mfc with Qt modal dialog and main process

I have an Mfc application where I want to open a Qt modal window, while the Mfc application is still running behind.
To start a Qt application from an Mfc, I got the how to there. This page creates a Dll but my solution creates a static library instead.
As for creating a Qt modal window with main application running behind, it looks easy.
My problem is that it looks like that I cannot combined both solution.
The following code starts the Qt window, but I strongly suspect that I am blocking my main thread and main thread is not able to receive events.
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget win( hWnd );
win.showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI(&win);
devicesConfigurationWizardUI.exec();
}
exec function is probably blocking main thread
Following solution should not block main thread, but the Qt windows appears only for few miliseconds and disappeared
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget win( hWnd );
win.showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI(&win);
devicesConfigurationWizardUI.show();
}
So, the question is, is it possible to use show function instead of exec function?
(This works if I am creating a Dll and calling the Dll in a worker thread from my Mfc application, but I do prefer using static library)
I did not read enough the walkthough.
It is said : "Since we want the dialog to be modeless we cannot create the QWinWidget on the stack, since it would be deleted when it leaves the scope, and all its children, including the dialog, would be deleted as well. Instead we create QWinWidget on the heap, using operator new"
So, to make my window non modal while giving control back to main thread, I should do this :
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget *win = new QWinWidget( hWnd );
win->showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI = new DevicesConfigurationWizard (win);
devicesConfigurationWizardUI->show();
}

cannot connect to parents slot

I thought I was beginning to understand this but it seems not.
I am trying to connect together a stacked widget so that the children can send a message to the parent to tell it to change the view. The QStackedWidget is a child of the main window and its pages are promoted fomrs that I created myself. I think that's the right way to do it?
So within one of the children forms I tried the following:
// Set up a mapping between the buttons and the pages
QSignalMapper *mapper = new QSignalMapper(this);
mapper->setMapping(ui->automatedButton, 1); // Value of the index
connect(ui->automatedButton, SIGNAL(clicked()), mapper, SLOT(map()));
connect(mapper, SIGNAL(mapped(int)), ((QStackedWidget*)parentWidget()), SLOT(setCurrentIndex(int)));
But that doesn't seem to work. I click the button and nothing changes. However if i do this:
void MySelectionForm::on_automatedButton_clicked()
{
((QStackedWidget*)parentWidget())->setCurrentIndex(1);
}
Which I thought was the same as what I was doing before?
I do see on message when running which I guess is associated:
QObject::connect: Cannot connect QSignalMapper::mapped(int) to (null)::setCurrentIndex(int)
Have you checked that parentItem() is not returning NULL when you call QObject::connect()? It sounds like maybe the parent item is being set after the constructor, which would mean that parentItem() would indeed return NULL. I suspect that might be how Qt Designer rolls. If so, you would need to connect the signal/slot outside of the constructor, either in another method (one that is not called inside the constructor), or outside of the child altogether.
According to the message,
((QStackedWidget*)parentWidget()), SLOT(setCurrentIndex(int)));
leads to
(null)::setCurrentIndex(int)
which means parentWidget() is NULL.
This basically means that the forms are not a child of the QStackedWidget.
If you create your forms manually, you have to do something like this
MyForm *form = new MyForm( ui->stackedWidget );

Resources