Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: AD от Июль 24, 2009, 10:40



Название: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 10:40
Не удается заставить правильно перемещаться визир. Визир (QFrame) находится в виджете GraphicDisplay, точнее список визиров.
Код
C++ (Qt)
/// Класс для переопределения виджета каждого визира
class VFFrame: public QFrame
{
Q_OBJECT
 
private:
QPoint _move_point; ///< точка целеуказания, куда необходимо двигать
 
signals:
void moveVF();
 
public:
void setMovePoint(const QPoint& pnt) { _move_point = pnt; }
};
 
/// Передвижение визира вслед за мышью
void VFFrame::mouseMoveEvent(QMouseEvent* events)
{
QFrame::mouseMoveEvent(events);
 
emit moveVF();
 
if(!_is_left_pressed) return;
if(_vf_shape == QFrame::VLine)
setGeometry(_move_point.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
setGeometry(x(), _move_point.y(), width(), height());
}
 
/// Отпускание кнопки мыши
void VFFrame::mouseReleaseEvent(QMouseEvent* events)
{
QFrame::mouseReleaseEvent(events);
 
emit moveVF();
 
if(!_is_left_pressed) return;
_is_left_pressed = false;
if(_vf_shape == QFrame::VLine)
setGeometry(_move_point.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
setGeometry(x(), _move_point.y(), width(), height());
_is_viewfinder = false;
}
 
/// Изменение размеров окна
void VFFrame::resizeEvent(QResizeEvent* events)
{
if(_vf_shape == QFrame::VLine)
setGeometry(x(), 0, 16, parentWidget() -> height());
else if(_vf_shape == QFrame::HLine)
setGeometry(0, y(), parentWidget() -> width(), 16);
}
 
/// Класс для отображения параметров по времени или по расстоянию
class GraphicDisplay : public QDialog, public Ui::GraphicDisplayClass
{
Q_OBJECT
 
private:
QList<VFFrame*> viewfinderList; ///< список всех визиров
QPoint _move_pointVF; ///< точка, куда следует перемещаться визир
void connectViewfinder();
void disconnectViewfinder();
 
private slots:
void setMovePoint();
};
 
/// Проверка на попадание фокуса на визир
bool GraphicDisplay::isViewfinder(const QPoint& pos)
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
_move_pointVF = pos;
return true;
}
return false;
}
 
/// Подсоединение сигнала
void GraphicDisplay::connectViewfinder()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
connect(pf, SIGNAL(moveVF()), this, SLOT(setMovePoint()));
break;
}
}
 
/// Разъединение сигнала
void GraphicDisplay::disconnectViewfinder()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
disconnect(pf, SIGNAL(moveVF()), this, SLOT(setMovePoint()));
break;
}
}
 
/// Нажатие на кнопку - рисование "резиновой ленты"
void GraphicDisplay::mousePressEvent(QMouseEvent* events)
{
QWidget::mousePressEvent(events);
 
if(isViewfinder(events -> pos())) { connectViewfinder(); return; }
// ....................................
}
 
/// Переопределение функции передвижения мыши
void GraphicDisplay::mouseMoveEvent(QMouseEvent* events)
{
if(isViewfinder(events -> pos())) return;
 
// .............................................
}
 
/// Возвращение прежнего вида курсору и изменение масштаба
void GraphicDisplay::mouseReleaseEvent(QMouseEvent* events)
{
if(isViewfinder(events -> pos()))
{ disconnectViewfinder(); return; }
 
// ....................................................................
}

Смысл в том, что во время движения в координату _move_point записывается предыдущее значение, т.к. движение мыша функций GraphicDisplay попадает позже, чем в движение мыша функций VFFrame. Как все-таки добиться эффекта, чтобы в функцию setMovePoint класса VFFrame передавалось значение events -> pos() класса GraphicDisplay?


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 13:02
Есть хоть какие-нибудь идеи, как можно откорректировать код? Ну хоть подтолкните, пожалуйста, на мысль, как выправить код. Как сделать перемещение визира плавным, а не скачкообразным?


