Cannot add a background image to a QDockWidget - qt

As far as I can tell, the way you make the background of a widget to be an image is to modify the qstylesheet to say:
background-image: url(<your image here>);
Which seems to work, unless it's the QDockWidget's main widget.
This is what I did:
Make a new qt widget project
Make a QWidget object, maybe put a button or two there to make sure you know it's being used.
Change the style sheet to the widget made in step 2 to contain: background-image: url(<your image here>);
In the MainWindow ui file, add the QDockWidget, promote it's main widget to the QWidget made in step #2.
When you run the program, you notice that the QDockWidget's background is still the default, however any buttons or labels you added to the widget have changed their backgrounds to the image you selected.
When you change the qss line from background-image to background-color, and select a color rather than an image, the widget does change color.
Does anyone have any suggestions for giving a QDockWidget a background image (and not just a background color)?

Apparently all you need to do is overwrite the widget's pain event with:
void MyWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

Related

Graphics errors with QWidgets in a QGraphicsScene

I am trying to have a widget live freely inside a large scrollable panel. When I scroll around the QGraphics view, the child widget does not draw properly— its sub-rectangles will blank out, and the borders (which are rounded) are not blended with the canvas. I have:
QApplication qapp(argc, argv);
QGraphicsView view;
QGraphicsScene scene;
// a simple QWidget with a gridlayout and some child objects:
MyQWidget mywidget(NULL);
scene.addWidget(&mywidget);
view.setScene(&scene);
view.setSceneRect(0,0,1280,1280);
view.show();
view.resize(512,512);
qapp.exec();
I get the same bogus rendering if I use a QGraphicsProxyWidget and scene.addItem() instead.
If instead I parent my custom widget to the QGraphicsView, it renders properly, but then the widget does not seem to be part of the QGraphicsScene, because it doesn't scroll around anymore and remains fixed to the parent window.
What's going on?
Edit: Images of the blanking problem, with MyWidget replaced by QPushButton:
Before scrolling:
Scrolling a little down and to the right:
Second edit: I have a Retina display. Could this be related? Is there special setup for Retina with Qt?
You should be getting an error when you try to add a widget to the scene which already has a parent:
QGraphicsProxyWidget::setWidget: cannot embed widget which is
not a toplevel widget, and is not a child of an embedded widget
QGraphicsScene::addWidget() is a wrapper around QGraphicsProxyWidget::setWidget which documentation says:
widget must be a top-level widget whose parent is 0.
To have borders blended you need to set widget to be transparent:
mywidget.setAttribute(Qt::WA_TranslucentBackground, true);

Qt widget displayed over other widgets

I have some information/notification widget that should be displayed when some even occurs. My idea was to have a widget that is hidden in top left corner and would be shown when needed. Problem is that if I just put there simple widget and show it, everything will be moved to the right, what I want is to show that widget on top anything that's in that area (it will hide what's there, but that's ok).
I can't use stacked widget, because information widget is in different dimensions then other widgets there. And if I just create floating widget and move it to that area it wont move if main window is moved.
Is there any way how to do that?
Just create and place the widget on the fly. Avoid using UI to place the widget because then the widget position is managed by the layouts.
EDIT: Remember to create the widget after of the dialog's initialization. If you don't take care about this your widget will be inserted at the bottom.
class Dialog : public QDialog
{
Q_OBJECT
std::unique_ptr<Ui::Dialog> _ui;
QWidget* _widgetOnTheTop;
public:
Dialog(QWidget* parent)
: QDialog(parent), _ui(new Ui::Dialog)
{
_ui->setupUi(this);
_widgetOnTheTop = new QPushButton(this);
_widgetOnTheTop->setGeometry(10,10,100,35);
_widgetOnTheTop->show();
}
};
Look at QDialog create and exec one of them when your error occurs, you can construct them to have the default ok cancel buttons
EDIT: Sorry i mean QMessageBos, see code below
QMessageBox error_message(QMessageBox::Icon::Critical, "Title of the window","Some Text about the error to show the user", QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::Cancel), this);
error_message.exec();

How to change QPushButton icon/text spacing with stylesheets?

