code sample
type here
void MainWindow::paintEvent(QPaintEvent *event){
QPainter chartPainter;
QPointF qChart;
QFont font;
chartPainter.begin(this);
qChart = QPointF(100, 100); // x postion, y position
chartPainter.drawText(qChart, "A", 0, 0);
}
How can I display the letter "A" in Qt as mirrored text?
How to apply mirrored text to all text, not just "A"?
QImage has a function called mirrored that can be applied. However, the method using QPainter is not found even after searching.
Is there a way that can be implemented with QPainter or some other way?
Yes you can create a QImage and draw on it. You draw over the image in the Painter.
.h
............
protected:
void paintEvent(QPaintEvent *);
private:
void drawText(const QString &text);
private:
Ui::Widget *ui;
QImage image;
bool drawing;
...........
.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
image = QImage(size(), QImage::Format_ARGB32_Premultiplied);
drawing = true; // for updating once
}
void Widget::paintEvent(QPaintEvent *)
{
image = image.mirrored(true, false);
QPainter painter(this);
painter.fillRect(rect(), QGradient(QGradient::SaintPetersburg));
painter.drawImage(rect(), image);
if(drawing)
drawText("Mirrored");
}
void Widget::drawText(const QString &text)
{
QPainter painter(&image);
// set a font
QFont font = painter.font();
font.setFamily("Helvetica");
font.setPixelSize(20);
painter.setFont(font);
// metrics for centerPos and size
QFontMetrics metrics(painter.fontMetrics());
painter.setPen(Qt::black);
// keep it in the middle if possible (is only for the optics. you don't have to do it like that)
painter.drawText((width()/2) -(metrics.horizontalAdvance(text))-10,
(height()/2) - (metrics.height()),
metrics.horizontalAdvance(text),
metrics.height(),
0,
text);
update();
drawing = false;
}
Just a small note: if you draw directly in the painter, the picture will flicker. you can work around it by calling the image directly in the constructor.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
image = QImage(size(), QImage::Format_ARGB32_Premultiplied);
//image.fill(Qt::white);
//drawing = true; // update once
drawText("Mirrored");
image = image.mirrored(true, false);
}
.........
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(rect(), QGradient(QGradient::SaintPetersburg));
painter.drawImage(rect(), image);
}
........
you need to set the Qt::TextDirection property of a QPainter object.
Add these to your code:
painter.setLayoutDirection(Qt::RightToLeft);
painter.drawText(rect, Qt::AlignRight, tr("Text goes here"));
Related
I'm new on Qt and c++, so I'm having some difficulties. I'm trying to create a widget that can get the mouseMoveEvent position and draw an ellipse on my pixmap on mouse position. Below you can see the code:
#include "myimage.h"
#include <QPainter>
#include <QPen>
#include <QColor>
#include <QMouseEvent>
#include <QDebug>
Myimage::Myimage(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true); // E.g. set in your constructor of your widget.
}
// Implement in your widget
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << event->pos();
}
void Myimage::paintEvent(QPaintEvent * event)
{
event->accept();
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
painter.drawEllipse(pos(), 10 ,10 );
}
The mouse position is being printed on console, but no ellipse on image.
Could you help me?
Regards,
Gabriel.
According to the doc:
pos : QPoint
This property holds the position of the widget within its parent
widget.
If the widget is a window, the position is that of the widget on the
desktop, including its frame.
...
Access functions:
QPoint pos() const void
move(int x, int y)
void move(const QPoint &)
As we see this data we do not want it, a possible solution is to create a variable that stores the value of the position obtaining through QMouseEvent and update the painting through the function update(), in addition the first time the Widget there should be no ellipse so we check that the position has been assigned through the function isNull() of QPoint, as I show below:
*.h
private:
QPoint mPoint;
*.cpp
Myimage::Myimage(QWidget *parent)
: QWidget(parent)
{
setMouseTracking(true);
}
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
mPoint = event->pos();
update();
}
void Myimage::paintEvent(QPaintEvent *)
{
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
if(!mPoint.isNull()){
painter.drawEllipse(mPoint, 10 ,10 );
}
}
there is a question disturbing me, maybe it's just a simple question for you guys.
i want to draw tow lines in my Qt widget, when the QComboBox cbx is set at the item 1, the first line is set to red, second black, on the contrary, 2, fist black, second red.
i used *paintEvent* to draw my lines. (actually, if you want to draw something, you must draw in paintEvent function) But i dont know how to change the line's color based on the QComboBox item, follows are my codes.
#include "changecolor.h"
#include "ui_changecolor.h"
QString st;
QPainter painter;
ChangeColor::ChangeColor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ChangeColor)
{
ui->setupUi(this);
setFixedSize(2000, 1000);
QComboBox * cbx = new QComboBox(this);
cbx->setSizeAdjustPolicy(QComboBox::AdjustToContents);
cbx->addItem("1", Qt::DisplayRole);
cbx->addItem("2", Qt::DisplayRole);
st = cbx->currentText();
connect(cbx, SIGNAL(currentTextChanged(QString)), this, SIGNAL(changeColorSlot(st, painter)));
}
void ChangeColor::paintEvent(QPaintEvent*)
{
// QPainter painter(this);
// painter.setPen(Qt::black);
// painter.drawLine(QPoint(100,100), QPoint(1100,100));
// painter.drawLine(QPoint(100,100), QPoint(100,600));
// changeColorSlot(painter, );
changeColorSlot(st, painter);
}
void ChangeColor::changeColorSlot(QString& st, QPainter& painter)
{
// QPainter painter(this);
if(st == tr("1"))
{
painter.setPen(Qt::black);
painter.drawLine(QPoint(100,100),QPoint(1100,100));
painter.setPen(Qt::red);
painter.drawLine(QPoint(100,100),QPoint(100,600));
}
else if(st == tr("2"))
{
painter.setPen(Qt::red);
painter.drawLine(QPoint(100,100),QPoint(1100,100));
painter.setPen(Qt::black);
painter.drawLine(QPoint(100,100),QPoint(100,600));
}
update();
}
ChangeColor::~ChangeColor()
{
delete ui;
}
these codes denote my painful coding life, i mean i have tried many times, but there is no right result. thank you guys.
you have to set the logic that controls the state(the color of the lines) outside the widget:
here an example with the relevant parts
.h:
#include
namespace Ui
{
class ChangeColor;
}
class ChangeColor : public QWidget
{
Q_OBJECT
public:
explicit ChangeColor(QWidget *parent = nullptr);
void paintEvent(QPaintEvent* pe);
~ChangeColor();
public slots:
void setColorState(int state);
private:
Ui::ChangeColor* ui{nullptr};
int state{};
};
.cpp
...
void ChangeColor::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter paint(this);
QColor RED_COLOR{255, 0, 0};
QColor BLACK_COLOR{0, 0, 0};
if(state == 0)
{
paint.setPen(QPen(RED_COLOR));
paint.setBrush(RED_COLOR);
}
else
{
paint.setPen(QPen(BLACK_COLOR));
paint.setBrush(BLACK_COLOR);
}
paint.drawLine(0, 0, width(), height());
}
void ChangeColor::setColorState(int state)
{
this->state = state;
update();
}
I suppose you are looking for this feature:
QComboBox* cb = ...;
QAbstractItemView* view = cb->view();
view->setAlternatingRowColors(true);
I'm new on Qt and c++, so I'm having some difficulties. I'm trying to create a widget that can get the mouseMoveEvent position and draw an ellipse on my pixmap on mouse position. Below you can see the code:
#include "myimage.h"
#include <QPainter>
#include <QPen>
#include <QColor>
#include <QMouseEvent>
#include <QDebug>
Myimage::Myimage(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true); // E.g. set in your constructor of your widget.
}
// Implement in your widget
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << event->pos();
}
void Myimage::paintEvent(QPaintEvent * event)
{
event->accept();
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
painter.drawEllipse(pos(), 10 ,10 );
}
The mouse position is being printed on console, but no ellipse on image.
Could you help me?
Regards,
Gabriel.
According to the doc:
pos : QPoint
This property holds the position of the widget within its parent
widget.
If the widget is a window, the position is that of the widget on the
desktop, including its frame.
...
Access functions:
QPoint pos() const void
move(int x, int y)
void move(const QPoint &)
As we see this data we do not want it, a possible solution is to create a variable that stores the value of the position obtaining through QMouseEvent and update the painting through the function update(), in addition the first time the Widget there should be no ellipse so we check that the position has been assigned through the function isNull() of QPoint, as I show below:
*.h
private:
QPoint mPoint;
*.cpp
Myimage::Myimage(QWidget *parent)
: QWidget(parent)
{
setMouseTracking(true);
}
void Myimage::mouseMoveEvent(QMouseEvent *event)
{
mPoint = event->pos();
update();
}
void Myimage::paintEvent(QPaintEvent *)
{
QPixmap pixmap2("/home/gabriel/Qt_interfaces/OpenCVTests/Webcam_PyQt5/Images/Court_top_View.jpg");
QRect rectangle(0, 0, width()-1, height()-1);
QPainter painter(this);
painter.drawRect(rectangle);
painter.drawPixmap(5, 5, width()-10, height()-10, pixmap2);
if(!mPoint.isNull()){
painter.drawEllipse(mPoint, 10 ,10 );
}
}
I have written application that draws two smiling faces:
First one is painted directly on QWidget:
void DirectFace::paintEvent(QPaintEvent *ev)
{
QPainter painter(this);
paintFace(painter);
}
Second one is painted on a QPixmap, which in turn is blitted to widget:
void BufferedFace::paintEvent(QPaintEvent *ev)
{
QPixmap buffer(width(), height());
buffer.fill(Qt::transparent);
QPainter painter(&buffer);
paintFace(painter);
QPainter p(this);
p.drawPixmap(ev->rect(), buffer, ev->rect());
}
So far so good. I wanted to see how my app looks like on high resolution screen (I don't have one), so I set QT_SCALE_FACTOR=2 and run my app:
First face is sharp and crisp, whereas the seconf one is pixelated. That's because it is drawn to low resolution pixmap. So I have enlarged that QPixmap and set correct devicePixelRatio:
void BufferedFace::paintEvent(QPaintEvent *ev)
{
qreal pixelRatio = qApp->devicePixelRatio();
QPixmap buffer(width() * pixelRatio, height() * pixelRatio);
buffer.setDevicePixelRatio(pixelRatio);
buffer.fill(Qt::transparent);
QPainter painter(&buffer);
paintFace(painter);
QPainter p(this);
p.drawPixmap(ev->rect(), buffer, ev->rect());
}
Result:
Second face looks like it's drawn with correct resolution but then upscaled. Now I'm stuck. How to draw on QPixmap and then draw that QPixmap so it works correctly on Retina/HiDPI screens?
Whole application:
#include <QtWidgets>
class SmilingFace : public QWidget
{
public:
SmilingFace(QWidget *parent) : QWidget(parent) {};
void paintFace(QPainter &painter);
};
class DirectFace : public SmilingFace
{
public:
DirectFace(QWidget *parent) : SmilingFace(parent) {}
void paintEvent(QPaintEvent *ev) override;
};
class BufferedFace : public SmilingFace
{
public:
BufferedFace(QWidget *parent) : SmilingFace(parent) {}
void paintEvent(QPaintEvent *ev) override;
};
void SmilingFace::paintFace(QPainter &painter)
{
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(Qt::lightGray));
painter.drawEllipse(1, 1, width()-2, height()-2);
painter.setPen(Qt::white);
painter.setFont(QFont("", 32));
painter.drawText(rect(), Qt::AlignHCenter, ";)");
}
void DirectFace::paintEvent(QPaintEvent *ev)
{
QPainter painter(this);
paintFace(painter);
}
void BufferedFace::paintEvent(QPaintEvent *ev)
{
QPixmap buffer(width(), height());
buffer.fill(Qt::transparent);
QPainter painter(&buffer);
paintFace(painter);
QPainter p(this);
p.drawPixmap(ev->rect(), buffer, ev->rect());
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.setWindowTitle("HiDPI");
DirectFace d(&w);
d.resize(48, 48);
d.move(16, 16);
BufferedFace i(&w);
i.resize(48, 48);
i.move(16 + 48 + 16, 16);
w.show();
return a.exec();
}
If you want HighDPI rendering, you should also use QRectF and QPointF arguments for the QPainter functions. In your paintFace(...) function adjust the drawEllipse and drawText functions to use QRectF arguments and not QRect. This might help.
It is not a good idea to use qApp->devicePixelRatio(). There are people with mixed HighDPI and Non-HighDPI monitors. As you are within a widget paintEvent(...) function, you can use directly the QWidget member function devicePixelRatioF(), and not the qApp->devicePixelRatio(). This will handle proper rendering of the widget, even when the user moves the widget in between monitors with a mixed resolution.
You should also enable High DPI scaling in Qt via: QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
Here comes the full solution, which renders the smiling face perfectly on HighDPI and Non-HighDPI screen, even when the widget is moved in between. Tested with Qt 5.9.2
#include <QtWidgets>
class SmilingFace : public QWidget
{
public:
SmilingFace(QWidget *parent) : QWidget(parent) {};
void paintFace(QPainter &painter);
};
class DirectFace : public SmilingFace
{
public:
DirectFace(QWidget *parent) : SmilingFace(parent) {}
void paintEvent(QPaintEvent *ev) override;
};
class BufferedFace : public SmilingFace
{
public:
BufferedFace(QWidget *parent) : SmilingFace(parent) {}
void paintEvent(QPaintEvent *ev) override;
};
void SmilingFace::paintFace(QPainter &painter)
{
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(Qt::lightGray));
painter.drawEllipse(QRectF(1, 1, width() - 2, height() - 2));
painter.setPen(Qt::white);
painter.setFont(QFont("", 32));
painter.drawText(QRectF(0, 0, width(), height()), Qt::AlignHCenter, ";)");
}
void DirectFace::paintEvent(QPaintEvent *ev)
{
QPainter painter(this);
paintFace(painter);
}
void BufferedFace::paintEvent(QPaintEvent *ev)
{
qreal dpr = devicePixelRatioF();
QPixmap buffer(width() * dpr, height() * dpr);
buffer.setDevicePixelRatio(dpr);
buffer.fill(Qt::transparent);
QPainter painter(&buffer);
paintFace(painter);
QPainter p(this);
p.drawPixmap(ev->rect(), buffer, buffer.rect());
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv);
QWidget w;
w.setWindowTitle("HiDPI");
DirectFace d(&w);
d.resize(48, 48);
d.move(16, 16);
BufferedFace i(&w);
i.resize(48, 48);
i.move(16 + 48 + 16, 16);
w.show();
return a.exec();
}
I have created an Image Viewer application that opens and saves an image and load the image on QLabel,then I created a ScrollArea to scroll for large images, in my second step I am trying to draw a selection rectangle to select a specific sub area , the steps I have taken to paint the selection rectangle as follows:
1- I used an PaintEvent to paint the rectangle.
2- I used MouseEvent to select the sub area.
The problem is, when I run my code, I can draw the rectangle on the QWidget but not on the ScrollArea.
Here is my code:
in imageviewer.h
class ImageViewer : public QWidget{
Q_OBJECT
public:
explicit ImageViewer(QWidget *parent = 0);
~ImageViewer();
private:
Ui::ImageViewer *ui;
private slots:
void on_openButton_pressed();
void on_saveButton_pressed();
private:
QPixmap image;
QImage *imageObject;
bool selectionStarted;
QRect selectionRect;
QMenu contextMenu;
protected:
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
};
this is imageviewer.cpp
ImageViewer::ImageViewer(QWidget *parent) :
QWidget(parent),
ui(new Ui::ImageViewer)
{
ui->setupUi(this);
ui->scrollArea->setWidget(ui->imageLabel);
}
open & save functions:
void ImageViewer::on_openButton_pressed()
{
QString imagePath = QFileDialog::getOpenFileName(this, tr("Open File") , "" ,
tr("JPEG (*.jpg *.jpeg);;PNG (*.png);;BMP (*.bmp)"));
imageObject = new QImage();
imageObject->load(imagePath);
image = QPixmap::fromImage(*imageObject);
ui->imageLabel->setPixmap(image);
ui->imageLabel->adjustSize();
}
void ImageViewer::on_saveButton_pressed()
{
QString imagePath = QFileDialog::getSaveFileName(this, tr("Save File") , "" ,
tr("JPEG (*.jpg *.jpeg);;PNG (*.png);;BMP (*.bmp)"));
*imageObject = image.toImage();
imageObject->save(imagePath);
}
paint & mouse event functions:
void ImageViewer::paintEvent(QPaintEvent *e){
QWidget::paintEvent(e);
QPainter painter(this);
painter.setPen(QPen(QBrush(QColor(0,0,0,180)),1,Qt::DashLine));
painter.setBrush(QBrush(QColor(255,255,255,120)));
painter.drawRect(selectionRect);
}
void ImageViewer::mousePressEvent(QMouseEvent *e){
if(e->button() == Qt::RightButton){
if(selectionRect.contains(e->pos()))
contextMenu.exec(this->mapToGlobal(e->pos()));
}
else{
selectionStarted = true;
selectionRect.setTopLeft(e->pos());
selectionRect.setBottomRight(e->pos());
}
}
void ImageViewer::mouseMoveEvent(QMouseEvent *e){
if(selectionStarted){
selectionRect.setBottomRight(e->pos());
repaint();
}
}
void ImageViewer::mouseReleaseEvent(QMouseEvent *e){
selectionStarted = false;
}
and this is a screenshot for my application
How can I draw a selection rectangle on QScrollArea?
You need QRubberBand which supposed to be directly applied to ui->imageLabel widget you use to display an image you want to scroll. Whether or not you should overload QLabel for ui->imageLabel is a question of implementation. Please see the example for mouse events which, of course better be applied to your label image class.
I understand from the way the code looks you used Qt Designer? That complicates overriding that label class but you can either not use .ui file or use event filter for trapping all the events for the given object.
What you currently have can be finally polished enough to be good for actual use, of course. It just makes much more effort for you. You can avoid overloading paintEvent etc. and the key is to apply the right approach directly where it needs to be applied.
// the example applied to authors code as requested
class MyImageLabel : public QLabel
{
public:
explicit MyImageLabel(QWidget *parent = 0) : QLabel(parent), rubberBand(0) {}
private:
QRubberBand* rubberBand;
QPoint origin;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event)
};
void MyImageLabel::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void MyImageLabel::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
void MyImageLabel::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
ImageViewer::ImageViewer(QWidget *parent) :
QWidget(parent),
ui(new Ui::ImageViewer)
{
ui->setupUi(this);
// resolve this better than:
delete ui->imageLabel; // you already have the QLabel object here?
ui->imageLabel = new MyImageLabel;
ui->scrollArea->setWidget(ui->imageLabel);
}