Qt Directx rendering - c++

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.

Related

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.

Registering an extension in Qt Designer with no associated widget plugin

TL;DR
I want to register a Qt Designer extension but don't want any widget plugin, so any of the following can solve my problem:
How do I create a plugin in Qt Designer that doesn't expose any widget in the widget box but that can register extensions?
How do I register the extension without subclassing of QDesignerCustomWidgetInterface?
I'm working on a set of plugins for Qt Designer. These plugins expose custom widgets to the designer. All widgets (some dozens) inherit from a common class (CCommonWidget), such as:
CCommonWidget
|-> CLabel
|-> CPushButton
...
CCommonWidget define some common properties for all widgets. I'd like to expose them to Qt Designer through extensions (for example, the QDesignerTaskMenuExtension).
I started with a test in the CLabel plugin. Here the two relevant methods:
// Register the extensions in Qt Designer
void CLabelPlugin::initialize(QDesignerFormEditorInterface *formEditor)
{
if (m_initialized) return;
auto extensionManager = formEditor->extensionManager();
Q_ASSERT(extensionManager);
extensionManager->registerExtensions(new CLabelPluginFactory(extensionManager), Q_TYPEID(QDesignerTaskMenuExtension));
m_initialized = true;
}
// The factory creates the menu extension if the widget is a CLabel
QObject* CLabelPluginFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const
{
if (iid != Q_TYPEID(QDesignerTaskMenuExtension)) return nullptr;
if (auto label = dynamic_cast<CLabel*>(object))
return new CLabelPluginMenu(label, parent);
return nullptr;
}
It worked flawlessly and I was about to extend the idea to the rest of the plugins. Instead of copy/pasting the code I started with a CCommonPlugin and make every one to inherit from it. In order to make it as re-usable as possible I changed the createExtension method to:
QObject* CCommonPluginFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const
{
if (iid != Q_TYPEID(QDesignerTaskMenuExtension)) return nullptr;
if (auto label = dynamic_cast<CCommonWidget*>(object))
return new CCommnPluginMenu(label, parent);
return nullptr;
}
Here I realized that even if only on plugin (CLabelPlugin) was registering the extension factory, any other widget which inherits from CCommonWidget will show the menu! (It was pretty obvious once I discovered it but before I just didn't think on it).
Now it was easier since I hadn't to change all the plugins (dozens) but just to create a new one, a dummy common plugin, that registers the extension factory.
The dummy plugin I've first created:
class CPluginCommon : public QObject, public QDesignerCustomWidgetInterface {
Q_OBJECT
Q_INTERFACES( QDesignerCustomWidgetInterface )
public:
explicit CPluginCommon( QObject* parent=0 );
public: // QDesignerCustomWidgetInterface
QWidget* createWidget( QWidget* parent ) { return nullptr; }
QString group() const { return QString(); }
QIcon icon() const { return QIcon(); }
QString includeFile() const { return QString(); }
bool isContainer() const { return false; }
QString name() const { return QString(); }
QString toolTip() const { return QString(); }
QString whatsThis() const { return QString(); }
virtual bool isInitialized() const override {
return m_initialized;
}
virtual void initialize(QDesignerFormEditorInterface *formEditor) override;
private:
bool m_initialized;
};
But a blank widget is displayed in the widget box of Qt Designer. I don't want an empty widget, I want no widget!
Another option is not to use these kind of plugins but I'm struggling to find a way to register the extension without a QDesignerCustomWidgetInterface, but all that I can find is to get the extensions manager within QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface *formEditor) (using formEditor->extensionManager()).
Quick answer
To hide the widget from the widget box just return an empty XML (this is an undocumented feature):
class CPluginCommon : public QObject, public QDesignerCustomWidgetInterface {
// ...
public:
QString domXml() const { return QString(); }
};
Reviewing the code of the plugins manager of Qt Designer, I found in the QDesignerPluginManagerPrivate::addCustomWidget method the following (c is a pointer to QDesignerCustomWidgetInterface)
const QString domXml = c->domXml();
if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box.
Given that, I saw that the default implementation of domXml does return a tiny XML fragment for a generic widget:
virtual QString domXml() const
{
return QString::fromUtf8("<widget class=\"%1\" name=\"%2\"/>")
.arg(name()).arg(name().toLower());
}
For completeness, as far as I have seen in the source code of the plugins manager of the Qt Designer, there is no way to load a plugin that doesn't inherits from QDesignerCustomWidgetInterface:
// Load plugins into widget database and factory.
void QDesignerIntegration::initializePlugins(QDesignerFormEditorInterface *formEditor)
{
// load the plugins
WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase*>(formEditor->widgetDataBase());
if (widgetDataBase) {
widgetDataBase->loadPlugins();
}
if (WidgetFactory *widgetFactory = qobject_cast<WidgetFactory*>(formEditor->widgetFactory())) {
widgetFactory->loadPlugins();
}
if (widgetDataBase) {
widgetDataBase->grabDefaultPropertyValues();
}
}
where
void WidgetDataBase::loadPlugins()
{
// ...
// 2) create a list plugins
ItemList pluginList;
const QDesignerPluginManager *pm = m_core->pluginManager();
foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets())
pluginList += createCustomWidgetItem(c, pm->customWidgetData(c));
// ...
}
void WidgetFactory::loadPlugins()
{
m_customFactory.clear();
QDesignerPluginManager *pluginManager = m_core->pluginManager();
QList<QDesignerCustomWidgetInterface*> lst = pluginManager->registeredCustomWidgets();
foreach (QDesignerCustomWidgetInterface *c, lst) {
m_customFactory.insert(c->name(), c);
}
}

