Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt 5 : 5.8.0 - Qt Creator : 4.3.0 - Qt Installer : 2.0.3 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Contraindre le mouvement d'une MainForm

30 Apr modifié dans Qt / Langage C++
Bonjour,

j'essaie de limiter le déplacement d'une QMainForm aux limites de l'écran sur mes OS de référence : Windows, Linux et mac OS X. J'utilise pour développer une Qt 5.8 (et une 5.6 pour produire du code XP MinGw :) ) sur une Debian 8.7.1 parce que finalement c'est sur Linux que le comportement me semble le plus délicat (pas plus que Mac, mais il n'y a qu'une distribution Mac).

J'ai simulé l'affichage de MDIchildren avec des QDialogs. Tout se passe bien dans les Resize et Move sauf dès que la QMainForm dépasse les limites visibles de l'écran.

J'ai encore un peu de mal avec les width et height et autres grandeurs telles que availableGeometry() et screenGeometry(). Pour l'instant le code est le suivant en ce qui concerne la gestion du bord droit de l'écran lors d'un mouvement à la souris

//Entête
#include <QEvent>
#include <QMoveEvent>
#include <QDesktopWidget>
protected:
bool eventFilter(QObject* pObject, QEvent* pEvent);

//Source
bool LimitMovement::eventFilter(QObject* pObject, QEvent* pEvent)
{
if (pEvent->type() == QEvent::KeyPress)
{
QKeyEvent* pKeyEvent = static_cast<QKeyEvent*>(pEvent);
int PressedKey = pKeyEvent->key();

if (PressedKey == Qt::Key_Return)
{
// Filter Return key
return true;
}

// Standard event processing
return QObject::eventFilter(pObject, pEvent);
}
else if (pEvent->type() == QEvent::MouseButtonPress)
{
// Filter Mouse event
QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>(pEvent);
QPoint newPosition = pMouseEvent->pos();
int widthScreen = QApplication::desktop()->availableGeometry().width();
int hightScreen = QApplication::desktop()->availableGeometry().height();
if ( (pMouseEvent->pos().x() + this->width()) >= hightScreen ) //L'approximation est là
{
pMouseEvent->ignore();
return false; //Semble non obligatoire ?
}
else
{
// Standard event processing
return QObject::eventFilter(pObject, pEvent);
}
}
}
Le code est fonctionnel mais manque de précision là où l'approximation est signalée. Visiblement j'ai un écart de quelques pixels (trop tard) pour le déclenchement du pMouseEvent->ignore();
Je cherche une lecture synthétique sur toutes les "mesures" des fenêtres (interne, externe, prenant compte des statusbar et autre toolsbar ou pas...)
Quelqu'un sait-il où on peut trouver ce genre de renseignement ?

Merci. Cordialement. AD.

