how to center a Qt mainform on the screen? - qt

I've tried these in my mainform's constructor:
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
move(desktopRect.center() - frameGeometry().center());
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
move(desktopRect.center() - rect().center());
but both put the bottom right corner of the form at about the center of the screen, instead of centering the form. Any ideas?

I've tried these in my mainform's constructor
That's likely the problem. You probably don't have valid geometry information at this point because the object isn't visible.
When the object is first constructed, it's essentially positioned at (0,0) with it's expected (width,height), as such:
frame geometry at construction: QRect(0,0 639x479)
But, after being shown:
frame geometry rect: QRect(476,337 968x507)
Thus, you can't yet rely on your frameGeometry() information.
EDIT: With that said, I presume you can easily move it as desired, but for completeness I'm dropping in Patrice's code which doesn't depend on the frame geometry information:
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
QPoint center = desktopRect.center();
move(center.x() - width() * 0.5, center.y() - height() * 0.5);

The move function (see QWidget doc) takes one QPoint or two int as parameter. This corresponds to the coordinates of the top-left corner of your Widget (relative to its parent; Here OS Desktop).
Try:
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
QPoint center = desktopRect.center();
move(center.x()-width*0.5, center.y()-height*0.5);

availableGeometry() is deprecated.
move(pos() + (QGuiApplication::primaryScreen()->geometry().center() - geometry().center()));

#include <QStyle>
#include <QDesktopWidget>
window->setGeometry(
QStyle::alignedRect(
Qt::LeftToRight,
Qt::AlignCenter,
window->size(),
qApp->desktop()->availableGeometry()
)
);
https://wiki.qt.io/How_to_Center_a_Window_on_the_Screen

move(QGuiApplication::primaryScreen()->geometry().center() - rect().center());

PyQT Python Version
# Center Window
desktopRect = QApplication.desktop().availableGeometry(self.window)
center = desktopRect.center();
self.window.move(center.x()-self.window.width() * 0.5,
center.y()-self.window.height() * 0.5);

Another solution, assuming the window in question is 800×800:
QRect rec = QApplication::desktop()->availableGeometry();
move(QPoint((rec.width()-800)/2, (rec.height()-800)/2));

Related

How to size the texture to occupy only a portion of a QQuickItem UI

I have overriden updatePaintNode in the following way to draw an OpenGL texture on a QQuickItem derived class called MyQQuickItem here.
QSGNode *MyQQuickItem::updatePaintNode(QSGNode * oldNode, QQuickItem::UpdatePaintNodeData * /*updatePaintNodeData*/)
{
QSGSimpleTextureNode * textureNode = static_cast<QSGSimpleTextureNode *>(oldNode);
if (!textureNode) {
textureNode = new QSGSimpleTextureNode();
}
QSize size(800, 800);
// myTextureId is a GLuint here
textureNode.reset(window()->createTextureFromId(myTextureId, size));
textureNode->setTexture(my_texture);
textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial);
QSizeF myiewport = boundingRect().size();
qreal xOffset = 0;
qreal yOffset = 10;
textureNode->setRect(xOffset, yOffset, myViewport.width(), myViewport.height());
return textureNode;
}
This renders the texture content well but covers the whole of my MyQQuickItem UI.
How can reduce the bottom margin of the texture to say fit 80% of the height of MyQQuickItem.
I want to render the texture to a portion of MyQQuickItem & leave the rest blank or black? Is that possible within updatePaintNode.
Note that the texture size is not the UI window size here. My texture size is 800 by 800. Whereas the UI window size is different and depends on the screen.
I found the answer to this:
Changing myViewport.height() gives the required end in Y direction one wishes to set. Similarly, changing myViewport.width() gives the required end in X direction one wishes to set.
4 parameters in TextureNode's setRect can stretch & fit the texture in the way one wishes within a portion of the QQuickItem.

Preventing font scale in QGraphicsItem

