No double / Only one QMessageBox at same time - c++

In my application, I have the following situation:
In a verify-method for inputs of a QTabWidget, I'm coming to determine an error for a sub-widget's input.
In cause of that I set this sub-widget as the active widget
In showEvent of this widget I'm firing an QTimer::singleShot to a method onShowEventFinished() in which I'm going to show a QMessageBox
After activating the the new sub-widget, I want to show at first the error message from my verify (step 1).
So then the message from verify shows up, but then in cause of my singleShot in step 3 the other one show up over it. I want to show the second one at the earliest, when the first one is closed.
(It's important that the first message shows up at the earliest when the regarding sub widget ist showing.)
Example code:
class MySubwidget : public QWidget
{
// omitted (ctor, etc.)
protected:
void showEvent( QShowEvent* e )
{
QShowEvent( e );
QTimer::singleShot( 200, this, SLOT(onShowEventFinished()) );
};
private slots:
void onShowEventFinished()
{
bool showEntryHint = false;
// omitted (some stuff to determine to show an entry hint or not)
if( showEntryHint )
{
QMessageBox t_MessageBox( this );
// omitted (set up the message box
t_MessageBox.exec();
}
};
};

I've tried different ways to solve this with QMutex, QWaitCondition or QSemaphore in a derived class from QMessageBox but this doesn't work because the execution of the message box is in the same thread.
This means, when the first message box is started with QMessageBox::exec() than QApplication::processEvents (called by QMessageBox) caused the invokement of my slot and called QMessageBox::exec() twice (for the second hint, while the first exec() is alive until the first message box is closed).
My currently workaround is now to have a subclass where I'm calling QApplication::processEvents() as long as another instance of MyMessageBox is shown:
class MyMessageBox : public QMessageBox
{
public:
// omitted (ctor, etc.)
int exec()
{
while( MessageBoxShowingCount > 0 )
QApplication::processEvents();
return QMessageBox::exec();
};
protected:
void showEvent( QShowEvent* e )
{
MessageBoxShowingCount++;
QMessageBox::showEvent(e);
};
void hideEvent( QHideEvent* e )
{
QMessageBox::hideEvent(e);
MessageBoxShowingCount--;
};
static int MessageBoxShowingCount = 0;
};
(For this solution I have replaced all my QMessageBox-instances by an MyMessageBox-instance.)

What you seem to need is an application-wide message manager. The QMessageBox then becomes that class's implementation detail and is not used directly anymore. When you want to show a message, you'd then use your MessageManager class.

Related

How to prevent GUI from hanging while allowing 2nd operation to be performed alongwith 1st operation in Qt

Here I am explaining my problem statement in detail and the efforts I have put so far
A) Problem Statement : During printing if 'Stop Printing' pushbutton is pressed, the printing should stop at that moment!
B) My Work :
1. StartPrinitng_Pressed :
void MainWindow :: on_StartPrinitng_Pressed()
{QSqlquery studentList;
studentList("SELECT Name, address FROM class WHERE Roll No = some variable")
while(studentList.next())
{
Name=studentList.value(0).toString();
address=studentList.value(1).toString();
QTimer:: singleShot(1000,this,SLOT(StopNow())); //calling stopNow function
if(StopPrintingNow==0)
{ //** I am printing the fetched data (in a string) by setting GPIO pins HIGH **// }
}
}
2. StopPrinting_Pressed :
void MainWindow::on_StopPrinting_Pressed()
{StopPrintingNow=1;}
3. StopNow Function Declaration :
void MainWindow::StopNow()
{
if(StopPrintingNow==1)
{ //** I have reset all serials ports; Break; **// }
else if(StopPrintingNow==0)
{ QTimer::singleShot(1000,this,SLOT(on_startPrinting_pressed())); }
}
C) Flow of program execution : As and when "StartPrinting" pushbutton is pressed, the query shown in my question executes which fetches data from database and perform simultaneous printing.
D)Problem Faced -
1.GUI is getting hanged while printing, hence StopPrinting button doesn't respond.
Qtimer is not calling "StopNow function " while printing (though I have called it at correct position)enter image description here
Handling of timers and button presses is both covered by the Qt event loop -- which is blocked while you are looping over that SQL query. You have two options:
1) Periodically dispatch events in your while loop.
This is as simple as
qApp->processEvents();
But you have to be careful, however: any events you trigger due to user interaction (or a timer) will block and your while loop will not run until the event is finished. In your case especially, you could end up running a second copy of your on_StartPrinitng_Pressed function.
2) Do the printing on a separate thread.
This involves some more code, but the gist of it is that you create a SqlPrinter object with a startPrinting slot and stopPrinting slot. You then create a QThread and change its owner thread to that thread. Slot invocations will happen across the thread boundary and all will be fine.
class SqlPrinter : public QObject {
Q_OBJECT
public:
SqlPrinter(QObject* parent = nullptr) : QObject(parent) {}
public slots:
void startPrinting();
void stopPrinting();
};
In your main code, then do something like this, assuming that you have the two buttons named MainWindow_StartButton and MainWindow_StopButton:
QThread* printerThread = new QThread(qApp);
SqlPrinter* printer = new SqlPrinter;
printer->moveToThread(printerThread);
printerThread->start();
QObject::connect(MainWindow_StartButton, &QPushButton::clicked, printer, &SqlPrinter::StartPrinting);
QObject::connect(MainWindow_StopButton, &QPushButton::clicked, printer, &SqlPrinter::StopPrinting);
Don't forget to clean up SqlPrinter afterwards!

