Why doesn't setPos() move the QGraphicsItems in the scene? - qt

In the code below, I have three QGraphicsItems that are laid out by QGraphicsLinearLayout, which is set as layout to a QGraphicsWidget.
#include <QApplication>
#include <QBrush>
#include <QDebug>
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
#include <QGraphicsLinearLayout>
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPen>
class MyShape : public QGraphicsRectItem, public QGraphicsLayoutItem {
public:
MyShape(void) {
setPen(QPen(QBrush(Qt::black), 1));
setBrush(QBrush(Qt::green));
setRect(0, 0, 20, 20);
}
virtual QSizeF sizeHint(Qt::SizeHint which,
const QSizeF& constraint = QSizeF()) const {
Q_UNUSED(which);
Q_UNUSED(constraint);
return boundingRect().size();
}
virtual void setGeometry(const QRectF& rect) {
setPos(rect.topLeft());
}
};
int main(int argc, char** argv) {
QApplication app(argc, argv);
QGraphicsScene scene;
MyShape* shape1 = new MyShape;
MyShape* shape2 = new MyShape;
MyShape* shape3 = new MyShape;
scene.addItem(shape1);
scene.addItem(shape2);
scene.addItem(shape3);
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
layout->addItem(shape1);
layout->addItem(shape2);
layout->addItem(shape3);
QGraphicsWidget* container = new QGraphicsWidget;
container->setLayout(layout);
scene.addItem(container);
container->setPos(300, 300); // This doesn't appear to have any affect
// Item for indicating origin
QGraphicsRectItem* tmp = scene.addRect(0, 0, 2, 2, QPen(),
QBrush(Qt::green));
tmp->setPos(0, 0);
qDebug() << tmp->scenePos();
qDebug() << container->scenePos();
QGraphicsView view;
view.setScene(&scene);
view.centerOn(0, 0);
view.show();
return app.exec();
}
Then, I try to move the QGraphicsWidget within the scene by calling setPos(), but it doesn't appear to work (the QGraphicsItems remain in the same place). However, something appears to happen since the scrollbars change. It seems that the QGraphicsWidget moves, without taking the QGraphicsItems along with it.
Why?
EDIT:
By suggestion from DerManu, I changed the inheritance of MyShape to QGraphicsWidget, and with the following code, it works:
#include <QApplication>
#include <QBrush>
#include <QDebug>
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
#include <QGraphicsLinearLayout>
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPen>
class MyShape : public QGraphicsWidget {
public:
MyShape(void) {
setMinimumSize(20, 20);
setMaximumSize(20, 20);
setPreferredSize(20, 20);
}
QRectF boundingRect(void) const {
QRectF box(0, 0, 22, 22);
box.translate(box.center() * -1);
return box;
}
void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget) {
Q_UNUSED(option);
Q_UNUSED(widget);
// Set box border appearance
painter->setPen(QPen(Qt::black, 1));
// Set box background appearance
painter->setBrush(QBrush(Qt::green));
QRectF box(0, 0, 20, 20);
box.translate(box.center() * -1);
painter->drawRect(box);
}
};
int main(int argc, char** argv) {
QApplication app(argc, argv);
QGraphicsScene scene;
MyShape* shape1 = new MyShape;
MyShape* shape2 = new MyShape;
MyShape* shape3 = new MyShape;
scene.addItem(shape1);
scene.addItem(shape2);
scene.addItem(shape3);
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(Qt::Vertical);
layout->addItem(shape1);
layout->addItem(shape2);
layout->addItem(shape3);
QGraphicsWidget* container = new QGraphicsWidget;
container->setLayout(layout);
scene.addItem(container);
container->setPos(200, 200); // This doesn't appear to have any affect
scene.setSceneRect(-300, -300, 600, 600);
// Item for indicating origin
QGraphicsRectItem* tmp = scene.addRect(0, 0, 2, 2, QPen(),
QBrush(Qt::green));
tmp->setPos(0, 0);
qDebug() << tmp->scenePos();
qDebug() << container->scenePos();
QGraphicsView view;
view.setScene(&scene);
view.centerOn(0, 0);
view.show();
return app.exec();
}
So what is it that QGraphicsWidget provides that I missed in the original code such that using setPos on the container item correctly moves the position of the MyShape items?

Related

How to draw a linear gradient arc with Qt QPainter?

