Search code examples
c++qtstatic-methods

Call Method from QT slot in static method


I have a static method which shows a list (Items) and return an array of selected object in Items. I would like to add a checkbox to reload the list along some parameters. We are using QT 3.3.

//// Static
int CMSUI_InputDialog::FittingList(QWidget* parent,
  const CString& Title,
  const CStringArray& Items,
  bool                  IsMultiSelect,
  int                       DefaultItem,
  bool                  OkIsDefault,
    CArray<int, int>& Selecteds)
{

 int ret = 0;
 Selecteds.RemoveAll();

 /// Create grid
 QDialog    dialog(parent, 0, true);
 dialog.setCaption(QString(Title));

 QGridLayout* pLayoutGrid = Init(&dialog, OkIsDefault);

 //Create checkBox
 QCheckBox* pCheckBox = new QCheckBox(&dialog, "m_pApply_Filter");
 pCheckBox->setText("Norm Filter");

 pLayoutGrid->addWidget(pCheckBox, 0, 0);

 QObject::connect(pCheckBox, SIGNAL(stateChanged(int)), &dialog, SLOT(checkboxClicked()));

 /// Create ListBox
 QListBox* pList = new QListBox(&dialog);
 pList->setMinimumSize(QSize(370, 90));

 pList->setSelectionMode(QListBox::Extended);

 // Load the list
 int Count = Items.GetSize();
 for (int i = 0; i < Count; i++)
 {
    QString QS(Items[i]);
    pList->insertItem(QS);
 }

 if (DefaultItem >= 0 && DefaultItem < Count)
    pList->setSelected(DefaultItem, true);

 pLayoutGrid->addWidget(pList, 1, 0);

 // Connect double clic on QDialog accept
 QObject::connect(pList, SIGNAL(doubleClicked(QListBoxItem*)), &dialog, SLOT(accept()));

 if (dialog.exec() == QDialog::Accepted)
 {
    for (int i = 0; i < Count; i++)
    {
        if (pList->isSelected(i))
            Selecteds.Add(i);
    }
    ret = 1;
  }

  return ret;
}

void CMSUI_InputDialog::checkboxClicked()
{
    //To do
}

checkboxClicked() is declared in CMSUI_InputDialog.h as slot

class CMSUI_InputDialog
{
protected :

    static QGridLayout* Init(QWidget* pParent, bool OkIsDefault);

public slots:
    void checkboxClicked();


public:
    /// CheckBox  + ListBox for fittings
    static int FittingList(QWidget* parent,
        const CString& Title,
        const CStringArray& Items,
        bool                    IsMultiSelect,
        int                     DefaultItem,
        bool                    OkIsDefault,
        CArray<int, int>& Selecteds);
};

I tried many things but I'm stupid with QT and didn't got any success to catch the action on the checkbox


Solution

  • There's little Qt-specific here as far as the primary problem goes: it's all understandable in plain C++, no need for Qt knowledge.

    1. You're not checking the result returned by the connect method: it returns false when it fails. The connect fails in your case, so there's no point in going any further than that. The slot will not be called. The reason for that: dialog is just a QDialog, not CMSUI_InputDialog. You can't add any slots to an existing Qt class.

    2. Slots must be methods in QObjects. The class where you added the "slot" is not derived from QObject. You'd need to create such a class (to replace the QDialog that you're using).

    3. slots is a macro that is empty (expands to nothing) and thus has no effect on the compilation. It has a purpose, though: the moc (meta object compiler) that processes the header file will notice that macro and process the subsequent methods as slots. It won't do that unless you also have the Q_OBJECT macro within the class.

    First, you'd want to factor out the options for that dialog into a FittingOptions struct, to make the code manageable.

    struct FittingOptions {
      const CStringArray& items,
      bool                isMultiSelect,
      int                 defaultItem,
      bool                okIsDefault,
    };
    

    The dialog should become a new class, where your slot would go:

    class FittingList : public QDialog {
      Q_OBJECT
      FittingOptions opt;
      CArray<int, int>* selecteds;
    
    public:
      FittingList(QWidget *parent, const FittingOptions &options) :
        QDialog(parent), opt(options), selecteds(0)
      {
        // TODO: initialization code that creates widgets etc.
      }
    
      void setSelecteds(CArray<int, int> &selecteds)
      {
        this->selecteds = &selecteds;
      }
    
      // ...
    
    public slots:
      void checkboxChanged() { /* TODO */ }
    };
    

    And then, put the initialization code from the FittingList method into the constructor. Change that method into:

    int CMSUI_InputDialog::FittingList(QWidget* parent,
      const CString& title,
      const CStringArray& items,
      bool                isMultiSelect,
      int                 defaultItem,
      bool                okIsDefault,
      CArray<int, int>&   selecteds)
    {
       const FittingOptions options = { 
         items, isMultiSelect, defaultItem, okIsDefault
       };
       ::FittingList dialog(parent, options);
       dialog.setCaption(QString(title));
       dialog.setSelecteds(selecteds);
    
       if (dialog.exec() != QDialog::Accepted) return 0;
       return 1;
    }
    

    Look at other examples within your project to see how they went about such problems. This code is quite stale by today's standards, but it's a maintenance job as far as I understand, so you got to do more of the same - since I imagine you're not upgrading the Qt version.

    Note: this is obviously something that concerns the original author(s) of the code, not you. You haven't written this stuff. Even in Qt 3's times this code would have been considered crusty - the seemingly pervasive use of globals/singletons is cringe-worthy. I always wonder why people who work on such presumably large scale projects won't look into the code they paid for and is available to them: Qt 3 includes full source code to Qt Designer, and that's where one might have looked for inspiration/tutoring. These days, Qt Creator is a much bigger code base than Designer (by more than an order of magnitude) and is fairly decently architected, so large-scale projects might take inspiration from there.