Название: Re: Перемещение QFrame
Отправлено: spectre71 от Июль 24, 2009, 13:15
Есть хоть какие-нибудь идеи, как можно откорректировать код? Ну хоть подтолкните, пожалуйста, на мысль, как выправить код. Как сделать перемещение визира плавным, а не скачкообразным?
Из твоего описания и кода непонятно что ты хочешь сделать!
Например говоришь о "_move_point", а в твоем коде она нигде не изменяется.


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 13:50
setMovePoint() ее меняет.
Мне необходимо, чтобы движение визира шло плавно, а не скачками назад-вперед.

Вот так изменяет setMovePoint:
Код
C++ (Qt)
// in header
private slots:
void setMovePoint();
 
/// Слот установки экранных координат во фрейм визира
void GraphicDisplay::setMovePoint()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
pf -> setMovePoint(_move_pointVF);
break;
}
}
 


Название: Re: Перемещение QFrame
Отправлено: ufna от Июль 24, 2009, 13:54
а в целом код данного куска можешь выложить? или это комм. тайна?

скачки взад-вперед происходят видимо по причинетого, что функция, идущей второй, кидает виджет на бывшее значение. Вывод - нужно из первого эвента менять некий флаг во втором или еще что, запрещающий "кидать назад".


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 14:12
Так я все, что относится к этому коду и выложил. Все, что идет далее в функция QGraphicDisplay::mouse....Event - Это масштабирование самого графика.


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 14:16
Вот еще раз код:
Код
C++ (Qt)
/// Класс для переопределения виджета каждого визира
class VFFrame: public QFrame
{
Q_OBJECT
 
private:
Shape _vf_shape; ///< форма визира (вертикальный или горизонтальный)
bool _is_left_pressed; ///< флаг указания нажатия на левую кнопку мыши
bool _is_viewfinder; ///< флаг сигнализации о наведении курсора мыши на визир
QPoint _move_point; ///< точка целеуказания, куда необходимо двигать
 
signals:
void moveVF();
 
protected:
virtual void mousePressEvent(QMouseEvent* events);
virtual void mouseMoveEvent(QMouseEvent* events);
virtual void mouseReleaseEvent(QMouseEvent* events);
virtual void resizeEvent(QResizeEvent* events);
virtual void enterEvent(QEvent* events) { _is_viewfinder = true; }
virtual void leaveEvent(QEvent* events) { _is_viewfinder = false; }
 
public:
VFFrame(QWidget *parent, const Shape shape = QFrame::VLine);
virtual ~VFFrame() {}
void setShape(const Shape shape) { setFrameShape(shape); _vf_shape = shape; }
bool isViewfinder() const { return _is_viewfinder; }
void setMovePoint(const QPoint& pnt) { _move_point = pnt; }
};
 
/// Переопределение нажатия на лекую кнопку мыши
void VFFrame::mousePressEvent(QMouseEvent* events)
{
QFrame::mousePressEvent(events);
 
emit moveVF();
 
switch(events -> button())
{
case Qt::LeftButton: ///< если нажата левая кнопка мыши
_is_left_pressed = true;
break;
}
}
 
/// Передвижение визира вслед за мышью
void VFFrame::mouseMoveEvent(QMouseEvent* events)
{
QFrame::mouseMoveEvent(events);
 
emit moveVF();
 
if(!_is_left_pressed) return;
if(_vf_shape == QFrame::VLine)
setGeometry(_move_point.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
setGeometry(x(), _move_point.y(), width(), height());
}
 
/// Отпускание кнопки мыши
void VFFrame::mouseReleaseEvent(QMouseEvent* events)
{
QFrame::mouseReleaseEvent(events);
 
emit moveVF();
 
if(!_is_left_pressed) return;
_is_left_pressed = false;
if(_vf_shape == QFrame::VLine)
setGeometry(_move_point.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
setGeometry(x(), _move_point.y(), width(), height());
_is_viewfinder = false;
}
 
 
/// Слот установки экранных координат во фрейм визира
void GraphicDisplay::setMovePoint()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
pf -> setMovePoint(_move_pointVF);
break;
}
}
 
/// Проверка на попадание фокуса на визир
bool GraphicDisplay::isViewfinder(const QPoint& pos)
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
_move_pointVF = pos;
return true;
}
return false;
}
 
