Extend Qt standard icons - c++

How would one extend the standard icons provided by the QStyle class with support for Windows and Linux in mind?
namespace Ui {
class TVLoader;
}
class TVLoader : public QMainWindow
{
Q_OBJECT
public:
explicit TVLoader(QWidget *parent = 0) :
QMainWindow(parent),
ui(new Ui::TVLoader)
{
ui->setupUi(this);
ui->actionAdd_TV_Show->setIcon(style()->standardIcon(?)); // this is where I would need some kind of "Add" icon which unfortunately doesn't exist
}

You man want to subclass QStyle if you want to provide your own icons, reimplement the standardIconImplementation() slot in your subclass and return a new icon from there. Below is an example:
class MyProxyStyle : public QProxyStyle
{
Q_OBJECT
public:
MyProxyStyle(QStyle *style = 0) : QProxyStyle(style) { }
public slots:
QIcon standardIconImplementation(StandardPixmap standardIcon,
const QStyleOption *option = 0,
const QWidget *widget = 0) const
{
// check the standardIcon parameter for the icon type
if (standardIcon==QStyle::SP_DesktopIcon)
{
// return your new icon here
standardIcon = QStyle::SP_DirIcon;
}
return QProxyStyle::standardIconImplementation(standardIcon, option, widget);
}
};
here's how can you use it:
// set new style for your widget
setStyle(new MyProxyStyle(style()));
// return different icon for QStyle::SP_DesktopIcon
action0->setIcon(style()->standardIcon(QStyle::SP_DesktopIcon));
hope this helps, regards

Since 4.6, Qt can use freedesktop icon theme:
QIcon undo_icon = QIcon::fromTheme("edit-undo");
But there is no icon themes in Windows (and MacOS). However, you can use any theme you want there, all you need is put this theme into (or part of it) into :/icons resource directory and do following in main():
if (!QIcon::hasThemeIcon("document-open")) {
QIcon::setThemeName("/");
}
(it is hack for QTBUG-16697).

Related

C++ Qt inherit from custom widget

I'm working on a project with Qt and C++.
Now my Question is:
Is inheritance possible in UI classes?
For example: This is the Widget I want to inherit from
//windowA.h
namespace Ui {
class WindowA;
}
class WindowA : public QWidget
{
Q_OBJECT
public:
explicit WindowA(QWidget *parent = nullptr);
~AddWindow();
QPushButton *button;
};
//windowA.cpp
WindowA::WindowA(QWidget *parent) :
QWidget(parent)
{
button = new QPushButton();
button->setText("Save");
connect(button, SIGNAL (clicked()), this, SLOT (//slot));
QGridLayout *layout = new QGridLayout();
layout->addWidget(button, 0, 0);
this->setLayout(layout);
}
This is the widget that inherits from WindowA
//windowB.h
namespace Ui {
class WindowB;
}
class WindowB : public WindowA
{
Q_OBJECT
public:
explicit WindowB(QWidget *parent = nullptr);
~WindowB();
};
How would I implement a QPushButton, so that it's possible to set different text in both classes?
I can add a QPushButton but the text set in WindowA would also be set in WindowB. The problem is to set a different text for the button in WindowB than it is set for the button in WindowA
If I understand your question correctly, all you need to do is change what text you set on the button in your constructor:
WindowB::WindowB(QWidget *parent) :
WindowA(parent)
{
button->setText("Something else!");
}

How to use QPROPERTY in QPalette?

I'm trying to use a Q_PROPERTY set in my stylesheet to change the value in QPalette, is this possible? For example, if I set QStyle to Fusion in my MainWindow widget, is it possible to change Qt::Window, etc using this method?
Everything compiles OK, but the only color displayed is black, so the variable is probably filled with a garbage value? As far as I know, the stylesheet overrides everything else, so at a guess, the stylesheet is not loaded in time for the constructor?
mainwindow.cpp
#include <QStyleFactory>
#include <QWidget>
#include <QFile>
#include "theme.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
{
QFile File("://stylesheet.qss");
File.open(QFile::ReadOnly);
QString StyleSheet = QLatin1String(File.readAll());
qApp->setStyleSheet(StyleSheet);
Theme *themeInstance = new Theme;
QApplication::setStyle(QStyleFactory::create("Fusion"));
QPalette dp;
dp.setColor(QPalette::Window, QColor(themeInstance->customColor()));
qApp->setPalette(dp);
}
theme.h
#ifndef THEME_H
#define THEME_H
class Theme : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor customColor READ customColor WRITE setCustomColor DESIGNABLE true)
public:
Theme(QWidget *parent = nullptr);
QColor customColor() const { return m_customColor; }
void setCustomColor(const QColor &c) { m_customColor = c; }
private:
QColor m_customColor;
};
#endif // THEME_H
stylesheet.qss
* { // global only for test purposes
qproperty-customColor: red;
}
The QSS are not called automatically, they are usually updated when the widgets are displayed, in your case as themeInstance is not shown does not use the stylesheet. Painting can be forced using the polish() method of QStyle
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr):QMainWindow{parent}{
qApp->setStyleSheet("Theme{qproperty-customColor: red;}");
Theme *themeInstance = new Theme;
qApp->setStyle(QStyleFactory::create("Fusion"));
qApp->style()->polish(themeInstance);
QPalette dp;
dp.setColor(QPalette::Window, QColor(themeInstance->customColor()));
qApp->setPalette(dp);
}
};

