Russian Qt Forum

Qt => Общие вопросы => Тема начата: gil9red от Январь 14, 2014, 03:53



Название: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 03:53
Описание алгоритма:
Если при перемещении или изменения размера окна расстояние между краем рабочего стола и окном меньше какого то значения, то перемещаем окно к тому краю.

Создал наследника QWidget, переопределил методы resizeEvent и moveEvent, доступ к геометрии рабочего стола получал через QDesktopWidget().availableGeometry(). Понял, что применение алгоритма в resizeEvent и moveEvent плохая идея - вызывается бесконечная рекурсия, а других идей нет :(

Как решить эту проблему? :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Bepec от Январь 14, 2014, 06:59
Просто посылайте сигнал при ресайзе, мовинге. А в принимаемом слоте уже рассчитывайте всё.

У меня аналогичный класс так и работает - у родителя перехватывается resize и move, которые посылают сигнал "прилипалкину". А там уже вызывается слот, который рассчитывает, учитывает состояние (прилип, не прилип) и перемещает.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 07:45
Просто посылайте сигнал при ресайзе, мовинге. А в принимаемом слоте уже рассчитывайте всё.

У меня аналогичный класс так и работает - у родителя перехватывается resize и move, которые посылают сигнал "прилипалкину". А там уже вызывается слот, который рассчитывает, учитывает состояние (прилип, не прилип) и перемещает.
Только, наверное, стоит сказать, что connect нужно делать с Qt::QueuedConnection, иначе опять получишь рекурсию.
Имхо, зарядить таймер будет проще.

Кстати, в обработчиках событий за исключением таймерных в любом тулките нужно стараться избегать действий приводящих к генерации новых событий.
Это аксиома)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 08:00
А можно подробнее насчет таймера? :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 08:12
А можно подробнее насчет таймера? :)
int QObject::startTimer ( int interval ) (http://doc.crossplatform.ru/qt/4.7.x/qobject.html#startTimer)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Bepec от Январь 14, 2014, 08:19
Ну не знаю, в любом случае будет вызов слота и соединение.

Но принцип тот же. Что отделяем зёрна от плевел. Или ставим задачу в очередь (сигнал), или ставим таймер в очередь :D


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 08:37
Ну не знаю, в любом случае будет вызов слота и соединение.
С чего это? Разве разговор шел про QTimer?

Цитировать
Но принцип тот же. Что отделяем зёрна от плевел. Или ставим задачу в очередь (сигнал), или ставим таймер в очередь :D
Не всегда удобно, да и излишне для своих внутренних дел самому испускать и обрабатывать свои же сигналы.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Bepec от Январь 14, 2014, 08:52
Тогда я тоже заинтересован. Как же тогда обрабатывать таймер?

Я вижу варианты:
1) проверочный цикл.
2) слот timerEvent.



Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 10:22
2) слот timerEvent.
Только это не слот, а
Код
C++ (Qt)
void QObject::timerEvent ( QTimerEvent * event ) [virtual protected]
и в выше приведённой ссылке есть пример.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Bepec от Январь 14, 2014, 10:31
Каюсь, глаз замылился :)
Тоже вариант.

С таймером  быстрее, но тут решать нужно самому. Кому то понятнее сигналы, кому то таймер. Хотя попробовать надо как нить. Ни разу не использовал его так.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 11:53
Для максимального приближения к реальной задачи написал окно без системной рамки - MovableWindow.
От него отнаследовался и в наследнике реализовал вот такой алгоритм. Нормально? :)

Код
C++ (Qt)
#include <QWidget>
#include <QMoveEvent>
#include <QResizeEvent>
#include <QTimer>
#include <QApplication>
#include <QDesktopWidget>
#include <QDebug>
#include <QPainter>
#include <QSizeGrip>
#include <QHBoxLayout>
 