/// Подсоединение сигнала
void GraphicDisplay::connectViewfinder()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
connect(pf, SIGNAL(moveVF()), this, SLOT(setMovePoint()));
break;
}
}
 
/// Разъединение сигнала
void GraphicDisplay::disconnectViewfinder()
{
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
disconnect(pf, SIGNAL(moveVF()), this, SLOT(setMovePoint()));
break;
}
}
 
/// Копирование визиров
void GraphicDisplay::copyingViewfinder(QMouseEvent* events)
{
if(events -> button() != Qt::LeftButton || events -> modifiers() != Qt::ControlModifier)
return;
foreach(VFFrame* pf, viewfinderList)
if(pf -> isViewfinder())
{
appendViewfinder(pf -> frameShape());
break;
}
}
 
/// Нажатие на кнопку - рисование "резиновой ленты"
void GraphicDisplay::mousePressEvent(QMouseEvent* events)
{
QWidget::mousePressEvent(events);
 
if(isViewfinder(events -> pos()))
{
copyingViewfinder(events);
connectViewfinder();
return;
}
// ...................
}
 
/// Переопределение функции передвижения мыши
void GraphicDisplay::mouseMoveEvent(QMouseEvent* events)
{
if(isViewfinder(events -> pos())) return;
// ......................
}
 
/// Возвращение прежнего вида курсору и изменение масштаба
void GraphicDisplay::mouseReleaseEvent(QMouseEvent* events)
{
if(isViewfinder(events -> pos()))
{ disconnectViewfinder(); return; }
}
 


Название: Re: Перемещение QFrame
Отправлено: spectre71 от Июль 24, 2009, 14:45
Не стал особо разбираться, но если правильно понял, то установка новых координат происходит по сигналу moveVF.
Так почему бы не передавать через него необходимую координату?


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 15:17
Понял правильно. Беда в том, что правильные координаты в функция mouse....Event класса GraphicDisplay!


Название: Re: Перемещение QFrame
Отправлено: ufna от Июль 24, 2009, 15:38
а почему нельзя сделать VFFrame доступ к GraphicDisplay? А в нем функцию, которая из отданных в него координат мыши вернет правильные координаты. Или наоборот, я не вникал кто чем двигает.


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 15:49
Корректные координаты доступны в защищенных функциях mouse....Event класса GraphicDisplay. Каким образом организовать доступ? Если бы знал, сам бы сделал, не спрашивая! :)


Название: Re: Перемещение QFrame
Отправлено: ufna от Июль 24, 2009, 15:55
имхо, можно так:
визир (это такая линия типа?) при нажатии на него делает grabMouse();
после этого, перемещение его идет только в его собственном мув эвенте. Подстройка идет под координаты мыши однозначно.

зачем в принципе в этом должен учавствовать GraphicDisplay?

и еще - можешь скомпилированный вариант выложить, чтобы увидеть как оно в деле? Еще есть идея, но вначале хочется убедиться.

либо так - в сиих функциях у визира не делается ничего, кроме установки внутрь себя флажка нажат/не нажат. А график дисплей в своем мув эвенте обращается к объекту визира (пусть заимеет на него указатель, если нету), смотрит на этот флажок, и если он "тру", то переносит визир куда нужно.


Название: Re: Перемещение QFrame
Отправлено: spectre71 от Июль 24, 2009, 16:04
Понял правильно. Беда в том, что правильные координаты в функция mouse....Event класса GraphicDisplay!
Если сделаешь GraphicDisplay доступным для VFFrame:
1) получашь mouse координаты mouseEvent у VFFrame - gpos= e->globalPos ().
2) pos = GraphicDisplay->mapFromGlobal(gpos)


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 16:17
Рисунок того, как это выглядит, можно посмотреть здесь:
http://www.forum.crossplatform.ru/index.php?showtopic=3093&st=0&gopid=22586&#entry22586

ufna, ты ведь там зарегистрирован. Мог бы посмотреть тему! :)


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 16:21
Если сделаешь GraphicDisplay доступным для VFFrame:
1) получашь mouse координаты mouseEvent у VFFrame - gpos= e->globalPos ().
2) pos = GraphicDisplay->mapFromGlobal(gpos)
А можно поподробнее пункты? Прости. пока не врубился как сделать!


