When I'm using a QTreeWidget (via qdesigner) with the dragDropMode set to InternalMove and then attempt to move one of the items, the scrollbar snaps back to the original position of the item I moved. I would like the scrollbar to stay at the position the item was dropped. Is there an easy way to do that? I adjusted the defaultDropAction, but to no avail.
Figured it out...
class MyTree : public QTreeWidget
{
Q_OBJECT
public:
MyTree(QWidget *parent = 0);
signals:
void verticalScrollBarValue(int val);
protected slots:
void setTreeVerticalScroll(int val);
protected:
void dropEvent(QDropEvent *event);
};
MyTree::MyTree(QWidget *parent) : QTreeWidget(parent)
{
connect(this, &MyTree::verticalScrollBarValue,
this, &MyTree::setTreeVerticalScroll);
}
void MyTree::setTreeVerticalScroll(int val)
{
verticalScrollbar()->setValue(val);
}
void MyTree::dropEvent(QDropEvent *event)
{
int v = verticalScrollbar()->value();
QTreeWidget::dropEvent(event);
emit verticalScrollBarValue(v);
}
Disclaimer: There may be a typo in there somewhere, I can't copy directly from my work environment.
Related
I have the following code:
void AppMPhase::closeEvent(QCloseEvent *closeEvent) {
QMessageBox* dialog = new QMessageBox(this);
dialog->setText("Warning: Initial configuration changed\nDo you want to restore it ?");
dialog->setIcon(QMessageBox::Warning);
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
connect(dialog, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(restoreInitialConfig(QAbstractButton*)));
dialog->exec();
}
void AppMPhase::restoreInitialConfig(QAbstractButton *button) {
if(!button->text().compare(QString("Yes"))) {
// restore Config
}
else {
// don't restore
}
MainWindow::closeEvent(closeEvent);
}
with AppMPhase the derived class of MainWindow
I would like to call the method closeEvent of the base class MainWindow in the "restoreInitialConfig" method, to close the window after we restore the config.
Is it possible ? it's different from the other questions I have seen because the method closeEvent is overriden in the AppMPhase class.
the AppMPhase class:
class AppMPhase : public QtUi::MainWindow
{
Q_OBJECT
public:
AppMPhase(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
~AppMPhase();
int readConfigFromExternFile(QString path);
int readCalibrationDate(QString path);
QSize sizeHint() const Q_DECL_OVERRIDE;
QtWrapperTestManager * testManager;
public Q_SLOTS:
void show();
public slots:
void currentTestFinished();
void createTest(unsigned int,QString);
void restoreInitialConfig(QAbstractButton* button);
signals:
void notifyPageTestCurrentTestFinished();
private:
enum AppPage
{
PAGE_START,
PAGE_ABOUT
};
bool isTestMangaerCreated;
std::map<QString, std::map<std::string, std::string> > conversion_Table_Cable_Ref_sensorParamMap;
Pages::GenericAppPage * appPage(const int index) const;
QToolButton * appPageButton(const int index) const;
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
The MainWindow class:
class SHARED_EXPORT_LIB_QT_UI MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void aboutToClose();
public slots:
virtual void show();
private slots:
void changeLanguage(const QString &language);
public:
MainWindow(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
virtual ~MainWindow();
protected:
void showCopyright(const QString ©RightHeader);
void showLanguageSelector(const QStringList &languages);
void setupUi(const MainPageList &pageList);
void setupUi(QWidget *centerWidget = Q_NULLPTR);
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
const Ui::MainWindow *const _ui;
MainPageItemList _mainPageItemList;
};
Thanks in advance.
Flo
There is a far easier way to achieve what you're wanting to do, without having to use signals and slots, and call base class functions from your slot.
It is possible to do the restoration directly from within your closeEvent handler.
This is made possible by the fact that QMessageBox::exec returns an integer code which matches one of the values in the StandardButton enum, depending on what button was pressed.
That then allows you to call restoreInitialConfig directly from within your closeEvent handler, as you know which button was pressed.
void AppMPhase::closeEvent(QCloseEvent* ev)
{
int res = QMessageBox(
QMessageBox::Icon::Warning,
"Restore configuration?",
"Warning: Initial configuration changed\nDo you want to restore it?",
QMessageBox::Yes | QMessageBox::No,
this).exec();
if (res == QMessageBox::Yes)
restoreInitialConfig();
MainWindow::closeEvent(ev);
}
Note that this also simplifies your restoreInitialConfig function, as there is no need to check button text, you know the answer was yes.
Note also I made use of this QMessageBox constructor which makes it very easy to create simple message boxes.
I want to make one QGraphicsItem in a QGraphicsScene move (or change size) when another one moves. But, trying to access either QGraphicsItem from the QGraphicsScene object causes crashes.
Here's a dummy example. I want to automatically:
Resize BlueItem's width when I click and drag RedItem.
Move RedItem when I click and drag BlueItem.
The dummy code:
redItem.h (A pixmap item)
#include <QGraphicsPixmapItem>
class RedItem : public QGraphicsPixmapItem
{
public:
RedItem();
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
redItem.cpp
RedItem::RedItem()
{
setPixmap(QPixmap(redPixMap));
setFlags(ItemIsMovable);
setCacheMode(DeviceCoordinateCache);
}
void RedItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
setPos(event->scenePos(),0); //I can override this part just fine.
}
blueItem.h (A resizable rectangle item)
#include <QGraphicsRectItem>
class BlueItem : public QGraphicsRectItem
{
public:
BlueItem();
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
blueItem.cpp is similar to redItem.cpp
graphicsViewWidget.h (The GraphicsView)
#include <QGraphicsView>
#include "blueItem.h"
#include "redItem.h"
class GraphicsViewWidget : public QGraphicsView
{
Q_OBJECT
public:
explicit GraphicsViewWidget(QWidget *parent = 0);
protected:
virtual void mouseMoveEvent(QMouseEvent *event);
private:
RedItem *red;
BlueItem *blue;
};
graphicsViewWidget.cpp
#include "graphicsViewWidget.h"
#include <QRectF>
void Qx::createItem(int frame)
{
red = new RedItem;
red->setPos(0, 0);
scene()->addItem(red);
blue = new BlueItem;
blue->setPos(10, 10);
scene()->addItem(blue);
}
void GraphicsViewWidget::mouseMoveEvent(QMouseEvent *event) //QGraphicsSceneMouseEvent
{
QGraphicsView::mouseMoveEvent(event);
//if(redItem moves)
// resize blueItem;
//if(blueItem moves)
// move redItem;
}
Any help or advice would be greatly appreciated.
If you make item B a child of item A, then it will live in its coordinate space. This means all transformations - moving, scaling, rotating... everything you apply to the parent item will also be applied to all its children and their children and so on...
I dont understand what`s going on: when i create QGraphicsView object directly and adding scene with a pixmap, all is ok, pixmap appears on the screen:
scene.addPixmap(pix);
QGraphicsView graphicsView;
graphicsView.setScene(&scene);
But when i try to inherit QGraphicsView class with purpose of reimplementing events, nothing happend and i got white screen without pixmap, but events like changing cursor is working:
scene.addPixmap(pix);
DrawArea graphicsView;
graphicsView.setScene(&scene);
.h file:
class DrawArea : public QGraphicsView
{
Q_OBJECT
public:
DrawArea(QWidget *parent = 0);
~DrawArea();
signals:
public slots:
void mousePressEvent(QMouseEvent * e);
void paintEvent(QPaintEvent *);
void enterEvent(QEvent *e);
private:
QPoint coord;
};
.cpp file:
DrawArea::DrawArea(QWidget *parent)
: QGraphicsView(parent){
}
DrawArea::~DrawArea(){
}
void DrawArea::mousePressEvent(QMouseEvent * event){
}
void DrawArea::paintEvent(QPaintEvent *event){
}
void DrawArea::enterEvent(QEvent *event){
viewport()->setCursor(Qt::CrossCursor);
}
Tell me if something missed, Thanks in advance.
You should process your events. Try this:
void DrawArea::mousePressEvent(QMouseEvent * event)
{
//some actions
QGraphicsView::mousePressEvent(event);
}
void DrawArea::paintEvent(QPaintEvent *event)
{
//some actions
QGraphicsView::paintEvent(event);
}
Also I think that you don't need paintEvent at all, do all needed things in the scene.
I am trying to implement a widget in Qt that has 2 child widgets of its own: one is a render area where I draw some points and connect lines between them and the other one is a ListBox where I would like to insert the list of all the points I drew with their coordinates from the render area. The 2 widgets where added with Qt Designer. Here is my code until now:
renderarea.h:
class RenderArea : public QWidget
{
Q_OBJECT
public:
RenderArea(QWidget *parent = 0);
QPoint point;
QList<QPoint> list;
protected:
void mousePressEvent(QMouseEvent *);
void paintEvent(QPaintEvent *event);
void updateList(QPoint p);
};
renderarea.cpp:
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
void RenderArea::mousePressEvent(QMouseEvent *e)
{
point = e->pos();
updateList(point);
this->update();
}
void RenderArea::updateList(QPoint p)
{
list.append(p);
}
void RenderArea::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.setPen(QPen(Qt::black,2));
for (int i = 0; i < list.size(); ++i)
painter.drawPoint(list[i]);
if (list.size()>1)
for(int j = 0; j < list.size()-1; ++j)
painter.drawLine(list[j], list[j+1]);
}
paintwidget.h:
class PaintWidget : public QWidget
{
Q_OBJECT
public:
explicit PaintWidget(QWidget *parent = 0);
~PaintWidget();
private:
Ui::PaintWidget *ui;
};
paintwidget.cpp:
PaintWidget::PaintWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::PaintWidget)
{
ui->setupUi(this);
}
PaintWidget::~PaintWidget()
{
delete ui;
}
My question is how to transmit from the render area widget to my ListBox that I drew another point and it should be displayed along with its coordinates in the list?
The general approach used in QT development is using signal/slots for communication between components of software. So basically you need to define a signal in your source component (for instance RenderArea or whereever your like) and connect your slot defined in another component somewhere (i.e your Form Window) and fire a signal upon an action.
There are examples in the referenced link too.
OrcunC gave you a good advice.
If your are new to signal/slots implementation here some hints you can start from.
renderarea.h
signal:
void pointAdded(QPoint*);
renderarea.cpp
void RenderArea::updateList(QPoint p)
{
list.append(p);
emit pointAdded(&list.back());
}
listbox.h
public slots:
void onPointAdded(QPoint*);
listbox.cpp
void ListBox::onPointAdded(QPoint* point)
{
//lets assume your ListBox is a QListWidget
addItem( QString::number(point->x()) + "," + QString::number(point->y()))
}
somewhere instance of ListBox and RenderArea are accessible
QObject::connect( renderArea, SIGNAL(pointAdded(QPoint*),
listBox, SLOT(onPointAdded(QPoint*)));
NOTE: nameing is very important for readability and maintenance the void RenderArea::updateList(QPoint p) in this case it's more void RenderArea::addPoint( const QPoint& p) (also notice the const reference telling the compiler that we are not changing p event if we have it's reference)
I'm trying to make a simple draggable item using the graphics framework. Here's the code for what I did so far:
Widget class:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
DragScene *scene = new DragScene();
DragView *view = new DragView();
QHBoxLayout *layout = new QHBoxLayout();
DragItem *item = new DragItem();
view->setAcceptDrops(true);
scene->addItem(item);
view->setScene(scene);
layout->addWidget(view);
this->setLayout(layout);
}
Widget::~Widget()
{
}
DragView class:
class DragView : public QGraphicsView
{
public:
DragView(QWidget *parent = 0);
};
DragView::DragView(QWidget *parent) : QGraphicsView(parent)
{
setRenderHints(QPainter::Antialiasing);
}
DragScene class:
class DragScene : public QGraphicsScene
{
public:
DragScene(QObject* parent = 0);
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
DragScene::DragScene(QObject* parent) : QGraphicsScene(parent)
{
}
void DragScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dropEvent(QGraphicsSceneDragDropEvent *event){
qDebug() << event->pos();
event->acceptProposedAction();
DragItem *item = new DragItem();
this->addItem(item);
// item->setPos(event->pos()); before badgerr's tip
item->setPos(event->scenePos());
}
DragItem class:
class DragItem : public QGraphicsItem
{
public:
DragItem(QGraphicsItem *parent = 0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
DragItem::DragItem(QGraphicsItem *parent) : QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemIsMovable);
}
QRectF DragItem::boundingRect() const{
const QPointF *p0 = new QPointF(-10,-10);
const QPointF *p1 = new QPointF(10,10);
return QRectF(*p0,*p1);
}
void DragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
if(painter == 0)
painter = new QPainter();
painter->drawEllipse(QPoint(0,0),10,10);
}
void DragItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event){
}
void DragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
}
void DragItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
QMimeData* mime = new QMimeData();
QDrag* drag = new QDrag(event->widget());
drag->setMimeData(mime);
drag->exec();
}
void DragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
}
main.cpp instantiates a Widget and shows it. When I try to drag the circle, the app just creates another circle over the original one, regardless of where I release the drag. qDebug() in DragScene's dropEvent() shows QPointF(0,0) everytime the drag ends. I'm having a hard time trying to understand exactly what I have to do, which classes I should subclass, which methods needs to be overriden, to make this work. The documentation on this isn't very detailed. I'd like to know how to make this work, and if there's some other, more comprehensive resource to learn about the graphics view framework, besides the official documentation (which is excellent btw, but it would be great if there was a more detailed treatise on the subject).
EDIT:
Following badgerr's advice, I replaced item->pos() in DragScene::dropEvent() with item->scenePos(), now the drop event creates a new circle in the drop site, which is more or less what I wanted. But the original circle is still in place, and while the drag is in progress, the item doesn't follow the mouse cursor.
The QGraphicsSceneDragDropEvent documentation says that pos() should return the cursor position in relation to the view that sent the event, which, unless I got it wrong, shouldn't be (0,0) all the time. Weird.
I've read in a forum post that you can use QDrag::setPixMap() to show something during the drag, and in examples I've seen pictures being set as pixmaps, but how do I make the pixmap just like the graphics item I'm supposed to be dragging?
There is an example with Qt Assistant called the "Drag and Drop Robot Example" which appears to use the QDrag method, I don't know if you've had a look at that already.
edit: Just a quick observation, you appear to be creating a new DragItem in your DropEvent, instead of using the mimeData() of the event itself, and since your item draws itself at 0,0, that might explain why you have a new one appearing at that position regardless of where you drop your DragItem.
When I wrote a graphics dragging thing similar to this, I went about it a slightly different way. Maybe it will help you:
Instead of using the QDrag stuff, I used only the mousePressEvent, mouseReleaseEvent, and mouseMoveEvent functions of QGraphicsItem. The mouse press/release set a flag indicating the drag state, and move followed a process of
call prepareGeometryChange();
update the bounds of the QGraphicsItem (my boundingRect() function returns these bounds. All my graphics item bounds are in QGraphicsScene space)
call update();
Then in my paint() function I draw a shape using the boundingRect().
Sorry I don't have a code sample to share, I'll knock one up if I get time.