I'm fairly new to Qt's method of stylesheets and I am looking to adjust the spacing between the icon and text on a QPushButton.
This is the gap I'm referring to: http://imageshack.us/scaled/thumb/593/4kem.png (stackoverflow won't let me post pics yet)
QPushButton {
qproperty-icon: theme_url("/button_action/add");
qproperty-iconSize: 14px;
}
Is there a parameter I can use to adjust this space? I've tried margin, padding, spacing? Perhaps there is a different selector that I can use to just grab the icon?
Actually with QPushButton you are out of luck. You can find a complete list of working options for styling push buttons in the qt documentation ( I am assuming you use qt5, but it is available for other versions as well).
What is often recommended if you want more control over the icon is to exchange your QPushButtons with QToolButtons wich are exactly the same except that they have extra features to make them compatible with toolbars -and- they have more options for icon placement. But this might not work for you because as you can see they are styled the exact same way as QPushButtons. But do look into the "toolButtonStyle" parameter which allows you to move the icon around.
Next on the list comes changing the artwork. If all you want is more space then just add the extra empty pixels to the artwork. This really hurts the perfectionist's mind but is effective.
Next on the list comes subclassing QPushButton and overriding the paint yourself. Sounds more daunting than it is. Subclassing in Qt has become a habit already and it works great. Here is a tutorial i googled in a jiffie (for 4.8 but should be about the same for 5) on the matter of subclassing. It even covers overriding the paint event.
Hope this was helpful.
At least with Qt5.5 (this may change in the future, I hope), besides overriding the paint event, which is complex and in my opinion messy for just a simple thing, there's no inherent Qt way to move that icon a few pixels here or there (small adjustments). If you remove the icon and use a stylesheet to set the background to your icon in your resource URL, you can use background-position property in the stylesheet, but unfortunately it only accepts words like center, left, etc., not pixel adjustments.
Here's how I resolved this problem. I created transparent PNGs that were slightly larger than I needed, where I had like a 4px margin around the icon itself inside the PNG. Then, I just moved the icon inside that canvas and reloaded it into the project resources file, and then mapped it again with the icon property in the designer for the QPushButton, and that worked. I now have icons that line up properly on the left with the text beside it. I can even widen the gap between icon and text.
As of Qt5.7 I don't know of any built-in functionality for the icon/text spacing.
What I usually do is give the image some more space to the right and set the icon size of the button accordingly.
Solution:
Resize your icon file to 32*16 px but leave its content as is.
Call setIconSize(QSize(32, 16)) on your button.
Result: Now there's 16 pixel empty space between its icon and text. 32 pixel is just my example, you can of course set the additional size to something that suites your style.
If you want to apply that on all your button elements simply create a tiny subclass that does it automatically in the constructor. There's really no need to override the paintEvent method for this.
Here is a workaround way to achieve that 'space between text and image':
QPushButton{
border:0px;
image: /*your url*/;
padding-top:16px;
image-position:top;
text-align:bottom;
padding-bottom:10px;
}
since qss doesn't support relative size, you need to implement resize() to change stylesheet when size changing to get a fixed space between text and image.
Here's an opportunistic approach to customize the spacing between text and icon. There's currently no regular way to change the width, so we need to add a transparent area to the right of the icon which is applied to paint device.
Firstly, add a new member variable "m_spacing"(double) and initialize it to 0.0 in constructor, as a factor of expanding the width.
Then, override the "paintEvent", "sizeHint" and "minimumSizeHint".
class CPushButton : public QPushButton {
Q_OBJECT
public:
double spaceRatio() const;
void setSpaceRatio(double ratio);
protected:
double m_spacing;
void paintEvent(QPaintEvent *event) override;
......
QSize CPushButton::sizeHint() const {
QSize sz = QPushButton::sizeHint();
int offset = iconSize().width() * m_spacing;
return QSize(sz.width() + offset, sz.height());
}
QSize CPushButton::minimumSizeHint() const {
QSize sz = QPushButton::minimumSizeHint();
int offset = iconSize().width() * m_spacing;
return QSize(sz.width() + offset, sz.height());
}
void CPushButton::paintEvent(QPaintEvent *event) {
QSize sz = iconSize();
QPixmap tmp = icon().pixmap(sz); // Get the pixmap to apply with right size
sz.rwidth() *= 1 + m_spacing; // Multiply width
QPixmap exp(sz); // Expended
exp.fill(Qt::transparent);
QPainter painter(&exp);
painter.drawPixmap(QRect(QPoint(), tmp.size()), tmp);
QStylePainter p(this); // From Qt source
QStyleOptionButton option; // From Qt source
initStyleOption(&option); // From Qt source
option.icon = QIcon(exp); // Change to real icon
option.iconSize = sz; // Change to real size
p.drawControl(QStyle::CE_PushButton, option); // From Qt source
Q_UNUSED(event)
}
Now you can create a CPushButton instance and set the spacingRatio to 0.1 or other positive real. Visually, it does increase the spacing between text and icon.
The simplest way to go for it is to create your own class inheriting from the QPushButton and override the PaintEvent and then manually place the pixmap of the icon where ever you want.
Best way to solve this problem is adding some blank space in the beginning of QPushButton text. it works well for me!
See here

Layering UI elements in Qt Designer

I have a QLabel that I'm constantly setting it's pixmap (video playback). In my application the user needs to be able to draw (boxes) above the video. How can I layer one of the QPaintDevice classes (QWidget, QPixmap, QImage, etc.) directly above and with the same size as the QLabel for painting. This element will need to have a transparent background so shapes drawn on it will appear over the video.
Add the widget you want to draw shapes on as a child widget of the video label. Add a layout first so the child widget will match the size of the parent widget. The code would be something like this:
QHBoxLayout *layout = new QHBoxLayout(videoWidget);
QLabel *overlayWidget = new QLabel();
overlayWidget->setAlignment(Qt::AlignCenter);
overlayWidget->setText("Overlaid Text");
layout->addWidget(overlayWidget);
You should see the text overlaid on the video and it should remain centered over the video widget if it is resized. For your final code, you would use some widget subclass of your own that allowed you to intercept mouse actions and draw rectangles but that's the basic idea.

Qt Stylesheet. Background-color, YES. background-image, NO

It's a weird problem with stylesheets: I have a window, child of class QWidget. I apply a stylesheet to it to ideally change the background of the entire window to an image with a repeat-x and repeat-y, tiling it.
The stylesheet "pipeline" works. If I use "background-color" and set it to say, red, the entire window will be painted red. however, if I use the background-image, it does not. if i add a CHILD WIDGET (using Qt-Designer) inside the window, the background-image will work, just inside that child widget, but not outside of it, all over the parent window.
Obviously I am doing something wrong, but am really clueless as to why the background-color would work on the entire window, but the background-image won't, unless there's a child widget, and then, only inside of it.
I had a similar problem and it was solved by reading Qt Stylesheets docs.
As it is said in the Qt's stylesheets reference, applying CSS styles to custom widgets inherited from QWidget requires reimplementing paintEvent() in that way:
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
Without doing it your custom widgets will support only the background, background-clip and background-origin properties.
You can read about it here: Qt Stylesheets reference in the section "List of Stylable Widgets" -> QWidget.
Hope, it will help!
I know this is kind of an old question, but I stumbled across it because I was having the exact same problem.
It turns out if you create your widget as a subclass of QFrame instead of a QWidget, the background-image property seems to take fine. Also , as Dave's example shows, if you just create a "raw" QWidget, it also seems to work fine. For whatever reason, if you create a new widget derived from QWidget, background-image no longer works.
If you create your widgets in Qt Designer or Creator, they don't have an option to create a QFrame-derived widget, so I just tell it I want a QWidget-derived class, then manually change the .cpp and .h file to derive from QFrame instead of QWidget.
The code below is working fine on my machine. Maybe you can see where it differs from what you have? Hope it is helpful.
#include <QtGui>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QWidget main_window;
main_window.setStyleSheet("background-image: url(Chrysanthemum.jpg); "
"background-position: top left; "
"background-repeat: repeat-xy;");
main_window.show();
return app.exec();
}

Resources