QPushButton not respecting QIcon mode changes - c++

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();
}
};

Related

Details Scroll area's setwidget function shows a toolbar

I have a scrollarea and I'm setting a basic QWidget as its widget, However when I do this I get a toolbar on top of the widget I just set;
This is the slot I use to create the QWidget and set it to the scroll area, this happens during runtime and the qwidget is deleted and recreated repeatedly during runtime.
void SlotCreateDetailsWidget()
{
if (m_DetailsWidget == nullptr)
{
m_DetailsWidget = new DetailsWidget(); // QWidget
m_Ui->DetailsScrollArea->setWidget(m_DetailsWidget);
m_DetailsWidget->show();
}
}
What do I need to do to get rid of this toolbar?
From https://doc.qt.io/qt-5/qwidget.html :
A widget without a parent widget is always an independent window
(top-level widget).
What you see depends on the underlying windowing system but you can customize it up to a certain point with void QWidget::setWindowFlag(Qt::WindowType flag, bool on = true) for example. You can also look at https://doc.qt.io/qt-5/qtwidgets-widgets-windowflags-example.html.

Qt4: Decorating a QLineEdit (painting around it)

I am trying to "decorate" a QLineEdit, or to be more accurate, to paint my own custom frame around it, to get the following result:
I tried to use Qt Style Sheets (CSS), but this will only enable trivial frame decorating (changing width/color/size etc...), nothing fancy like the above.
I also tried inheriting from QLineEdit and overriding its void QLineEdit::paintEvent(QPaintEvent* e), but then I realized that reimplementing it means I will lose the QLineEdits "editness" (sorry for butchering the language here) - the textbox, the cursor, and the ability to insert text.
How can I achieve the above text box?
Is this a combination of QLabel perfectly located behind a QLineEdit?
Try to use composition. Create your own Widget inherited it from QWidget, paint what you want in QWidget::paintEvent and place QLineEdit above it. Probably you'll have to center it and use css for QLineEdit to make it look smooth.
class MyWidget: public QWidget
{
explicit MyWidget(QWidget* parent = 0):
QWidget(parent),
line_edit(new QLineEdit(this))
{
// place line_edit in center of QWidget
}
private:
QLineEdit* line_edit;
}
Or you can override void QLineEdit::paintEvent(QPaintEvent* e) like this
void QLineEdit::paintEvent(QPaintEvent* e)
{
//paint your border
QLineEdit::paintEvent(e);
}
And you won't lose the QLineEdits "editness".

How to create QToolBar in QWidget?

I am trying to add a QToolBar in a QWidget. But I want its functionality to work as if it was a QMainWindow.
Apparently I can not create QToolBar in a QWidget, and using setAllowedAreas does not work with QWidget : it only works with QMainWindow. Also, my QWidget is in a QMainWindow.
How can I create a QToolBar for my widget?
The allowedAreas property only works when the toolbar is the child of a QMainWindow. You can add the toolbar to a layout, but it won't be movable by the user. You can still relocate it programmatically, however.
To add it to a layout for a fictional class inheriting QWidget:
void SomeWidget::setupWidgetUi()
{
toolLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
//set margins to zero so the toolbar touches the widget's edges
toolLayout->setContentsMargins(0, 0, 0, 0);
toolbar = new QToolBar;
toolLayout->addWidget(toolbar);
//use a different layout for the contents so it has normal margins
contentsLayout = new ...
toolLayout->addLayout(contentsLayout);
//more initialization here
}
Changing the toolbar's orientation requires the additional step of calling setDirection on the toolbarLayout, e.g.:
toolbar->setOrientation(Qt::Vertical);
toolbarLayout->setDirection(QBoxLayout::LeftToRight);
//the toolbar is now on the left side of the widget, oriented vertically
QToolBar is a widget. That's why, you can add a QToolBar to any other widget by calling addWidget for layout or by setting the QToolBar parent to your widget.
As you can see in documentation of QToolBar setAllowedAreas method:
This property holds areas where the toolbar may be placed.
The default is Qt::AllToolBarAreas.
This property only makes sense if the toolbar is in a QMainWindow.
That's why it is impossible to use setAllowedAreas if toolbar is not in QMainWindow.
As far as I know, the only way to properly use the toolbar is with the QMainWindow.
If you want to use the full functionality of the toolbar, create a mainwindow with the window flag Widget. This way you can add it inside some other widget without having it displayed as a new window:
class MyWidget : QMainWindow
{
public:
MyWidget(QWidget *parent);
//...
void addToolbar(QToolBar *toolbar);
private:
QMainWindow *subMW;
}
MyWidget::MyWidget(QWidget *parent)
QMainWindow(parent)
{
subMW = new QMainWindow(this, Qt::Widget);//this is the important part. You will have a mainwindow inside your mainwindow
setCentralWidget(QWidget *parent);
}
void MyWidget::addToolbar(QToolBar *toolbar)
{
subMW->addToolBar(toolbar);
}

