How to determine the visible area of QQuickPaintedItem? - qt

Is there an equivalent of Win32 GetUpdateRect function in QML? For example, if a control derived from QQuickPaintedItem is inside Flickable is there a way to get the smallest rectangle that should be redrawn in
QQuickPaintedItem::paint(QPainter *painter)
?

When you call QQuickPaintedItem::update(), the given QRect parameter will be set as clip bounding rect for your QPainter in QQuickPaintedItem::paint.
So, if you want to redraw a specific region of your item, just call QQuickPaintedItem::update() with the rect you want to repaint.
item->update(QRect(10, 20, 30, 20));
void CharacterItem::paint(QPainter *painter)
{
qDebug() << painter->clipBoundingRect() << painter->clipPath();
}
It will display:
QRectF(10,20 30x20)
QPainterPath: Element count=5
-> MoveTo(x=10, y=20)
-> LineTo(x=40, y=20)
-> LineTo(x=40, y=40)
-> LineTo(x=10, y=40)
-> LineTo(x=10, y=20)

Related

How to get minimize/restore/close rectangle?

I want to draw some text in the end of menu bar like this
But when the child window is maximized in the MDI project it looks like this
I need to fix this output.
I want to check if the active child is maximized and if it is I want to get minimize/restore/close rectangle to get them total width.
How can I get active child window and how can I get it's rectangle of buttons?
// in yourMainWindow.cpp
...
auto child = mdiArea->addSubWindow(yourWidget);
connect(child, &QMdiSubWindow::windowStateChanged, this, &yourMainWindow::yourSlot);
...
void yourMainWindow::yourSlot(Qt::WindowStates oldState, Qt::WindowStates newState)
{
if (newState.testFlag(Qt::WindowMaximized)) {
auto child = qobject_cast<QMdiSubWindow *>(sender());
if (!child)
return;
QStyleOptionComplex opt;
opt.initFrom(child);
auto size = child->style()->sizeFromContents(QStyle::CT_MdiControls, &opt, QSize(100, 20));
qDebug() << size;
}
}

Dragged QGraphicsItem not visible in items() function

I have created a QGraphicsScene scene and added some graphcis items (lines, rectangles) etc to the scene.
I can loop through them using this list :
QList<QGraphicsItem*> all = items();
I enabled movement for these items and I am able to drag them by click selecting them. But after an element has been dragged, it stops showing up in the call to items() function of the QGraphicsScene.
QList<QGraphicsItem*> all = items();
None of the dragged items show up in the above list, while non-dragged ones do show up.
Does dragging the QGraphicScene elements change their parent ? or any other reason somebody could suggest for such an issue ?
{P.S. Code is too very big to share}
Edit 1 :
I am using the flags QGraphicsItem::ItemIsSelectable and QGraphicsItem::ItemIsMovable for making the items movable.
foreach(QGraphicsItem* itemInVisualScene, items())
{
itemInVisualScene->setFlag(QGraphicsItem::ItemIsSelectable, itemsMovable);
itemInVisualScene->setFlag(QGraphicsItem::ItemIsMovable, itemsMovable);
}
By default I add few rectangle to the scene. Then in the 'move mode' I drag them around. Then in the 'add mode' I click on screen to add new rectangles. I have written a logic to check if I am clicking on any existing drawn rectangle :
void Scene::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
if(eDrawLines == sceneMode)
{
dragBeginPoint = event->scenePos();
dragEndPoint = dragBeginPoint;
QList<QGraphicsItem*> all = items();
for (int i = 0; i < all.size(); i++)
{
QGraphicsItem *gi = all[i];
// Clicked point lies inside existing rect
if( QGraphicsRectItem::Type == gi->type() && gi->contains(dragBeginPoint))
{
std::cout << "Pressed inside existing rect" << std::endl;
return;
}
}
std::cout << "Point not found, add new rectangle" << std::endl;
}
QGraphicsScene::mousePressEvent(event);
}
This adding of rectangles work fine for rects which were not dragged in the 'move mode'. But rects which were moved do not seem to recognize the click anymore. My control comes out of the loop even when I click on an existing rectangle which was dragged earlier.
QGraphicsItem's transform is changed after dragging and therefore need to transform the point to item's local coordinates.
gi->contains(gi->mapFromScene(dragBeginPoint))
To convert or get item's position in scene coordinates, use
gi->mapToScene(0,0) or gi->scenePos()

How can I show/hide background drawing on QGraphicsScene or QGraphicsView?