Réponses

  • 30 Apr modifié
    Je voulais un truc approchant, je crois que cela peut intéresser.
    http://forum.qtfr.org/discussion/17684/coller-un-dialog-au-bord-de-l-ecran-s-il-s-en-rapproche/p1
    Comme tu dis, la detection du mouvement par Qt de la fenêtre ou de la souris se fait trop tard, c'est à dire après qu'elle est bougée.
    attention au moveEvent, car lorsque l'application le reçoit, le widget est déjà dans la nouvelle position. Le déplacer à nouveau pendant la gestion du moveEvent n'est pas une bonne idée du tout !
  • 30 Apr modifié
    Et en plus mon code est foireux au niveau du placement des accolades (le dernier else... Je me demande comment cela pouvait produire un effet =)))... On dira un effet... de bord. :))
    OK Merci. Lambe021 mais le code Windows... est pour windows.

    N'empêche que si quelqu'un a une doc synthétique sur ce comprennent exactement les Height et compagnie... (dans le genre résultat renvoyé avec TitleBar.Height compris ou non ?), je suis preneur.

    En analysant plus finement je m'aperçois que les événements à prendre en compte event->type() dans mon déplacement sont Move et d'autres dont j'ignore la signification exacte... Je me repenche sur la question.
    Cordialement. AD.
  • 30 Apr modifié
    Bon,

    j'avance un peu en simplifiant le code
    bool LimitMovement::eventFilter(QObject* pObject, QEvent* pEvent)
    {
    if (pEvent->type() == QEvent::Move)
    {
    qDebug() << "Move";
    int widthScreen = QApplication::desktop()->availableGeometry().width();
    int hightScreen = QApplication::desktop()->availableGeometry().height();
    if ( (this->pos().x() + this->width()) >= widthScreen )
    {
    qDebug() << "Oups";
    pEvent->ignore();
    return false;
    }
    else
    {
    qDebug() << pEvent->type();
    // Standard event processing
    QObject::eventFilter(pObject, pEvent);
    }
    }
    }
    Le Oups se déclenche au bon moment... mais la fenêtre continue à sortir de l'écran. Sachant que le moment est détecté maintenant je peux arrêter le déplacement des Dialogs MDI et les... cacher pour ne pas voir l'horreur. Mais pour le fun, y a-t-il un moyen à ce stage de bloquer la réalisation de l'event Move ?
  • 30 Apr modifié
    mes OS de référence : Windows, Linux et mac OS X.
    La solution 2 de @PapaJaac semble marcher sur Linux.
    mais le code Windows... est pour windows
    Bin, c'est portable. Le code en lui même n'est pas Os dépendant, c'est juste le comportement de ta fenêtre qui va différer.

    Si je suis bien, si tu écris ceci avec m_minDistance (0), ton code est déjà écrit, non ?
    (Je suis nul, donc attend confirmation d'un autre).
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QDesktopWidget>

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    m_minDistance (50),
    m_timer (new QTimer (this))
    {
    ui->setupUi(this);

    connect (m_timer, SIGNAL(timeout()), this, SLOT(testPosition()));
    m_timer->start (1000);
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    delete m_timer;
    }
    //------------------------------------------------------------------------------
    void MainWindow::testPosition ()
    {
    // la position actuelle de la fenêtre et sa nouvelle position éventuelle
    QRect windowRect = frameGeometry ();
    QPoint const currentPosition = windowRect.topLeft ();
    QPoint newPosition = currentPosition;

    // taille et position du bureau
    QDesktopWidget * desktopWidget = QApplication::desktop ();
    QRect desktopRect = desktopWidget->availableGeometry();

    // calcul des écarts haut (top) bas (bottom) gauche (left) et droit (right)
    int deltaTop = qAbs (windowRect.top () - desktopRect.top ());
    int deltaLeft = qAbs (windowRect.left () - desktopRect.left ());
    int deltaBottom = qAbs (windowRect.bottom () - desktopRect.bottom ());
    int deltaRight = qAbs (windowRect.right () - desktopRect.right ());

    // comparaison des écarts à minDistance (50 pixels)
    // on récupère la position souhaitée dans newPosition
    if (deltaBottom > 0 and deltaBottom < m_minDistance) newPosition.ry () += deltaBottom;
    if (deltaRight > 0 and deltaRight < m_minDistance) newPosition.rx () += deltaRight;
    if (deltaTop > 0 and deltaTop < m_minDistance) newPosition.ry () -= deltaTop;
    if (deltaLeft > 0 and deltaLeft < m_minDistance) newPosition.rx () -= deltaLeft;

    // faut-il déplacer la fenêtre ?
    if (newPosition != currentPosition) move (newPosition);
    }
  • 30 Apr modifié
    Mais, car il y a un "mais", on ne peut pas CHANGER la position de l'écran à l'intérieur de la méthode moveEvent, car ce changement de position appellera de nouveau moveEvent, etc... Ça fera un beau plantage.

    Heureusement, il y a une astuce: le réappel de la méthode ne se fera que si la fenêtre est VISIBLE (non-hide). Il suffira donc de cacher la fenêtre, de provoquer le changement de position, et de réafficher la fenêtre.
    Regarde sur Google.
  • Cela semble être effectivement une solution. J'aurais préféré ne pas utiliser de timer. Je continue à regarder.
  • showFullScreen() ?
    Je réfléchis aussi.
  • 30 Apr modifié
    Je ne comprends pas pourquoi existe Event->ignore() si on ne peut pas s'en servir... J'ai bien vu le série des QEvent::Type qui défile dans la sortie de l'application mais j'ai détecté aussi des marqueurs précédant ou suivant l'action Move par exemple Window(De)activate... Donc je me disais que tous ces marqueurs sont "activés", les évènements qui sont liés au déplacement de la fenêtre pourrait-être "désactivés". Je vais concentrer sur l'utilisation des Events.Ignore/Accept. J'ai réussi à obtenir un résultat imparfait par la surcharge des évènements en Lazarus. Je ne vois pas pourquoi cela ne serait pas faisable en Qt ?
  • 30 Apr modifié
    Parce que le widget est déjà déplacé, il est trop tard (je dis cela du haut de mon inexpérience).
    Tu peux mettre tous les event->ignore(); que tu veux, ton Widget bougera toujours : fait le test.
    void Dispatch::moveEvent(QMoveEvent* event)
    {
    event->ignore();
    qDebug()<<"moveEvent()";
    }
    Perso, je ferais un truc comme ça :
    void Dispatch::moveEvent(QMoveEvent* event)
    {
    event->accept();
    if(this->isVisible())
    {
    this->hide();
    this->moveTo()
    this->show();
    }
    Else
    {
    if(this == Q_NULLPTR)
    qDebug()<<"C'est Normal";
    if(this != Q_NULLPTR)
    qDebug()<<"C'est Bizarre (ou pas)";
    }

    }
    En réécrivant ça :
    class Fenetre(QtGui.QWidget):

    def __init__(self, parent=None):
    super(Fenetre, self).__init__(parent)
    self.setWindowTitle(u"test")
    self.resize(400, 300)

    # calcul des dimensions disponibles de l'écran courant
    self.screen = QtGui.QDesktopWidget().availableGeometry()
    self.wscreen, self.hscreen = self.screen.width(), self.screen.height()

    def moveEvent(self, xyold):
    # nouvelle position de la fenêtre
    x, y = self.x(), self.y()
    # taille courante de la fenêtre
    wfen, hfen = self.width(), self.height()
    # repositionnement si la position est hors limite
    if x<0:
    self.hide(); self.move(0, y); self.show()
    if y<0:
    self.hide(); self.move(x, 0); self.show()
    if x>self.wscreen-wfen:
    self.hide(); self.move(self.wscreen-wfen-1, y); self.show()
    if y>self.hscreen-hfen:
    self.hide(); self.move(x, self.hscreen-hfen-1); self.show()

    #############################################################################
    if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    fen = Fenetre()
    fen.show()
    sys.exit(app.exec_())
    [Edit]: j'ai trouvé! les dimensions hors-tout de la fenêtre sont données par "frameSize". Il suffit donc de remplacer la ligne:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    wfen, hfen = self.width(), self.height()
    par:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    wfen, hfen = self.frameSize().width(), self.frameSize().height()
  • 30 Apr modifié
    Peut -être qu'à la place du hide();
    bool QObject::blockSignals(bool block)
  • 30 Apr modifié
    Ton widget est complètement personnalisé ?
    http://doc.qt.io/qt-5/qtwidgets-widgets-shapedclock-example.html
    Ici, comme c'est toi qui fait bouger le widget et qui permet le déplacement (et non les propriétés par default de la fenêtre Qt gérée par l'OS) ...
    Par contre, comme la souris peut aller ou elle veut, ta fenêtre risque de bondir fréquemment.
  • 30 Apr modifié
    Bon je vais me contenter de la détection de la sortie de la QMainWindow de l'écran : les QDialogs seront alors cachées. j'ai pas mal appris sur les event. Si la fenêtre ne dispose pas de barre de titre, c'est jouable mais si elle en dispose, c'est l'OS qui pilote et donc là on ne peut rien faire.
    Merci pour le temps passé. Cordialement. AD.
  • 30 Apr modifié
    A tout hasard :

    Tu affiches une fenêtre complètement transparente par dessus ton widget ?
    Puis tu lies le déplacement du premier widget au second, sauf si ... ?
    Au relâchement du clique, tu repositionnes le widget 1 sur le widget 2
    Pour les bouton de la barre des tâches, c'est plus chaud.

    //////////////////////////////////////////////////////

    Autre idée :
    Tu enlèves la barre des tâches et tu en fait une perso ?
  • 3 May modifié
    @ApproxDev
    c'est l'OS qui pilote et donc là on ne peut rien faire.
        QWindowList windowList = QGuiApplication::allWindows();
    for(int i = 0 ; i < windowList.size() ; i++)
    {
    windowList.at(0)->installEventFilter(mouseClickEater);
    connect(windowList.at(0),SIGNAL(mousePressEvent(QMouseEvent*)),this,SLOT(sourisPressEvent(QMouseEvent*))); //A tester
    }
    #include "mouseclickeater.h"

    bool MouseClickEater::eventFilter(QObject *obj, QEvent *event)
    {
    qDebug()<<"filter : " << obj->objectName() << event;
    if (event->type() == QEvent::MouseButtonPress)
    {

    }
    }
    #ifndef MOUSECLICKEATER_H
    #define MOUSECLICKEATER_H

    #include <QMouseEvent>
    #include <QDebug>
    #include <QObject>
    #include <QMoveEvent>

    class MouseClickEater : public QObject
    {
    Q_OBJECT

    protected:
    bool eventFilter(QObject *obj, QEvent *event);
    };

    #endif // MOUSECLICKEATER_H
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-26, screenPos=712,254)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-25, screenPos=712,255)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-24, screenPos=712,256)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-23, screenPos=712,257)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-22, screenPos=712,258)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-21, screenPos=712,259)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-20, screenPos=712,260)
    filter : "dialog" QMouseEvent(NonClientAreaMouseMove, localPos=192,-19, screenPos=712,261)
    filter : "dialog" QMouseEvent(NonClientAreaMouseButtonPress, LeftButton, localPos=192,-19, screenPos=712,261)
    filter : "dialog" QMouseEvent(NonClientAreaMouseButtonRelease, LeftButton, localPos=192,-19, screenPos=712,261)
    NonClientAreaMouseMove
    NonClientAreaMouseButtonPress
    NonClientAreaMouseButtonRelease
    Ici, tu as bien tous les événements (c'est d'ailleurs super intéressant !!!).

    Ensuite, je fais :
    bool MainWindow::eventFilter(QObject *obj, QEvent *event)
    {
    QPoint newPosition = this->pos();
    if(oldPosition != newPosition)
    move(oldPosition);
    }
    Le widget ne bouge plus (a quelques scintillement prêt). A voir si tu acceptes cela et le résultat sur l'Os sur lequel tu es.
    Mais, tu as certainement la place de faire ce que tu veux ici.
    filter : "dialog" QMoveEvent(0,0, non-spontaneous)
    filter : "dialog" QMoveEvent(283,59)
    filter : "dialog" QMoveEvent(0,0, non-spontaneous)
    filter : "dialog" QMoveEvent(289,62)
    filter : "dialog" QMoveEvent(0,0, non-spontaneous)
    filter : "dialog" QMoveEvent(289,62)
    non-spontaneous ?? Certainement le move();
    Les événements non désirés peuvent être propagés au widget parent. Cela pourrait éviter le scintillement.
    Bizarrement les event viennent du dialog et non de statusBar()

    Donc, je me suis trompé.
  • 3 May modifié
    Voila. avec cela en plus de scintillement et plus aucun problème :
          public static void Up(MouseButtons button) {
    switch (button) {
    case MouseButtons.Left:
    mouse_event((uint) MouseEventFlags.LEFTUP, 0, 0, 0, 0);
    break;
    case MouseButtons.Right:
    mouse_event((uint) MouseEventFlags.RIGHTUP, 0, 0, 0, 0);
    break;
    }
    }
    Au click, le programme déclick.
    Tu sauvegarde la position de la souris par rapport au widget, puis, dès que le programme revient dans la zone cible, tu remets le widget sous la souris et tu click.

    Le plus simple serait de pouvoir refiler l'event au parent.

    Edit : c'est OS dépendant, erreur.
Connectez-vous ou Inscrivez-vous pour répondre.