How do you suppress a Qt main menu keyboard shortcut? - qt

For example, consider a main menu item that has the Delete key as a shortcut (with Qt::WindowShortcut as context). I want another QWidget to handle the Delete key when focused. This is not possible because the Delete key is processed by the main menu. I've tried grabbing the keyboard on QWidget focus but that doesn't do anything. Is this event possible?

I was able to get the behavior I wanted by installing an event filter on qApp when the QWidget is focused (remove it when losing focus), and returning true for all QEvent::Shortcut types.
void MyWidget::focusInEvent( QFocusEvent *event )
{
qApp->installEventFilter(this);
}
void MyWidget::focusOutEvent( QFocusEvent *event )
{
qApp->removeEventFilter(this);
}
bool MyWidget::eventFilter( QObject *target, QEvent *event )
{
if (event->type() == QEvent::Shortcut)
{
// If I care about this shortcut, then return true to intercept
// Else, return false to let the application process it
}
return false;
}
If there's a better way, I'd love to hear it!

Related

Qt: Issuing a message before emitting a signal

Brief description of my application and my question:
In a QTabWidget I have several groupBoxes containing each 2 QRadioButtons.
E.g.
Select Menu (groupBox): (RadioButton:) A (RadioButton:) B
At one point of time, only one Menu can be active.
Both radioButtons are connected to each othe ->
When I click radioButton A and set it true, radioButton B is automatically set false - and the other way round.
When trying to change the menu setting, before a click signal is emitted, I would like to issue a QMessageBox Warning "Are you sure you want to change the menu? This can cause severe damage to your device." -Yes/No.
When clicking Yes, I would like to change the menue setting. When clicking No, I would like everything to remain as before.
The only problem I have is: When issuing the QMessageBox in the on_radio_button_toggled slot, the radioButton state has already changed to true.
Even if I change the state in the slot again and correct them, it looks like the state has already changed when the pop up message shows up. I don't want that because that implies that the state of the menue has already changed.
Where or How can I let a QMessageBox pop up before emitting the actual signal slot - when clicking the radio Button?
Thank you very much for your help.
Update:
I have now implemented an eventFilter as recommended. Here is my source code:
ui->radioButtonMenu1->installEventFilter(this);
ui->radioButtonMenu2->installEventFilter(this);
SubmenuOne is a QWidget. It is integrated into the MainWindow by a QTabWidget (via placeholder).
bool SubmenuOne::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
QMessageBox::StandardButton reply;
reply= QMessageBox::question(this,tr("WARNING"),tr("Changing the settings may cause severe damage to your device! Please confirm your decision."),QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
//QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
//keyEvent->accept();
//event->accept();
qDebug("Yes.");
return false;
}
else
{
qDebug("No.");
return true;
}
}
}
You have to use bool eventFilter(QObject *obj, QEvent *event); Declare event filter in your window, then instal event filter to every radiobutton like this: radioButton1->installEventFilter(this);. Then at eventFilter check event type: if (event->type() == QEvent::MouseButtonPress) and show your QMessageBox. Then you can accept event and return true or false depending on user choise.
bool SubmenuOne::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
QMessageBox::StandardButton reply;
reply= QMessageBox::question(this,tr("WARNING"),tr("Changing the settings may cause severe damage to your device! Please confirm your decision."),QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
static_cast<QRadioButton*>(obj)->setChecked(True);
}
event->accepted();
return true;
}
return QMainWindow::eventFilter(obj, event); //you forget this. QMainWindow is just base class of your SubmenuOne.
}

Qt : QWheelEvent does not refer to a value

I'm trying to create a Ctrl + Mousewheel macro to zoom in and out of an image view in my application.
Currently I am trying to use the current code:
new QShortcut(QKeySequence(Qt::CTRL + QWidget::wheelEvent(QWheelEvent *event)), this, SLOT(zoom()));
However I get the error QWheelEvent does not refer to a value. I have all the necessary includes in my header file so I do not understand why I'm getting the error.
Is it illegal to bind the widget event in conjunction within a QKeySequence? If so, how should I handle the event?
You can't use QKeySequence in this way. You should reimplement wheelEvent or use next event filter (it is example how to zoom in/out in textEdit, you can use this code for your special case):
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->plainTextEdit && event->type() == QEvent::Wheel )
{
QWheelEvent *wheel = static_cast<QWheelEvent*>(event);
if( wheel->modifiers() == Qt::ControlModifier )
if(wheel->delta() > 0)
ui->plainTextEdit->zoomIn(2);
else
ui->plainTextEdit->zoomOut(2);
}
return QObject::eventFilter(obj, event);
}
Main idea: catch wheel event and check is Ctrl modifier is pressed.
To use eventFilter you should also:
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
Note: I showed example with event filter because it is not require subclassing, it is not better or something else, reimplement wheelEvent with similar code and you will get absolutely same result.

