Styling a custom QWidget - qt

In qtcreator i have a mainwindow containing the centralWidget of QWidget type, and another QWidget.
Styling these two QWidget is as easy as it gets, in my stylesheet.css :
QWidget#centralWidget{
background-color:pink;
}
#nestedWidget{
background-color:salmon;
border-radius:20%;
}
Now i want to promote nestedWidget to a class that inherits from QWidget :
class newWidget : public QWidget
{
Q_OBJECT
...
}
I also promote nestedWidget to the type of newWidget in qtcreator.
My problem is that nestedWidget is not styled accordingly (it has no style) when it is a newWidget, while it is correctly styled when it is a QWidget.
How can i fix it so it is correctly styled wether it is a QWidget or a newWidget ?

Related

How to make QToolButton go beyond the edge of QToolbar?

How can I make the button go beyond the edge of QToolbar?
Below is the code as I create the toolbar:
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0)
private:
QToolBar* _toolBar;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
_toolBar = new QToolBar;
QAction *actionAdd = new QAction(QIcon(":/images/add.png"), "", this);
_toolBar->addAction(actionAdd);
addToolBar(Qt::ToolBarArea::TopToolBarArea, _toolBar);
}
style.qss
QToolBar {
background: #018ac4;
height: 150px;
}
As said before, it is not possible to solve this correctly using QtWidgets.
However I see two options to visually create that effect:
Take the button out of the tool bar and add it to the main window instead, but do not add it to a layout. Usually i would say reposition it on resize events, but since it is in the top left, you might as well just call setGeometry() once on startup and not worry about it later. You probably have to add last, or call rise() though.
Make it look like the button sticks out, while it really doesn't. Make the toolbar as large as the button, but paint the lower part of the toolbar in the brighter blue, so that it looks like it is part of the widget below it.
It is not possible with widgets. A QWidget can not paint outside of its area. See this answer : https://stackoverflow.com/a/48302076/6165833.
However, the QToolBar is not really the parent of the QAction because addAction(QAction *action) does not take the ownership. So maybe the QMainWindow could paint your QAction the way you want but AFAIK this is not doable through the public API of Qt.
What you could do is use QML (but you would need to use QML for the whole window then).

Promote custom widget with custom constructor in QT Creator

I know, this is basically the same question, but my problem goes further.
The following tree explains my structure:
QWidget
|
CustomWidget
| |
MyTable MyWidgetAroundIt
I have promoted MyTable in Qt Designer. So, I can add it to MyWidgetAroundIt. That worked quite well. The only problem is, CustomWidget requires it's parent to be a CustomWidget too, its constructor looks like:
CustomWidget(CustomWidget* parent) : QWidget(parent), _specialValue(parent->getSpecialValue)
This causes compile errors, as the designer generated code tries to initialize MyTable with a QWidget*, instead of the CustomWidget*. What could/should I do to prevent this and/or give the designer a hint about this requirement?
A widget whose parent can't be a QWidget is not a widget anymore. Your design breaks the Liskov Substitution Principle and has to be fixed.
You're free to enable special functionality if the widget happens to be of a certain type, but a widget must be usable with any widget for a parent.
Thus:
CustomWidget(QWidget* parent = nullptr) :
QWidget(parent)
{
auto customParent = qobject_cast<CustomWidget*>(parent);
if (customParent)
_specialValue = customParent->specialValue();
}
or:
class CustomWidget : public QWidget {
Q_OBJECT
CustomWidget *_customParent = qobject_cast<CustomWidget*>(parent());
SpecialType _specialValue = _customParent ? _customParent->specialValue() : SpecialType();
SpecialType specialValue() const { return _specialValue; }
public:
CustomWidget(QWidget * parent = nullptr) : QWidget(parent) {}
};

What is parent for in Qt?