I'm trying to develop a custom QProgressBar that will look like the following image :
I created a class that extends QProgressBar and implemented the paintEvent() :
void CircularProgressBar::paintEvent(QPaintEvent*) {
int progress = this->value();
int progressInDegrees = (double)(progress*360)/100;
int barWidth = 20;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(Qt::black, barWidth, Qt::SolidLine,Qt::RoundCap));
painter.drawArc(barWidth/2, barWidth/2, this->width() - barWidth, this->height() - barWidth,
90*16, progressInDegrees*-16);}
This works great to draw the circular progress bar, but I'm having trouble with the linear gradient color of the bar. I tried creating a QPen with a QLinearGradient object and I tried setting the QPainter brush to a QLinearGradient object, but neither strategy worked. Is it possible to draw an arc with QPainter that has a linear gradient color?
I know this is an old question but I came across it some days ago and I think I have a solution. What you want is to create a conical gradient and clip the disk you want to use as circular loading bar. Here is an example:
widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class QPaintEvent;
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void setLoadingAngle(int loadingAngle);
int loadingAngle() const;
void setDiscWidth(int width);
int discWidth() const;
protected:
void paintEvent(QPaintEvent *);
private:
int m_loadingAngle;
int m_width;
};
#endif // WIDGET_H
widget.cpp:
#include "widget.h"
#include <QPaintEvent>
#include <QPainter>
#include <QConicalGradient>
#include <QPen>
Widget::Widget(QWidget *parent) :
QWidget(parent),
m_loadingAngle(0),
m_width(0)
{
}
Widget::~Widget()
{
}
void Widget::setLoadingAngle(int loadingAngle)
{
m_loadingAngle = loadingAngle;
}
int Widget::loadingAngle() const
{
return m_loadingAngle;
}
void Widget::setDiscWidth(int width)
{
m_width = width;
}
int Widget::discWidth() const
{
return m_width;
}
void Widget::paintEvent(QPaintEvent *)
{
QRect drawingRect;
drawingRect.setX(rect().x() + m_width);
drawingRect.setY(rect().y() + m_width);
drawingRect.setWidth(rect().width() - m_width * 2);
drawingRect.setHeight(rect().height() - m_width * 2);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QConicalGradient gradient;
gradient.setCenter(drawingRect.center());
gradient.setAngle(90);
gradient.setColorAt(0, QColor(178, 255, 246));
gradient.setColorAt(1, QColor(5, 44, 50));
int arcLengthApproximation = m_width + m_width / 3;
QPen pen(QBrush(gradient), m_width);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.drawArc(drawingRect, 90 * 16 - arcLengthApproximation, -m_loadingAngle * 16);
}
main.cpp:
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.setDiscWidth(20);
w.setLoadingAngle(270);
w.show();
return a.exec();
}
And the result is:
Of course, it is not the complete and exact solution but I think it is everything you need to know in order to achieve what you want. The rest are details not hard to implement.
This solution is not exactly what you're after; the gradient goes from top to bottom, rather than around the circle:
#include <QtWidgets>
class Widget : public QWidget
{
public:
Widget() {
resize(200, 200);
}
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const QRectF bounds(0, 0, width(), height());
painter.fillRect(bounds, "#1c1c1c");
QPen pen;
pen.setCapStyle(Qt::RoundCap);
pen.setWidth(20);
QLinearGradient gradient;
gradient.setStart(bounds.width() / 2, 0);
gradient.setFinalStop(bounds.width() / 2, bounds.height());
gradient.setColorAt(0, "#1c1c1c");
gradient.setColorAt(1, "#28ecd6");
QBrush brush(gradient);
pen.setBrush(brush);
painter.setPen(pen);
QRectF rect = QRectF(pen.widthF() / 2.0, pen.widthF() / 2.0, width() - pen.widthF(), height() - pen.widthF());
painter.drawArc(rect, 90 * 16, 0.65 * -360 * 16);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
However, it is an arc with a linear gradient! :p

Need to draw Transparent qimage that includes drawing of a circle using Qt

I am trying to draw a transparent image using QImage but everytime it gives black background. I have a image background,on that I want to draw a circle which should be trasparent(with no background).How can I do that?
I have used this code
QImage image(size, QImage::Format_ARGB32);
image.fill(qRgba(0,0,0,0));
// Pick an arbitrary size for the circle
const int centerX = size.width() / 2;
const int centerY = size.height() / 2;
const int radius = std::min(centerX, centerY) * 2 / 3;
const int diameter = radius * 2;
// Draw the circle!
QPainter painter(&image);
painter.setPen(Qt::yellow);
painter.drawEllipse(centerX-radius, centerY-radius, diameter, diameter);
http://qt-project.org/doc/qt-4.8/qpainter.html#settings
http://qt-project.org/doc/qt-4.8/qpainter.html#setBrush
The painter's brush defines how shapes are filled.
Hope that helps.
EDIT: Added an awesome example:
Basically what happens below, is the window is set to have a background color (so that the alpha value of the QImage is noticeable and predicable). The QImage is initialized to have a color with an alpha value less than 255. The image gets painted when the widget updates (when shown in the main).
widget.cpp
#include "widget.h"
#include <QImage>
#include <QPainter>
#include <QPalette>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
init_image();
QPalette p = this->palette();
p.setColor(QPalette::Background, Qt::white);
this->setPalette(p);
}
void Widget::init_image()
{
image = new QImage(200, 200, QImage::Format_ARGB32);
int opacity = 50;// Set this between 0 and 255
image->fill(QColor(0,0,0,opacity));
QPainter painter (image);
painter.setPen(Qt::green);
painter.drawEllipse(10, 10, 100, 100);
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent * e)
{
QPainter painter(this);
painter.drawImage(0,0, *image,0,0,-1,-1,Qt::AutoColor);
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPaintEvent>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
void init_image();
public slots:
void paintEvent(QPaintEvent *);
private:
QImage * image;
};
#endif // WIDGET_H
Main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

