To draw an ellipse in Qt, we can simply go QGraphicsScene->addEllipse();
Is there an equivalent method to draw an 'x'? And when redrawing a scene, how would you delete said 'x' (say, to move it)?
Use QGraphicsScene::addSimpleText(const QString &text, const QFont &font=QFont()) to draw the letter "x".
Related
This is my first time using Qt and I have to make a MSPaint equivalent with Qt. I am however having trouble with painting my lines. I can currently draw a line by clicking somewhere on the screen and releasing somewhere else, however when I draw a second line the previous line is erased. How could I keep the previously painted items when painting another item?
void Canvas::paintEvent(QPaintEvent *pe){
QWidget::paintEvent(pe);
QPainter p(this);
p.drawPicture(0,0,pic);
}
void Canvas::mousePressEvent(QMouseEvent *mp){
start = mp->pos();
}
void Canvas::mouseReleaseEvent(QMouseEvent *mr){
end = mr->pos();
addline();
}
void Canvas::addline()Q_DECL_OVERRIDE{
QPainter p(&pic);
p.drawLine(start,end);
p.end();
this->update();
}
Canvas is a class that derives QWidget, it has 2 QPoint attributes start and end.
Class body:
class Canvas : public QWidget{
Q_OBJECT
private:
QPoint start;
QPoint end;
QPicture pic;
public:
Canvas(){paint = false;setAttribute(Qt::WA_StaticContents);}
void addline();
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent( QMouseEvent * );
//void mouseMoveEvent( QMouseEvent * );
void mouseReleaseEvent( QMouseEvent * );
};
QPicture records QPainter commands. Also from its documentation you can read this:
Note that the list of painter commands is reset on each call to the
QPainter::begin() function.
And the QPainter constructor with a paint device does call begin(). So each time the old recorded commands are deleted.
It may sound tempting to use it, since it does say a few good things, for example, that it is resolution independent, but this is not how drawing applications work in reality. Switch to a QPixmap and your drawings will persist.
Also, don't forget to initialize the pixmap, because by default it will be empty and you will not be able to draw on it.
Canvas() : pic(width,height) {...}
Furthermore, if you would like the introduce the concept of brushes as in artistic brushes and not QBrush, you might want to look at this approach to draw the line.
EDIT: Note that you should be able to prevent QPicture from losing its content by not calling begin() on it more than once. If you create a painter, dedicated to only drawing on it at class scope, and call begin in the constructor, different recorded drawing operations should persist. But as their number increases it will take more and more time to draw the QPicture to your widget. You could come around that by using both a QPicture and a QPixmap, and draw to both, use the picture to record the actions and the pixmap to avoid continuously redrawing the picture, even though you will do double the work it will still be more efficient, while you still retain the possibility to use the picture to re-rasterize in a different resolution or save the drawing history. But I doubt QPicture will do well as your drawing application begins to take shape of an actual drawing application, for example when you start using pixmap brushe stencils and such.
I have my own QGraphicsScene and two QGraphicView’s. In the QGraphicsScene I use the
drawForeground(QPainter *painter, const QRectF &rect)
function to draw a grid. Now I want the grid only to be visible in the first, but not in the second view…is this possible?
The QGraphicsView is a window into a world (the QGraphicsScene). What you're asking would be like saying that it's raining when I look outside my windows, but can I have it only rain when I look through one of them?!
However, you can change the curtains(!), so override the function of the QGraphicsView, rather than the QGraphicsScene. I suggest using this: -
QGraphicsView::drawForeground(QPainter *, const QRectF &);
Apply this just to the view you want to change. With two views, you'll need a flag to set which of the views you want to apply this to. For example: -
void MyGraphicsView::drawForeground(QPainter* painter, const QRectF& rect)
{
QGrahicsView::drawForeground(painter, rect);
if(m_bDrawGrid)
DrawGrid();
}
I use QGraphicsView, QGrapichsScene and QGraphicsItem for drawing some charts. I have implemented QGraphicsItem::paint function for drawing text (values of charts), but it is not being called every time that is must draw something new. My paint function
void CLabelItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget = 0*/)
{
if ( GetValue() < 0 )
{
return;
}
painter->drawText(boundingRect(), m_value.toString());
}
So my question is - why QGraphicsItem::paint can be not called and how may I make it to be called?
You need to call QGraphicsItem::update() from the function that modify the m_value variable to trigger a repaint.
In general with Qt graphics items are drawn when paintEvent is called, and then you can handle painting inside a paint function.
QGraphicsPixmapItem, like QGraphicsItem, has a method update(x0, y0, width, height), in order to redraw a pixmap only partly on a QGraphicsScene. Calling this will schedule a paint() (in Qt's event loop) on the QGraphicsItem, and after this paint() is executed the boundingbox (x,y,width,height) will be redrawn to the QGraphcisScene.
The unfortunate part is that there is no way to schedule the paint-event with a boundingbox, meaning that QGraphicsPixmapItem::paint() is forced to repaint the whole QPixmap, therefore reimplementing this paint()-method in a subclass gives no way to only partly update the QPixmap, therefore making a small (local) update to the QPixmap unacceptably slow.
Such a subclass would look something like this:
class LocallyUdatablePixmapItem : public QGraphicsPixmapItem {
private:
QImage ℑ
public:
LocallyUdatablePixmapItem(QImage &img) : QGraphicsPixmapItem(), image(img) {}
paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QStyle *widget) {
//locall update, unfortunately without a boundig box :( therefore -> slow
}
};
Another option would be to keep the 'internal QPixmap' of the QGraphicsPixmapItem, and draw the QImage to it partly, like this:
//some initialization of variables
QGraphicsScene scene = ...;
QImage img = ...; //some image data that I wish to manipulate from time to time
QPixmap pixmap = QPixmap::fromImage(this->shown);
QPainter painter = new QPainter(&this->pixmap);
QGraphicsPixmapItem item = this->addPixmap(this->pixmap);
item->setPixmap(this->pixmap);
//this should not matter, but actually it does, as I will explain shortly
//delete painter;
//painter = new QPainter(item->pixmap());
//For some reason I decide to update (manimulate) img within a small boundigbox
int x0, y0, width, height; //bounding box, assume they are set to whatever is appropriate for the previous update
painter->drawImage (x0, y0, img, x0, y0, width, height);
//now the pixmap is updated, unfortunately the item is not. This does not affect it:
item->update(x0, y0, width, height);
//nor does this:
item->update();
//but this, which makes the whole thing slow, does:
item.setPixmap(&pixmap);
Given I that I needed to set the pixmap to fix it, I assumed it was somehow not set in the initialization, therefore uncommenting the mentioned lines before seemed like a nice idea. Unfortunately, the drawImage() call then segfaults into:
QPaintDevice: Cannot destroy paint device that is being painted
I would like to have an alternative to the "item.setPixmap(&pixmap);", which does not redraw the whole thing, but does work nicely. Any input is very well appreciated :)
Before I propose a solution, a few thoughts:
First, the Graphics View framework is intended to be a solution for displaying many graphic objects, so one large image isn't really that fitting. Of course, I realize your example is probably just a contrived one, so this point might not really apply. Second, since the framework is very transform-centric, it might not make sense to only redraw parts of a QGraphicsItem unless all the transforms are identity, there is no scrolling, etc.
Anyways, if you only want to draw part of a QGraphicsItem, you could simply store the rect that needs to be updated, and access it from inside your paint() method. For example:
CustomItem::setPaintRect(const QRectF &rect)
{
paintRect = rect;
update();
}
CustomItem::paint(QPainter *painter /* etc. */)
{
painter->fillRect(paintRect, brush);
}
So it appears that Qt4 doesn't let you draw on windows outside of a paint event. I have a lot of code that expects to be able to in order to draw rubber band lines (generic drawing code for a particular, proprietary interface that I then implement in the given UI). I've read about the pixmap method, it would be a lot of work and I don't think it's really what I want.
Is there a workaround that allows me to do what I want anyway? I just need to draw XOR bands on the screen.
Tried the WA_PaintOutsidePaintEvent flag. Then I saw the bit that says it doesn't work on Windows.
In modern compositing desktops window painting needs to be synchronized by the window manager so that the alpha blending and other effects can be applied, in order, to the correct back buffers - the result of which is then flipped onto the screen to allow tear-free window animations.
Invoking painting operations out-of-band of this process - while supported for legacy reasons on the underlying platforms - would subvert this process and cause a number of very non optimal code paths to be executed.
Basically, when you have painting to do on a window: Call the invalidate function to schedule the painting soon, and paint during the paint event.
Just paint to a QPixmap, and copy it to the real widget in the paintEvent. This is the only standard way. You shouldn't try to workaround it.
Seems like if you could get access to the Hwnd of the window in question, you could paint on that surface. Otherwise, I'm not sure. If by pixmap method you mean something like this, I don't think it's a bad solution:
m_composed_image = QImage(size, QImage::Format_ARGB32);
m_composed_image.setDotsPerMeterX(dpm);
m_composed_image.setDotsPerMeterY(dpm);
m_composed_image.fill(Qt::transparent);
//paint all image data onto new image
QPainter painter(&m_composed_image);
painter.drawImage(QPoint(0, 0), m_alignment_image);
As it's mentioned in one of the answers, The best way to do it will be to make a pixmap buffer. The painting works will be done in the buffer and when it's done, repaint() will be scheduled. And the paintEvent() function just paints the widget by copying the pixel buffer
I was trying to draw a circle on a widget area after user inputs values and pushes a button. This was my solution. connecting the drawCircle() slot to the clicked() signal.
class PaintHelper : public QWidget
{
Q_OBJECT
private:
QPixmap *buffer;
public:
explicit PaintHelper(QWidget *parent = 0) : QWidget(parent)
{
buffer=new QPixmap(350,250);// this is the fixe width of this widget so
buffer->fill(Qt::cyan);
}
signals:
public slots:
void drawCircle(int cx, int cy, int r){
QPainter painter(buffer);
painter.setBrush(QBrush(QColor(0,0,255)));
// A part of mid-point algorithm to draw 1/8 pacrt of circle
int x1=0,y1=r;
int p=1-r;
for(int i=0;y1>=x1;i++){
painter.drawPoint(x1+cx,y1+cy);
x1++;
if(p>0){
p+=3+x1;
}
else{
y1--;
p+=2*x1-2*y1;
p++;
}
}
this->repaint();
}
// QWidget interface
protected:
void paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawPixmap(0,0,*buffer);
}
};