Qt: resizable and movable main window without title bar - qt

I need draw a Qt-window without the title bar. Generally this can be easy by just setting the CustomizeWindowHint window attribute. But then window has fixed size and position.
I need a movable and resizable window. This post shows how to move the window manually so I wonder whether there is a way to at least keep the borders resizable.
Of course I can also resize the window manually but I expect jumping windows and many sorts of weird behavior so I hope there is a way to use the existing functionality

From the docs on Qt::CustomizeWindowHint:
This flag must be set to allow the WindowTitleHint, WindowSystemMenuHint, WindowMinimizeButtonHint, WindowMaximizeButtonHint and WindowCloseButtonHint flags to be changed.
Basically, Qt::CustomizeWindowHint is not meant to be used without other title bar hints.
Use Qt::FramelessWindowHint in conjunction with some event reimplentation for dragging (see the solution to your other question). You can get a size grip to show like so:
auto statusBarWidget = statusBar();
statusBarWidget->setSizeGripEnabled(true);
You may need to do some work with the size grip and status bar to work with the frameless window flag set.
Edit: since you aren't using a statusbar, add a "hotspot" in your central widget to start resizing instead of moving, i.e. check if the mouse is in the bottom right corner of the window and set the cursor appropriately. If they click, begin resizing instead of moving. This can all be done by reimplementing the mouse events.