Drawing lines programmatically with Qt

I want to add lines programmatically in a QLabel between two points. From what I found, it seems that the only way to do it is to subclass a QWidget to change the PaintEvent() protected method.
So, I create a new class 'QLineObject' from QWidget. This is my header file :
class QLineObject : public QWidget
{
Q_OBJECT
public:
QLineObject();
QLineObject(Point from, Point to);
protected:
void paintEvent(QPaintEvent *event);
private:
Point fromPoint;
Point toPoint;
};
And the implementation file :
QLineObject::QLineObject()
{
Point point;
point.x = 0.0;
point.y = 0.0;
fromPoint = point;
toPoint = point;
}
QLineObject::QLineObject(Point from, Point to)
{
fromPoint = from;
toPoint = to;
}
void QLineObject::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawLine(fromPoint.x, fromPoint.y, toPoint.x, toPoint.y);
}
Here's come the problem. I can't find how to add this widget in my main window. If I create a new instance of QLineObject and call show(), it popup a new window. I'm sure I'm just missing something. Is there someone who want to help me? I would like to know how to create and add it from somewhere else that my main window constructor.
Thank you!
You shouldn't be calling show on the QLineObject. Instead, pass the main window as the parent to your constructor and pass that to the inherited QWidget. Then call show on the main widget, which in this case is the main window...
class QLineObject : public QWidget
{
public:
QLineObject(QWidget* parent);
};
QLineObject::QLineObject(QWidget* parent)
: QWidget(parent)
{
}
QWidget* pWidget = new QWidget;
QLineObject* pLineObject = new QLineObject(pWidget);
pWidget->show();
Alternatively, you can use the QLabel as the parent.
QLabel* pLabel = new QLabel(pWidget);
QLineObject* pLineObject = new QLineObject(pLabel);
pWidget->show();
Also, you probably want to be calling QWidget::paintEvent in your overridden paintEvent.
I would do the following:
QMainWindow mw;
QLineObject lo;
mw.setCentralWidget(&lo);
mw.show();

How to properly use adjustSize() in Qt

I'm new to Qt, and this issue about auto resizing has driven me crazy.
I create a class called RenderArea that inherits QWidget. In its paintEvent(), I use a QPainter to draw an image. In order for the whole window to scale with the image, I resize before painting. The relevant code is
if (image && !image->isNull())
{
resize(image->size());
painter.drawImage(image->rect(), *image, image->rect());
}
However, RenderArea will stretch too much through other widgets (like buttons and menus). It is contained in a centralWidget with a vertical layout. But when I call centralWidget->adjustSize() it does not scale everything together, but instead shrinks RenderArea t and hides the image.
How do I instruct the central widget as well as the window to scale with the new size of my customized widget? I know I could use a QLabel and set its scaledContents to be true, but I need a lot of other sophisticated rendering so a simple QLabel is not enough.
The sizeHint function should return recommended widget's size. So RenderArea should return image size as its sizeHint. When the image is changed the updateGeometry function should be called to update a cached sizeHint value:
class RenderArea : public QWidget
{
public:
explicit RenderArea(const QImage &image, QWidget *parent = 0)
: QWidget(parent), m_image(image)
{
}
QSize sizeHint() const
{
return m_image.isNull() ? QWidget::sizeHint() : m_image.size();
}
void paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (!m_image.isNull())
{
QPainter painter(this);
painter.drawImage(rect(), m_image, m_image.rect());
}
}
void setImage(const QImage &image)
{
m_image = image;
updateGeometry();
}
private:
QImage m_image;
};
When the child widget is resized, the parent widget isn't doing it automatically. Fortunately there is QWidget::adjustSize function which lets us to resize the parent widget to fit its content:
class Window : public QWidget
{
Q_OBJECT
private slots:
void onImageChanged(const QString &fileName)
{
m_area->setImage(QImage(fileName));
adjustSize();
}
private:
RenderArea *m_area;
};

Extend Qt standard icons

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).