What is the signal for when a widget loses focus?

In a dialog, when the tab key is pressed, the focus changes to another widget. In Qt, is there any signal for when a widget loses its focus? Can I use it to check if the input is valid or not? If not, can I set the focus back and ask the user to re-input?
There's no signal but if you want to know when your widget has lost focus, override and reimplement void QWidget::focusOutEvent(QFocusEvent* event) in your widget. It will be called whenever your widget has lost focus. To give focus to a widget, use QWidget::setFocus(Qt::FocusReason).
To validate input in a QLineEdit or QComboBox you can subclass QValidator and implement your own validator, or use one of the existing subclasses, QIntValidator, QDoubleValidator, or QRegExpValidator. Set the validator with QLineEdit::setValidator(const QValidator*) and QComboBox::setValidator(const QValidator*) respectively.
If you want to validate the contents of a modal dialog box, one way would be to override QDialog::exec() with an implementation like this:
int MyDialog::exec() {
while (true) {
if (QDialog::exec() == QDialog::Rejected) {
return QDialog::Rejected;
}
if (validate()) {
return QDialog::Accepted;
}
}
}
bool MyDialog::validate() {
if (lineEdit->text().isEmpty()) {
QMessageBox::critical(this, "Invalid value", "The specified value is not valid");
lineEdit->setFocus();
lineEdit->selectAll();
return false;
}
return true;
}
It will not allow the user to close the dialog with the OK button or any other button with the Accepted role unless the contents of the dialog is successfully validated. In this example I assume the dialog has a QLineEdit named lineEdit and the validate function will make sure that its content is not empty. If it is, it will set the focus to the QLineEdit and show the dialog again.
It is also possible (and easier) to create the signal yourself
In the .cpp (do not forget to include the moc)
class FocusWatcher : public QObject
{
Q_OBJECT
public:
explicit FocusWatcher(QObject* parent = nullptr) : QObject(parent)
{
if (parent)
parent->installEventFilter(this);
}
virtual bool eventFilter(QObject *obj, QEvent *event) override
{
Q_UNUSED(obj)
if (event->type() == QEvent::FocusIn)
emit focusChanged(true);
else if (event->type() == QEvent::FocusOut)
emit focusChanged(false);
return false;
}
Q_SIGNALS:
void focusChanged(bool in);
};
And to connect it:
connect(new FocusWatcher(myWidget), &FocusWatcher::focusChanged, this, &View::doSomething);

QMainWindow - wait until 'show' function is done