QListView with checkboxes for viewing of filesystem

It is necessary to list of directories by given path in QListView whith icons and checkboxes, then transmit names of marked folders to the program. For list directories i use code:
#include <QtGui/QApplication>
#include <QFileSystemModel>
#include <QListView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemModel model;
QListView listView;
listView.setModel(&model);
listView.setRootIndex(model.setRootPath("C:\\Program Files"));
listView.show();
return a.exec();
}
How to add checkboxes and transmit it after pushing button?
Thanks.
There's nothing you can do that would fit in just a few lines. You can either derive from QFileSystemModel and add a checkbox column, or create a proxy model that will do the same.
Note that you can use built-in selection mechanism to Ctrl-click/⌘-click to expand the selection to multiple items:
//main.cpp
#include <QApplication>
#include <QFileSystemModel>
#include <QGridLayout>
#include <QListView>
#include <QPushButton>
#include <QMessageBox>
class Win : public QWidget
{
Q_OBJECT
QListView * view;
QPushButton * button;
public:
Win(QAbstractItemModel * model, const QModelIndex & idx) :
view(new QListView(this)), button(new QPushButton("List Selection", this))
{
QGridLayout * lay = new QGridLayout;
lay->addWidget(view, 0, 0, 1, 2);
lay->addWidget(button, 1, 0);
setLayout(lay);
view->setSelectionMode(QAbstractItemView::MultiSelection);
view->setModel(model);
view->setRootIndex(idx);
connect(button, SIGNAL(clicked()), SLOT(showSelection()));
}
public slots:
void showSelection() {
QString str;
foreach (QModelIndex i, view->selectionModel()->selectedIndexes()) {
str.append(i.data().toString());
str.append("\n");
}
QMessageBox::information(this, "Selected items", str);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemModel model;
Win v(&model, model.setRootPath("/"));
v.show();
return a.exec();
}
#include "main.moc"

How to correctly use QGraphicsLinearLayout together with QGraphicsScene in order to position QGraphicsItems?

I'm struggling with getting QGraphicsLinearLayout to work together with QGraphicsScene such that a group of QGraphicsItems are positioned in a linear fashion on the scene. I've tried using the approach below:
#include <QApplication>
#include <QBrush>
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
#include <QGraphicsLinearLayout>
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPen>
class MyShape : public QGraphicsRectItem, public QGraphicsLayoutItem {
public:
MyShape(void) {
setPen(QPen(QBrush(Qt::black), 1));
setBrush(QBrush(Qt::green));
setRect(0, 0, 20, 20);
}
virtual QSizeF sizeHint(Qt::SizeHint which,
const QSizeF& constraint = QSizeF()) const {
Q_UNUSED(which);
Q_UNUSED(constraint);
return boundingRect().size();
}
};
int main(int argc, char** argv) {
QApplication app(argc, argv);
QGraphicsScene scene;
MyShape* shape1 = new MyShape;
MyShape* shape2 = new MyShape;
MyShape* shape3 = new MyShape;
scene.addItem(shape1);
scene.addItem(shape2);
scene.addItem(shape3);
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
layout->addItem(shape1);
layout->addItem(shape2);
layout->addItem(shape3);
QGraphicsWidget* container = new QGraphicsWidget;
container->setLayout(layout);
scene.addItem(container);
QGraphicsView view;
view.setScene(&scene);
view.show();
return app.exec();
}
This doesn't work, as all items are still placed on top of each other, instead of next to each other (according to the layout dictated by the linear layout). What am I doing wrong?
Ah, I had forgotten to override the setGeometry function!
So the implementation of the MyShape class should look like this:
class MyShape : public QGraphicsRectItem, public QGraphicsLayoutItem {
public:
MyShape(void) {
setPen(QPen(QBrush(Qt::black), 1));
setBrush(QBrush(Qt::green));
setRect(0, 0, 20, 20);
}
virtual QSizeF sizeHint(Qt::SizeHint which,
const QSizeF& constraint = QSizeF()) const {
Q_UNUSED(which);
Q_UNUSED(constraint);
return boundingRect().size();
}
virtual void setGeometry(const QRectF& rect) {
setPos(rect.topLeft());
}
};
And now it works as expected. =)