Typecasting EventHandler in C++/CLI

I have a form (which I'll call MainForm) embedded with a TabControl. Every time the user creates a new tab it is filled with an instance of a pre-built Panel (which I'll call MyPanel) which contains many controls.
My MyPanel class has a private variable bool save_state which is set to false every time one of the (editable) controls is edited and set to true when the user "saves" the state of the panel.
I want a visual flag to keep track of tabs that have unsaved changes (e.g. the tab "Tab1" will instead display the text "Tab1 *" if it has unsaved changes). So I want to set up the event handler in my MainForm which can call a method in MyPanel to add the handler to each control.
Since not all my controls use the same EventHandler type (for example, I also need to track DataGridViewRowsAddedEvent, among others), I currently have several methods adding the appropriate handler to the corresponding controls (one for each type of Event Handler), each of which is running the same code (i.e. set the save_state bit to false and append " *" to the tab text.
For example, in MainForm.cpp I have:
#include "MyPanel.h"
void markUnsaved(void) {
// set panel bit to false
// append " *" to tab text if we haven't already
}
void MainForm::handler1(Object ^sender, EventArgs ^e) {
markUnsaved();
}
void MainForm::handler2(Object ^sender, DataGridViewRowsAddedEventArgs ^e) {
markUnsaved();
}
void Main::FormaddNewPanelToTab(int tab_index) {
// check index is valid ...
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->addEventHandlerToControls(gcnew EventHandler(this, &MainForm::handler1));
new_panel->addDgvEventHandlerToControls(gcnew DataGridViewRowsAddedEventHandler(this, &MainForm::handler2));
// rest of code...
}
Though this currently works as intended, this (along with the several other Event Handler types I have to manage) makes my code look really silly.
I am hoping to be able to have have a single event handler in MainForm and a single method in MyPanel which type-casts the Event Handler passed and adds it to all the controls with the appropriate types.
I have tried doing simple casts such as:
void MyPanel::addHandlerToControls(EventHandler ^handler) {
control_NUD->ValueChanged += handler; // this works because ValueChanged is of type EventHandler
control_DGV->RowsAdded += (DataGridViewRowsAddedEventHandler ^)handler; // this compiles but throws an exception
// rest of the code...
}
to no avail.
Any help would be greatly appreciated!
I know this is maybe a bit late for answer but I'd want to show how would I solve this.
Firs of all I suggest to get rid from idea of casting event handlers. Kind of such approach may work in C# (with some adjustments) but as far as I know it's not possible in C++ /CLI.
I'd go for adding new event to a MyPanel class that will be invoked every time when the data on a panel is changed. But to avoid adding a lot of different handlers to a control events in a MyPanel class it's better to create one generic method that will handle all the neccessary control's events and fire new event. Maybe this sounds messy, let me show the code:
public ref class MyPanel
{
// Add a new event
public:
event EventHandler^ DataChanged;
// Add a method that will fire new event
// this methid will be invoked on every control's event that you'll subscribe
private:
generic <typename T>
void DataChangedHandler(System::Object^ sender, T e)
{
// Fire the event
DataChanged(this, EventArgs::Empty);
}
// Once the controls are initialized you may add the event handlers
// I put it in a constructor only for example
MyPanel()
{
control_NUD->ValueChanged += gcnew EventHandler(this, &MyPanel::DataChangedHandler<EventArgs^>);
control_DGV->RowsAdded += gcnew DataGridViewRowsAddedEventHandler(this, &MyPanel::DataChangedHandler<DataGridViewRowsAddedEventArgs^>);
// and so on...
}
}
/// And now in a main form we only need to subscribe to a DataChanged event
public ref class MainForm
{
//...
// the handler
void MyHandler(Object^ sender, EventArgs^ e)
{
markUnsaved();
}
void FormaddNewPanelToTab(int tab_index)
{
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->DataChanged += gcnew EventHandler(this, &MainForm::MyHandler);
}
//...
}
Hope this helps.

Making two QT dialogs close each others

I've seen this question : Qt/C++ - Closing two widgets when one is closed
However I do not have this MainWindow <-> Widget relationship.
class Ui_DialogResults
{
public:
QDialog *_Dialog;
void setupUi(QDialog *Dialog)
{
_Dialog = Dialog;
}
};
class Ui_DialogSearch
{
public:
QDialog *_Dialog;
void setupUi(QDialog *Dialog)
{
_Dialog = Dialog;
}
};
namespace Ui {
class Ui_Search : public Ui_DialogSearch {};
class Ui_Results : public Ui_DialogResults {};
}
class Search : public QDialog{
public:
Search(){
ui.setup(this);
}
void closeEvent(QCloseEvent *event)
{
//saves scan settings here
pResults->_Dialog->close();
}
Ui::Ui_Search ui;
Ui::Ui_Results *pResults;
}
class Results : public QDialog{
public:
Results(){
ui.setup(this);
}
void closeEvent(QCloseEvent *event)
{
//saves scan settings here
pSearch->_Dialog->close();
}
Ui::Ui_Search *pSearch;
Ui::Ui_Results ui;
}
When a Search dialog is open, a result dialog is also open and vice versa.
The Thread opening both dialogs also fills the pointer in the Search instance towards Result and vice versa.
Each class has overriden closeEvent()so that they close each others as such.
I would have expected cross recursive consequences and crash but it apparently works perfectly.
Qt doc states
The QCloseEvent class contains parameters that describe a close event.
Close events are sent to widgets that the user wants to close, usually by choosing "Close" from the window menu, or by clicking the X title bar button. They are also sent when you call QWidget::close() to close a widget programmatically.
Is this UB?
Since you already have a mutual reference between the search and results dialog, you can easily avoid any potential incidents by setting pointers to null and doing some basic checks.
The dialog that initiates the closing sets its own pointer in the other dialog to null, copies the pointer to the other to a temporary value, sets the other pointer to null and closes the other dialog through the temp pointer. If the pointer to the other dialog is already null, that would mean the other dialog has initiated the closing, so do nothing except call the base class implementation in order to close the dialog.
Is it undefined Behavior? No. Undocumented? Possibly. (Did not find any explicit statement that this is supposed to work). Unintended? Probably not. Qt is pretty mature, although not always documented to the last detail. I bet they only allow only one close event per widget.

How to know when the VerticalScrollBar is showing?

I need to know when the verticalScrollBar of my QTableWidget is being shown.
I am currently using the following instruction:
Header:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QDebug>
#include <QWidget>
#include <QScrollBar>
namespace Ui {
class MyClass;
}
class MyClass: public QWidget
{
Q_OBJECT
public:
explicit MyClass(QWidget *parent = 0);
~MyClass();
private:
void populateTable(QVector<QString> content);
private:
Ui::MyClass *ui;
};
#endif // MYCLASS_H
Populate table function:
void MyClass::populateTable(QVector<QString> content)
{
while( ui->myTableWidget->rowCount() > 0 )
{
ui->myTableWidget->removeRow(0);
}
QTableWidgetItem* item;
for (int row = 0; row < content.length(); ++row)
{
ui->myTableWidget->insertRow(row);
item = new QTableWidgetItem( QString::number(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 0, item);
item = new QTableWidgetItem( content.at(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 1, item);
}
qDebug() << "This : " << this->isVisible();
qDebug() << "QTableWidget : " << ui->myTableWidget->isVisible();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible(); // <-HERE
}
Output:
// Called from the constructor
This : false
QTableWidget : false
VerticalScrollBar : false
// Called by a button pressed
This : true
QTableWidget : true
VerticalScrollBar : true
This : true
QTableWidget : true
VerticalScrollBar : false
But it returns a wrong value. When the ScrollBar is visible it returns false and when it is not visible it returns true. Note: myTableWidget (QTableWidget) is always visible.
Is there any other way that I can do this?
I'm using Qt version 5.3.2
In general the code you are using is supposed to work - checked on Qt 5.3.0.
However, you must be sure that when you are making the call the QTableWidget itself is visible.
For example if you make the call inside the MainWindow constructor you will certainly get a false answer. Only after the form is shown the call to isVisible() on particular scrollbar would return the correct value.
EDIT:
With your code pasted I was able to reproduce the issue. I needed to go through the Qt code a bit to see whats going on. Basically it turns out that for QTableView which is parent class of QTableWidget scroll bar values are updated via updateGeometries (do not confuse it with the regular updateGeometry the one I'm mentioning is protected). Internally this method is called either directly or the event is processed through the event loop. In short, it depends on whether you add columns or rows.
In your example, if you insertColumn instead of insertRow (and switch the arguments in setItem) after checking the visibility of horizontalScrollBar you will get the proper result right away.
I could confirm this by subclassing the QTableWidget and overriding event method. It shows that when adding columns following events are executed: MetaCall (invoke call) and LayoutRequest. On the other hand, when adding rows first event passed is Timer.
I'm not Qt implementer so I'm not sure what is the purpose the difference. However, this info helps solving your problem in a more elegant way.
You can implement MyTableWidget which overrides the event method.
class MyTableWidget: public QTableWidget
{
Q_OBJECT
public:
bool event(QEvent *e) override
{
const bool result = QTableWidget::event(e);
if (e->type() == QEvent::LayoutRequest)
{
// call what you need here
// or emit layoutUpdated(); and connect some slots which perform
// processing dependent on scrollbar visibility
}
return result;
}
signals:
void layoutUpdated();
}
However, such event might get called in other situations not only when the view needs to be updated due to model data updates.
Another solution would be to avoid overriding the event method but creating your own method to trigger the required updates. For example:
void MyTableWidget::updateLayout()
{
QEvent ev{QEvent::LayoutRequest};
QTableWidget::updateGeometries();
QTableWidget::event(&ev);
}
This would call directly updateGeometries which recalculates scrollbar min/max values and perform a direct event method call for LayoutRequest (without processing through eventloop). Which if I'm correct indirectly updates scrollbar visibility.
Calling this method before checking the visibility should also fix your problem.
ui->myTableWidget->updateLayout();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible();
// prints "VerticalScrollBar : true false"
I wanted to get notified via a signal, when a QScrollBar gets visible. I'm writing this answer here, since this is the first google result I got and no result had a proper answer.
First of all, there is no signal for changed visibility, so we will use valueChanged.
Now you can add a function that checks if the scrollbar isVisible() or not.
Problem: It won't work (and someone with more knowledge in Qt could probably explain why, I sadly cannot).
The trick is, to use a QTimer (Python code):
self.horizontalScrollBar().valueChanged.connect(lambda x: QTimer.singleShot(0, some_func))

form properties not called during resize

I have a .NET form with a boolean property called _isResized, which is set to true when the SizeChanged event is called and is set to false when the bool isResized() function is called.
ref class net_window : public System::Windows::Forms::Form
{
private:
bool _isResized;
public:
net_window(void)
: _isResized(false){
InitializeComponent();
}
void InitializeComponent(void){
this->SizeChanged += gcnew EventHandler(this, &net_window::eventResized);
}
bool isResized(void){
bool temp = _isResized;
_isResized = false;
return temp;
}
Void eventResized(Object^ sender, EventArgs^ e){
_isResized = true;
}
};
I also have an native C++ class called window which acts as a layer around the .NET form. This class is updated every frame of the main loop and uses the bool isResized() function of the form to see if the size has changed.
class Window
{
private:
void* _net_window_handle;
Window(){
_net_window_handle = new gcroot<net_window^>;
(*(gcroot<net_window^>*)_net_window_handle) = gcnew net_window;
}
~Window(){
delete _net_window_handle;
}
void update(void)//Called every frame.
{
if( (*(gcroot<net_window^>*)_element)->isResized() )
//Do stuff.
}
};
The delegate added to SizeChanged is called whenever the form is being resized, so _isResized is set to true as soon as the form size changes, but for some reason the thread of the main loop freezes whenever the window class uses the bool isResized() function, until the user has released the edge of the form and thus the resizing has finished.
Is it not possible to access .NET form values as long as it is being resized. Is there an alternative?
Thanks
EDIT
I use a main loop which calls System::Windows::Forms::Application::DoEvents() every frame. When the thread of the loop enters this function and the main window is resizing, it freezes. Is there a way to avoid this problem.
DoEvents strikes again. Windows pumps a modal message loop when the user starts dragging a window edge. Which means that your DoEvents loop isn't running anymore. Use a timer instead, SetTimer with a 15 or 31 msec interval is about right. Having your code respond to the Resize event directly of course highly preferred.
Thanks Hans Passant