Is there a signal that tells when 'show' function finishes?
I have a problem in my code: If I write:
QMainWinObj.show();
QMainWinObj.someGuiFunc();
the code doesn't work. But, if I write:
QMainWinObj.show();
sleep(3000);
QMainWinObj.someGuiFunc();
It does.
So I think the problem is that 'show' dosn't finish its jub before I call 'someGuiFunc'. That's why I want to have some kind of a sign that 'show' is finished..
This may be a bit dated but since nobody else answered it except the one:
Since there is no "Show" signal I suggest overriding the show event like this:
In your mainwindow.cpp file:
void MainWindow::show()
{
QMainWindow::show();
QApplication::processEvents();
emit windowShown();
}
In your mainwindow.h file, somewhere in MainWindow's declaration:
...
class MainWindow: public QMainWindow
{
...
signals:
void windowShown();
...
}
...
Then, when you go to the designer, right click on the main window (very top of the object tree), and select "Change signals/slots". In the "Signals" frame, click the "+" button, and you will need to add "windowShown()" and then press enter, and then the OK button (note that the elipses "..." denote other code that is already in your header).
That's it -- you can now use the signals/slots editor to link slots up to the 'windowShown' signal whenever you want. Now if you want something more like Microsoft's "Loaded" event which I think is used in .NET you will need to create some instance variable and flag it so that every time the window is shown, it isnt emitted, for example:
void MainWindow::show()
{
QMainWindow::show();
QApplication::processEvents();
emit windowShown();
if (firstTimeShown == true)
{
emit windowLoaded();
firstTimeShown = false;
}
}
Also, don't forget to initialize the variable to 'true' in your constructor:
MainWindow::MainWindow(QObject* parent)
...
{
firstTimeShown = true; // put this somewhere before ui->setupUi()
}
If you decide to put it in the initializer list however, make sure it is in proper order. The compiler will complain if the variables are not instantiated in a top-to-bottom fashion as declared in the class' header.
Now, make sure when you define firstTimeShown in your header, that you make it private. And lets not forget the added signals:
class MainWindow : public QMainWindow
{
...
signals:
void windowLoaded();
void windowShown();
private:
bool firstTimeShown;
...
That's about it. With the flexibility of signals and slots, its pretty easy to mimic any event that you might find from windows forms or from MFC. It just takes a little effort on the programmer's part. Once you get the hang of it however it it'll be like second nature.
note: there probably are optimizations or better and more precise ways of making the "Loaded" and "Shown" signals perform but I have left things like this out for simplicity's sake. And to come back to the question at hand, calling QApplication::processEvents() is most likely what you want to do instead of waiting a fixed amount of time because who knows how long it will take if the user is running 100 other things on top of it, etc, etc. Hope that helped, the extra explanation was included hoping that it might give you a better way to do the things that you want to do instead of waiting for something to be done, 'knowing' it is done is a much better alternative.
There is no such signal, but having QMainWindow subclassed you can override showEvent event.
void MainWindow::showEvent(QShowEvent *){
//your code
}
More info here: http://qt-project.org/doc/qt-4.8/qwidget.html#showEvent
Be aware it's called every time your window is about to be displayed.
Problem can decide without subclassing, just installing event filter like this:
class CWidgetIsPainting_EF : public QObject
{
bool m_bIsPainted = false;
public:
CWidgetIsPainting_EF( QObject * parent = 0 ) : QObject (parent) { }
inline bool IsPainted() const { return m_bIsPainted; }
inline void setIsPainted( bool bIsPainted ) { m_bIsPainted = bIsPainted; }
protected:
bool eventFilter( QObject * obj, QEvent *event )
{
if (event->type() == QEvent::Paint)
{
m_bIsPainted = true;
return true;
};
return QObject::eventFilter(obj, event);
}
};
...
...
CWidgetIsPainting_EF * pPaintingEF = new CWidgetIsPainting_EF( m_pWidget );
m_pWidget->installEventFilter( pPaintingEF );
...
...
while ( !pPaintingEF->IsPainted() )
QApplication::processEvents();
Override bool event(QEvent *event) and catch the Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}

How to make a Qt dialog to deal with key event instead of all its child widget?

I have a dialog inherits QDialog. the dialog has many widgets including Qbuttons, QLabel, QGraphicsView, etc. Many widgets such as button can process SPACE key pressing event when they get focus. Now I don't want any of the child widgets to deal with it, but let the main dialog do. Since there are many widgets in main dialog,so I didn't intallEventFilter for them, but for qApp.
code as follow:
QApplication app(argc, 0);
MyDialog *pDlg = new MyDialog(...);
qApp->installEventFilter(pDlg);
app.exec();
And eventfilter of main dialog:
bool MyDialog::eventFilter(QObject *obj, QEvent *e)
{
if(e->type() == QEvent::KeyPress)
{
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
if (ke->key == Qt::Key_Space && !ke->isAutoRepeat())
{
// do my things in the main dialog
return true;
}
}
return qApp->eventFilter(watched, event);
}
Unfortunately, after using this code, the main dialog's layout is curious, seems some widgets didn't remember their size policy. Maybe some Qt resize or repaint event not processed? Could any one tell me how to catch the key event in main dialog, but not affect other function?
Basically if you developing a dialog based App in Qt, by default keypress events are captured by main dialog class, provided you define keypressevent in the main class.
EDIT
Use postevent() for this purpose
In your child widgets key press event do
void childwdgt::keyPressEvent(QKeyEvent *e)
{
if (e->type() == QEvent::KeyPress)
{
{
QKeyEvent* newEvent = new QKeyEvent(QEvent::KeyPress,e->key(), e->modifiers ());
qApp->postEvent (yourParentWdgt, newEvent, 0);
}
}
Similarly you can handle other type of key events also.

Resources