I'm trying to implement behavior similar Excel in a QTableView, where a border is painted around the entire current selection. I have tried this what feels like a hundred different ways and keep getting problems. I can draw the border easily enough, but remnants of the border are left whenever the selection changes. Here is one example I've tried in QTableView::paintEvent ...
void MyTableView::paintEvent(QPaintEvent* event)
{
// call QTableView's paint event first so we can draw over it
QTableView::paintEvent(event);
// activeSelection is a list of indexes that is updated in another function
// the function also calls QTableView::repaint whenever this list changes
// in an attempt to erase the previously drawn border
if(!activeSelection.size())
return;
QRect rect = visualRect(activeSelection.at(0)) |
visualRect(activeSelection.at(activeSelection.size() - 1));
// temporarily draw smaller border so it doesn't lie on the grid lines
rect.adjust(4, 4, -4, -4);
QPen pen(Qt::black, 2);
QPainter painter(viewport());
painter.setPen(pen);
painter.drawRect(rect);
}
That code produces results such as this
I would love any suggestions on how to make this run more smoothly. I had tried doing this in the delegate, but then the delegate needs to know all the indexes that are selected and it can't paint over the grid lines drawn by the QTableView. Plus, my table class needs to know where the border has been drawn.
try to call update(); in your selectionChanged function. this will slow out your implementation, but will remove garbage.
Related
I'm trying to make my widget paint a rectangle with every paintEvent it receives. The rectangles are supposed to increase in size by 1px at a time, filling a square. What I get, however, is only the latest (and largest) rectangle.
void TestClass::paintEvent(QPaintEvent* e){
static int size = 1;
QStylePainter painter(this);
painter.setPen(Qt::blue);
painter.drawRect(QRect(50, 50, size, size));
size++;
}
I don't understand why it would be that way. I expected the painter to just paint on top of what is already there. Instead it seems to delete the previously drawn rectangle, leaving me with a single rectangle at any time. Any ideas?
setAutoFillBackground(true/false) does not change anything but the color of the background.
To evoke a paintEvent I update() inside mousePressEvent(). So my rectangles grow with every click.
Thanks a lot.
So, to answer my own question, I found out that:
(1) update(QRect area) erases the area specified by its argument before doing anything else. Calling update without argument erases the whole widget area.
(2) The area that has been cleared is the only area that any painting is done on, even if your paintEvent() looks to paint somewhere else. The untouched part of the widget is not affected.
For example, consider this paintEvent().
void myWidget::paintEvent(QPaintEvent* e){
QPainter painter(this);
static int counter = 1;
if (counter % 2){
painter.fillRect(0, 0, 199, 199, Qt::blue); //Fill with blue color
} else {
painter.fillRect(0, 0, 199, 199, Qt::green); //Fill with green color
}
counter++;
}
Calling update() repeatedly will toggle between green and blue color for every pixel. However, upon calling update(QRect(0, 0, 50, 50)) only the top left quarter of the widget's area will change its color, the other pixels will remain untouched, even if paintEvent(..) contains the instruction to always paint over the whole widget's area.
I do not know whether my answer is fully correct under any circumstances, but I expect more noobs to be confused about the relationship between update() and paintEvent() and so I'll offer this as a first aid.
Greets.
The QWidget::paintEvent docs state that
When the paint event occurs, the update region has normally been
erased, so you are painting on the widget's background.
So, probably that's the case in your example.
You're not guaranteed any sort of state preservation between invocations of paintEvent. That's the long and the short of it. The only correct semantics of your implementation of paintEvent are: paint (at least) the entire region that was passed to you, as in: touch every pixel, unless your widget was pre-cleared prior to painting.
I would like to know whether it is possible to shift part of a drawing by copying its pixels rather than redrawing it.
I work in an embedded environment, where performance is a key factor. We use Qt 4.8.
I have a set of real-time data points that I want to draw. I define the following class:
class SetOfDataPoints : public QGraphicsItem
{
public:
<constructor>
QRectF boundingRect() const { ... }
void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL) { ... }
<other methods>
};
At regular intervals, I read a new data point, add it to the instance of SetOfDataPoints, and shift the SetOfDataPoints to the left (by calling QGraphicsItem::moveBy() on the SetOfDataPoints), so the new data point becomes visible. As a result, SetOfDataPoints::paint() gets called, and in that method I have to draw the entire set of data points. The drawing currently consists only of line segments that connect the data points, but will become more elaborate in the future.
Now, it feels inefficient to redraw the whole set of data points, when most of the graph is actually just shifted to the left. I would like to shift the pixels of the unchanged part of the graph to the left, and draw only the one line segment that connects the last two points. At least I would like to try, and measure how much that improves performance.
Is there a way to do that in Qt 4.8?
This won't work in general:
Your item doesn't exist as any pixels until it's rendered. You don't know how many pixels it is drawn on, or even if there are any, since there are 0 or more views your scene is shown on, and the item might be visible to various extent on these views.
There are transformations applied to your item. It doesn't have to be rectangular.
Your item is composited with the items below it, unless it is completely opaque.
Your item, shown on a view, is composited with the backing store of the widget the view is on.
You can optimize for special cases. If your item is not cached, then it's always painted on a view, and the widget argument of paint will point to that widget. You then have direct access to the backing store, and the painter gives you the transformation used to go from item coordinates to the backing store's device coordinates. You can then inspect the path on the widget tree from the view to the window for opacity. If all intervening widgets paint opaque, and your item has an orientation-preserving transformation, you can certainly do a blit on the image, and redraw only a small part of the item.
If your item is cached, it should then be cached in device coordinates. You can do the blitting too, as you're painting on a pixmap. That pixmap is then composited onto the backing store of the window the view is on. There's a separate cache pixmap for each view.
When blitting, you must always recognize how much of the previous pixels are correct. For each view or cache pixmap, you should keep a region that is valid. That region initally should be empty.
In my QGraphicsView, I display a map. I need to display a horizontal ruler and a vertical ruler on the top edge and left edge respectfully.
The map needs to scrolled but the above rulers should be displayed at their fixed positions, but change their scale values.
I tried to implement this using drawForeground method. Due to the maps large size I only paint the visible area. So I need to update() every time scrolling is done. But this result sometimes flickers.
I feel it would be best to have separate layer like approach.
What is the best way to approach the problem?
The correct way to implement a ruler on the top and left is to derive from QGraphicsView, and then call in the constructor:
// add two rulers on top and left.
setViewportMargins(20, 20, 0, 0);
// add grid layout
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setSpacing(0);
gridLayout->setMargin(0);
// create rulers
hRuler = new Ruler(Qt::Horizontal);
vRuler = new Ruler(Qt::Vertical);
// add items to grid layout
QWidget* corner = new QWidget();
corner->setBackgroundRole(QPalette::Window);
corner->setFixedSize(20, 20);
gridLayout->addWidget(corner, 0, 0);
gridLayout->addWidget(hRuler, 0, 1);
gridLayout->addWidget(vRuler, 1, 0);
gridLayout->addWidget(viewport(), 1, 1);
// finally set layout
setLayout(gridLayout);
This solution was initially presented here, and it works very well. The result looks like this.
I just changed ViewportUpdateMode of the graphics view to FullViewportUpdate to get away from flicker.
You can use SmartViewportUpdate for somewhat good results also.
The downside is, during animations, this takes more process power.
I am new to QT. i am working on the Graphics.
i am using QWidget for drawing graphics(For drawing graphics in QWidget paint event). i need to draw background and foreground graphics. Background is fixed graphics. foregrounds i am drawing lines.
Each 100 millisecond i need to draw 20points. This drawing time is 8 sec. Total i need to draw 1600 points (total points represents the contentious line).
i am using QTimer to invoke this drawing in each 100ms. first few drawing drawn very fast. in the middle of the drawing it's become slow.
the problem is i need to draw all the foreground and background in each 100ms.
Please help me to fix the problem. if any one have sample code please provide. Thanks in advance.
Is there any way to draw only partial area ie. only particular modified region of the graphics?
QPainter-drawing can be very slow without hardware support. Using QGraphicsView won't help if all lines are visible, since it internally uses QPainter anyway.
If you just have to draw 20 new points (or lines) per update and per update background gets cleared so you have to render everything again, there are few things you could try:
1) Disable background autofill. See: QWidget::autoFillBackground
Add something like this to your widget init:
setAutoFillBackground(false);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setAttribute(Qt::WA_NoSystemBackground, true);
Now on the first update render background and first lines. For next updates just skip rendering background and render only new lines.
2) Use double buffering. For example, create QImage of the size of your widget.
.h
private:
QImage m_targetImage;
.cpp
// constructor
m_targetImage = QImage(width(), height(), QImage::Format_ARGB32);
// paint event
// draw to image
QPainter p;
p.begin(&m_targetImage);
static bool firstUpdate = true;
if (firstUpdate)
{
// draw background)
p.drawImage(...);
firstUpdate = false;
}
// draw latest lines
p.drawLines(....);
p.end();
// draw image in widget paint
QPainter painter;
painter.begin(this);
painter.drawImage(0, 0, m_targetImage);
painter.end();
3) Use QGLWidget if possible. Inherit your widget from QGLWidget instead of QWidget. This method doesn't work on all platforms and speed increase might not be enough. Also using OpenGL brings all kind of new problems.
I was just going through one code used to draw one chart. This code is written in the updateDisplayList function of the ItemRenderer of ColumnChart. I am not good at the graphics part of Flex. Can anybody please explain me what this code is doing? I can see the final output, but am not sure how is this achieved.
var rc:Rectangle = new Rectangle(0, 0, width , height);
var g:Graphics = graphics;
g.clear();
g.moveTo(rc.left,rc.top);
g.beginFill(fill);
g.lineTo(rc.right,rc.top);
g.lineTo(rc.right,rc.bottom);
g.lineTo(rc.left,rc.bottom);
g.lineTo(rc.left,rc.top);
g.endFill();
Regards, PK
That code is drawing a rectangle, albeit in a bit of a roundabout way.
The drawing api in flash uses a "draw head". I can't see any reason for using g instead of graphics other than to save some typing. g.clear() erases anything that has been drawn before.
g.moveTo(rc.left, rc.top) moves that into position, in this case the top left corner of the rectangle (0,0). g.beginFill(fill) starts a fill, nothing surprising there.
The g.lineTo(x, y) calls move the draw head around to the the four corners of the rectangle and finally g.endFill() completes the fill.
You can get the same result doing this:
graphics.clear();
graphics.beginFill(fill);
graphics.drawRect(0, 0, width , height);
// this last call is only needed if you're going to draw even more,
// if not you can omit that too
graphics.endFill();
It basically draws a rectangle.
//clear any existing drawings
g.clear();
Set the current drawing position to the top-left corner of the rectangle, which is 0, 0
g.moveTo(rc.left,rc.top);
//start filling with the color specified by `fill`
g.beginFill(fill);
Draw a line to top-right corner of the rectangle from the current location (which is top-left corner). The lineTo method updates the current location so that subsequent drawings start from the new point.
g.lineTo(rc.right,rc.top);
Draw the remaining sides of the rectangle:
g.lineTo(rc.right,rc.bottom);
g.lineTo(rc.left,rc.bottom);
g.lineTo(rc.left,rc.top);
//end the fill.
g.endFill();
Check out the livedocs page for Graphics class for more info.
All the visual components in Flex inherit directly/indirectly from the UIComponent class. The updateDisplayList method of UIComponent draws the object and/or sizes and positions its children. This is an advanced method that you might override when creating a subclass of UIComponent. When you override it in your child class, you should call super.updateDisplayList with the correct parameters to make sure that the base class components are properly updated.
Degrafa makes this kind of thing much easier.