Qt: Why does my paintEvent() erase everything before doing its job? - qt

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.

Related

How to shift the pixels of part of a QGraphicsItem?

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.

Add QRadioButton into QWidget without layout

I am trying to do something that I thought it would be terribly easy : Adding QRadioButton into my QWizardPage without any layout.
The following code that adds QRadioButton is being called when user clicks button Next (signal currentIdChanged calls the code):
int newPositionY = 0;
for (vector<Events::VCS::PnPDevice>::const_iterator it=m_devices.begin(); it!=m_devices.end(); it++)
{
if (it->type == Events::VCS::HEADSET)
{
//add a radio button
stringstream text;
text << (it->name) << " " << (it->serialNumber);
QRadioButton* radioButton = new QRadioButton(ui.wpSINGLE_USER);
radioButton->setGeometry(50, 20 + newPositionY, 260, 40);
radioButton->setText(text.str().c_str());
newPositionY = newPositionY + 40
}
}
}
I added this little piece of code to see what is going on with my QRadioButton
QList<QRadioButton*> listButton = ui.wpSINGLE_USER->findChildren<QRadioButton*>();
int size = listButton.size();
QRect rect1 = listButton[0]->rect();
QRect rect2 = listButton[1]->rect();
I then realized that it seems that the problem might be QRect.
The value for rect1 and rect2 are erroneous. rect1 = (0, 0, 259, 39) and rect2 = (0, 0, 259, 39) Correct value for rect1 should be (50, 20, 260, 40) andn for rect2 (50, 60, 260, 40)
So, what is the problem, how to add QRadioButton into QWidget without layout?
EDIT
That is strange, if, instead of adding the QRadioButton into the QWizardPage when user clicks button next I am adding them into the QWizard contructor, it works.
Can somebody tell me why I am not able to add QRadioButton into my QWizardPage into my slot function?
Thanks
QRect is behaving properly. You should check the geometry(), not the rect().
http://doc-snapshot.qt-project.org/4.8/qwidget.html#geometry-prop
http://doc-snapshot.qt-project.org/4.8/qwidget.html#rect-prop
http://doc-snapshot.qt-project.org/4.8/application-windows.html#window-geometry
Here are some QWizard examples that are worth studying...
http://qt-project.org/doc/qt-4.8/dialogs-trivialwizard.html
http://qt-project.org/doc/qt-4.8/dialogs-classwizard.html
http://qt-project.org/doc/qt-4.8/dialogs-licensewizard.html
Generally speaking...
Using Layouts, makes your life easier. There is a learning curve and the code looks funny at first, but you will be grateful that you did in the long run. If at all possible, I avoid using Qt Designer, and I use layouts, and nested layouts to position everything. Add in stretches and spacers and anything else you need. It's great. And then if you need something to show or hide later, you nest that in a QWidget, and you show and hide that widget to adjust when it is visible.
http://qt-project.org/doc/qt-4.8/layout.html
http://qt-project.org/doc/qt-4.8/tutorials-widgets-windowlayout.html
It might be that when a QRadioButton is added without a layout, it isn't updating your widget.
If you change what is in your widget, you need to post an update() event for it to be reflected.
http://doc-snapshot.qt-project.org/4.8/qwidget.html#update
Also when you don't use layouts, the layering and positioning can go a little crazy. Make sure that you are not drawing something else on top of the location where the button is added.
Here is some more things to keep in mind. Though it looks like you may be taking care of some of this already...
Another issue that can happen is if you add a QRadioButton not into a layout and forget to parent it, it probably won't get added at all. Parenting of QObjects is handled nicely by layouts, too.
And finally, you need to make sure that your button exists for the scope of your parent widget. So if you declare it in a slot, and it has a local scope, by the time you leave the slot, it is gone. (You put it on the heap, so it should be okay).
So make it a member variable for your subclassed QWizardPage, and make it a pointer. Qt handles the parenting of objects well, and the deletion correctly if they are on the object tree properly.
http://qt-project.org/doc/qt-4.8/objecttrees.html
Also be sure to look at using a 'QGroupBox' in your layout when you are using QRadioButtons. This handles the exclusive attribute well.
http://doc-snapshot.qt-project.org/4.8/widgets-groupbox.html
http://doc-snapshot.qt-project.org/4.8/qradiobutton.html#details
Hope that helps.

Qt QTableView draw border around active cells

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.

Qt mouse cursor transparency

I would like to change the stock cursor with a translucent one, a simple filled circle, of various sizes, depending on the level of zoom in the underlying widget (say, RGBA = 200, 200, 200, 128).
Is this at all possible with Qt? If not, is it a limitation in Qt or the underlying libs? Do you have suggestions as to how this could be accomplished by other means, e.g., hiding the cursor and overlaying a transparent pixmap at the cursor position (albeit slower)? TIA
QCursor can take a QPixmap which does support alpha channel. So I don't see why it can't be done.
I just figured this out for a project of my own. I did it with this code in the constructor of the relevant widget:
m_LPixmap = new QPixmap(32,32);
m_LPixmap->fill(Qt::transparent); // Otherwise you get a black background :(
QPainter painter(m_LPixmap);
QColor red(255,0,0,128);
painter.setPen(Qt::NoPen); // Otherwise you get an thin black border
painter.setBrush(red);
painter.drawEllipse(0,0,32,32);
m_Cursor = QCursor(*m_LPixmap);
setCursor(m_Cursor);

Optimized Line drawing in QT

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.

Resources