How to draw triangle and rhombus shapes using QT - c++

I need to draw triangle shape and rhombus shapes like this image.In this code which design triangle shape (figure 1) but I need to add this shape to text "TRI" . And I also need to implement this code to design rhombus shape like (figure 2). please help me to solve this.
Figure 1
void MainWindow::on_btnTri_clicked()
{
QPen redPen(Qt::black);
redPen.setWidth(2);
QRectF rect = QRectF(0, 0, 200, 200);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
QGraphicsPathItem* item = ui->graphicsView->scene()->addPath(path, redPen);
item->setFlag(QGraphicsItem::ItemIsMovable, true);
item->setFlag(QGraphicsItem::ItemIsSelectable,true);
}
Figure 2 I use this code to design figure 2 But which cannot pass parameters to change there size,My figure 1 designed code I can able pass two parameters to QRectF(0, 0, para1, para2); this for change triangle's size.so I need to change this code to do the same thing using QPainterPath or any other way.
void MainWindow::on_btnRomb_clicked()
{
QPolygonF romb;
romb.append(QPointF(20,40));
romb.append(QPointF(0,20));
romb.append(QPointF(20,0));
romb.append(QPointF(40, 20));
QGraphicsPolygonItem* itemR = ui->graphicsView->scene()->addPolygon(romb);
itemR->setFlag(QGraphicsItem::ItemIsMovable);
}

you must use the addText() method of QPainterPath, to place it in the center you must calculate the width and height of the text for it QFontMetrics is used:
QPen redPen(Qt::black);
redPen.setWidth(2);
QRectF rect(0, 0, 200, 200);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
path.moveTo(rect.center());
QFont font("Times", 20, QFont::Bold);
QFontMetrics fm(font);
QString text = "TRI";
QSize size = fm.size(Qt::TextSingleLine, text);
path.addText(rect.center()+ QPointF(-size.width()*0.5, size.height()*0.5), font, text);
QGraphicsPathItem *item = ui->graphicsView->scene()->addPath(path, redPen);
item->setFlag(QGraphicsItem::ItemIsMovable, true);
item->setFlag(QGraphicsItem::ItemIsSelectable,true);
For the case of the diamond you should only get the midpoints of each vertex:
QPainterPath path;
QRectF rect(0, 0 , 100, 100);
path.moveTo(rect.center().x(), rect.top());
path.lineTo(rect.right(), rect.center().y());
path.lineTo(rect.center().x(), rect.bottom());
path.lineTo(rect.left(), rect.center().y());
path.lineTo(rect.center().x(), rect.top());
QGraphicsPathItem* itemR = ui->graphicsView->scene()->addPath(path);
itemR->setFlag(QGraphicsItem::ItemIsMovable);

Related

How many pixels do items in QComboBox need?

I would like to know how many pixels need this items in QComboBox:
red - checkbox
green - distance between end of checkbox and beggining of the text
blue - distance between end of border and beggining of the checkbox
In doc of QStyle I find two methods:
subElementRect()
pixelMetric()
I think I have to use them, but I don't know, which args I need to use.
It depends on the style.
QCommonStyle for example draws the ComboBox's label like this:
if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
QRect editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget);
p->save();
p->setClipRect(editRect);
if (!cb->currentIcon.isNull()) {
QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal
: QIcon::Disabled;
QPixmap pixmap = cb->currentIcon.pixmap(qt_getWindow(widget), cb->iconSize, mode);
QRect iconRect(editRect);
iconRect.setWidth(cb->iconSize.width() + 4);
iconRect = alignedRect(cb->direction,
Qt::AlignLeft | Qt::AlignVCenter,
iconRect.size(), editRect);
if (cb->editable)
p->fillRect(iconRect, opt->palette.brush(QPalette::Base));
proxy()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pixmap);
if (cb->direction == Qt::RightToLeft)
editRect.translate(-4 - cb->iconSize.width(), 0);
else
editRect.translate(cb->iconSize.width() + 4, 0);
}
if (!cb->currentText.isEmpty() && !cb->editable) {
proxy()->drawItemText(p, editRect.adjusted(1, 0, -1, 0),
visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter),
cb->palette, cb->state & State_Enabled, cb->currentText);
}
p->restore();
}
This means, that the actual size of the icon's rectangle can be determined by subclassing QComboBox (to access its protected initStyleOption method) and creating a new public getIconRect method in the following way:
QRect ComboBox::getIconRect()
{
QStyleOptionComboBox opt;
initStyleOption(&opt);
QRect rect(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField));
rect.setWidth(opt.iconSize.width() + 4);
return rect;
}
Then in MainWindow for example create a ComboBox and call its getIconSize method like this:
auto *cmbBox = new ComboBox(this);
qDebug() << cmbBox->getIconRect();
For me, on Windows 10, this gives:
QRect(3,3 20x14)
There could be other ways, but if you insist on using a method similar to QStyle::subControlRect, those are the correct arguments;