Название: Re: Перемещение QFrame
Отправлено: spectre71 от Июль 24, 2009, 16:39
void VFFrame::mouseMoveEvent(QMouseEvent* events)
{
   QFrame::mouseMoveEvent(events);
        QPoint gpos= events->globalPos ().
        QPoint pos = GraphicDisplay->mapFromGlobal(gpos) ;
// pos - mouse pos in GraphicDisplay
   emit moveVF();
 
   if(!_is_left_pressed) return;
   if(_vf_shape == QFrame::VLine)
      setGeometry(_move_point.x(), y(), width(), height());
   else if(_vf_shape == QFrame::HLine)
      setGeometry(x(), _move_point.y(), width(), height());
}

pos - mouse позиция относительно GraphicDisplay. Далее делай с ней что хочешь


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 16:50
Спасибо.


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 24, 2009, 17:50
Подсказанное решение практически полностью подходит. Ни слоты не нужны, ничего. СПАСИБО ОГРОМНОЕ!!!

Единственное, все-таки курсор идет слегка левее визира. Но основная проблема, по сути, решена. Есть ли возможность попасть ровно под курсор?


Название: Re: Перемещение QFrame
Отправлено: AD от Июль 27, 2009, 10:16
Сделал вариант, который "почти" правильный! :)

Код
C++ (Qt)
/// Переопределение нажатия на лекую кнопку мыши
void VFFrame::mousePressEvent(QMouseEvent* events)
{
QFrame::mousePressEvent(events);
 
switch(events -> button())
{
case Qt::LeftButton: ///< если нажата левая кнопка мыши
_is_left_pressed = true;
break;
}
 
if(!_is_left_pressed) return;
 
QPoint gpos(events -> globalPos());
QPoint pos((_graph) ? _graph -> mapFromGlobal(gpos) : ((_mgraph) ? _mgraph -> mapFromGlobal(gpos) : gpos));
if(_vf_shape == QFrame::VLine)
pos.setX(pos.x() - _DELTA_COORD),
setGeometry(pos.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
pos.setY(pos.y() - _DELTA_COORD),
setGeometry(x(), pos.y(), width(), height());
}
 
/// Передвижение визира вслед за мышью
void VFFrame::mouseMoveEvent(QMouseEvent* events)
{
QFrame::mouseMoveEvent(events);
if(!_is_left_pressed) return;
 
QPoint gpos(events -> globalPos());
QPoint pos((_graph) ? _graph -> mapFromGlobal(gpos) : ((_mgraph) ? _mgraph -> mapFromGlobal(gpos) : gpos));
if(_vf_shape == QFrame::VLine)
pos.setX(pos.x() - _DELTA_COORD),
setGeometry(pos.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
pos.setY(pos.y() - _DELTA_COORD),
setGeometry(x(), pos.y(), width(), height());
}
 
/// Отпускание кнопки мыши
void VFFrame::mouseReleaseEvent(QMouseEvent* events)
{
QFrame::mouseReleaseEvent(events);
if(!_is_left_pressed) return;
 
_is_left_pressed = false;
QPoint gpos(events -> globalPos());
QPoint pos((_graph) ? _graph -> mapFromGlobal(gpos) : ((_mgraph) ? _mgraph -> mapFromGlobal(gpos) : gpos));
if(_vf_shape == QFrame::VLine)
pos.setX(pos.x() - _DELTA_COORD),
setGeometry(pos.x(), y(), width(), height());
else if(_vf_shape == QFrame::HLine)
pos.setY(pos.y() - _DELTA_COORD),
setGeometry(x(), pos.y(), width(), height());
_is_viewfinder = false;
}
 

Это "почти" состоит в том, что я из координаты вычитаю константу. У меня она равна 20. А есть возможность сделать без таких хаков? Заранее благодарен.


Название: Re: Перемещение QFrame
Отправлено: BRE от Июль 27, 2009, 10:18
Это "почти" состоит в том, что я из координаты вычитаю константу. У меня она равна 20. А есть возможность сделать без таких хаков? Заранее благодарен.
http://www.forum.crossplatform.ru/index.php?showtopic=3093&view=findpost&p=22694