I would like to have certain things drawn on QGraphicsScene, but not be QGraphicsItem (it would interfere with the processing of the QGraphicsItem collection).
Example: a scene bounding rectangle, a grid
I am overriding the drawBackground(QPainter *painter, const QRectF &rect) for that purpose. (I should subclass the scene... )
void MyView::showHideBounds()
{
m_showBackgroundBounds = !m_showBackgroundBounds;
// can't triger an update ???
update(); // neither does anything
viewport()->update();
}
void MyView::drawBackground(QPainter *painter, const QRectF &rect)
{
QPen pen;
if(m_showBackgroundBounds)
pen = QPen(QColor(0, 0, 0), 10, Qt::PenStyle(Qt::SolidLine));
else
pen = QPen(QColor(255, 255, 255), 10, Qt::PenStyle(Qt::SolidLine));
painter->setPen(pen);
painter->drawRect(QRect(QPoint(-scene()->sceneRect().size().toSize().width()/2,
-scene()->sceneRect().size().toSize().height()/2),
scene()->sceneRect().size().toSize()));
}
I would like the option to show/hide either the bounding rectangle or the grid.
The only thing I can think of is paint over them with the color of the background brush ? Is there any other option ?
As I have written it above, it works - except I need user action on items (or a zoom or some other scene changing action) to trigger refresh, or call an update... (the function showHideBounds doesn't - not sure how to make it force a refresh)
I would call the drawBackground from the showHideBounds function - but I don't know how to get the painter
[Also, the drawBackground seems to be drawn automatically... how can I give it the rect argument it needs ? (it seems if I draw the rect it does draw the scene rectangle but I only see the right and bottom edges)]
In order to redraw a particular section of scene, you can call
QGraphicsScene->invalidate(rect_to_redraw, Backgroundlayer)
Note that if drawBackground(*painter, rect) paints over area outside rect, it will not update automatically. In that case invalidate has to be called with appropriate rect parameters.

Using QGraphicsScene ItemAt() to detect QGraphicsLineItem

I have a QGraphicsScene which stores QGraphicsLinesItems and QGraphicsRectItems.
I am using the QGraphicsScene method to itemsAt() and I pass through x and y co ordiantes to return the QGraphicsItem and I use the following:
QPointF getItemPos= this->mapToScene(this->mapFromGlobal(QCursor::pos()));
QGraphicsItem *itm = scene->itemAt(getItemPos.x(),getItemPos.y());
QGraphicsLineItem *lineItm;
QGraphicsRectItem *rectItm;
if((lineItm = dynamic_cast<QGraphicsLineItem*>(itm))){
// do stuff with as_pnedge
qDebug("Line");
}else if((rectItm = dynamic_cast<QGraphicsRectItem*>(itm))){
// do stuff with as_pnitem
qDebug("Rect");
}
else
{
qDebug("Select Item");
}
The issue I am having is that QGraphicsRectItem is returned fine and can be detected. But no matter where I click around the QGraphicsLineItems it can never detect and return the item. Any assistance would be very much appreciated.
If your line uses a cosmetic pen (width of zero), it means that the shape() method will return a zero width QPainterPath (source code, search for "qt_graphicsItem_shapeFromPath").
You will have to derive from QGraphicsLineItem and reimplement shape() to clamp the minimum pen width for QPainterPathStroker to something reasonable.

Draw shift when drawing on QGraphicsView

I have a problem.
I have a class that inherits QGraphicsView, let's suppose it called "g". I reimplemented mousePressEvent method, the code of that method is:
void GraphWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::MiddleButton)
createNode(event->pos().x(), event->pos().y());
update();
QGraphicsView::mousePressEvent(event);
}
The code of createNode method is:
Node *GraphWidget::createNode(qreal x, qreal y, int id)
{
Node* node = new Node(this);
scene()->addItem(node);
node->setPos(x, y);
return node;
}
I use this class "g" as a central widget in my mainwindow class. So it's working like QGraphicsView.
The problem is when I press the middlebutton on the "draw area" - the point is created but not in the place when I clicked - the point is shifted. Why? So when I try to draw those points by pressing the middlebutton - all them are drawed on the wrong place (not under my cursor, they are drawn lefter left and above my cursor position).
How can I fix that?
QGraphicsView and QGraphicsScene have different coordinate spaces. When you call setPos, it should be in scene coordinates, but since your in the mouse event of the view, your x and y are going to be in view coordinates.
I suspect that mapping your x and y coordinates to the scene space should fix the issue:
node->setPos( mapToScene(QPoint(x, y) );

Resources