Edit: I got it
There is some jumping behavior but this is a working manual resize/move of window without a title bar I implemented for my personal project:
//Below two methods partially from Qt Shaped Clock example
void MainWindow::mousePressEvent(QMouseEvent *event){
//From Qt Documentation:
//Reason why pos() wasn't working is because the global
//position at time of event may be very different
//This is why the mpos = event->pos(); line before was
//possibly causing jumping behavior
if (event->button() == Qt::LeftButton){
//Coordinates have been mapped such that the mouse position is relative to the
//upper left of the main window
mpos = event->globalPos() - frameGeometry().topLeft();
//At the moment of mouse click, capture global position and
//lock the size of window for resizing
global_mpos = event->globalPos();
storeWidth = this->width();
storeHeight= this->height();
event->accept();
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event){
//mapped mouse relative to upper left of window
rs_mpos=event->globalPos()-frameGeometry().topLeft();//general position tracker for resizing
QTextStream out(stdout);
//How much of the corner is considered a "resizing zone"
//I was experiencing jumping behavior with rs_size is 10 so
//I recommend rs_size=50
int rs_size=50;
//Big if statement checks if your mouse is in the upper left,
//upper right, lower left, and lower right
if ( (abs(rs_mpos.x()) < rs_size && abs(rs_mpos.y()) < rs_size) ||
(abs(rs_mpos.x()) > this->width()-rs_size && abs(rs_mpos.y()) <rs_size) ||
(abs(rs_mpos.x()) < rs_size && abs(rs_mpos.y())> this->height()-rs_size) ||
(abs(rs_mpos.x()) > this->width()-rs_size && abs(rs_mpos.y())> this->height()-rs_size)
){
//Below for debugging
/*
out << rs_mpos.x() << " , " << rs_mpos.y() << "\n";
out << "window: " << this->width() << " , " << this->height() << "\n";
out << "globalpos: " << event->globalPos().x() << " , "
<< event->globalPos().y() << "\n";
*/
//Use 2x2 matrix to adjust how much you are resizing and how much you
//are moving. Since the default coordinates are relative to upper left
//You cannot just have one way of resizing and moving the window.
//It will depend on which corner you are referring to
//adjXfac and adjYfac are for calculating the difference between your
//current mouse position and where your mouse was when you clicked.
//With respect to the upper left corner, moving your mouse to the right
//is an increase in coordinates, moving mouse to the bottom is increase
//etc.
//However, with other corners this is not so and since I chose to subtract
//This difference at the end for resizing, adjXfac and adjYfac should be
//1 or -1 depending on whether moving the mouse in the x or y directions
//increases or decreases the coordinates respectively.
//transXfac transYfac is to move the window over. Resizing the window does not
//automatically pull the window back toward your mouse. This is what
//transfac is for (translate window in some direction). It will be either
//0 or 1 depending on whether you need to translate in that direction.
//Initiate matrix
int adjXfac=0;
int adjYfac=0;
int transXfac=0;
int transYfac=0;
//Upper left corner section
if ( (abs(rs_mpos.x()) < rs_size && abs(rs_mpos.y()) < rs_size)){
this->setCursor(Qt::SizeFDiagCursor);
//Upper left. No flipping of axis, no translating window
adjXfac=1;
adjYfac=1;
transXfac=0;
transYfac=0;
}
//Upper right corner section
else if(abs(rs_mpos.x()) > this->width()-rs_size &&
abs(rs_mpos.y()) <rs_size){
this->setCursor(Qt::SizeBDiagCursor);
//upper right. Flip displacements in mouse movement across x axis
//and translate window left toward the mouse
adjXfac=-1;
adjYfac=1;
transXfac = 1;
transYfac =0;
}
//Lower left corner section
else if(abs(rs_mpos.x()) < rs_size &&
abs(rs_mpos.y())> this->height()-rs_size){
this->setCursor(Qt::SizeBDiagCursor);
//lower left. Flip displacements in mouse movement across y axis
//and translate window up toward mouse
adjXfac=1;
adjYfac=-1;
transXfac=0;
transYfac=1;
}
//Lower right corner section
else if(abs(rs_mpos.x()) > this->width()-rs_size &&
abs(rs_mpos.y())> this->height()-rs_size){
this->setCursor(Qt::SizeFDiagCursor);
//lower right. Flip mouse displacements on both axis and
//translate in both x and y direction left and up toward mouse.
adjXfac=-1;
adjYfac=-1;
transXfac=1;
transYfac=1;
}
if (event->buttons()==Qt::LeftButton ){
//Calculation of displacement. adjXfac=1 means normal displacement
//adjXfac=-1 means flip over axis
int adjXdiff = adjXfac*(event->globalPos().x() - global_mpos.x());
int adjYdiff = adjYfac*(event->globalPos().y() - global_mpos.y());
//if transfac is 1 then movepoint of mouse is translated
QPoint movePoint(mpos.x() - transXfac*adjXdiff, mpos.y()-transYfac*adjYdiff);
move(event->globalPos()-movePoint);
resize(storeWidth-adjXdiff, storeHeight-adjYdiff);
event->accept();
}
}
//in any move event if it is not in a resize region use the default cursor
else{
this->setCursor(Qt::ArrowCursor);
//simple move section
if (event->buttons()==Qt::LeftButton &&
resizeZone==false){
move(event->globalPos() - mpos);
event->accept();
}
}
}
Set these in your header file
private:
QPoint mpos; //For dragging, relative mouse position to upper left
QPoint global_mpos; //For resizing, global mouse position at mouse click
QPoint rs_mpos; //for resizing
int storeWidth; //fix window size at mouseclick for resizing
int storeHeight;

Related

Zoom in / Zoom out in QT without using scroll button

I want to zoom in/out a part in openGL using mouse. Now I can do it well using the scroll button in mouse. But my goal is now to perform the same operation by holding the left mouse button and moving the mouse to and fro in the screen.
I don't know how to perform the zooming action by detecting the to and fro motion of mouse.
// function for mouse scroll events
void VDUI_GLWidget::wheelEvent (QWheelEvent * e) {
// here comes the code for zoom in / out based on the scrolling
MouseWheel (e->delta () / float (WHEEL_STEP), QTWheel2VCG (e->modifiers ()));
updateGL();
}
Now I want to perform the same using mouse move and mouse press events. I'm unable to pick the event which detects the to and fro motion
Now I have found the solution. Finding the difference between the current & previous Y axis position, I can zoom in / out the Please refer this below mentioned code.
// function for mouse move events
void GLWidget::mouseMoveEvent (QMouseEvent * e) {
static int curY = 0, prevY = 0;
if (e->buttons()) {
curY = e->y(); // current position of Y
if( (curY - prevY) > 0 ) // call zoom in function
else // call zoom out function
updateGL();
}
prevY = curY;
}

How to scale the contents of a QGraphicsView using the QPinchGesture?

I'm implementing an image viewer on an embedded platform. The hardware is a sort of tablet and has a touch screen as input device. The Qt version I'm using is 5.4.3.
The QGraphicsView is used to display a QGraphicsScene which contains a QGraphicsPixmapItem. The QGraphicsPixmapItem containts the pixmap to display.
The relevant part of the code is the following:
void MyGraphicsView::pinchTriggered(QPinchGesture *gesture)
{
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
if (changeFlags & QPinchGesture::ScaleFactorChanged) {
currentStepScaleFactor = gesture->totalScaleFactor();
}
if (gesture->state() == Qt::GestureFinished) {
scaleFactor *= currentStepScaleFactor;
currentStepScaleFactor = 1;
return;
}
// Compute the scale factor based on the current pinch level
qreal sxy = scaleFactor * currentStepScaleFactor;
// Get the pointer to the currently displayed picture
QList<QGraphicsItem *> listOfItems = items();
QGraphicsItem* item = listOfItems.at(0);
// Scale the picture
item.setScale(sxy);
// Adapt the scene to the scaled picture
setSceneRect(scene()->itemsBoundingRect());
}
As result of the pinch, the pixmap is scaled starting from the top-left corner of the view.
How to scale the pixmap respect to the center of the QPinchGesture?
From The Docs
The item is scaled around its transform origin point, which by default is (0, 0). You can select a different transformation origin by calling setTransformOriginPoint().
That function takes in a QPoint so you would need to find out your centre point first then set the origin point.
void QGraphicsItem::setTransformOriginPoint(const QPointF & origin)

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()

Shift `QGraphicsTextItem` position relative to the center of the text?

I have a number of classes that inherit from QGraphicsItem, that get to be arranged in a certain way. For simplicity of calculations, I made the scenes, and items, centered in (0, 0) (with the boundingRect() having +/- coordinates).
QGraphicsTextItem subclass defies me, its pos() is relative to top left point.
I have tried a number of things to shift it so it centers in the text center (for example, the suggested solution here - the code referenced actually cuts my text and only shows the bottom left quarter).
I imagined that the solution should be something simple, like
void TextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
painter->translate( -boundingRect().width()/2.0, -boundingRect().height()/2.0 );
QGraphicsTextItem::paint(painter, option, widget );
}
the above "sort of" works - but as I increase the item scale -> increase the font, the displayed item is cut off...
I tried to set the pos() - but the problem is, I still need to track the actual position on the scene, so I cannot just replace it.
A slightly unpleasant side effect - centering the QGraphicsView on the element does not work either.
How can I make my QGraphicsTextItem show its position relative to the center of the text ?
Edit: one of the experiments of changing the boundingRect():
QRectF TextItem::boundingRect() const
{
QRectF rect = QGraphicsTextItem::boundingRect();
rect.translate(QPointF(-rect.width()/2.0, -rect.height()/2.0));
return rect;
}
I had to shift the initial position, as well as the resize, to trigger a new position - I was unable to do it in paint() because, as I thought from the start, any repaint would continuously recalculate the position.
Only the initial position needs to be adjusted - but as the font size (or style...) changes, its bounding rectangle also changes, so the position must be recalculated - based on previous position.
In the constructor,
setPos(- boundingRect().width()/2, - boundingRect().height()/2);
in the function that modifies item (font) size,
void TextItem::setSize(int s)
{
QRectF oldRect = boundingRect();
QFont f;
f.setPointSize(s);
setFont(f);
if(m_scale != s)
{
m_scale = s;
qreal x = pos().x() - boundingRect().width()/2.0 + oldRect.width()/2.0;
qreal y = pos().y() - boundingRect().height()/2.0 + oldRect.height()/2.0;
setPos(QPointF(x, y));
}
}