Qt C++ How to Scale Text to Width w/QPainter

I am looking for an effective way to use the drawText method of QPainter according to a QRect. For example if I were to have a QRect as the origin with a size of 50x50: QRect(0, 0, 50, 50); and wanted to fit text so it fits from left to right. Here are a few of the examples that I currently have, but to no extent can I get them to move fluently.
Example 1:
font.setStyleHint(QFont::AnyStyle, QFont::PreferAntialias);
painter.setRenderHint(QPainter::TextAntialiasing);
QStyleOptionGraphicsItem option;
qreal lod = option.levelOfDetailFromTransform(painter.worldTransform());
QRectF r = textRect;
QFont f = font;
qreal aspectRatio = painter.fontMetrics().lineSpacing() / painter.fontMetrics().averageCharWidth();
int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (m_text.length() * 3)) * aspectRatio;
f.setPixelSize(pixelsize);
int flags = Qt::AlignCenter | Qt::TextDontClip | Qt::TextWordWrap;
if ((pixelsize * lod) < 13)
flags |= Qt::TextWrapAnywhere;
QFontMetricsF fm(f);
QRectF tbr = fm.boundingRect(r, flags, m_text);
pixelsize = f.pixelSize() * qMin(r.width() * 0.65 / tbr.width(), r.height() * 0.65 / tbr.height());
f.setPixelSize(pixelsize);
painter.setFont(f);
painter.drawText(r, flags, m_text);
However this leaves me with jittery text that is not smooth.
Example 2:
float factor = (rectangle.width() / painter.fontMetrics().width(m_text))/2;
if ((factor < 1) || (factor > 1.25))
{
font.setPointSizeF(font.pointSizeF()*factor);
painter.setFont(font);
}
QPainterPath textPath;
textPath.addText(textRect.topLeft(), font, m_text);
painter.drawPath(textPath);
Example 3:
QImage image(size(), QImage::Format_ARGB32_Premultiplied);
{
QPainter p(&image);
p.setRenderHint(QPainter::TextAntialiasing);
QFont font;
font.setFamily("Roboto medium");
font.setPointSize((double)scalar/10);
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
p.setPen(Qt::black);
p.setFont(font);
p.drawText(rect(), Qt::AlignLeft , m_text);
}
painter.drawImage(rectangle, image);
Honestly I am using these method to make circular buttons that can either have text, text+icon, or just an icon. I would like them to be scalable. Any method that can do as such would be greatly appreciated. Thank you very much.

Understanding QT's graphics view coordinate system

I have the following code:
QGraphicsScene* pScene = new QGraphicsScene(this);
ui->graphicsView->setScene(pScene);
pScene->addRect(0, 0, 200, 200);
QGraphicsRectItem* pRect1 = pScene->addRect(40, 40, 100, 100);
QGraphicsRectItem* pRect2 = new QGraphicsRectItem(20, 20, 19, 19, pRect1);
QPointF pf1 = pRect1->pos();
QPointF pf2 = pRect2->pos();
QPointF pf3 = pRect2->mapFromParent(pRect1->pos());
QPointF pf4 = pRect2->mapToParent(pRect2->pos());
QPointF spf1 = pRect1->scenePos();
QPointF spf2 = pRect2->scenePos();
Nothing special, just a QGraphicsView with QGraphicsScene and a few QGraphicsRectItem(s).
Question: Why do all the points (pf1, pf2, pf3, pf4 and even spf1, spf2) equal QPointF(0.0, 0.0) after execution?
I'm using Qt 5.4.1.
From my understanding spf1 must be QPointF(40.0, 40.0) and spf2 must be QPointF(20.0, 20.0).
If you will look in the documentation then you will find:
QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const
QPen &pen = QPen(), const QBrush &brush = QBrush())
Creates and adds a rectangle item to the scene, and returns the item
pointer. The geometry of the rectangle is defined by rect, and its pen
and brush are initialized to pen and brush. Note that the item's
geometry is provided in item coordinates, and its position is
initialized to (0, 0). For example, if a QRect(50, 50, 100, 100) is
added, its top-left corner will be at (50, 50) relative to the origin
in the items coordinate system
.As result, the coordinate of the Item is (0,0) but you can set it with:
pRect1->setPos(QPoint(30,30));
and look what will happen. Hope it will help you to understand QGraphicsScene coordinate system!
P.S.:
QGraphicsRectItem* pRect1 = pScene->addRect(0, 0, 100, 100);
QGraphicsRectItem* pRect2 = pScene->addRect(0, 0, 20, 20);
pRect1->setPos(QPoint(40,40));
pRect2->setPos(QPoint(20,20));

Qt- fill circle with images

