How can set a Qwidget inside a QGraphicsWidget? - c++

Is it possible that a QGraphicsWidget be a parent for QWidget? I have a QGraphicsItem and I want to add a QWidget inside this item, How can I set a QWidget in a QGraphicsItem or QGraphicsWidget that is a child of QGraphicsItem?

QGraphicsWidget is basically a QGraphicsItem for QWidgets ... it provides a layout you can use so it should be no problem at all
use:
QGraphicsScene::addWidget( QWidget * widget, Qt::WindowFlags wFlags = 0 )

QWidget and QGraphicsWidget are very different. However, the QGraphics system provides a QGraphicsProxyWidget for embedding QWidget and QWidget-based items in a QGraphicsScene.
You can directly create the QGraphicsProxyWidget and call the function setWidget, before adding the QGraphicsProxyWidget to your QGraphicsScene: -
QGraphicsScene* pScene = new QGraphicsScene;
QWidget* pWidget = new QWidget;
QGraphicsProxyWidget* pProxy = new QGraphicsProxyWidget(parent); // parent can be NULL
pProxy->setWidget(pWidget);
pScene->addItem(pProxy);
The proxy widget can now be moved, scaled etc in the scene and the functionality of its QWidget will have signals passed through to it to work as expected.
Alternatively, the QGraphicsScene contains a shortcut function addWidget, which internally creates the QGraphicsProxyWidget for you and returns it from the function: -
QGraphicsProxyWidget* pProxy = pScene->addWidget(pWidget);

Related

Change background color of a Qlayout containing QButtons

I have this Qt code:
QHBoxLayout *taggerBox = new QHBoxLayout;
falseBtn = new QToolButton;
falseBtn->setText(tr("False"));
voidBtn = new QToolButton;
voidBtn->setText(tr("Void"));
taggerBox->addWidget(falseBtn);
taggerBox->addWidget(voidBtn);
I would like to change the background of the QHBoxLayout (NOT the background of each button). I didn't find any way to change the background color of a QLayout.
How can I do this ?
Thanks!
QLayout is not a visual element, it's a container that adjust location of contained widgets. You can change background of QFrame or other widgets you included QLayout into.
Since QHBoxLayout is not a QWidget it hasn't its own appearance. So you can't change its color.
You'll have to add an intervening widget that you set the layout on, and change that widget's background. E.g.
auto *taggerBox = new QWidget;
auto *layout = new QHBoxLayout(taggerbox);
falseBtn = new QToolButton(tr("False"));
voidBtn = new QToolButton(tr("Void"));
layout->addWidget(falseBtn);
layout->addWidget(voidBtn);
auto palette = taggerBox->palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox->setPalette(palette);
If you're doing this in the constructor of some class, then likely the objects have the same lifetime as the class as there's no point to dynamically allocate them. In such circumstances, the widgets and layouts should be class members instead:
class MyClass : ... {
QWidget taggerBox;
QHBoxLayout taggerLayout{&taggerBox};
QToolButton falseBtn{tr("False")};
QToolButton voidBtn{tr("Void")};
public:
MyClass(...);
};
MyClass::MyClass(...) : ... {
taggerLayout.addWidget(&falseBtn);
taggerLayout.addWidget(&voidBtn);
auto palette = taggerBox.palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox.setPalette(palette);
...
}

adding a Q3DScatter to an existing QDialog form