I am using QGraphicsTextItem to paint the text on the scene. Text is painted along the path (QGraphicsPathItem), wich is parent of my QGraphicsTextItem - so the text rotation is changed to be along the path element and is sticked to it while zooming the view. But the font size of QGraphicsTextItem is also changing while zooming the view - this is what I am trying to avoid. Of I set QGraphicsItem::ItemIgnoresTransformations flag to the QGraphicsTextItem it stops rotating while it's parent (QGraphicsPathItem) does.
I do understand that I have to re-implement QGraphicsTextItem::paint function, but I am stuck with the coordination system. Here is the code (Label class inherits public QGraphicsTextItem):
void Label::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
{
// Store current position and rotation
QPointF position = pos();
qreal angle = rotation();
// Store current transformation matrix
QTransform transform = painter->worldTransform();
// Reset painter transformation
painter->setTransform( QTransform() );
// Rotate painter to the stored angle
painter->rotate( angle );
// Draw the text
painter->drawText( mapToScene( position ), toPlainText() );
// Restore transformation matrix
painter->setTransform( transform );
}
The position (and rotation) of my text on the screen is unpredictable :(
What am I doing wrong? Thank you very much in advance.
I solved a problem this way - for drawing a line/circle/rectangle/path, which I want to be transformed, I use an appropriate QGraphicsLine/Ellipse/Rect/PathItem. For drawing the text (which I do NOT want to be transformed) I use QGraphicsSimpleTextItem. I set text's flag to ignore transormations and set it's parent to Line/Ellipse/Rect/Path item. The Line/Ellipse/Rect/Path item transforms, but text does not - this is what I wanted. I can also rotate text and set it's position.
Thank you very much for answers.
The following solution worked perfectly for me:
void MyDerivedQGraphicsItem::paint(QPainter *painter, const StyleOptionGraphicsItem *option, QWidget *widget)
{
double scaleValue = scale()/painter->transform().m11();
painter->save();
painter->scale(scaleValue, scaleValue);
painter->drawText(...);
painter->restore();
...
}
We can also multiply the scaleValue by other mesures we want to keep its size constant outside the save/restore environment.
QPointF ref(500, 500);
QPointF vector = scaleValue * QPointF(100, 100);
painter->drawLine(ref+vector, ref-vector);
I had this issue once. Instead of ignoring transformations, you need to scale down the items you don't want to be zoomed in in your zoom-in function.
When you zoom in, if you change the scale by ds for example, scale the items by 1.0 / ds
You might need to change their positions though.
I hope this helps.
Edit: I hope I understood the question right.

Customizing shape of bounding rect

I am drawing a line using mouse clicks. The line is drawn using paint function as:
painter->drawLine(start_p, end_p);
The bounding rect of line is defined as:
QRectF Line::boundingRect() const
{
// bounding rectangle for line
return QRectF(start_p, end_p).normalized();
}
This shows the line painted. I get the bounding rect for this as shown:
I want to have the bounding rect according to the shape of the item, something like:
How to achieve this?
Edit
While selecting any of the overlapping lines, the one with bounding rect on top is selected(see figure below). Even making use of setZValue won't work here.
I want to implement this by minimizing the bounding rect to the shape of line.
If you have an item that is not shaped like a rectangle, or is a rotated rectangle use QGraphicsItem::shape.
This function should return a QPainterPath. You should be able to create your path by using QPainterPath::addPolygon.
Here is a small example:
QPainterPath Item::shape() const
{
QPainterPath path;
QPolygon polygon;
polygon << QPoint(0, 0);
polygon << QPoint(5, 5);
polygon << QPoint(width, height);
polygon << QPoint(width - 5, height - 5);
path.addPolygon(polygon);
return path;
}
You of course should calculate your points inside the path in a different way, but you get the point. Now when you click on an item, it will only select it if the click happened inside the shape defined by the QPainterPath.
If you ever need to make curvy lines, you can use QPainterPathStroker::createStroke as suggested by cmannett85.
There are two relevant functions in a QGraphicsItem that you should be interested in. The first is boundingRect. This, as you probably realise is a rectangle which encompasses the whole item. Qt uses this for such things as quickly calculating how much of an item is visible and simple item collision.
That's great if you have rectangular items; you can just override boundingRect() in any items you inherit from QGraphicsItem or QGraphicsObject.
If you have a shape that isn't regular and you want to do things such as collision with an item's shape, then theshape() function needs overriding too in your class.
This returns a QPainterPath, so you can do something like this: -
QPainterPath Line::shape()
{
QRectF rect(start_p, end_p).normalized();
// increase the rect beyond the width of the line
rect.adjust(-2, -2, 2, 2);
QPainterPath path;
path.addRect(rect);
return path; // return the item's defined shape
}
Now, you can use a painter to draw the shape() item, instead of the boundingRect() and collision will work as expected.
boundingRect is always used for optimize painting process of of scene. So you have have no room for manipulation here.
BUT if you want change area for mouse interaction there is shape method. By default this method returns QPainterPath rectangle received from boundingRect method.
So just override this method and provide desired shape.
QPainterPath YourGraphicsItem::shape() const {
static const qreal kClickTolerance = 10;
QPointF vec = end_p-start_p;
vec = vec*(kClickTolerance/qSqrt(QPointF::dotProduct(vec, vec)));
QPointF orthogonal(vec.y(), -vec.x());
QPainterPath result(start_p-vec+orthogonal);
result.lineTo(start_p-vec-orthogonal);
result.lineTo(end_p+vec-orthogonal);
result.lineTo(end_p+vec+orthogonal);
result.closeSubpath();
return result;
}
You must draw yourself bounding if you want some thing like this. let Qt have it's QRect for bounding and define your new QRect dependent to the corner of previous QRect, top-left and bottom-right. for example if the top-left corner is (2,2) your new QRect top-left is (1,2) and top-right is (2,1) and ....

How to move a QGraphicsItem when scrolling the QGraphicsView?

I have a QGraphicsScene and a QGraphicsView that displays it. I need to add a QGraphicsItem to the scene and keep it at the same position even when I scroll the view. I tried overriding view's scrollContentsBy() method as follows but it didn't do the trick.
void FETimelineView::scrollContentsBy( int dx, int dy )
{
QGraphicsView::scrollContentsBy(dx, dy);
QRectF oRect = p_CatBar->rect();
p_CatBar->setPos( oRect.x() + dx, oRect.y() + dy );
}
Btw, FETimelineView is my QGraphicsView and p_CatBar is of type QGraphicsItem. Please help, thanks in advance.
Rather than moving it by the scrolled amount, You can get the position that you want it to be relative to the view and then set it directly according to that. So it would be something like this: -
// Assuming that the Graphics Item top left needs to be at 50,50
// and has a width and height of 30,20
void FETimelineView::scrollContentsBy( int dx, int dy )
{
QGraphicsView::scrollContentsBy(dx, dy);
// get the item's view position in scene coordinates
QRect scenePos = mapToScene(QRect(50, 50, 30, 20));
p_CatBar->setPos(scenePos);
}
I think the easier way it's actually the inverse of what you ask: try setting the flag ItemIgnoresTransformations in QGraphicsItem::GraphicsItemFlags.
There are other flags that could help you, see the docs.

QGraphicsItem -> get the correct bounding rectangle

I am trying the hello triangle example of OpenGL ES 2.0. I am using Qt, so I created a QGraphicsScene and added that code as a QGraphicsItem. It draws correctly, but I cannot get the bounding rectangle correctly. The triangle vertices are
GLfloat afVertices[] =
{-0.4f,-0.4f,0.0f,
0.4f ,-0.4f,0.0f,
0.0f ,0.4f ,0.0f};
and my viewport is glViewport(0, 0, 800, 480);
What would be the correct bounding rect coordinates?
I set the viewport to a QGLWidget. The thing with the QGraphicsItem is that I have to re-implement the bounding rectangle of the item and if I just use
QRectF myGraphicsItem::boundingRect() const
{
return QGraphicsItem::boundingRect();
}
it says undefined reference to `QGraphicsItem::boundingRect() const'
I had originally used
QRectF myGraphicsItem::boundingRect() const
{
return QRectF(-0.4, -0.4, 0.8, 0.8);
}
but the result is a very small bounding box. The seemingly correct one was created when I was used values like QRectf(300, 200, 200, 200) by trial and error -which is too 'manual'-, so I was wondering maybe there is some kind of coordinate correspondence or transformation that I'm unaware of.
QGraphicsItem::boundingRect() is a pure virtual function. Thus, there is no implementation. You must provide your own implementation. Based upon your vertices, probably
QRectF myGraphicsItem::boundingRect() const
{
return QRectF(-0.4, -0.4, 0.8, 0.8);
}
I'm not sure I follow, if you're using a QGraphicsItem (with or without an OpenGL viewport), you would typically use QGraphicsItem::boundingRect() to get the bounding rectangle?
I would do (in Python):
# inside class
def parentBoundingRect(self):
return self.mapToParent(self.boundingRect()).boundingRect()
# or if that doesn't work
def parentBoundingRect(self):
pos = self.pos()
rect = self.transform().mapToPolygon(self.boundingRect()).boundingRect()
return QRectF(pos.x(), pos.y(), rect.width(), rect.height())
# or if that doesn't work, keep playing with it til it does! :)

Resources