It's simple to draw line or ellipse just by using scene.addellipse(), etc.
QGraphicsScene scene(0,0,800,600);
QGraphicsView view(&scene);
scene.addText("Hello, world!");
QPen pen(Qt::green);
scene.addLine(0,0,200,200,pen);
scene.addEllipse(400,300,100,100,pen);
view.show();
now what should i do to set some pixel color? may i use a widget like qimage? by the way performance is an issue for me.thanks
I think that performing pixel manipulation on a QImage would slow down your application quite a lot. A good alternative is to subclasse QGraphicsItem in a new class, something like QGraphicsPixelItem, and implement the paint function like this:
// code untested
void QGraphicsPixelItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0)
{
painter->save();
foreach(const QPoint& p, pointList) {
// set your pen color etc.
painter->drawPoint(p);
}
painter->restore();
}
where pointList is some kind of container that you use to store the position of the pixels you want to draw.
Related
What is the proper way to draw thousands of rects in QT (around 100,000 or more)?
I tried:
Simple with paintEvent() of QWidget.
Drawing objects to QImage then this image to QWidget.
Using QGraphicsScene (maybe I didn't use it properly, I just added rects to scene)
Every time drawing was really slow and I don't have more ideas on how to do this (maybe with opengl/directx but this doesn't sound like a good idea). I know that there exist applications that do that so there should be some way.
EDIT:
I wonder how drawRects() work? Is there a chance that filling some uchar* array and passing it to QImage will be better?
The first trick is to do the drawing in a separate thread onto a QImage, then pass that into the main thread. This won't make it quicker, but it'll make it not block the GUI thread.
// https://github.com/KubaO/stackoverflown/tree/master/questions/threaded-paint-36748972
#include <QtWidgets>
#include <QtConcurrent>
class Widget : public QWidget {
Q_OBJECT
QImage m_image;
bool m_pendingRender { false };
Q_SIGNAL void hasNewRender(const QImage &);
// Must be thread-safe, we can't access the widget directly!
void paint() {
QImage image{2048, 2048, QImage::Format_ARGB32_Premultiplied};
image.fill(Qt::white);
QPainter p(&image);
for (int i = 0; i < 100000; ++i) {
QColor c{rand() % 256, rand() % 256, rand() % 256};
p.setBrush(c);
p.setPen(c);
p.drawRect(rand() % 2048, rand() % 2048, rand() % 100, rand() % 100);
}
emit hasNewRender(image);
}
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.drawImage(0, 0, m_image);
}
public:
Widget(QWidget * parent = 0) : QWidget(parent) {
this->setAttribute(Qt::WA_OpaquePaintEvent);
setMinimumSize(200, 200);
connect(this, &Widget::hasNewRender, this, [this](const QImage & img) {
m_image = img;
m_pendingRender = false;
update();
});
refresh();
}
Q_SLOT void refresh() {
if (!m_pendingRender) {
m_pendingRender = true;
QtConcurrent::run([this] { paint(); });
}
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Widget w;
QPushButton button{"Refresh", &w};
button.connect(&button, &QPushButton::clicked, &w, [&w]{ w.refresh(); });
w.show();
return app.exec();
}
#include "main.moc"
As a separate concern, you can then split the drawing across multiple parallel jobs, by clipping each job's painter to a sub-area of the shared image, and noting that fully clipped rectangle draws are no-ops, and partially clipped ones will only fill the pixels they affect.
Solution which I found:
Create array of uint32_t which can contain all pixels of widget, fill it using memcpy(). Create QImage with this array and use drawImage() to show it.
It can have some optimization like (for profiler) merging rects that are continues ( start time second is equal to end of first ). Don't draw rects that are out of time bounds. Maybe skip too small rects.
For drawing things like text, tool tips you can still use Qt functions.
For alpha blending in simplest case you can just take existing values, blend them in loop and write blended values or maybe use SIMD for this.
Of course for more complex shapes it will get harder to draw but still, I think, it will be faster than using Qt functions.
I am designing a timer with Qt. With QGraphicsEllipseItem, I drew a circle and now I need to animate the QPen around this circle (change color) every second. I found QGraphicsPathItem, but I need some examples on how to move forward. Can anyone show me an example?
You have two problems:
QGraphicsEllipseItem is not a QObject so QPropertyAnimation can't be used directly on this item
QGraphicsItemAnimation doesn't cover property you want to animate.
What you can do?
IMO best approach is to provide some custom QObject on which you could do this animation. You can inherit QObject or use fake QGraphicsObject (which is a QObject).
class ShapeItemPenAnimator : public QGraphicsObject {
Q_OBJECT
private:
QAbstractGraphicsShapeItem *mParent;
QPropertyAnimation *mAnimation;
public:
QPROPERTY(QColor penColor
READ penColor
WRITE setPenColor)
explicit ShapeItemPenAnimator(QAbstractGraphicsShapeItem * parent)
: QGraphicsObject(parent)
, mParent(parent) {
setFlags(QGraphicsItem::ItemHasNoContents);
mAnimation = new QPropertyAnimation(this, "penColor", this);
}
QColor penColor() const {
return mParent->pen().color();
}
public slots:
void setPenColor(const QColor &color) {
QPen pen(mParent->pen());
pen.setColor(color);
mParent->setPen(pen);
}
public:
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0) {
}
QRectF boundingRect() const {
return QRectF();
}
QPropertyAnimation *animation() const {
return mAnimation;
}
}
Now you just attach this object to your QGraphicsEllipseItem and set animation you need.
// yourEllipse
ShapeItemPenAnimator *animator = new ShapeItemPenAnimator(yourEllipse);
animator->animation()->setEndValue(....);
animator->animation()->setStartValue(....);
animator->animation()->setDuration(....);
animator->animation()->setEasingCurve(....);
There are several classes helping with animations of QGraphicsItem in Qt. I suggest looking into QGraphicsItemAnimation and QPropertyAnimation. You can use the second one to animate the color of an item. Here is an example of using QPropertyAnimation:
How to make Qt widgets fade in or fade out?
I currently working on a text editor using Qtextedit and I want to Draw a shapes like triangle, square and ellipseā¦ etc in the editor to Richness the document.
So I was wondering if is it possible to do this with Qtextedit and only Qtextedit.
Actually I am new to Qt so any ideas any tutorial any links would be highly appreciated
Thanks in advance and sorry for my english.
Best regards.
Sure it's possible, if I understand you correctly. All you need is just implement your own TextEdit derived from QTextEdit and reimplement paintEvent()
For example:
QMyTextEdit.h
class QMyTextEdit : public QTextEdit
{
public:
QMyTextEdit(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent * event);
};
QMyTextEdit.cpp
QMyTextEdit::QMyTextEdit(QWidget *parent) :
QTextEdit(parent)
{
}
void QMyTextEdit::paintEvent(QPaintEvent *event)
{
QTextEdit::paintEvent(event);
QPainter painter(viewport());
QPen pen;
pen.setColor(Qt::blue);
pen.setWidth(2);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing, true);
QPoint center = viewport()->rect().center();
painter.drawRect(center.x() - 10,center.y() - 10,20,20);
}
protected:
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->drawRect(2, 2, 10, 10);
}
Rectangle is not painting. But when paintSection removed it is painting. I need to draw rectangle after call base paintSection.
As it was answered in this your question, rect is an area your should paint at.
If you paint outside of this area your drawings might be erased by painting of other cells.
So use rect to draw a rect:
painter->drawRect(rect.adjusted(2, 2, -2 , -2));
You need to protect the painter across the call to super, which modifies it. Try this:
painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore();
Also, as Ezee noted, you should be using the rect passed in as the basis for the coordinates you draw at; as suggested in that answer, something like:
painter->drawRect(rect.adjusted(2, 2, -2 , -2));
I am currently using a QLabel to do this, but this seems to be rather slow:
void Widget::sl_updateLiveStreamLabel(spImageHolder_t _imageHolderShPtr) //slot
{
QImage * imgPtr = _imageHolderShPtr->getImagePtr();
m_liveStreamLabel.setPixmap( QPixmap::fromImage(*imgPtr).scaled(this->size(), Qt::KeepAspectRatio, Qt::FastTransformation) );
m_liveStreamLabel.adjustSize();
}
Here I am generating a new QPixmap object for each new image that arrives. Since QPixmap operations are restricted to the GUI Thread, this also makes the GUI feel poorly responsive.
I've seen there are already some discussions on this, most of them advising to use QGraphicsView or QGLWidget, but I have not been able to find a quick example how to properly use those, which would be what I am looking for.
I'd appreciate any help.
QPixmap::fromImage is not the only problem. Using QPixmap::scaled or QImage::scaled also should be avoided. However you can't display QImage directly in QLabel or QGraphicsView. Here is my class that display QImage directly and scales it to the size of the widget:
Header:
class ImageDisplay : public QWidget {
Q_OBJECT
public:
ImageDisplay(QWidget* parent = 0);
void setImage(QImage* image);
private:
QImage* m_image;
protected:
void paintEvent(QPaintEvent* event);
};
Source:
ImageDisplay::ImageDisplay(QWidget *parent) : QWidget(parent) {
m_image = 0;
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}
void ImageDisplay::setImage(QImage *image) {
m_image = image;
repaint();
}
void ImageDisplay::paintEvent(QPaintEvent*) {
if (!m_image) { return; }
QPainter painter(this);
painter.drawImage(rect(), *m_image, m_image->rect());
}
I tested it on 3000x3000 image scaled down to 600x600 size. It gives 40 FPS, while QLabel and QGraphicsView (even with fast image transformation enabled) gives 15 FPS.
Setting up a QGraphicsView and QGraphicsScene is quite straight-forward: -
int main( int argc, char **argv )
{
QApplication app(argc, argv);
// Create the scene and set its dimensions
QGraphicsScene scene;
scene.setSceneRect( 0.0, 0.0, 400.0, 400.0 );
// create an item that will hold an image
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(0);
// load an image and set it to the pixmapItem
QPixmap pixmap("pathToImage.png") // example filename pathToImage.png
item->setPixmap(pixmap);
// add the item to the scene
scene.addItem(item);
item->setPos(200,200); // set the item's position in the scene
// create a view to look into the scene
QGraphicsView view( &scene );
view.setRenderHints( QPainter::Antialiasing );
view.show();
return app.exec();
}
I recommend not use QLabel but write own class. Every call of setPixmap causes layout system to recalculate sizes of items and this can propagate to topmost parent (QMainWindow) and this is quite big overhead.
Conversion and scaling also is a bit costly.
Finally best approach is to use profiler to detect where is the biggest problem.