Almost every QtWidgets class can have parent. And usually it's optional to set parent at object initialization. For example,If I create a class that inherits QWidget class, I will do the following on the constructor:
Widget::Widget(QWidget* parent): QWidget(parent) {
hbox = new QHBoxLayout(this);
yes_button = new QPushButton("&Yes");
no_button = new QPushButton("&No", this);
cancel_button = new QPushButton("&Cancel", hbox);
}
I can set or not set parent. I can set cancel_button to be a child of hbox. I can too set cancel_button to be a child of yes_button, but I think it's a bad thing to do.
What's the point of this? And, is it really necessary to set parent for every QWidget based class that I create?
Besides helping with draw order in GUI objects, it also helps with memory management, so that when you destroy a QObject, all of it's children are destroyed too. See http://doc.qt.io/qt-4.8/objecttrees.html for more details. When something changes in the parent (e.g. when it is resized), it can notify its children to update themselves too.
To answer your question, you're not required to set the parent for everything (that's why it's an optional parameter, after all), but most of the time it's better to set it correctly.
Firstly, a QWidget is a QObject, and QObjects are nodes in a QObject tree. The child nodes are memory-managed by the parent, unless you deallocate them before the parent has a chance to do so. Thus, memory management is one reason for widgets, or any other QObjects, to have a parent.
Secondly, visible parentless widgets are always top-level windows. Conversely, it's impossible to have a non-top-level widget that is parentless. When you show a parentless widget, it acquires its own window. The opposite is not necessarily true - it's possible to give a child widget a Qt::Window flag, and it becomes a top-level window as well.
The corollary is that any widget contained in other widgets has a parent - otherwise it'd be a top-level window. This parent might not be set explicitly by you, but it's set nevertheless.
I think that your question can be rephrased as: When do I need to explicitly give widget constructor a parent? The answer is:
Whenever the widget is a top level window that you intend to have a parent. Such windows are not subject to layout management, so there's no mechanism to set that parent for you. Top-level transient dialogs need to have parents so that they are properly positioned in relation to the parent window.
Whenever you have a child widget not subject to layout management.
Widgets subject to layout management are parented upon insertion into a layout:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
QLabel label("Hello");
QPushButton button("Goodbye");
layout.addWidget(&label);
layout.addWidget(&button);
QObject::connect(&button, &QPushButton::clicked, [&app]{ app.quit(); });
window.show();
return app.exec();
}
Finally, not all widgets or QObjects need to be explicitly created on the heap. Since all QObject-derived classes in Qt (and many other classes, too!) use the PIMPL idiom, when you allocate them individually on the heap, you're really doing the heap allocation twice. First you allocate the instance of the class - sometimes the instance is as small as a pointer or two - and then the class's constructor allocates its PIMPL. Explicit heap allocation is a case of premature pessimization.
To avoid this pessimization, your Widget should look as follows:
class Widget : public QWidget {
Q_OBJECT
QHBoxLayout m_layout;
QPushButton m_yesButton, m_noButton, m_cancelButton;
public:
Widget(QWidget * parent = 0);
};
Widget::Widget(QWidget * parent) :
QWidget(parent),
m_layout(this),
m_yesButton("&Yes"),
m_noButton("&No"),
m_cancelButton("&Cancel")
{
m_layout.addWidget(&m_yesButton);
m_layout.addWidget(&m_noButton);
m_layout.addWidget(&m_cancelButton);
}
If you wished to use the PIMPL idiom, you could do that, too:
// Widget.h - Interface
class WidgetPrivate;
class Widget : public QWidget {
{
Q_OBJECT
Q_DECLARE_PRIVATE(Widget)
QScopedPointer<WidgetPrivate> const d_ptr;
public:
Widget(QWidget * parent = 0);
~Widget();
};
// Widget.cpp - Implementation
class WidgetPrivate {
Q_DISABLE_COPY(WidgetPrivate)
Q_DECLARE_PUBLIC(Widget)
Widget * const q_ptr;
QHBoxLayout layout;
QPushButton yesButton, noButton, cancelButton;
public:
WidgetPrivate(Widget * q);
};
WidgetPrivate::WidgetPrivate(Widget * q) {
q_ptr(q),
layout(q),
yesButton("&Yes"),
noButton("&No"),
cancelButton("&Cancel")
{
layout.addWidget(&yesButton);
layout.addWidget(&noButton);
layout.addWidget(&cancelButton);
}
Widget::Widget(QWidget * parent) :
QWidget(parent),
d_ptr(new WidgetPrivate(this))
{}
Widget::~Widget() {}
// necessary, since WidgetPrivate is unknown to the interface!
Of course, you should be using QDialogButtonBox instead of all this :)

QScrollArea derivative is empty if it's created from an .ui file

Consider following simple example:
Area.hh
#pragma once
class Area;
#include <QScrollArea>
class Area : public QScrollArea {
Q_OBJECT
public:
Area (QWidget *_parent = 0);
};
Area.cc
#include "main.hh"
#include "Area.hh"
#include <QLabel>
Area::Area (QWidget *_parent) :
QScrollArea (_parent)
{
QLabel *label = new QLabel ("Show me please");
setWidget (label);
}
This scroll area should show a label inside it. And it does so well if you just create an Area object and show it like this:
Area *area = new Area();
area->show();
However, if you add a QScrollArea with Qt Creator and promote it to Area class, then it shows nothing inside and there are no scrollbars. What can I do to show it properly?
Qt Designer adds an empty widget inside the QScrollArea, overwriting yours.
To prevent that, use a base QWidget instead of a QScrollArea, and promote that widget to an Area class. Qt's Ui compiler won't considered it to be a QScrollArea, so it won't generate a call to setWidget anymore.

Qt StyleSheet custom style attribute custom QGLwidget

I have created a subclass of QGLwidget and I was hoping that I could use a stylesheet to tell openGL how to render a scene.
For Example:
qApp->setStyleSheet("CustomWidget { background-color: yellow }");
Then in my paintGL method:
QColor bg = "Get 'background-color' style somehow"
glClearColorf(bg.redF(), bg.greenF(), bg.blueF(), 0);
glClear(GL_COLOR_BUFFER_BIT)
Also, is it possible to create custom style sheet attributes?
qApp->setStyleSheet("CustomWidget { foo-attr: 1 }");
I have read up on the QStyle and QStyleOption classes, but I don't quite understand how to apply them to a practical application.
You can declare Q_PROPERTY in your custom widget and then set them with
CustomWidget
{
qproperty-yourPropertyName: "value";
}
You can access BG of your custom widget with QPalette
QColor bg = palette().color(QPalette::Window);
But I'm not sure if it will work

Resources