class MovableWindow: public QWidget
{
public:
   MovableWindow( QWidget * parent = 0 )
       : QWidget( parent )
   {
       setWindowFlags( Qt::Window | Qt::FramelessWindowHint );
 
       QHBoxLayout * layout = new QHBoxLayout();
       layout->setContentsMargins( 0, 0, 0, 0 );
       layout->addWidget( new QSizeGrip( this ), 0, Qt::AlignLeft | Qt::AlignTop );
       layout->addWidget( new QSizeGrip( this ), 0, Qt::AlignRight | Qt::AlignBottom );
       setLayout( layout );
 
       setMinimumSize( 50, 50 );
       resize( 150, 150 );
   }
 
private:
   bool isMousePress;
   QPoint oldPosition;
 
protected:
   void mousePressEvent( QMouseEvent * event )
   {
       if ( event->button() == Qt::LeftButton )
       {
           oldPosition = event->pos();
           isMousePress = true;
       }
   }
   void mouseReleaseEvent( QMouseEvent * )
   {
       isMousePress = false;
   }
   void mouseMoveEvent( QMouseEvent * event )
   {
       if ( isMousePress )
       {
           QPoint delta = event->pos() - oldPosition;
           move( pos() + delta );
       }
   }
   void paintEvent( QPaintEvent * )
   {
       QPainter painter( this );
       painter.setPen( QPen( Qt::black, 2.0 ) );
       painter.drawRect( rect() );
   }
};
 
class AttachableWindow: public MovableWindow
{
   Q_OBJECT
 
public:
   explicit AttachableWindow( QWidget * parent = 0 )
       : MovableWindow( parent )
   {
   }
 
private:
   int timerId;
 
protected:
   void timerEvent( QTimerEvent * event )
   {
       const QRect & availableGeometry = QDesktopWidget().availableGeometry();
       int d_x = availableGeometry.x();
       int d_y = availableGeometry.y();
       int d_width = availableGeometry.width();
       int d_height = availableGeometry.height();
 
       int distance = 10;
 
       if ( x() >= d_x && x() <= distance )
           move( d_x, y() );
 
       if ( y() >= d_y && y() <= distance )
           move( x(), d_y );
 
       if ( x() + width() >= d_width - distance && x() + width() <= d_width )
           move( d_width - width(), y() );
 
       if ( y() + height() >= d_height - distance && y() + height() <= d_height )
           move( x(), d_height - height() );
   }
   void mousePressEvent( QMouseEvent * event )
   {
       MovableWindow::mousePressEvent( event );
       timerId = startTimer( 333 );
   }
   void mouseReleaseEvent( QMouseEvent * event )
   {
       MovableWindow::mouseReleaseEvent( event );
       killTimer( timerId );
   }
};
 

UPDATE. Убрана лишняя проверка и уничтожение "левых" таймеров


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 12:11
В AttachableWindow::timerEvent() не мешало бы сделать проверку на QTimerEvent::timerId () == timerId и
killTimer( timerId ) делать там же, а то пока не отпустишь кнопку таймер будет срабатывать.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 12:30
В AttachableWindow::timerEvent() не мешало бы сделать проверку на QTimerEvent::timerId () == timerId и
killTimer( timerId ) делать там же, а то пока не отпустишь кнопку таймер будет срабатывать.

Такой проверки хватит? :)
Код
C++ (Qt)
   void timerEvent( QTimerEvent * )
   {
       if( event->timerId() != timerId )
       {
           killTimer( event->timerId() );
           return;
       }
...
 


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 12:32
А зачем же в этом случае прибивать таймер ???
Прибивать его следует только при QTimerEvent::timerId () == timerId.


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 12:36
А зачем же в этом случае прибивать таймер ???
Прибивать его следует только при QTimerEvent::timerId () == timerId.

Значит неправильно понял вашу идею :)
А смысл прибивать таймер, если его и так прибьет после того как будет отпущена мышь? :)
Таймер ведь работает пока зажата кнопка мыши :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 12:38
Не понравилась мне реализация с наследованием, поэтому переписал :)
Название класса кажется не удачным :-\