How to set visibility on widgets within a subclass of QWidgetAction

Qt 5.6.3, eglfs Linux platform.
I have a selection of classes derived from QWidgetAction. The QWidgetActions are all parented from a menu, and the widgets they contain are parented from the same menu. The contained widgets are all set as the default widget for the QWidgetAction. Nothing has been reimplemented from QWidgetAction.
I thought that setting the visibility of the QWidgetAction would automatically set the visibility of the custom widget set contained within? Is this not true, as doing so is certainly not showing and hiding the widgets as required!? Must I do something else to pass the visibility change to the contained widgets? Must I directly request the widget from the QWidgetAction and then apply visibility to it directly (which seems like a hack)?
I'm interested in how the QWidgetActions are supposed to be implemented. The documentation is almost non-existent, so I'm after peoples experience with them as much as anything. I have intermittent issues with what looks like a double delete of a custom widget and visibility not behaving as it should.
class Base : public QWidgetAction
{
Q_OBJECT
public:
explicit Base(QWidget* parent, QString labelText = "", QString iconPath = "", Qt::AlignmentFlag alignment = Qt::AlignHCenter) :
QWidgetAction(parent),
mCustomWidget(nullptr),
mParentWidget(nullptr),
mTextLabel(nullptr),
mAlignment(alignment),
mLabelText(labelText),
mIconPath(iconPath) {}
virtual ~Base() {}
protected:
QWidget *mCustomWidget;
QWidget *createTheWidgetSet(QWidget *parent)
{
if (mParentWidget == nullptr) {
mParentWidget = new QWidget(parent);
mCustomWidget = createCustomWidget(mParentWidget);
if (mCustomWidget != nullptr) {
if (!mLabelText.isEmpty()) {
mCustomWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
}
}
int rightMargin = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, mParentWidget);
layout->setContentsMargins(1, 2, rightMargin, 2);
if (!mLabelText.isEmpty()) {
QString some_calced_text{};
mTextLabel = new QLabel(some_calced_text, mParentWidget);
layout->addWidget(mTextLabel);
} else {
if(mAlignment == Qt::AlignLeft){
int some_calced_val{20};
layout->addSpacing(some_calced_val);
}
}
if(mAlignment == Qt::AlignRight){
layout->addStretch();
}
layout->addWidget(mCustomWidget);
if(mAlignment == Qt::AlignLeft){
layout->addStretch();
}
}
setDefaultWidget(mParentWidget);
return mCustomWidget;
}
virtual QWidget *createCustomWidget(QWidget *parent) = 0;
private:
Q_DISABLE_COPY(Base)
QWidget *mParentWidget;
QLabel *mTextLabel;
Qt::AlignmentFlag mAlignment;
QString mLabelText;
QString mIconPath;
};
class SpinBoxActionWidget : public Base
{
Q_OBJECT
public:
explicit SpinBoxActionWidget(QWidget* parent, QString labelText = "", QString iconPath = "") :
Base(parent, labelText, iconPath),
mSpinBox(nullptr)
{
createTheWidgetSet(parent);
}
virtual ~SpinBoxActionWidget() {}
QSpinBox* getSpinBox() const
{
return mSpinBox;
}
protected:
QWidget *createCustomWidget(QWidget *parent) override
{
if (mSpinBox == nullptr) {
mSpinBox = new QSpinBox(parent);
mSpinBox->setFixedHeight(22);
}
return mSpinBox;
}
private:
Q_DISABLE_COPY(SpinBoxActionWidget)
QSpinBox *mSpinBox;
};
/* Elsewhere in code.... */
{
QMenu theMenu = new QMenu(parentWindow);
SpinBoxActionWidget theAct = new SpinBoxActionWidget(theMenu);
SpinBoxActionWidget theSecondAct = new SpinBoxActionWidget(theMenu);
theMenu->addAction(theAct);
theMenu->addAction(theSecondAct);
/* I now assume that I can do this, and the entire entry in the menu
* represented by "theAct" can be made visible and invisible.
* This doesn't work however, either the widget remains visible,
* or is partially hidden.
theAct->setVisible(true);
theAct->setVisible(false);
*/
}
You are not reimplementing the interface, that's why it doesn't work.
First, note that QWidgetAction derives from QAction which is not a QWidget; however, it does have a setVisible() function, which will actually just forward the call to all widgets created by the action.
You have to reimplement QWidgetAction::createWidget(parent) to add a new widget; your createCustomWidget was doing nothing useful. Here is a very simple example:
class SpinAction : public QWidgetAction
{
Q_OBJECT
public:
SpinAction(QObject* parent) : QWidgetAction(parent) {}
virtual ~SpinAction() {}
QWidget* createWidget(QWidget* parent) { return new QSpinBox(parent); }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// reimplement this function
};
You can add your action to whatever container you want, menus, toolbars, etc... This example will create a new widget for each container, and these created widgets won't be synchronized (for example on the spinbox value).
I just tested it in a main window, with a widget action added to a menu and a toolbar, and calling setVisible() works flawlessly.

