In a Qt application On Windows, when the mouse cursor hovers over a QPushButton, it will be highlighted with a shimmering outline.
I would like to tell any QPushButton to be highlighted like that, programmatically, without it having the mouse cursor actually hovering it.
Is this possible? I've tried setting focus to the button and played with different stylesheets, but I can't seem to access the mouse-hover outline effect.
Solution
If you want to simulate, that a button is constantly under the mouse, my solution would be to inherit from QPushButton and reimplement the paintEvent method, explicitly setting the state of the button to QStyle::State_MouseOver:
option.state |= QStyle::State_MouseOver;
Note: Please, be aware, that this solution will have different effects on different operating systems, as they use different ways to indicate a hovered button. Under Windows 7 for example the whole button will be highlighted, instead of adding a glowing border.
Background
QPushButton is drawn by QStylePainter, as seen in the source code:
void QPushButton::paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);
p.drawControl(QStyle::CE_PushButton, option);
}
The way the button is drawn is determined by the settings held in QStyleOptionButton, which is a subclass of QStyleOption. QStyleOption in turn has a public state attribute, which holds the state flags that are used when drawing the button. The flag QStyle::State_MouseOver is used to indicate that the button is under the mouse.
Example
Finally, here is an example you could try:
In GlowyButton.cpp:
#include "GlowyButton.h"
#include <QStylePainter>
#include <QStyleOptionButton>
...
void GlowyButton::paintEvent(QPaintEvent * /*event*/)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);
option.state |= QStyle::State_MouseOver;
p.drawControl(QStyle::CE_PushButton, option);
}
...
The full code of the example could be downloaded from GitHub.
Related
When applying a QIcon with QPushButton::setIcon(), it appears that mode changes don't respect the mode changes set for the QIcon
QIcon helpIcon;
helpIcon.addPixmap(QPixmap(":/icons/style/help.png"), QIcon::Normal);
helpIcon.addPixmap(QPixmap(":/icons/style/help_hover.png"), QIcon::Active); //ignored?
QPushButton *myButton = new QPushButton(this);
myButton->setIcon(helpIcon);
What I would expect to happen is the icon should change from one pixmap to the other when the button is hovered. Instead, the icon stays the same. It only changes when the button is pressed. I've tried every combination of QIcon::State and QIcon::Mode with no change.
Running Qt 5.12.1
This is simply how QPushButton handles states (and hence icon mode/state), which is different from QToolButton. More specifically it is how the current QStyle subclass uses the button state information to paint the icon. For example, here is the QPushButton::paintEvent() code (follow that to see initStyleOption() call where state data is initialized), then the style options are handed off to the currently active QStyle (which could be eg. WindowsVista/Macintosh/Fusion/StyleSheets depending on OS or settings). Then if we look at, for example, the relevant QFusionStyle code we can see that the icon Active mode is only used when the button has focus. Or same in QCommonStyle (which Windows/WindowsVista/Macintosh styles all fall back to).
To work around this you could use CSS and set an image property for the QPushButton:hover state, or implement your own QProxyStyle::drawControl() method for the CE_PushButtonLabel control element.
CSS: QPushButton:hover { image: url(/path/to/icon.png); }
I resolved this by subclassing QPushButton and listening for the enter and leave events. This class switches between base and hover icons. This is more useful for me vs stylesheets because I resize the icons based on the user's DPI.
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent = nullptr);
void setBaseIcon(QIcon icon){
baseIcon = icon;
setIcon(baseIcon);
}
void setHoverIcon(QIcon icon){hoverIcon = icon;}
private:
QIcon baseIcon;
QIcon hoverIcon;
protected:
virtual void enterEvent(QEvent *){
setIcon(hoverIcon);
update();
}
virtual void leaveEvent(QEvent *){
setIcon(baseIcon);
update();
}
};
I have created a QPushButton in Qt without applying any style, so it inherits the style from Windows10, with this result:
Then I wanted to change temporary the color of the button, so I used:
pushButton->setStyleSheet("background-color: rgb(255,220,220)")
getting this result:
Already this result does not satisfy me because also the style is slightly different from the original one. Anyway the next step was that the button had to return to the "normal" style when pressed, so I added this command
pushButton->setStyleSheet("background-color: rgb(240,240,240)")
but the result is different from the starting button:
Can you please give me some advice to better manage the style?
Thanks
Actually when you set background-color alone to QPushButton, The background may not appear unless you set some value for border.
Look here for (List of Stylable Widgets: QPushButton)
http://doc.qt.io/qt-5/stylesheet-reference.html
I think in windows 10 for some reason, you are able to see something without even setting border.
But the recommended way is to set some border value.
So try setting border value as said below, and see if it addresses your requirement:
pushButton->setStyleSheet("background-color: rgb(255,220,220);border: none; ")
In the above said link you can find below information:
Warning: If you only set a background-color on a QPushButton, the background may not appear unless you set the border property to some value. This is because, by default, the QPushButton draws a native border which completely overlaps the background-color.
Here are some snippets you may find quite similar and helpful.
I had an Update button which I turned into red Cancel button. Once the update action is finished or cancel is pressed, I restored the original color and text.
// Global variables to save off button state
QPalette update_btn_palette_restore;
QString update_btn_text_restore;
....
// Update button is pressed.
// Save the palette and text.
update_btn_palette_restore = _ui->update_button->palette ();
update_btn_text_restore = _ui->update_button->text ();
// Change the color palette and text
QPalette p=palette();
p.setBrush(QPalette::Button,Qt::red);
_ui->update_button->setPalette(p);
_ui->update_button->setText ("Cancel");
....
// Handler for when either cancel is pressed or update has finished
if(! update_btn_text_restore.isEmpty ()) {
_ui->update_button->setText (update_btn_text_restore);
_ui->update_button->setPalette(update_btn_palette_restore);
}
I'm opening a modal window from my main window and my interest is to make the background dark so the top window is perfectly visible but the main one looks dark like in the "shade".
You can show some half-transparent widget over the mainwindow and it will create shadow effect.
For example, such widget.
class Overlay : public QWidget
{
public:
Overlay(QWidget *parent) {
setPalette(Qt::transparent);
setAttribute(Qt::WA_TransparentForMouseEvents);
}
protected:
void paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(QBrush(QColor(0,0,0, 150)));
painter.setPen(Qt::NoPen);
painter.drawRect(rect());
}
};
Then create this widget, resize and show:
overlay_.reset(new Overlay(this));
overlay_->resize(size());
overlay_->setVisible(true);
You can play with the shadow color and transperancy by changing brush in paintEvent.
Hope this is the effect you wished.
This is up to the window manager to add such effect.
For example, KWin and Mutter both have their way to handle dialogs. KWin does shade the main window, and I think Mutter does it too with some additional effect.
In Mac OS, modal window are already have special properties to put it in focus on relation of it's patent window.
The way windows handle this is by forcing the focus on the modal I think. But it really is the window manager's job, and up to the user's preference to choose what effect should be active.
I would like to be able to move a QSlider with arrows of the keyboard at any time.
I want to be able to click anywhere on the QWindow and keep QSlider activated to move the cursor with the arrows.
My problem is that move the cursor with arrows is only allowed if we click on the QSlider before.
I hope my question is clear enough.
Does anyone know how to move the QSlider with arrows of the keyboard without clicking on the QSlider before please?
There are two approaches:
In Qt terms, you'd like to give slider the focus. Widgets have the setFocus method, so you need to call slider->setFocus(Qt::OtherFocusReason).
Since you want the slider to get focus whenever the underlying window has focus, you need to put the setFocus call in your implementation of focusInEvent for the parent widget.
You can forward the key events from the underlying widget to the slider. In the parent widget, reimplement keyPressEvent and keyReleaseEvent. When the desired keys are detected, forward them to the slider:
// same for keyReleaseEvent!
void MyWindow::keyPressEvent(QKeyEvent * ev) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
slider->event(ev);
}
}
I want to show a little image on my mouse position.
So i did that:
void AreaScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
MapData::pEnd.setX(event->scenePos().x());
MapData::pEnd.setY(event->scenePos().y());
this->update(0, 0, this->width(), this->height());
}
The pEnd is my point.
On the drawForeground i did that:
void AreaScene::drawForeground(QPainter *painter, const QRectF &rect){
qDebug() << "called";
if(MapData::tileIndex!=-1&&MapData::pEnd.x()!=-1){
painter->drawPixmap(MapData::pEnd.x(),MapData::pEnd.y(), *MapData::tileImage, (((int)(MapData::tileIndex%(MapData::tileImage->width()/MapData::tileSize.x())))*MapData::tileSize.y()),
(((int)(MapData::tileIndex/(MapData::tileImage->width()/MapData::tileSize.x())))*MapData::tileSize.x()),
MapData::tileSize.x(), MapData::tileSize.y());
}
}
Note:
The tile index is the position of the subrectangle on the tileImage (QPixelMap)
So i get the points, the image and the subrectange inside it.
It works if i keep pressing the right or the left mouse buttons it updates, but i want to update it when i move the mouse, i know the drawForeground is not even called at all.
Is there a way to call it, force to update so i can show the little tile on the screen?
The another option (i think) is change the mouse icon to the tile image, but i did a little research and didn't find a way to do that.
Thanks ppl
Call setMouseTracking(true); on the QGraphicsView that is displaying the scene. That will tell the view to generate mouse move events whenever the mouse is hovered over the view. Otherwise, the view will only generate mouse move events when you click and drag while holding down a mouse button.