I'm trying to animate a widget that is inside a grid and a container, whenever hovered it is animated being expanded horizontally.
As the widget is inside of a container it gets cut because it cant expand above the parent widget area.
What I thought: when the widget is hovered, the code changes her parent to the centralWidget then it can expand above other widgets, and when it's no longer hovered restore her 'original' parent.
An example:
As it could be seen in the gif below, before hovering:
The widget is 'attached' to the GridLayout and inside of the Widget container.
After the mouse leaves:
It's no longer attached to the GridLayout / Widget.
I tried to 'restore' the widget to her parent with:
this->setParent(widgetParent);
But it didn't work or I may be doing something wrong.
I could not think of another way of doing all this (expanding a widget inside of a container above her parent limits), any different suggestion is welcome.
class AnimatedButton : public QPushButton
{
Q_OBJECT
public:
QPropertyAnimation* anim;
struct WidgetPos { int x = 0; int y = 0; int w = 0; int h = 0; };
WidgetPos wp;
QWidget* widgetParent;
QWidget* centralwidget;
void CreateAnimation(QByteArray propertyName)
{
if (propertyName == "geometry")
{
anim = new QPropertyAnimation(this, propertyName);
this->anim->setDuration(100);
this->anim->setEasingCurve(QEasingCurve::Linear);
this->wp.x = this->x();
this->wp.y = this->y();
this->wp.w = this->width();
this->wp.h = this->height();
}
}
AnimatedButton(QWidget* parent = 0) : QPushButton(parent)
{
widgetParent = parent;
this->installEventFilter(this);
}
bool eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::Enter)
{
if (!this->wp.x)
this->CreateAnimation("geometry");
// setParent to the central widget so the widget can resize above her default parent area.
this->setParent(this->centralwidget);
this->show();
this->anim->stop();
this->anim->setStartValue(
QRect(this->x(), this->y(), this->width(), this->height()));
this->anim->setEndValue(
QRect(this->x(), this->y(), (this->wp.w + 200) - this->width(), this->height()));
this->anim->start();
}
else if (event->type() == QEvent::Leave)
{
this->anim->stop();
this->anim->setStartValue(
QRect(this->x(), this->y(), (this->wp.w + 200) - this->width(), this->height()));
this->anim->setEndValue(
QRect(this->wp.x, this->wp.y, this->wp.w, this->wp.h));
this->anim->start();
// Restore default parent (--------NOT WORKING---------)
this->setParent(widgetParent);
this->show();
}
return QWidget::eventFilter(obj, event);
}
};
QtWidgetsApplication::QtWidgetsApplication(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
// Pass the central widget to the AnimatedButton class.
ui.pushButton->centralwidget = ui.centralWidget;
return;
}
Related
I have a widget with this structure:
main->QMainWindow->QFrame->QGraphicsView->QScene->QGraphicsPixMapItem->QPixmap
I had to do it this way because im not using Qt creator or QML, just widgets. Anyway I added an event filter to my QMainindow to be movable when clicking and dragging whenever side of the window And it worked. But due to the QGraphicsView implementation if I try to drag it doesnt work but still receives input (a menu opens when i click). What makes QGraphicsview so stubborn and how do i make the window to be draggable when i click and drag on the QGraphics view, Even installing the eventFilter on the view and frame but with no results. Thanks in advance and this is my code.
This is the problem, the red square is the QGrapicsView, the white one is the MainWindow/Qframe. See how i still can make right clic on the red one and the menu still appears. I made a debug and see i get 2 objs when i click, the mainwindow and the Qgraphicview so i dont know why the move functionality just doesn work.
enter image description here
Main.cpp
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return QApplication::exec();
}
MainWindow.h
protected:
bool eventFilter(QObject *obj, QEvent *event) override{
if (event->type() == QEvent::MouseButtonPress ) {
qDebug() << event;
auto *ev = (QMouseEvent *) event;
if (ev->button() == Qt::RightButton) {
//Opens menu, it works well when i click on the QGraphics as well
auto *menu = new QMenu(this);
auto *idle = new QAction("Idle", this);
auto *close = new QAction("Quit", this);
menu->addAction(idle);
menu->popup(ev->globalPos());
connect(close, &QAction::triggered, [](){QCoreApplication::quit();});
connect(idle, &QAction::triggered, [this](){changeDance(0);});
}
else{
// "grab" the mainWindow
this->oldPos = ev->globalPos();
}
}
if (event->type() == QEvent::MouseMove) {
//drag the window
auto *ev = (QMouseEvent *) event;
const QPoint delta = ev->globalPos() - oldPos;
move(x() + delta.x(), y() + delta.y());
oldPos = ev->globalPos();
}
}
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent) {
setWindowTitle("waifu"); //waifu
setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
setFixedSize(screenWidth, screenHeight);
//setAttribute(Qt::WA_TranslucentBackground, true);
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setWindowFlags(Qt::BypassGraphicsProxyWidget);
//mainWindow->frame->view->scene->pixmap
installEventFilter(this);
frame = new Frame(this);
setCentralWidget(frame);
view = new View(frame);
view->setFixedSize(50, 50);
scene = new Scene(view);
view->setScene(this->scene); // That connects the view with the scene
frame->layout()->addWidget(view);
myItem = new QGraphicsPixmapItem(*new QPixmap());
//scene->setBackgroundBrush(QBrush(Qt::yellow));
scene->addItem(myItem);
view->show();
Frame.h
class Frame : public QFrame {
Q_OBJECT
public:
explicit Frame(QMainWindow *parent = 0) : QFrame(parent) {
setMouseTracking(true);
setStyleSheet("background-color: red;"); //delete
setLayout(new QVBoxLayout);
layout()->setContentsMargins(0, 0, 0, 0);
setWindowFlags(Qt::FramelessWindowHint); //No windowing
setAttribute(Qt::WA_TranslucentBackground); // No background
setStyleSheet("background-color: transparent;");
};
};
QGraphicsView.h
class View : public QGraphicsView {
Q_OBJECT
public:
explicit View(QFrame *parent) : QGraphicsView(parent) {
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setMouseTracking(true);
};
protected:
private:
};
Tried to install the event filter located in the main window, in the Qframe and QGraphicsView class. with the parameter event filter being *parent.
I have extended a class from QpushButton in my Qt Program that overrides mousepress & mousemove events and cause the parent widget to move with left mouse click in the layout. but when I change some widget's properties in parent widget for example QLabel's text or QpushButton's text ,the parent widget goes back to it's original position in the layout , what's wrong with my code & how can I prevent this ?
any help is appreciated
here is My code :
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
MoveButton *movebut = new MoveButton(this);
ui->horizontalLayout->addWidget(movebut);
connect(movebut , &MoveButton::positionChanged , ui->widgetMove , [this](const QPoint &newPos){
ui->widgetMove->move(ui->widgetMove->mapToParent(newPos));
currentPos = mapToParent(newPos);;
});
}
MainWidget::~MainWidget()
{
delete ui;
}
MoveButton::MoveButton(QWidget *parent) : QPushButton(parent)
{
setText("Draggable Button");
setMouseTracking(true);
setContentsMargins(0 , 0 , 0 , 0);
setMaximumHeight(30);
}
QSize MoveButton::minimumSizeHint() const
{
return QSize(100 ,30);
}
void MoveButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
offset = event->pos();
qDebug() << offset;
}
}
void MoveButton::mouseMoveEvent(QMouseEvent *event)
{
//this->setCursor(Qt::OpenHandCursor);
if(event->buttons() & Qt::LeftButton)
{
emit positionChanged(event->pos() - offset);
}
}
void MainWidget::on_pushButton_clicked()
{
ui->pushButton->setText("Clicked");
}
I have removed all spacers , but I have this issue again
I want to create a scrollable widget which contains a collection of child widgets managed by QBoxLayout, and it must be easy to add and remove widgets from this collection. But when I add child widgets to it, viewport widget did not expand its size (remain initial size), instead children are overlapping on themselves. I do not know what to do to fix this.
Here is the code:
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QtGui/QScrollArea>
#include <QResizeEvent>
#include <QDebug>
class CMainWidget : public QScrollArea
{
Q_OBJECT
public:
CMainWidget(QWidget *parent = 0);
~CMainWidget();
protected:
virtual void resizeEvent(QResizeEvent *pEvent);
virtual void keyPressEvent(QKeyEvent *pEvent);
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "rootitem.h"
CMainWidget::CMainWidget(QWidget *parent)
: QScrollArea(parent)
{
QWidget* pViewport = new QWidget();
QBoxLayout* pLayout = new QBoxLayout(QBoxLayout::TopToBottom);
pLayout->setSizeConstraint(QLayout::SetNoConstraint);
for (int iWidgetIndex = 0; iWidgetIndex < 20; iWidgetIndex++)
pLayout->addWidget(new CRootItem());
pViewport->setLayout(pLayout);
pViewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
setWidget(pViewport);
}
CMainWidget::~CMainWidget()
{
}
void CMainWidget::resizeEvent(QResizeEvent *pEvent)
{
QWidget* pViewport = widget();
int iHeight = pViewport->height();
int iWidth = pEvent->size().width();
pViewport->resize(iWidth, iHeight);
}
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
pLayout->addWidget(new CRootItem());
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Here is the image which shows child widgets overlapping after inserting a couple of new items:
EDIT
Just solved my problem using dirty approach: subtraction and adding item fixed height from viewport's. Is there more fashion way to handle this problem?
Code:
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
QWidget* pItem = new CRootItem();
pLayout->addWidget(pItem);
QSize Size = pViewport->size();
Size.rheight() += pItem->height() + pLayout->spacing();
pViewport->resize(Size);
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
QSize Size = pViewport->size();
Size.rheight() -= pItem->widget()->height() + pLayout->spacing();
pViewport->resize(Size);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Comment out your resizeEvent. You rarely need to set the explicit size or position of a widget unless it's a top level window. Let the layout do the work.
I want to create a personalized UI with my Qt application. For this reason I want to edit the default window frame that you can see at any type of window application.
The application that contains the default window title and frame:
http://0000.2.img98.net/out.php/i12977_with-default-title-and-frame.jpg
The application that edit the window title and frame:
http://0000.2.img98.net/out.php/i12978_without-defualt-title-andframe.jpg
You need to create a new class derived from QWidget, and pass Qt::FramelessWindowHint argument to QWidget constructor, like this:
class MyWidget : public QWidget {
public:
MyWidget(QWidget* parent) : QWidget(parent, Qt::FramelessWindowHint) {...}
After it you need to reimplement QWidget::paintEvent (QPaintEvent * event) and draw any design you want.
For example, you have main window design as a PNG image.
class MyWidget : public QWidget {
Q_OBJECT
private:
QPushButton* button;
QLabel* label;
QComboBox* combobox;
QPixmap pixmap;
public:
explicit MyWidget(QWidget *parent = 0) : QWidget(parent, Qt::FramelessWindowHint) {
// Create some controls
button = new QPushButton();
label = new QLabel();
combobox = new QComboBox();
QVBoxLayout* l = new QVBoxLayout();
l->addWidget(button);
l->addWidget(label);
l->addWidget(combobox);
setLayout(l);
resize (500, 500);
setAttribute(Qt::WA_TranslucentBackground); // enable translucent background
pixmap = QPixmap("./1.png"); // load design picture
};
protected:
virtual void paintEvent (QPaintEvent* event) {
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 0));
painter.drawRect(this->rect());
painter.drawPixmap(this->rect(), pixmap, pixmap.rect());
};
Since we don't see a titlebar, we need to implement window drag operation:
private:
bool pressed;
QPoint mousePressPoint;
protected:
virtual void mousePressEvent ( QMouseEvent * event ) {
QWidget::mousePressEvent(event);
if (!pressed) {
pressed = true;
mousePressPoint = event->pos();
}
}
virtual void mouseMoveEvent ( QMouseEvent * event ) {
QWidget::mouseMoveEvent(event);
if (pressed) {move(event->globalPos() - mousePressPoint);}
}
virtual void mouseReleaseEvent ( QMouseEvent * event ) {
QWidget::mouseReleaseEvent(event);
if (pressed) {pressed = false;}
}
I have a a QWidgetAction which holds a QWidget composed of a QLineEdit and a QPushButton. Once the user press the button the QWidgetAction call the trigger slot.
Now I have a QMenu which I activate with exec. The problem is that even though trigger is called (I've connected it to a print function as well to check) the menu won't close.
Regular QActions works well.
Any idea why?
P.S. Googling this issue I came across people with the same problem, but no solutions.
Years old question, but still I have an answer, hope it helps anybody!
I will describe my complete solution which not only hides the menu but also manages the visual representation.
QWidgetAction subclass: MyClass.h
class MyClass : public QWidgetAction {
Q_OBJECT
public:
MyClass(QObject* parent);
bool eventFilter(QObject*, QEvent*) override;
signals:
void mouseInside();
void mouseOutside();
protected:
QWidget* createWidget(QWidget* parent) override;
private:
QWidget* w;
QWidget* border;
QFormLayout *form;
QHBoxLayout *mainLayout;
}
QWidgetAction subclass MyClass.cpp
QWidget* MyClass::createWidget(QWidget* parent) {
w = new QWidget(parent);
border = new QWidget(parent);
mainLayout = new QHBoxLayout(w);
layout = new QFormLayout();
border->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum));
border->setMaximumWidth(10);
border->setMinimumWidth(10);
border->setMaximumHeight(1000); //Anything will do but it needs a height
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
w->setLayout(mainLayout);
mainLayout->addWidget(border);
mainLayout->addLayout(layout);
layout->setContentsMargins(6, 11, 11, 11);
layout->setSpacing(6);
// Insert your widgets here, I used a QFormLayout
QLineEdit *l = new QLineEdit(w);
form->addRow("Test", l);
// I added a button to accept input
QPushButton* b = new QPushButton("Send", w);
connect(b, SIGNAL(clicked()), this, SLOT(trigger()));
layout->addWidget(b);
w->installEventFilter(this); // This is to avoid non button clicks to close the menu
return w;
}
bool MyClass::eventFilter(QObject*, QEvent* ev) {
if (ev->type() == QEvent::MouseButtonPress
|| ev->type() == QEvent::MouseButtonDblClick
|| ev->type() == QEvent::MouseButtonRelease) {
return true;
} else if (ev->type() == QEvent::Enter) {
border->setStyleSheet("background-color: #90c8f6;");
emit mouseInside();
} else if (ev->type() == QEvent::Leave) {
border->setStyleSheet("");
emit mouseOutside();
}
return false;
}
Finally to insert the QWidgetAction in a menu, in your code add the following:
QMenu *m = new QMenu(this);
MyClass *item = new MyClass(m);
connect(item, &QAction::triggered, [=] { m->hide(); YOUR CODE HERE}); // Add your action here
// This is to give a visual cue to your item, while deselecting the stuck
// action which was previously selected
connect(item, &MyClass::mouseInside, [=] { m->setActiveAction(nullptr); });
ui->yourButton->setMenu(m);
m->addAction(item);