Код
C++ (Qt)
class AddAttachable: public QObject
{
   Q_OBJECT
 
public:
   AddAttachable( QObject * parent = 0 )
       : QObject( parent ),
         window( 0 ),
         timerId( -1 ),
         d_distance( 10 ),
         activate( true )
   {
 
   }
   void setDistance( int d )
   {
       d_distance = d;
   }
   int distance()
   {
       return d_distance;
   }
   void installTo( QWidget * widget )
   {
       if ( window )
           remove();
 
       window = widget;
       window->installEventFilter( this );
   }
   void remove()
   {
       window->removeEventFilter( this );
       window = 0;
   }
 
private:
   QWidget * window;
   int timerId;
   int d_distance;
   bool activate;
 
public slots:
   void setActivate( bool act )
   {
       activate = act;
   }
   bool isActivate()
   {
       return activate;
   }
 
protected:
   void timerEvent( QTimerEvent * event )
   {
       if ( !window || !activate ) // паранойя
           return;
 
       const QRect & availableGeometry = QDesktopWidget().availableGeometry();
       int d_x = availableGeometry.x();
       int d_y = availableGeometry.y();
       int d_width = availableGeometry.width();
       int d_height = availableGeometry.height();
 
       int x = window->x();
       int y = window->y();
       int width = window->width();
       int height = window->height();
 
       if ( x >= d_x && x <= d_distance )
           window->move( d_x, y );
 
       if ( y >= d_y && y <= d_distance )
           window->move( x, d_y );
 
       if ( x + width >= d_width - d_distance && x + width <= d_width )
           window->move( d_width - width, y );
 
       if ( y + height >= d_height - d_distance && y + height <= d_height )
           window->move( x, d_height - height );
   }
 
   bool eventFilter( QObject * object, QEvent * event )
   {
       if ( object == window && activate )
       {
           if ( event->type() == QEvent::MouseButtonPress )
               timerId = startTimer( 333 );
 
           else if ( event->type() == QEvent::MouseButtonRelease )
               killTimer( timerId );
       }
 
       return QObject::eventFilter( object, event );
   }
};
 
class MovableWindow: public QWidget
{
public:
   MovableWindow( QWidget * parent = 0 )
       : QWidget( parent )
   {
       setWindowFlags( Qt::Window | Qt::FramelessWindowHint );
 
       QHBoxLayout * layout = new QHBoxLayout();
       layout->setContentsMargins( 0, 0, 0, 0 );
       layout->addWidget( new QSizeGrip( this ), 0, Qt::AlignLeft | Qt::AlignTop );
       layout->addWidget( new QSizeGrip( this ), 0, Qt::AlignRight | Qt::AlignBottom );
       setLayout( layout );
 
       setMinimumSize( 50, 50 );
       resize( 150, 150 );
 
       AddAttachable * addAttachable = new AddAttachable( this );
       addAttachable->installTo( this );
   }
 
private:
   bool isMousePress;
   QPoint oldPosition;
 
protected:
   void mousePressEvent( QMouseEvent * event )
   {
       if ( event->button() == Qt::LeftButton )
       {
           oldPosition = event->pos();
           isMousePress = true;
       }
 
       QWidget::mousePressEvent( event );
   }
   void mouseReleaseEvent( QMouseEvent * event )
   {
       isMousePress = false;
 
       QWidget::mouseReleaseEvent( event );
   }
   void mouseMoveEvent( QMouseEvent * event )
   {
       if ( isMousePress )
       {
           QPoint delta = event->pos() - oldPosition;
           move( pos() + delta );
       }
 
       QWidget::mouseMoveEvent( event );
   }
   void paintEvent( QPaintEvent * )
   {
       QPainter painter( this );
       painter.setPen( QPen( Qt::black, 2.0 ) );
       painter.drawRect( rect() );
   }
};
 


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 12:39
А смысл прибивать таймер, если его и так прибьет после того как будет отпущена мышь? :)
Таймер ведь работает пока зажата кнопка мыши :)
Согласен)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Igors от Январь 14, 2014, 13:06
А если по-народному?
Код
C++ (Qt)
bool MyWidget::event( QEvent * e )
{
static bool inDrag = false;
 
qDebug() << e->type();
switch ((int) e->type()) {
case QEvent::NonClientAreaMouseButtonPress:
inDrag = true;
break;
 
case QEvent::NonClientAreaMouseButtonRelease:
if (inDrag)
qApp->postEvent(this, new QEvent(QEvent::User));
inDrag = false;
break;
 
case QEvent::Move:
if (!inDrag)
qApp->postEvent(this, new QEvent(QEvent::User));
break;
 
case QEvent::User:
if (pos().x() < 50)
move(QPoint(50, pos().y()));
break;
}
 
return false;
}
 


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 13:15
Igors, интересное решение :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Igors от Январь 14, 2014, 13:22
На нормальном ОС даже не нужно отслеживать драг - окно как бы "упирается", сдвинуть дальше его пользователь не может. Но на чмошном Вындоуз возникает неприятный эффект "мельтешения"  :'(  


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 14:01
На нормальном ОС даже не нужно отслеживать драг - окно как бы "упирается", сдвинуть дальше его пользователь не может. Но на чмошном Вындоуз возникает неприятный эффект "мельтешения"  :'(  
Наверное нужно всё-таки говорить не про ОС, а оконный менеджер.
В случае ТС оный не используется вовсе, так что только ручками)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Igors от Январь 14, 2014, 17:36
Наверное нужно всё-таки говорить не про ОС, а оконный менеджер.
В случае ТС оный не используется вовсе, так что только ручками)
Хмм... я полагал что FrameLess вообще не заслуживает обсуждения - ведь там никакого реентера нет, зачем совать таймер? Ну ладно, примерчик в аттаче (была заготовка для драга, добавил отсечки)