Adding multiple text field in a Qt pushButton element

I am trying to implement a Qt push button and some of its properties are going to change under certain circumstances, such as the size, the color and the text field. While I have implemented most of the functionality, I'm looking to add (probably) another text field at a custom place over the button which is going also to change according to different input. For example:
Desired button layout
In this example, I want to be able to change dynamically both the numeric value and the currency. Is there a way to do it, or something similar?
EDIT: I tried to derive my button class from QPushButton and add some extra text in it, but with no luck. Below you'll find the relevant code:
.h file:
namespace Ui {
class myButton;
}
class myButton : public QPushButton
{
Q_OBJECT
public:
explicit myButton(QWidget *parent = 0);
~myButton();
void setSize(int _xs, int _ys);
void setPosition(int _xp, int _yp);
private:
Ui::Tile *ui;
int xSize = 95;
int ySize = 95;
protected:
void paintEvent(QPaintEvent *);
};
.cpp file
myButton::myButton(QWidget *parent) :
QPushButton(parent),
ui(new Ui::myButton)
{
ui->setupUi(this);
}
myButton::~myButton()
{
}
//Paint event of button
void myButton::paintEvent(QPaintEvent *paint){
QPushButton::paintEvent(paint);
QPainter p(this);
p.save();
p.drawText(QPoint(80,10),"FirstName"); // Simple Text.
p.setPen(Qt::blue); // Changing the color of pen.
p.setFont(QFont("Arial", 50)); // Changing the font.
p.drawText(QPoint(80,20),"MiddleName");
p.drawText(QPoint(80,30),"Lastname");
p.restore();
}
Then I'm calling my new button with something like:
myButton *newBtn2 = new myButton(this);
newBtn2->show();

Qt Directx rendering

I'm rendering in Qt widget by DirectX.
I disabled qt paint engine
QPaintEngine *paintEngine() const { return NULL; }
but sometimes I want to enable qt paint and disable directX rendering.
I'm disabling DirectX rendering, but how can I enable Qt paintEngine?
The possible solutions include:
Use a QWindow, and then switch rendering between [raster][0] and DirectX.
Use a DirectX-rendered QWindow or QWidget, and render the optional content to a QImage. You can use that image as a texture and easily overlay it over the DirectX-rendered content.
A quick workaround with little code changes would be to re-create the widget each time the rendering mode changes. This could be implemented very cleanly by having the state of the widget reside in a PIMPL. See the example below.
class MyWidgetPrivate {
public:
bool qtRendering;
// your data members etc.
MyWidgetPrivate(bool qtRendering) :
qtRendering(qtRendering)
{}
};
class MyWidget : public QWidget {
Q_OBJECT
Q_DECLARE_PRIVATE(MyWidget)
QScopedPointer<MyWidgetPrivate> const d_ptr;
QPaintEngine *paintEngine() const {
Q_D(const MyWidget);
return d->qtRendering ? QWidget::paintEngine() : nullptr;
}
MyWidget(QScopedPointer<MyWidgetPrivate> & data, QWidget * parent, bool qtRendering) :
QWidget(parent),
d_ptr(data.take())
{
d_ptr->qtRendering = qtRendering;
}
public:
MyWidget(QWidget * parent, bool qtRendering) :
QWidget(parent),
d_ptr(new MyWidgetPrivate(qtRendering))
{}
void setQtRendering(bool qtRendering) {
if (qtRendering == d_ptr->qtRendering) return;
auto geom = geometry();
auto parent = this->parentWidget();
auto & d = const_cast<QScopedPointer<MyWidgetPrivate>&>(d_ptr);
QScopedPointer<MyWidgetPrivate> pimpl(d.take());
this->~MyWidget(); // destroy in place
new (this) MyWidget(pimpl, parent, qtRendering); // reconstruct in place
setGeometry(geom);
}
};
Use
QWidget::setAttribute(Qt::WA_OpaquePaintEvent, false);
to reenable Qt background drawing on your widget.