I have a working application. I added a menuBar() to the main window with some menus. Then, I hid it to free screen space. I wrote the code below so that when user presses ALT key, the menu bar appears if it's hidden, and it hides if it's displayed.
void MainWindow::keyPressEvent( QKeyEvent *k ) {
if(k->modifiers() & Qt::AltModifier) {
menuBar()->setHidden(!menuBar()->isHidden());
if(menuBar()->hasFocus()) {
QMessageBox::information(this, "Info", "Focus !");
}
}
}
As you can see, I also added a QMessageBox to see when the menuBar has the focus. And this box appears only half of the time. It goes like this :
Application launched, menubar hidden
I press ALT, menubar displayed, no message box, no focus
I press ALT, menubar hidden
I press ALT, menubar displayed, message box, focus
I press ALT, menubar hidden
I press ALT, menubar displayed, no message box, no focus
I press ALT, menubar hidden
I press ALT, menubar displayed, message box, focus
etc.
How to make sure when the menuBar is displayed, it always has focus ?
I wanted to do the same thing. My solution, complete example, as a gist:
https://gist.github.com/xim/ee56564f425151ea2fa70f730d644873
Tested against Qt 5.9.4.
As it contains a lot of other junk, a minimal example:
class AutoHidingMenuBar : public QMenuBar {
Q_OBJECT
public:
AutoHidingMenuBar() : QMenuBar() {
setMaximumHeight(0);
connect(qApp, &QApplication::focusChanged, this, &AutoHidingMenuBar::focusChanged);
}
private slots:
void focusChanged(QWidget *from, QWidget *to) {
bool inFocus = hasFocus() || isAncestorOf(focus) || hasFocusedChild();
if (inFocus && maximumHeight() == 0) {
auto action = activeAction();
setMaximumHeight(100);
if (action) {
// XXX This is a bit of a hack. We could do
// QCoreApplication::processEvents();
// setActiveAction(action);
// with almost the same effect, but then we *open* the first menu on single alt press...
auto evt = new QMouseEvent(QEvent::MouseMove, actionGeometry(action).center(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(this, evt);
}
} else if (!inFocus && maximumHeight() != 0)) {
setMaximumHeight(0);
}
}
private:
bool hasFocusedChild() {
QObjectList queue{children()};
while (!queue.empty()) {
auto child = queue.takeFirst();
auto widget = dynamic_cast<QWidget *>(child);
if (widget && widget->hasFocus())
return true;
queue.append(child->children());
}
return false;
}
};
Have you tried just adding the setFocus command?
void MainWindow::keyPressEvent( QKeyEvent *k ) {
if(k->modifiers() & Qt::AltModifier) {
menuBar()->setHidden(!menuBar()->isHidden());
menuBar()->setFocus(Qt::MenuBarFocusReason);
if(menuBar()->hasFocus()) {
QMessageBox::information(this, "Info", "Focus !");
}
}
}
Related
I have a widget on top of my mainwindow, more like and Advertisement Banner. It is preventing the user from using some functionalities behind it so I set it to
setAttribute( Qt::WA_TransparentForMouseEvents );
But that specific ad banner has 2 buttons, and if it set as 'transparent', they can't be used obvoiusly. My question is that, how can I still use the button functionalities inside the banner of it set to transparent for mouse events?
It looks like this:
Made a solution using the Mouse Events of the said Banner Widget:
void advertisement::mouseMoveEvent(QMouseEvent *event)
{
qDebug()<< event->button();
if (isRightBtn)
{
this->setAttribute(Qt::WA_TransparentForMouseEvents);
main->_IsCamRotate = true;
main->triggerMouseMove(event);
}
}
void advertisement::mousePressEvent(QMouseEvent *event)
{
qDebug()<< event->button();
switch(event->button())
{
case Qt::MouseButton::RightButton:
isRightBtn = true;
break;
}
}
void advertisement::mouseReleaseEvent(QMouseEvent *event)
{
if (!this->rect().contains(event->pos()) && event->button() == Qt::MouseButton::RightButton)
{
main->triggerMouseRelease(event);
isRightBtn = false;
this->setAttribute(Qt::WA_TransparentForMouseEvents, false);
}
}
I have a QDockWidget in my Mainwindow with a QTableWidget and two QPushbuttons.
Of course, I can click the buttons with my mouse, but I want also to "click" them with left- and right-arrow-key.
It nearly works perfect. But before they are clicked via key, it seems like the focus jumps to the right/left of the QTableWidget (the items in it, it goes through all columns).
Is it possible that I have the KeyPressEvents only for the buttons in the QDockWidget?
You can use an event filter like this:
class Filter : public QObject
{
public:
bool eventFilter(QObject * o, QEvent * e)
{
if(e->type() == QEvent::KeyPress)
{
QKeyEvent * event = static_cast<QKeyEvent *>(e);
if((event->key() == Qt::Key_Left) || (event->key() == Qt::Key_Right))
{
//do what you want ...
return true;
}
}
return QObject::eventFilter(o, e);
}
};
keep an instance of the filter class in your main window class:
private:
Filter filter;
then install it in your widgets, e.g. in your main window class constructor:
//...
installEventFilter(&filter); //in the main window itself
ui->dockWidget->installEventFilter(&filter);
ui->tableWidget->installEventFilter(&filter);
ui->pushButton->installEventFilter(&filter);
//etc ...
You may want to check for modifiers (e.g. Ctrl key), to preserve the standard behaviour of the arrow keys:
//...
if(event->modifiers() == Qt::CTRL) //Ctrl key is also pressed
{
if((event->key() == Qt::Key_Left) || (event->key() == Qt::Key_Right))
{
//...
I have a long QPushButton (well, a subclass of one) with a menu attached. The drop-down menu indicator is on the right side of the button, but when pressed the menu drops down from the bottom-left corner. This seems to me like it will be clunky and unintuitive for my users.
I've looked through the QPushButton source code, and tried:
this->setLayoutDirection(Qt::RightToLeft);
which did move the menu to the right side, but it broke the button as it also moved the indicator to the left side and made the menus backwards.
Is there another way to make the menu drop from the right side?
It can be done by installing an event filter on the menu used for the QPushButton which moves it when shown.
class rightSideMenuFilter : public QObject
{
public:
bool eventFilter(QObject * obj, QEvent *event)
{
QPushButton* parentButton = dynamic_cast<QPushButton*>(obj->parent());
if (!parentButton)
return false;
QMenu* menu = dynamic_cast<QMenu*>(obj);
if (!menu)
return false;
if (event->type() == QEvent::Show && obj == parentButton->menu())
{
QPoint pos = menu->pos();
qDebug() << "pos" << pos;
pos.setX(pos.x() + parentButton->width() - menu->width());
parentButton->menu()->move(pos);
return true;
}
return false;
}
};
I tried to implement a way for changing the background when an SVG button is pressed and reseting when it is released. My problem is that the mouseReleaseEvent is not called when I hide the QSvgWidget on which the mousePressEvent was called.
Here is my code:
SvgButton.cpp
#include "SvgButton.h"
SVGButton::SVGButton(QByteArray backgroundImage, QWidget *parent) :
QPushButton(parent)
{
this->init(backgroundImage);
}
SVGButton::SVGButton(QString backgroundImagePath, QWidget *parent) : QPushButton(parent)
{
SVGDom normalBackgroundImage(backgroundImagePath);
this->init(normalBackgroundImage.byteArray());
}
void SVGButton::init(QByteArray backgroundImage)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
_backgroundImageWidget = new QSvgWidget();
_backgroundImageWidget->load(backgroundImage);
setLayout(new QHBoxLayout(this));
layout()->addWidget(_backgroundImageWidget);
this->setFlat(true);
}
void SVGButton::select()
{
this -> setStyleSheet("background-color:rgba(0, 0, 0, 10);");
}
void SVGButton::deselect()
{
this -> setStyleSheet("background-color:rgba(0, 0, 0, 0)");
}
int SVGButton::tag()
{
return _tag;
}
void SVGButton::setTag(int tag)
{
_tag = tag;
}
void SVGButton::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
SVGButton::~SVGButton()
{
delete _backgroundImageWidget;
}
and BaseNavigationButton.cpp
#include "BaseNavigationButton.h"
const int kButtonWidth = 140;
const int kButtonHeight = 70;
BaseNavigationButton::BaseNavigationButton(QString backgroundImagePath, QString pressedBackgroundImagePath, QWidget *parent)
: SVGButton(backgroundImagePath, parent)
{
this->setMinimumSize(kButtonWidth, kButtonHeight);
if (!pressedBackgroundImagePath.isNull())
{
SVGDom pressedBackgroundImage(pressedBackgroundImagePath);
_pressedBackgroundImageWidget = new QSvgWidget();
_pressedBackgroundImageWidget->load(pressedBackgroundImage.byteArray());
layout()->addWidget(_pressedBackgroundImageWidget);
_pressedBackgroundImageWidget->hide();
}
else
{
_pressedBackgroundImageWidget = NULL;
}
}
void BaseNavigationButton::mouseReleaseEvent(QMouseEvent * event)
{
qDebug() << "SVGButton::mouseReleaseEvent";
if (_pressedBackgroundImageWidget) {
_backgroundImageWidget->setVisible(false);
_pressedBackgroundImageWidget->setVisible(true);
//_backgroundImageWidget->show();
//_pressedBackgroundImageWidget->hide();
}
QPushButton::mouseReleaseEvent(event);
//emit released();
}
void BaseNavigationButton::mousePressEvent(QMouseEvent *event)
{
qDebug() << "SVGButton::mousePressEvent";
if(_pressedBackgroundImageWidget)
{
_backgroundImageWidget->setVisible(true);
_pressedBackgroundImageWidget->setVisible(false);
}
QPushButton::mousePressEvent(event);
// emit pressed();
}
BaseNavigationButton::~BaseNavigationButton()
{
if (_pressedBackgroundImageWidget)
{
delete _pressedBackgroundImageWidget;
}
}
The SVGDom basically just create a ByteArray from the SVG images. The code works, it is relatively correct, the only problem is that I described above.
When you hide a QWidget, it lose the focus, and a Widget --or some child widget-- must have the focus in order to the events work on it.
Try this simple example:
Press the mouse button when the cursor is over a button.
Move the pointer out of the button without release the mouse button.
Release the mouse button.
As you will see, this not will trigger the button clicked --clicked is a mousePressEvent followed by a mouseReleaseEvent-- event.
Hence, you cannot receive mouse buttons events from hidden objects.
What can I do to implement the "mouse pressed" style behaviour?
If by "mouse pressed style behaviour" you mean: "I want my widget style change when I press the mouse button".
Well, you can use the setStyleSheet function and applpy a CSS style to your widget. See Qt Style Sheets Examples
I have QtInputDialog and I dont like it that, when i press enter it closes.
I would like to type value and confirm it by pressing enter on keyboard. After that line edit resets and i can type another value.
Dialog initialization:
void MainWindow::on_button1_clicked() {
dialog = new QInputDialog();
dialog->installEventFilter(this);
dialog->show();
}
Event filter:
bool MainWindow::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(e)->matches(QKeySequence::InsertParagraphSeparator)) {
qDebug() << dialog->textValue(); //use this value as you wish
dialog->setTextValue(QString());
return true; //block this event
}
}
return false;
}
Note that the dialog still can be closed using mouse click on "OK".