Вспоминая недавнюю тему - не лучше ли было иметь этот сервис "внешним", не загружая код рабочих окон ?  :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: GreatSnake от Январь 14, 2014, 17:41
Хмм... я полагал что FrameLess вообще не заслуживает обсуждения - ведь там никакого реентера нет, зачем совать таймер?
Согласен, через postEvent() красивее)

Цитировать
Вспоминая недавнюю тему - не лучше ли было иметь этот сервис "внешним", не загружая код рабочих окон ?  :)
Хм... чего-то не понял про что речь?


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Igors от Январь 14, 2014, 18:08
Хм... чего-то не понял про что речь?
Ну вот написан какой-то код (любой из вариантов выше, здесь не суть). Как его использовать? Когда "все с нуля" эта проблема не чувствуется - захотел создал новый класс, почему нет. А вот когда уже что-то наработано - ой как не хочется это ворошить, а если еще хоть часть кода не своя... Вот если  gil9red призадумается как сделать это "easy to use"  :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 23:09
Хм... чего-то не понял про что речь?
Ну вот написан какой-то код (любой из вариантов выше, здесь не суть). Как его использовать? Когда "все с нуля" эта проблема не чувствуется - захотел создал новый класс, почему нет. А вот когда уже что-то наработано - ой как не хочется это ворошить, а если еще хоть часть кода не своя... Вот если  gil9red призадумается как сделать это "easy to use"  :)

Подумаю, но не обещаю, что сделаю :D


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Old от Январь 14, 2014, 23:24
Подумаю, но не обещаю, что сделаю :D
Ну так вы уже придумали AddAttachable. Объект этого класса можно использовать вне перемещаемого объекта: есть готовый виджет, стало нужно что бы он начал прилипать - добавили объект класса AddAttachable и привязали к нему виджет.

Внимательно посмотрите на условие, паранойя ли? ;)  [window = 0; activate = true] -> какой результат?

Код
C++ (Qt)
   void timerEvent( QTimerEvent * event )
   {
       if ( !window && !activate ) // паранойя
           return;
 


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: gil9red от Январь 14, 2014, 23:53
Подумаю, но не обещаю, что сделаю :D
Ну так вы уже придумали AddAttachable. Объект этого класса можно использовать вне перемещаемого объекта: есть готовый виджет, стало нужно что бы он начал прилипать - добавили объект класса AddAttachable и привязали к нему виджет.
Просто я не уверен, что не захочу допилить класс, также как не уверен, что захочу :D

Внимательно посмотрите на условие, паранойя ли? ;)  [window = 0; activate = true] -> какой результат?
Код
C++ (Qt)
   void timerEvent( QTimerEvent * event )
   {
       if ( !window && !activate ) // паранойя
           return;
 
Результат будет true, только тогда, когда обе переменные имеют значение false (или 0) - не заметил - косяк :)
Надо бы условие поменять на логическое "или" (||) :)
А паранойя, потому что, в eventFilter прописано условие создания таймера и timerEvent не должен случиться, только если его не вызовут другие таймера :)


Название: Re: Прикрепление окна к краю рабочего стола
Отправлено: Old от Январь 15, 2014, 00:00
А паранойя, потому что, в eventFilter прописано условие создания таймера и timerEvent не должен случиться, только если его не вызовут другие таймера :)
В таких случаях принято ставит assert.