Items in a QGraphicsScene near the mouse

I am trying to find the items under the mouse in a scene. The code I am using is as follows:
QPainterPath mousePath;
mousePath.addEllipse(mouseEvent -> pos(),5,5);
QList<QGraphicsItem *> itemsCandidate = this->items(mousePath);
if (!(itemsCandidate.contains(lastSelectedItem))) lastSelectedItem =itemsCandidate.first();
PS: this refers to a scene.
The code should find the items intersected by a small circle around the mouse position and keep the item pointer unchanged if the previous intersected one is still intersected, or take the first in the QList otherwise.
Unfortunately, this code does not work with items inside each other. For example, if I have a Rect side a Rect, the outer Rect is always intersecting the mouse position even when this one is near the inner Rect. How can i solve this?
UPDATE: This seems not to be a problem with polygons, but with Rect, Ellipses, etc.
UPDATE: This code is in the redefined scene::mouseMoveEvent
You can reimplement ‍mouseMoveEvent in ‍QGraphicsView‍ to capture mouse move events in view and track items near the mouse like:
void MyView::mouseMoveEvent(QMouseEvent *event)
{
QPointF mousePoint = mapToScene(event->pos());
qreal x = mousePoint.x();
qreal y = mousePoint.y();
foreach(QGraphicsItem * t , items())
{
int dist = qSqrt(qPow(t->pos().x()-x,2)+qPow(t->pos().y()-y,2));
if( dist<70 )
{
//do whatever you like
}
}
QGraphicsView::mouseMoveEvent(event);
}

Resources