Dans mon application, j'ai un QTableWidget avec une colonne où l'utilisateur peut cliquer pour sélectionner une couleur. Il fonctionne correctement avec la restriction suivante : lorsque l'utilisateur tape la touche d'échappement, la boîte de dialogue est annulée et se ferme comme prévu, mais l'événement de touche reste dans la file d'attente. Cela conduit à un second effet de cet événement de touche, lorsque la boîte de dialogue a été fermée, comme si l'utilisateur appuyait deux fois sur la touche.

Voici tout le code:

void CChildrenConfigScreen::actionCellClicked(int row, int col)
{
   //   ui->config_children_table->selectRow(row);
   switch(col) {
      case eColChildColor: {
            CBiStateButton* cbox = reinterpret_cast<CBiStateButton*>(ui->config_children_table->cellWidget(row, 0));
            assert( cbox != nullptr );

            if( cbox->state() != Qt::Unchecked ) {
               QTableWidgetItem* cell = ui->config_children_table->item(row, col);
               cell->setSelected(false);
               // =======
               QColorDialog dialog(this);
               dialog.setCurrentColor(cell->background().color());
               dialog.exec();
               if( dialog.result() == QDialog::Accepted ) {
                  cell->setBackground(QBrush(dialog.currentColor()));
               } else {
                  // drop escape key event ???
               }
               // =======
            }
         }
         break;
   }
}

Alors, mes questions:

  1. Est-ce le comportement normal ? Ou un bug dans Qt ?
  2. Est-ce que je fais quelque chose de mal?
  • Si oui, quoi ?
  • Si non, que puis-je faire pour "manger" cet événement ? (de préférence sans avoir à créer une classe fille QColorDialog)
0
Jacques 14 nov. 2020 à 01:23

1 réponse

Meilleure réponse

Voici la solution que j'ai mise en place. Pas vraiment beau mais le meilleur que j'ai pu faire jusqu'à présent et fonctionne selon les besoins. La définition est un peu verbeuse mais l'utilisation est vraiment pratique.

Tout d'abord, un objet basé sur une pile (Android et MacOS pas encore implémentés, uniquement Windows) :

// ==============================
// Used to disable the Escape key

class CKeyEventFilter {
   public:
      typedef char KeyType;
      typedef  std::function<void()> Functor;

   private:
      class CFilter: public QAbstractNativeEventFilter
      {
         public:

         private:
            KeyType mKey;
            Functor mAction;

         public:
            explicit CFilter(KeyType key, Functor action)
               : mKey(key)
               , mAction(action)
            {
            }

            bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
            {
               bool stop_it = false;

#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
               if (eventType == "xcb_generic_event_t") {
                   xcb_generic_event_t* event = reinterpret_cast<xcb_generic_event_t *>(message);
                   assert(event != nullptr);
                   // ...
               }
#elif defined(Q_OS_MACOS)
               if (eventType == "mac_generic_NSEvent" {
                   MSG  * event = reinterpret_cast<NSEvent*>(message);
                   assert(event != nullptr);
                  // ...
               }
#elif defined(Q_OS_WINDOWS)
               if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") {
                   MSG* event = reinterpret_cast<MSG*>(message);
                   assert(event != nullptr);

                   if(event->message == WM_KEYDOWN || event->message == WM_KEYUP ) {
                      if( event->wParam == mKey ) {
                         // keep stop_it to false for the key being processed
                         // but tells the 'next client' that he doesn't need
                         // to process it a second time.
                         mAction();
                      }
                   }
               }
#else
#error "Yet unsupported OS"
#endif

               return stop_it;
            }
      };

      CFilter mFilter;

   public:
      explicit CKeyEventFilter(KeyType key, Functor action)
         : mFilter(key, action)
      {
         qApp->installNativeEventFilter(&mFilter);
      }

      virtual ~CKeyEventFilter()
      {
         qApp->removeNativeEventFilter(&mFilter);
      }
};

Usage:

{
   // Workaround with this stack based object (0x1B = 27 = ascii code for Esc char)
   CKeyEventFilter filter(0x1B, []() { CMainWindow::win()->setSkipEscape(); } );

   // Example of purpose: if dialog is left with the Escape Key,
   // CMainWindow::setSkipEscape() in the lambda above will be called
   QTableWidgetItem* cell = ui->config_children_table->item(row, col);
   cell->setSelected(false);
   QColorDialog dialog(this);
   dialog.setCurrentColor(cell->background().color());
   dialog.exec();
   if( dialog.result() == QDialog::Accepted ) {
      cell->setBackground(QBrush(dialog.currentColor()));
   }
}

Ignorez la touche Echap là où elle a en fait déjà été traitée (dans cet exemple, dans le QColorDialog ci-dessus, mais peut être une QMessageBox ou toute autre boîte de dialogue) :

void CMainWindow::popScreen()
{
   // mSkipEscape is initialized to false in CMainWindow's constructor
   // and set by CMainWindow::setSkipEscape() in the lambda above.
   if( mSkipEscape ) {
      mSkipEscape = false;
   } else {
      if( ! mScreenStack.empty()) {
         EScreens screen = mScreenStack.top();
         mScreenStack.pop();
         this->goScreen(screen);
      }
   }
}

J'espère que cela sera utile à certaines personnes.

0
Jacques 21 nov. 2020 à 16:25