I would like to insert a Q3DScatter instance onto one of my forms. I am able to construct the graph and display it with:
Q3DScatter *graph = new Q3DScatter();
QWidget *container = QWidget::createWindowContainer(graph);
...do some graph stuff...
container.show()
This successfully creates the graph and shows it in its own window/container. I would now like to place it inside another form that has been designed in qt-creator. I have placed an empty widget on the form(QDialog) as a placeholder, but am unable to move the Q3DScatter into the widget.
I see Q3DScatter::Q3DScatter(const QSurfaceFormat *format = Q_NULLPTR, QWindow *parent = Q_NULLPTR) in the documentation but I dont have a QWindow parent to attach it to, as the form I would like to add it to is a QDialog...
I am very new to widgets in general, so please correct me if this is the wrong approach.
Add the container as a child of the dialog. Put in a layout if you need to resize when the dialog is resized. Example:
MyDialog::MyDialog(Q3DScatter *scatter, QWidget *parent) : QDialog(parent)
{
QHBoxLayout * const l = new QHBoxLayout(this);
l->setMargin(0);
l->addWidget(QWidget::createWindowContainer(scatter, this);
}

Do I have to delete these pointers?

This is the MainWindow class which I call and use the function show() to make it visible to the user.
class MainWindow : public QMainWindow
{
Q_OBJECT
QWidget *centralWidget;
QGridLayout* gridLayout;
QGridLayout* infoBoxLayout;
QHBoxLayout* buttonGroup;
QHBoxLayout* subCategoryLayout;
//... more widgets
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void setupUi();
void setupConnections();
private slots:
void add();
void edit();
void remove();
void find();
void clearAll();
void screenshotDesktop();
void screenshotApp();
void currentSubCategoryChanged( const QString& );
void curretCategoryChanged( const int );
void keyPressEvent( QKeyEvent * );
};
I created for each widget (those pointers after the macro Q_OBJECT) a new object on the heap with new. However, I did not delete them anywhere in the program. Does this cause a memory leak in Qt? Or does something from Qt delete them automatically when destroying the class?
If a widget has a parent set, then Qt will handle deleting the widget.
In the case of a MainWindow, when you close it, the MainWindow and its children will be cleaned up, so pass the parent to the widget's constructor: -
QHBoxLayout* buttonGroup = new QHBoxLayout(this); // where this is the parent (MainWindow)
If you create a Widget such as this: -
QHBoxLayout* buttonGroup = new QHBoxLayout;
And haven't passed in the parent, then it will not be cleaned up and you'll have to handle that yourself.
if you add them to the gui hierarchy then they will be cleaned up when the MainWindow is deleted
this is because parents assume ownership over their children (which is set with the various adds of the gui)
so a this->add(centralWidget); will call centralWidget->setParent(this); which will let centralWidget be deleted when MainWindow is deleted
you are free to delete QObjects yourself but beware dangling pointers (QPointer will help here). though I suggest using deleteLater() to ensure no strange behavior when a pointer still lives on the stack.
for more info about the object tree see here
The automatic memory management through parent-child relationships is done by the QObject. QWidget happens to be a QObject, and it so happens that widgets that have parent widgets have the same underlying QObjects as parents.
A QObject with children automatically deletes its children in its destructor.
A QObject or a QWidget may be adopted by another object. For example, adding widgets to a layout will automatically reparent them to the widget the layout is set on. Even if a layout doesn't have a widget set on it yet, the reparenting will be done at the time you add the layout to a widget (or to a layout that has a widget set). It's pretty clever and saves a lot of typing and reduces possibilities for mistakes.
The idiomatic, minimum-typing way of adding widgets to another widget is:
MyWidget() {
QLayout * layout = new QHBoxLayout(this); // set a layout on this widget
layout->addWidget(new QLabel("foo")); // the label is reparented to this
layout->addWidget(new QPushButton("bar")); // the button is reparented to this
}

Derrived Widget not centered on parent when shown as dialog

I have a class MyListWidget derrived from QWidget. I passed parent and flags to the base class QWidget constructor (tried both Qt::Dialog and Qt::Popup in tests) but the custom widget is shown in the center of the screen instead centered to its parent.
MyListWidget* myListWidget = new MyListWidget(this, Qt::Dialog);
This is the constructor:
MyListWidget::MyListWidget(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags),
ui(std::auto_ptr<Ui::MyListWidget>(new Ui::MyListWidget))
{
ui->setupUi(this);
}
If I put this widget into a separate dialog, anything works as expected. But why?
Wrapping works:
QDialog* popup = new QDialog(this, Qt::Popup);
QVBoxLayout* hLayout = new QVBoxLayout(popup);
// ... doing list creation like above
hLayout->addWidget(mmyListWidget);
popup->setLayout(hLayout);
const int width = mapListWidget->width();
const int height = mapListWidget->height();
popup->resize(width, height);
Any ideas what could happend here?
QWidget is not shown on center by default, so you need to center it manually (you can do that in the constructor):
MyListWidget::MyListWidget(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags),
ui(std::auto_ptr<Ui::MyListWidget>(new Ui::MyListWidget))
{
ui->setupUi(this);
move(
parent->window()->frameGeometry().topLeft() +
parent->window()->rect().center() - rect().center()
);
}
P.S. Beware of std::auto_ptr, you probably want to use std::unique_ptr these days.
I'm not quite sure what you're trying to achieve but I have the feeling you should derive MyListWidget from QDialog.
Regards,
Ben

How do I place the QScrollBar on the content of the QScrollArea?

I want to make a scrollbar that fades in and out depending on usage. I subclassed QScrollBar and got the look that I want. The problem is that the scrollbar is placed next to the content. How do I instead make it go on top of the content?
I created a new QScrollbar which I connected to the original via signals and then used widget->setParent and then widget->setGeometry() to paint it on top
I quicker solution is to reparent the QScrollBars that the QScrollArea creates and add it to a new QLayout to position it how you want.
QScrollArea *scrollArea = new QScrollArea();
QScrollBar *scrollBar = scrollArea->horizontalScrollBar();
scrollBar->setParent(scrollArea);
scrollBar->setFixedHeight(20);//required for later
QVBoxLayout *scrollAreaLayout = new QVBoxLayout(scrollArea);
scrollAreaLayout->setContentsMargins(0, 0, 0, 10);//use whatever margins you want
scrollAreaLayout->addStretch(1);
scrollAreaLayout->addWidget(scrollBar);
This gets the basic functionality working, however the QScrollArea still adds space where the scrollbar would have been. To remove this, subclass QProxyStyle and override pixelMetric().
#include <QProxyStyle>
class StyleFixes : public QProxyStyle
{
public:
int pixelMetric(PixelMetric metric, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR) const override
{
if(metric == PM_ScrollBarExtent)
{
return 0;
}
return QProxyStyle::pixelMetric(metric, option, widget);
}
};
Then just apply it in main.cpp
QApplication::setStyle(new StyleFixes);
This will remove the arrows on the scrollbar however so you'll need to style it yourself.