drawRect function of Qpainter not giving rectangles of equal dimension - qt

In Qt i have a Qpixmap of 5 px width and 4 px height ,I am filling it with rectangles of width .08 and height .08 but the rectangles formed are not of equal dimension as I have given`.
I want to draw alternate black and white rectangles of equal dimension
QLabel *a=new QLabel();
QPixmap b(5,4);
a->setFixedSize(b.size());
QPainter painter(&b);
heightOfCheckeredBox=.08;
widthOfCheckeredBox=.08;
bool switchBetweenBlackAndWhiteBoxRowDirection=true;
int switchBetweenBlackAndWhiteBoxColumnDirection=0;
for(qreal i=0;i<4;i+=heightOfCheckeredBox)
{
for(qreal j=0;j<5;j+=widthOfCheckeredBox)
{
if(switchBetweenBlackAndWhiteBoxRowDirection)
{
painter.setPen(Qt::white);
painter.setBrush(Qt::white);
switchBetweenBlackAndWhiteBoxRowDirection=false;
}
else
{
painter.setPen(Qt::gray);
painter.setBrush(Qt::gray);
switchBetweenBlackAndWhiteBoxRowDirection=true;
}
QRectF rectangle(j,i,widthOfCheckeredBox,heightOfCheckeredBox);
painter.drawRect(rectangle);
}
switchBetweenBlackAndWhiteBoxColumnDirection++;
if(int(switchBetweenBlackAndWhiteBoxColumnDirection)%2==0)
switchBetweenBlackAndWhiteBoxRowDirection=true;
else
switchBetweenBlackAndWhiteBoxRowDirection=false;
}
a->setPixmap(b);
a->show();

Since you're drawing without antialiasing, the tiny pixel size makes little sense - what did you expect?
The size of the pixmap is given in pixels. Your pixmap contains 5x4 = 20 pixels. You're trying to draw 20 rectangles on it. For this to work, each rectangle must be a square exactly one pixel wide and high. Alas, you're trying to draw squares 12.5x smaller than a pixel. That doesn't make much sense, and I have no idea where you got the value of 0.08 from. Just draw the squares one pixel wide.
Example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/checkered-paint-42205180
#include <QtWidgets>
QPixmap checkers(const QSizeF & rectSize, const QSize & pixmapSize) {
QPixmap pixmap{pixmapSize};
QPainter painter{&pixmap};
painter.setPen(Qt::NoPen);
const QColor colors[] = {Qt::white, Qt::black};
QPointF pos{};
bool oddRow{}, color{};
while (pos.y() < pixmap.height()) {
painter.setBrush(colors[color ? 1 : 0]);
painter.drawRect({pos, rectSize});
color = !color;
pos.setX(pos.x() + rectSize.width());
if (pos.x() >= pixmap.width()) {
pos.setX({});
pos.setY(pos.y() + rectSize.height());
oddRow = !oddRow;
color = oddRow;
}
}
return pixmap;
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QLabel label;
auto pix = checkers({1, 1}, {60, 30});
label.resize(200, 200);
label.setAlignment(Qt::AlignCenter);
label.setPixmap(pix);
label.show();
return app.exec();
}

Related

how to avoid pixmap cutting issues when we using qpainter while rotation

label=new QLabel(this);
label->setGeometry(this->width()/2,this->height()/2,label->width(),label->height());
QPixmap myPixmapForNow;
myPixmapForNow.load("C://Users//abc//Documents//QpixMap//hub_needle.png");
label->setMinimumSize(QSize(myPixmapForNow.width(),myPixmapForNow.width()));
label->setAlignment(Qt::AlignCenter);
QPixmap rotated(label->width(),label->width());
QPainter p(&rotated);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.translate(myPixmapForNow.size().width() / 2,
(myPixmapForNow.size().height() / 2));
qDebug()<<"before rotation width:"<<rotated.size().width()<<"height:"<<rotated.size().width();
p.rotate(arg1);
p.translate(-myPixmapForNow.size().width() / 2,
-(myPixmapForNow.size().height() / 2));
qDebug()<<"after rotation height:"<<-rotated.size().width()<<"height:"<<-rotated.size().height();[![enter image description here][1]][1]
p.drawPixmap(QRect(0,0,myPixmapForNow.width(),myPixmapForNow.height()), myPixmapForNow);
p.end();
label->setPixmap(rotated);
After rotation
before rotation
I must admit the OP could have explained the issue a bit more in detail. Unfortunately, OP didn't react on comments.
However, out of curiosity, I tried to puzzle this out in a little demo. (I really like to write little Qt demos, especially with image manipulation and cat pictures.)
My first assumption was that OP has struggled with the order of transformations.
While translations are commutative (changing order doesn't change result), this is not the case for rotations (and other transformations).
However, after having wrapped OPs code into a MCVE, I convinced myself that the order of transformations matched my expectation – a rotation about the center of image.
So, I focused on the title
how to avoid pixmap cutting issues when we using qpainter while rotation
The reason for the “cutting issue” is simple:
To paint a rotated image (rectangle), the output may require a greater range of pixels then the original.
There are two possibilities to fix this:
enlarge the QPixmap for output
scale the result to match the original size of QPixmap.
So, this leaves the task to determine the output size of the rotated image beforehand, to either make the output QPixmap respectively larger or to add the respective scaling.
The bounding rectangle of a rotated rectangle can be calculated with trigonometric functions (sin, cos, etc.) I decided instead (for an IMHO more naïve way) to let Qt do the work for me.
To achieve this, the transformation has to be calculated before creating the QPixmap and QPainter. Hence, the prior
qPainter.translate(cx, cy);
qPainter.rotate(ra);
qPainter.translate(-cx, -cy);
is replaced by:
QTransform xform;
xform.translate(cx, cy);
xform.rotate(ra);
xform.translate(-cx, -cy);
which can be later applied as is:
qPainter.setTransform(xform);
I used the fact that all four corners of the rotated rectangle will touch the bounding rectangle. So, the bounding rectangle can be calculated by applying min() and max() to the x and y components of the rotated image corners:
const QPoint ptTL = xform * QPoint(0, 0);
const QPoint ptTR = xform * QPoint(w - 1, 0);
const QPoint ptBL = xform * QPoint(0, h - 1);
const QPoint ptBR = xform * QPoint(w - 1, h - 1);
QRect qRectBB(
QPoint(
min(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
min(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())),
QPoint(
max(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
max(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())));
Afterwards, the output may be adjusted using the origin and size of qRectBB.
The whole demo application testQPainterRotateCenter.cc:
#include <algorithm>
// Qt header:
#include <QtWidgets>
int min(int x0, int x1, int x2, int x3)
{
return std::min(std::min(x0, x1), std::min(x2, x3));
}
int max(int x0, int x1, int x2, int x3)
{
return std::max(std::max(x0, x1), std::max(x2, x3));
}
QPixmap rotate(
const QPixmap &qPixMapOrig, int cx, int cy, int ra,
bool fitIn, bool keepSize)
{
int w = qPixMapOrig.width(), h = qPixMapOrig.height();
QTransform xform;
xform.translate(cx, cy);
xform.rotate(ra);
xform.translate(-cx, -cy);
if (fitIn) {
// find bounding rect
const QPoint ptTL = xform * QPoint(0, 0);
const QPoint ptTR = xform * QPoint(w - 1, 0);
const QPoint ptBL = xform * QPoint(0, h - 1);
const QPoint ptBR = xform * QPoint(w - 1, h - 1);
QRect qRectBB(
QPoint(
min(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
min(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())),
QPoint(
max(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
max(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())));
qDebug() << "Bounding box:" << qRectBB;
// translate top left corner to (0, 0)
xform *= QTransform().translate(-qRectBB.left(), -qRectBB.top());
if (keepSize) {
// center align scaled image
xform *= w > h
? QTransform().translate((w - h) / 2, 0)
: QTransform().translate(0, (h - w) / 2);
// add scaling to transform
const qreal sx = qreal(w) / qRectBB.width();
const qreal sy = qreal(h) / qRectBB.height();
const qreal s = std::min(sx, sy);
xform *= QTransform().scale(s, s);
} else {
// adjust w and h
w = qRectBB.width(); h = qRectBB.height();
}
}
QPixmap qPixMap(w, h);
qPixMap.fill(Qt::gray);
{ QPainter qPainter(&qPixMap);
qPainter.setRenderHint(QPainter::Antialiasing);
qPainter.setRenderHint(QPainter::SmoothPixmapTransform);
qPainter.setRenderHint(QPainter::HighQualityAntialiasing);
qPainter.setTransform(xform);
qPainter.drawPixmap(0, 0, qPixMapOrig.width(), qPixMapOrig.height(), qPixMapOrig);
} // end of scope -> finalize QPainter
return qPixMap;
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
const QString file = QString::fromUtf8("cats.jpg");
QPixmap qPixMapOrig;
qPixMapOrig.load(file);
int cx = qPixMapOrig.width() / 2, cy = qPixMapOrig.height() / 2;
int ra = 0;
// setup GUI
QWidget qWin;
qWin.setWindowTitle(
file % QString(" (")
% QString::number(qPixMapOrig.width())
% " x " % QString::number(qPixMapOrig.height())
% ") - testQPainterRotateCenter");
QVBoxLayout qVBox;
QHBoxLayout qHBox1;
QLabel qLblCX(QString::fromUtf8("center x:"));
qHBox1.addWidget(&qLblCX);
QLineEdit qEditCX;
qEditCX.setText(QString::number(cx));
qHBox1.addWidget(&qEditCX, 1);
QLabel qLblCY(QString::fromUtf8("center y:"));
qHBox1.addWidget(&qLblCY);
QLineEdit qEditCY;
qEditCY.setText(QString::number(cy));
qHBox1.addWidget(&qEditCY, 1);
QLabel qLblRA(QString::fromUtf8("rotation angle:"));
qHBox1.addWidget(&qLblRA);
QSpinBox qEditRA;
qEditRA.setValue(ra);
qHBox1.addWidget(&qEditRA, 1);
qVBox.addLayout(&qHBox1);
QHBoxLayout qHBox2;
QCheckBox qTglFitIn(QString::fromUtf8("Zoom to Fit"));
qTglFitIn.setChecked(false);
qHBox2.addWidget(&qTglFitIn);
QCheckBox qTglKeepSize(QString::fromUtf8("Keep Size"));
qTglKeepSize.setChecked(false);
qHBox2.addWidget(&qTglKeepSize);
qVBox.addLayout(&qHBox2);
QLabel qLblImg;
qLblImg.setPixmap(qPixMapOrig);
qLblImg.setAlignment(Qt::AlignCenter);
qVBox.addWidget(&qLblImg, 1);
qWin.setLayout(&qVBox);
qWin.show();
// helper to update pixmap
auto update = [&]() {
cx = qEditCX.text().toInt();
cy = qEditCY.text().toInt();
ra = qEditRA.value();
const bool fitIn = qTglFitIn.isChecked();
const bool keepSize = qTglKeepSize.isChecked();
QPixmap qPixMap = rotate(qPixMapOrig, cx, cy, ra, fitIn, keepSize);
qLblImg.setPixmap(qPixMap);
};
// install signal handlers
QObject::connect(&qEditCX, &QLineEdit::textChanged,
[&](const QString&) { update(); });
QObject::connect(&qEditCY, &QLineEdit::textChanged,
[&](const QString&) { update(); });
QObject::connect(&qEditRA, QOverload<int>::of(&QSpinBox::valueChanged),
[&](int) { update(); });
QObject::connect(&qTglFitIn, &QCheckBox::toggled,
[&](bool) { update(); });
QObject::connect(&qTglKeepSize, &QCheckBox::toggled,
[&](bool) { update(); });
// runtime loop
return app.exec();
}
The Qt project file testQPainterRotateCenter.pro:
SOURCES = testQPainterRotateCenter.cc
QT += widgets
Output:
The rotated image without zoom to fit:
Zoomed to fit:
Zoomed to fit original size:
Notes:
While fiddling originally with a square image of 300×300 pixels, I became aware that rotating a non-square rectangle may result in a bounding box with a different aspect-ratio than the original. Hence, an additional translation might be desirable to align the scaled output in the original bounding box again. I switched to a non-square sample image of 300×200 pixels to illustrate this.
With the fit in calculations, the translations before/after rotation are actually obsolete. The result will be translated in any case to the intended position.
Instead of Qt::gray, the “background color” (i.e. the color the QPixmap is filled with initially) might be set complete transparently. I decided to stick to the Qt::gray for illustration.

Qt 4.8, Screenshot of large QGraphicsScene

I have QGraphicsScene, which size is 62450x4750. Somethimes I need to make screenshot of whole scene and save it to file. I tried like this:
QPixmap wholeScene(scene.sceneRect().size().toSize());
{
QPainter wholeScenePainter(&wholeScene);
scene.render(&wholeScenePainter);
}
// saving pixmap
or
QPixmap wholeScene(scene.sceneRect().size().toSize());
{
QPainter wholeScenePainter(&wholeScene);
int x = 0;
int portion = 32768; //
while( x < scene.sceneRect().width()) {
int width = scene.sceneRect().width() - x > portion ? portion : scene.sceneRect().width() - x;
QRect rect(x, 0, width, scene.sceneRect().height());
scene.render(&wholeScenePainter, rect, rect);
x += width;
}
}
// saving pixmap
or
QPixmap wholeScene(scene.sceneRect().size().toSize());
{
QPainter wholeScenePainter(&wholeScene);
int x = 0;
int portion = 4096;
while( x < scene.sceneRect().width()) {
int width = scene.sceneRect().width() - x > portion ? portion : scene.sceneRect().width() - x;
QRect rect(x, 0, width, scene.sceneRect().height());
QPixmap temp(rect.size());
QPainter p(&temp);
scene.render(&p, QRect(0, 0, rect.width(), rect.height()), rect);
wholeScenePainter.drawPixmap(x,0, temp);
//temp.save(QString("print%1.png").arg(QString::number(x)), "PNG");
x += width;
}
}
// saving pixmap
But every time I get the Image(Pixmap) cuted by width on 32768 px.
According to the documentation, QPainter does not support coordinates larger than +/- 32768. This does not appear to be fixed in Qt 5 either.
Maybe you can solve this by rendering the scene in multiple passes, e.g. through translation and clipping. You can render the scene in multiple blocks of max 32768x32768 pixels and put them in the proper position in the final image.

Why do I see gaps between pixmap tiles in QGraphicsView when I enable Antialiasing

I am using QGraphicsView framework to display a big image from smaller QPixmap tiles. I also want to enable Antialiasing since the scene will contain line items. Why do I see gaps between the tiles when I enable Antialiasing?
class MainWindow : public QWidget
{
public:
MainWindow(QWidget *parent = 0);
protected:
void resizeEvent(QResizeEvent*);
private:
QGraphicsScene* _scene;
QGraphicsView* _view;
qreal _scale;
static const int _imageWidth = 512;
static const int _imageHeight = 128;
};
MainWindow::MainWindow(QWidget *parent)
: QWidget(parent)
{
_scene = new QGraphicsScene(this);
_view = new QGraphicsView(_scene, this);
//this causes gaps to appear ?
_view->setRenderHints(QPainter::Antialiasing);
_scene->setBackgroundBrush( QBrush( QColor( Qt::lightGray ) ) );
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(_view);
setWindowTitle(QString("GapsBetweenTiles- QT Version %1")
.arg(QT_VERSION_STR));
QImage img = QImage(_imageWidth, _imageHeight, QImage::Format_RGB32);
img.fill(QColor(00, 50, 50));
int offset = 0;
for (int k=0; k < 10; ++k) {
QGraphicsPixmapItem* pixitem = _scene->addPixmap(
QPixmap::fromImage(img));
pixitem->setTransformationMode(Qt::SmoothTransformation);
pixitem->setPos(0, offset);
offset += _imageHeight;
}
_scale = 1.0;
}
void MainWindow::resizeEvent(QResizeEvent* )
{
int scaledWidth = (qreal)_view->width() -
_view->verticalScrollBar()->width() ;
qreal scale = (qreal)scaledWidth / (qreal)_imageWidth;
qreal scaleMult = scale / _scale;
_view->scale(scaleMult, scaleMult);
_scale = scale;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
The gaps appear when scaled image height (_imageHeight * scale) becomes fractional.
Each QGraphicsPixmapItem is drawn as a separate object. If such object has fractional height the border is smoothed when anti-aliasing enabled (fractional bordering line is partially painted).
There are three possible gap layouts in your case:
no gaps if scaled image height is integer
periodic series of 1 object with gap and 1 without gap if fractional part of height is 0.5
periodic series of 3 objects with gaps and 1 object without gap if fractional part is 0.25 or 0.75; here the 2nd gap is brighter than the 1st and 3rd gaps.
So, if you want perfect object alignment the scaled height should be integer.
In your example the scaled height is integer when scaled width is divisible by 4.
It can be verified by adding into resizeEvent the following line:
scaledWidth = (scaledWidth / 4) * 4;
By the way, you can disable anti-aliasing only for QGraphicsPixmapItem objects by removing the line:
pixitem->setTransformationMode(Qt::SmoothTransformation);

How to write a text around a circle using QPainter class?

The question is simple ! I want something like this. Either using QPainter class or using Qt Graphics Framework:
There are several ways to do this using a QPainterPath specified here.
Here is the second example from that page:
#include <QtGui>
#include <cmath>
class Widget : public QWidget
{
public:
Widget ()
: QWidget() { }
private:
void paintEvent ( QPaintEvent *)
{
QString hw("hello world");
int drawWidth = width() / 100;
QPainter painter(this);
QPen pen = painter.pen();
pen.setWidth(drawWidth);
pen.setColor(Qt::darkGreen);
painter.setPen(pen);
QPainterPath path(QPointF(0.0, 0.0));
QPointF c1(width()*0.2,height()*0.8);
QPointF c2(width()*0.8,height()*0.2);
path.cubicTo(c1,c2,QPointF(width(),height()));
//draw the bezier curve
painter.drawPath(path);
//Make the painter ready to draw chars
QFont font = painter.font();
font.setPixelSize(drawWidth*2);
painter.setFont(font);
pen.setColor(Qt::red);
painter.setPen(pen);
qreal percentIncrease = (qreal) 1/(hw.size()+1);
qreal percent = 0;
for ( int i = 0; i < hw.size(); i++ ) {
percent += percentIncrease;
QPointF point = path.pointAtPercent(percent);
qreal angle = path.angleAtPercent(percent); // Clockwise is negative
painter.save();
// Move the virtual origin to the point on the curve
painter.translate(point);
// Rotate to match the angle of the curve
// Clockwise is positive so we negate the angle from above
painter.rotate(-angle);
// Draw a line width above the origin to move the text above the line
// and let Qt do the transformations
painter.drawText(QPoint(0, -pen.width()),QString(hw[i]));
painter.restore();
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Widget widget;
widget.show();
return app.exec();
}

Set a cross in QImage at the position, where mousepressed

I have a graph shown in a QImage and want to set a cross (+) in yellow colour for measurement, if right mouse button is pressed.
void foo::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
QPoint pos = event->pos();
int x = pos.x();
int y = pos.y();
QLine line(x-5,y,x+5,y);
QLine line(x,y-5,x,y+5);
QPainter painter(&my_image);
painter.setPen( Qt::red );
painter.setBrush( Qt::yellow );
/*
QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
*/
painter.drawLine(line); //no effect
}
}
if I do it in Paintevent(...), I destroy the original pic. how can I do it.
additional Information:
the imag is indexed.
my_image.setColorCount(33);
for(int i = 0;i<33;i++)
{
my_image.setColor(i,qRgb((unsigned char)palette[i*3], (unsigned char)palette[i*3+1], (unsigned char)palette[i*3+2]));
}
my_imag has a black-Background and I want to draw a cross in white color --> (this is the index 32)
int color = 32;//_index_value_of_cross_color;
for (int ix=x-5;ix<x+5;ix++) {
my_image.setPixel(ix,y,color);
}
for (int iy=y-5;iy<y+5;iy++) {
my_imag.setPixel(x,iy,color);
}
but I see no effect !
From your comments, you cannot paint on a QImage with Format_Indexed8.
From the QImage docs:
Warning: Painting on a QImage with the format QImage::Format_Indexed8 is not supported.
Choose a different format like QImage::Format_ARGB32_Premultiplied and things should work.
Another quick and dirty alternative is to simply set the values in the image data.
You will have to do a little more work - because there is no line command, see setpixel
int x = pos.x();
int y = pos.y();
int color = _index_value_of_cross_color;
for (int ix=x-5;ix<x+5;ix++) {
my_image.setPixel(ix,y,color);
}
for (int iy=y-5;iy<y+5;iy++) {
my_image.setPixel(x,iy,color);
}

Resources