I'm using QTCreator and I created a QWidget, then I have hidden the title bar with setWindowFlags(Qt::CustomizeWindowHint);.
But I can't select or move my widget. How can I use the mouseEvent to solve that?
If you want to be able to move your window around on your screen by just clicking and dragging (while maintaining the mouse button pressed), here's an easy way to do that:
#include <QtGui>
class W: public QWidget
{
Q_OBJECT
public:
explicit W(QWidget *parent=0) : QWidget(parent) { }
protected:
void mousePressEvent(QMouseEvent *evt)
{
oldPos = evt->globalPos();
}
void mouseMoveEvent(QMouseEvent *evt)
{
const QPoint delta = evt->globalPos() - oldPos;
move(x()+delta.x(), y()+delta.y());
oldPos = evt->globalPos();
}
private:
QPoint oldPos;
};
In mousePressEvent, you save the global (screen-coordinate) position of where the mouse was, and then in the mouseMoveEvent, you calculate how far the mouse moved and update the widget's position by that amount.
Note that if you have enabled mouse tracking, you'll need to add more logic to only move the window when a mouse button is actually pressed. (With mouse tracking disabled, which is the default, mouseMoveEvents are only generated when a button is held down).
Related
I'm creating a marquee selection type tool in a QWidget and it all works fine, except for a rendering bug when dragging the marquee. If I call update without any arguments everything works beautifully, but if I call update to only include the region of the marquee as the user is dragging, then two of the edges get cutoff if the mouse is moving at a moderate speed.
Here's an image of what it looks like while dragging towards the lower right corner:
Screenshot while clicking and dragging in the widget
I thought replacing update() with repaint() might fix it but that didn't work either.
What is the correct way that I should be doing this? I've included some very basic code that demonstrates the problem.
#include <QPainter>
#include <QPaintEvent>
#include <QWidget>
class Widget : public QWidget
{
public:
Widget(QWidget *parent = nullptr)
: QWidget(parent)
{
}
void mousePressEvent(QMouseEvent *e) override
{
startPt = e->pos();
rect = QRect();
update(); // clear the entire widget
}
void mouseMoveEvent(QMouseEvent *e) override
{
rect = QRect(startPt, e->pos()).normalized();
update(rect.adjusted(-2,-2,2,2)); // adjusted to include stroke
}
void paintEvent(QPaintEvent *event) override
{
QPainter p(this);
p.setBrush(Qt::NoBrush);
p.setPen(QPen(Qt::black, 2));
p.drawRect(rect);
}
private:
QRect rect;
QPoint startPt;
};
In Windows when I create a QMainWindow I can move it around the screen by clicking the title bar and dragging it.
In my application I've hidden the title bar by using setWindowFlags(Qt::CustomizeWindowHint) and I'm trying to build a custom title bar using a widget and setting it in the menu space with setMenuWidget(myWidget).
Now I want to reproduce the original behaviour: I want to click on my MyWidget widget inside the QMainWindow and, while mouse is pressed, dragging the mouse moves the window.
Is there a way to do it?
This is an example on how to implement a fake title bar, that has standard buttons (minimize, maximize, close), and can be dragged to move the whole window (this is based on the approach in #Kevin's answer).
#include <QtWidgets>
class FakeTitleBar : public QWidget{
Q_OBJECT
public:
explicit FakeTitleBar(QWidget* parent= nullptr):QWidget(parent){
label.setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
layout.addWidget(&label);
layout.addWidget(&buttonMinimize);
layout.addWidget(&buttonMaximize);
layout.addWidget(&buttonClose);
//connecting buttons' signals to slots
connect(&buttonMinimize, &QPushButton::clicked,
this, &FakeTitleBar::MinimizeWindow);
connect(&buttonMaximize, &QPushButton::clicked,
this, &FakeTitleBar::MaximizeWindow);
connect(&buttonClose, &QPushButton::clicked,
this, &FakeTitleBar::CloseWindow);
//setting vertical fixed size policy
//so that the title bar does not take up any additional space
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
//a bit of styling
setStyleSheet("QPushButton {margin:0px; padding:5px;}"
"QWidget {background-color:blue; color:white;}");
}
public slots:
//slots for corresponding buttons
void MinimizeWindow(){
window()->showMinimized();
}
void MaximizeWindow(){
if(!window()->isMaximized())
window()->showMaximized();
else
window()->showNormal();
}
void CloseWindow(){
window()->close();
}
protected:
void mousePressEvent(QMouseEvent* event){
//save the press position (this is relative to the current widget)
pressPos= event->pos();
isMoving= true;
}
void mouseMoveEvent(QMouseEvent* event){
//isMoving flag makes sure that the drag and drop event originated
//from within the titlebar, because otherwise the window shouldn't be moved
if(isMoving){
//calculate difference between the press position and the new Mouse position
//(this is relative to the current widget)
QPoint diff= event->pos() - pressPos;
//move the window by diff
window()->move(window()->pos()+diff);
}
}
void mouseReleaseEvent(QMouseEvent* /*event*/){
//drag and drop operation end
isMoving= false;
}
//double-clicking on the title bar should maximize the window
void mouseDoubleClickEvent(QMouseEvent* /*event*/){
MaximizeWindow();
}
//in order for the style sheet to apply on this custom widget
//see https://doc.qt.io/qt-5/stylesheet-reference.html#qwidget-widget
void paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
private:
QHBoxLayout layout{this};
QLabel label{"Fake Title Bar"};
QPushButton buttonMinimize{"-"};
QPushButton buttonMaximize{"M"};
QPushButton buttonClose{"X"};
QPoint pressPos;
bool isMoving{false};
};
//sample usage
class Widget : public QWidget{
public:
explicit Widget(QWidget* parent= nullptr):QWidget(parent){
setWindowFlags(Qt::CustomizeWindowHint);
layout.addWidget(&titleBar);
layout.addWidget(&label);
layout.setContentsMargins(0, 0, 0, 0);
label.setAlignment(Qt::AlignCenter);
//default size for the window
resize(320,240);
}
~Widget(){}
private:
QVBoxLayout layout{this};
FakeTitleBar titleBar;
QLabel label{"this is a sample window"};
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
#include "main.moc"
You just need to implement the necessary mouse event handling by overwriting MyWidget's mousePressEvent(), mouseMoveEvent() and mouseReleaseEvent() handlers.
Detect the mouse down, get current mouse position
While moving, get current mouse position, calculate difference, save new position, move window by diff
You can get the window (top level widget) from inside MyWidget through the window() method.
I'm trying to add zoom in/out functionality to a graphic I'm drawing in Qt.
What I first did was extend QGraphicsScene with my own class GraphicsScene and overload the wheel event.
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
GraphicsScene(QObject *parent, bool drawAxes){ /*drawing stuff here.. */}
virtual void wheelEvent(QGraphicsSceneWheelEvent *mouseEvent);
signals:
void mouseWheelTurned(int);
};
void GraphicsScene::wheelEvent(QGraphicsSceneWheelEvent* mouseEvent) {
int numDegrees = mouseEvent->delta() / 8;
int numSteps = numDegrees / 15; // see QWheelEvent documentation
emit mouseWheelTurned(numSteps);
}
When the wheel is turned, an event is sent to the view which contains the scene, and there a scale is performed:
class GraphicsView : public QGraphicsView{
Q_OBJECT
qreal m_currentScale;
public:
GraphicsView(QWidget * parent): QGraphicsView(parent){ m_currentScale = 1.0; }
public slots:
void onMouseWheelTurned (int);
};
void GraphicsView::onMouseWheelTurned(int steps) {
qreal sign = steps>0?1:-1;
qreal current = sign* pow(0.05, abs(steps));
if(m_currentScale+current > 0){
m_currentScale += current;
QMatrix matrix;
matrix.scale(m_currentScale, m_currentScale);
this->setMatrix(matrix);
}
}
This works, but I noticed if I zoom in a lot, for example to the top of the graphic, so that the graphic is no longer fully in the viewport, and then I zoom out, the program first scrolls to the botton of the graphic. I can see the vertical scrollbar sliding down. Only when it has reached bottom, does it start to zoom out. What could be the problem?
I would want to zoom in/ out without this scroll up / down behaviour.
You'd be better off handling this in the Scene and sending an event to the View.
Simply handle the event directly in the view with QGraphicsView::wheelEvent, then call its scale function.
I have grid of QFrames in QGridLayout and a popup menu with some actions, which are targeted on the cell where mouse right click happens. On the implementation of ContextMenuEvent I get clicked QPoint using common event->pos() but how I get access to my correct cell object by that point? Or is there some better alternative solution path for this purpose?
void X::contextMenuEvent(QContextMenuEvent* event)
{ // QPoint target = event->pos();
// TODO: m_gridLayout-> ...
// myDerivedCell->setSomething();
}
There are a bunch of solutions here. The simplest is to go through your widgets, calling bool QWidget::underMouse () const. My favorite is this:
frame_i->setContextMenuPolicy(Qt::CustomContextMenu);
connect(frame_i, SIGNAL(customContextMenuRequested(QPoint))
, SLOT(onContextMenu(QPoint)));
...
void X::onContextMenu(const QPoint &pos)
{
QFrame *w = qobject_cast < QFrame * >(sender());
...
}
I have a QGraphicsView window on my widget and have just put in an event for mouse wheel which zooms in on the image.
However as soon as i zoom in scroll bars are displayed and the scroll functionality on the mouse wheel overrides the zoom function i have.
i was wondering if there is any way that i can remove scrolling all together and add a drag to move option or maybe a CTRL and mouse wheel to zoom and mouse wheel alone would control scrolling
here is my zoom function (Which im aware isnt perfect) but if anyone could shed some light on that it would be a bonus
cheers in advance
void Test::wheelEvent(QWheelEvent *event)
{
if(event->delta() > 0)
{
ui->graphicsView->scale(2,2);
}
else
{
ui->graphicsView->scale(0.5,0.5);
}
}
You reimplemented wheelEvent for QWidget/QMainWindow that contains your QGraphicsView, however, wheelEvent of QGraphicsView remains intact.
You can derive from QGraphicsView, reimplement wheelEvent for derived class and use derive class instead of QGraphicsView - this way you won't even need wheelEvent in your QWidget/QMainWindow, and you can customize reimplemented wheelEvent to do what you want. Something like that:
Header file:
class myQGraphicsView : public QGraphicsView
{
public:
myQGraphicsView(QWidget * parent = nullptr);
myQGraphicsView(QGraphicsScene * scene, QWidget * parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent * event);
};
Source file:
myQGraphicsView::myQGraphicsView(QWidget * parent)
: QGraphicsView(parent) {}
myQGraphicsView::myQGraphicsView(QGraphicsScene * scene, QWidget * parent)
: QGraphicsView(scene, parent) {}
void myQGraphicsView::wheelEvent(QWheelEvent * event)
{
// your functionality, for example:
// if ctrl pressed, use original functionality
if (event->modifiers() & Qt::ControlModifier)
{
QGraphicsView::wheelEvent(event);
}
// otherwise, do yours
else
{
if (event->delta() > 0)
{
scale(2, 2);
}
else
{
scale(0.5, 0.5);
}
}
}
Scrolling can be disabled with the following code:
ui->graphicsView->verticalScrollBar()->blockSignals(true);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->horizontalScrollBar()->blockSignals(true);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
I think your question has a bit simpler answer.. To disable scroll bars just set scroll bar policy (QGraphicsView is just QScrollView), so step 1)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
that will disable scroll bars..
step 2) (if you want to keep it simple)
QGraphicsView * pView; // pointer to your graphics view
pView->setInteractive(true);
pView->setDragMode(QGraphicsView::ScrollHandDrag);
thats the fastest way to get results you want