I've tried to fill circle with four images. Firstly , each photo brush in the same size ,hereafter scale final image with that size. But result is not what I want.
At the moment circle in foreground and photos on background, like here:
How to fill circle with photos and remove rectangle?
Here is my code:
QPixmap *CGlobalZone::profPicFromFourPics(QList<QPixmap> pixmapList)
{
QPixmap *avatar = NULL;
QImage roundedImage(CGlobalZone::AVATAR_WIDTH_M*2, CGlobalZone::AVATAR_HEIGHT_M*2, QImage::Format_ARGB32);
roundedImage.fill(Qt::transparent);
QBrush brush0(pixmapList[0]);
QBrush brush1(pixmapList[1]);
QBrush brush2(pixmapList[2]);
QBrush brush3(pixmapList[3]);
QPainter painter(&roundedImage);
QPen pen(QColor(176, 216, 242), 1);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(brush0);
painter.drawRect(0 , 0 , CGlobalZone::AVATAR_WIDTH_M , CGlobalZone::AVATAR_HEIGHT_M );
painter.setBrush(brush1);
painter.drawRect(CGlobalZone::AVATAR_WIDTH_M , 0 , CGlobalZone::AVATAR_WIDTH_M*2 , CGlobalZone::AVATAR_HEIGHT_M );
painter.setBrush(brush2);
painter.drawRect(CGlobalZone::AVATAR_WIDTH_M , CGlobalZone::AVATAR_HEIGHT_M , CGlobalZone::AVATAR_WIDTH_M*2 , CGlobalZone::AVATAR_HEIGHT_M*2 );
painter.setBrush(brush3);
painter.drawRect(0 , CGlobalZone::AVATAR_HEIGHT_M , CGlobalZone::AVATAR_WIDTH_M*2 , CGlobalZone::AVATAR_HEIGHT_M*2 );
painter.drawEllipse(0, 0, CGlobalZone::AVATAR_WIDTH_M*2-3 , CGlobalZone::AVATAR_HEIGHT_M*2-3 );
avatar = new QPixmap(QPixmap::fromImage(roundedImage).scaled(QSize(CGlobalZone::AVATAR_WIDTH_M, CGlobalZone::AVATAR_HEIGHT_M),
Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
return avatar;
}
I would do this in the following way (details in source comments):
// The avatar image. Should be four, but use one for demonstration.
QPixmap source("avatar.png");
// Initialize the avatar and bring it to a standard size.
// This step may be skipped if avatars have the same sizes.
const int width = CGlobalZone::AVATAR_WIDTH_M;
const int height = CGlobalZone::AVATAR_HEIGHT_M;
source = source.scaled(width, height);
// Set up the final image that contains four avatar images.
QPixmap target(2 * width, 2 * height);
target.fill(Qt::transparent);
QPainter painter(&target);
// Set clipped region (circle) in the center of the target image
QRegion r(QRect(width / 2, height / 2, width, height), QRegion::Ellipse);
painter.setClipRegion(r);
painter.drawPixmap(0, 0, source); // First avatar
painter.drawPixmap(width, 0, source); // Second avatar
painter.drawPixmap(0, height, source); // Third avatar
painter.drawPixmap(width, height, source); // Fourth avatar
target.save("test.png");
Use painter paths for the task. Rather than drawEllipse you should do
int dim = CGlobalZone::AVATAR_WIDTH_M*2;
QPainterPath entirePath;
QPainterPath ellipsePath;
entirePath.addRect(0, 0, dim, dim);
ellipsePath.addEllipse(0, 0, dim-3, dim-3);
QPainterPath outOfEllipse = entirePath.subtracted(ellipsePath);
painter.fillPath(outOfEllipse, QBrush(Qt::transparent));
edit: Because QPainterPath is for complex cases, you should use QRegion. After testing I found out there might small pixel errors when filling inside and outside the same path (in your case it is going to be fine).

How to fill a rounded rectangle with different color segments by area in Qt?

I am new to Qt and I tried looking for examples online and the documentations but couldn't find something. I want something like this:
I tried it using a QLinearGradient but it isn't quite what I want. I want solid colors.
Here's what I've tried:
void drawBackground ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const {
QLinearGradient linearGrad(QPointF(option.rect.x(), 0), QPointF(option.rect.x() + option.rect.width(), 0));
int total = index.data(StatisticsModel::TotalCount).toInt();
linearGrad.setColorAt(0.0, QColor(255, 255, 255, 0));
int sum = 0;
for (int i = 7; i >= 1; i--) {
int count = index.data(StatisticsModel::Grade0 + i).toInt();
if (count) {
sum += count;
linearGrad.setColorAt(1.0-((double)(total-sum))/total, Prefs::gradeColor(i));
}
}
QRect rect(option.rect);
rect.adjust(1, 1, -1, -1);
QPainterPath path;
path.addRoundedRect( rect, 2.0, 2.0 );
painter->setBrush(QBrush(linearGrad));
painter->drawPath(path);
}
Any help would be appreciated.
Well the best way to color rounded rectangle like this I guess would be to create QPainterPath for it then construct normal rectangles which should be of specified color intersect them with initial rounded rectangle QPainterPath using function QPainterPath::intersected and draw them, selecting corresponding solid color brush and using function drawPath