How to make QTextBrowser scale nicely within QVBoxLayout?

I'd like to display a list of short html snippets in a scrollable list. Thus i place a QVboxLayout into a QScrollArea and add multiple QTextBrowsers:
#include <QApplication>
#include <QScrollArea>
#include <QTextBrowser>
#include <QVBoxLayout>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QScrollArea scrollArea;
scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea.setWidgetResizable(true);
QWidget *scrollWidget = new QWidget;
QVBoxLayout *layout = new QVBoxLayout;
for(int i=0;i<6;i++) {
QString text = "some <b>text</b> ";
for(int j=0;j<i;j++) text += text;
QTextBrowser *browser = new QTextBrowser();
QTextDocument *doc = new QTextDocument(browser);
doc->setHtml(text);
browser->setDocument(doc);
layout->addWidget(browser);
}
scrollWidget->setLayout(layout);
scrollArea.setWidget(scrollWidget);
scrollArea.show();
return app.exec();
}
My problem: The boxes all have the same size. I want them to be as big as necessary, but not bigger. How can i make the textbrowsers to take only as much space as they really need?
Ok, i found a solution derived from http://www.qtcentre.org/threads/39839-QTextBrowser-height-adjusted-to-content. Is there a simpler/mor elegant solution?
#include <QApplication>
#include <QScrollArea>
#include <QTextBrowser>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QDebug>
#include <QShowEvent>
#include <QResizeEvent>
class MyBrowser : public QTextBrowser {
private:
QTextDocument *doc;
public:
MyBrowser(const QString &text, QWidget *parent = 0) : QTextBrowser(parent) {
doc = new QTextDocument(this);
doc->setHtml(text);
setDocument(doc);
}
void adjust() {
QMargins margins = contentsMargins();
int width = size().width() -
margins.left() - margins.right() - doc->documentMargin()*2;
doc->setPageSize(QSizeF(width,-1));
int height = doc->size().height() + margins.top() + margins.bottom();
setMaximumHeight(height);
setMinimumHeight(height);
}
void showEvent(QShowEvent *) {
adjust();
}
void resizeEvent(QResizeEvent *) {
adjust();
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QScrollArea scrollArea;
scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea.setWidgetResizable(true);
QWidget *scrollWidget = new QWidget;
QVBoxLayout *layout = new QVBoxLayout;
for(int i=0;i<6;i++) {
QString text = "some <b>text</b> ";
for(int j=0;j<i;j++) text += text;
MyBrowser *browser = new MyBrowser(text, scrollWidget);
layout->addWidget(browser, 0, 0);
}
scrollWidget->setLayout(layout);
scrollArea.setWidget(scrollWidget);
scrollArea.show();
return app.exec();
}
Instead of doing
layout->addWidget(browser);
You should do:
layout->addWidget(browser, 0, 0);
This will tell QT not to stretch the widget according to its layout.

Resources