Draggable QWidget

I have a MainWindow.cpp class with multiple images displayed that emit a clicked() signal. Once clicked on I want to open a widget that's a fixed size inside MainWindow and I want this widget to be able to be dragged around as long as it stays inside the MainWindow class.
I was looking at example code to try and write this widget class, in particular the Qt MainWindow Example. However, once one of the dockwindows are dragged around the display the Operating System specific titlebar (which lets you maximize, minimize, and close the window) gets displayed. I do not want this titlebar to be displayed.
How would I go about creating this class of draggable widgets?
Check setTitleBarWidget
Setting to a void widget will work:
It is not possible to remove a title bar from a dock widget.
However, a similar effect can be achieved by setting a default constructed QWidget
as the title bar widget.
Edit:
By request:
yourDockableWidget->setTitleBarWidget( new QWidget( yourDockableWidget ) );
In the example you are following, you could do it in constructor:
ColorSwatch::ColorSwatch(const QString &colorName, QWidget *parent, Qt::WindowFlags flags)
: QDockWidget(parent, flags)
{
/*...*/
setTitleBarWidget( new QWidget( this ) );
/*...*/
}
Now your widget wont have SO titlebar when undocked;

Setting a QDialog to be an alien

In a standalone GUI application where I don't have a windowmanager nor a composite manager I want to display a QDialog to the user to ask for values.
The dialog is quite big, so I want to make it translucent so that the user can look through it to see what happens in the application while the dialog is shown.
The problem is that for translucency of X native windows, there needs to be a composite manager. Qt internal widgets can be painted translucent because they don't correspond to native X windows (aliens) and are completely only known to Qt.
Is there a way to make the background of a QDialog translucent without having a composite manager running? Perhaps making it a normal child widget/alien of the application's main window? Is there a better alternative to this?
I don't know of any way of turning a QDialog into a normal child widget. Looking at the Qt for X11 code, I can't figure out a way of not setting the Qt::WindowFlags passed to the QWidget (parent) constructor so that it would be a plain widget and not a window of its own (but I could be wrong, didn't spend a lot of time on that).
A simple alternative is to use a plain QWidget as your container, instead of a QDialog. Here's a sample "PopupWidget" that paints a half-transparent-red background.
#include <QtGui>
class PopupWidget: public QWidget
{
Q_OBJECT
public:
PopupWidget(QWidget *parent): QWidget(parent)
{
QVBoxLayout *vl = new QVBoxLayout;
QPushButton *pb = new QPushButton("on top!", this);
vl->addWidget(pb);
connect(pb, SIGNAL(clicked()), this, SLOT(hide()));
}
public slots:
void popup() {
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
raise();
show();
}
protected:
void paintEvent(QPaintEvent *)
{
QPainter p(this);
QBrush b(QColor(255,0,0,128));
p.fillRect(0, 0, width(), height(), b);
}
};
To show it, call it's popup() slot which will raise it to the top of the widget stack, make it as large as its parent, and show it. This will mask all the widgets behind it (you can't interact with them with the mouse). It hides itself when you click on that button.
Caveats:
this doesn't prevent the user from using Tab to reach the widgets underneath. This could be fixed by toggling the enabled property on your "normal" widget container for example. (But don't disable the PopupWidget's parent: that would disable the popup widget itself.)
this doesn't allow for a blocking call like QDialog::exec
the widgets in that popup won't be transparent, you'd have to create custom transparent-background versions for all the widget types you need AFAIK.
But that's probably less of a hassel than integarting a compositing manager in your environment.