QComboBox Signal FocusInEvent or Highlighted/Activated?



  • I have a Dialog form that has a table on it that is populated by some comboboxes(3 total). After the table is populated, I don't want the user to accidently change the table by changing the data in the comboboxes. If the user clicks (or selects an item in the combobox) I want a messsagebox that asks if the user really wants to change the table and if not, set the comboindex to what it was prior to the user clicking on the comboboox. I think there are a few ways to achieve this but I am not sure which would be the best way.

    1. If the table is not empty, set combox property setEnabled (false) //This doesn't seem to be a property but would be the easiest and probably the best way... if unabled, no changes can be made.

    2. If the table is not empty, check combobox if item is activated //I need to be able to revert to the original selected item if the user clicked the combobox accidentily. I haven't tried this as I am not sure if it will work without setting member variables to store the current selected item in each combobox. Once an item is activated, isn't it the current item? Or would currentIndex still return the original index before committing the selection?

    3. Capture the FocusInEvent on the combobox and check the table to see if it is not empty. //I tried this as it seemed the best method but I ran accross several problems.

    Here is the code I used and the problems I ran accross:
    in Constructor
    @ui->cmbName->installEventFilter(this);@

    Function
    @bool MainPage::eventFilter (QObject *object, QEvent *event)
    {
    if (event->type()==QEvent::FocusIn)
    {
    int row =lapModel->rowCount();

        if (object== ui->cmbName)
        {
              if(!row ==0)
              {
                int test=QMessageBox::warning(this,"Run Table loaded", "If you select a new Name, \n "
                                          "a new table will be loaded. \n"
                                           "Do you want to load a new table?",
                                           QMessageBox::Yes | QMessageBox::No );
                    if (test==QMessageBox::No)
                    {
                        ui->txtLineDescription->setFocus();
                        return false;
                    }
                    else
                    {
                        return true;
                    }
              }
    
        }
    

    }
    return true;
    }@

    When I use the
    @ui->cmbName->installEventFilter(this);@
    the combobox is not visible. Even if I implement show() after. When I populate the comboboxes, i blocked signals, but in stepping through the program it looks like this function gets called when each item is added to the combobox. I don't think the combobox will contain so many elements as to create a problem but it doesn't seem effecient.

    I am sure that subclassing of some sort would work, but as I am a newbie I am not sure how to go about it.

    What are the thoughts on what would be the best way to achieve this? And a little bit of code (or a lot if you are motivated...lol) to get me heading in the right direction



  • You have to let your base class eventFilter() implementation take over at some point. Probably the last line should read:
    @return QMainWindow::eventFilter(object, event);@
    Assuming of course that you are here deriving from QMainWindow.



  • I am not sure where I should put the return statement you mentioned. If it is in the function, isn't the return state bool? Also, I did recode the function for the "selected" event and as I suspected, the combobox has changed when this event has fired, so I tried the "highlighted" event and it works pretty close to what I need. The only issue is sometimes the popup remains open. I even tried a hidePopup at the end of the function and it still remains open. Any ideas?



  • Put the statement where it is supposed to. It depends on the actual implementation of your method. At least replace the last "return true" with the call of the base class' implementation.



  • Well, I am a newbie at Qt so it is taking a bit to sink in. I changed the last return true with @return QMainWindow::eventFilter(object, event);@ And I got a compile error of
    error: cannot call member function 'virtual bool QObject::eventFilter(QObject*, QEvent*)' without object So I thought that maybe it should reflect the class It is in and changed it to: @return MainPage::eventFilter(object, event); @ This compiled but crashed. I degugged the program and it was a continuous loop.
    Where do I go from here?

    I am still not sure if this is the best method to accomplish what I want to do. Any comments on what would be the best method and how to fix the hidePopup issue I mentioned?



  • If you call MainPage::eventFilter() you get a recursive function, which causes a stack overflow, which crashes your program. What does MainPage inherit from? QWidget? In that case you should call QWidget::eventFilter().



  • I am still getting a recursive function, except this time in a different part. I changed the return to:
    @return QDialog::eventFilter(object, event);@ Once a user clicks on the comboBox and the event filter fires, it keeps looping as if the focusInEvent is being called. I am lost.



  • Give us the full project or a fully compiling example of the problem, please.



  • [quote author="poporacer" date="1299217306"]I am still getting a recursive function, except this time in a different part. I changed the return to:
    @return QDialog::eventFilter(object, event);@ Once a user clicks on the comboBox and the event filter fires, it keeps looping as if the focusInEvent is being called. I am lost.[/quote]

    You will end up in an endless loop if you work that way on focus in event. The reason is very simple:

    Your combo box gets the focus, you put it to the QMessageBox. With closing the message box, it returns to the combo box, which results in calling the event filter... Hope you see the loop. And you also explicitly set the focus.

    The base class event filter normally does nothing so returning false in the end should also work. True filters the event out, see "documentation":http://doc.qt.nokia.com/latest/qobject.html#eventFilter .



  • [quote author="Gerolf" date="1299230208"]The base class event filter normally does nothing so returning false in the end should also work. True filters the event out, see "documentation":http://doc.qt.nokia.com/latest/qobject.html#eventFilter .[/quote]

    You never know if the base class' event filter does something, so it is good practice to call it. If it is "empty", it returns false anyways, if not it works as expected. Only leave the call to the base class' implementation out if you're absolutely sure what you are doing and you know for sure that you do not want the base class functionality. and even if the base's event filter is empty now, this might change in the future. So, call it and forget it :-)

    This holds not only for eventFilter, but for the vast majority of virtual functions in Qt.



  • @Gerolf, Thanks for the link to the documentation. now I understand it better, you return true if you captured the event and do not want any further processing and false if you want to continue with normal event processing... Correct? But I don't see where I explicitly set the focus to the combobox. I do set the focus to another widget. I did this to try to prevent the recursiveness.
    @Volker, You say that it is good practice to call the base class' event filter. I am not sure what you mean by this. Are you saying that in pest practices, it should be included in the end as I have it or instead of returning false you should always
    @return QDialog::eventFilter(object, event);@
    ??
    And after seeing how often the focusInEvent is triggered, I think I will change it to mouseClick
    Thanks!

    [EDIT: fixed @-tags, Volker]



  • [quote author="poporacer" date="1299385172"@Volker, You say that it is good practice to call the base class' event filter. I am not sure what you mean by this. Are you saying that in pest practices, it should be included in the end as I have it or instead of returning false you should always
    @return QDialog::eventFilter(object, event);@
    ??
    And after seeing how often the focusInEvent is triggered, I think I will change it to mouseClick
    Thanks!
    [/quote]

    For all the cases where you do not handle the event yourself, call the base class' implementation and return its value. For the cases where you handle the event yourself, you must decide if the base class should get the event too; most times the answer is "yes", but there is no general guideline here.

    Be aware that this holds for almost every virtual method that you reimplement, not only eventFilter.



  • Well, I still had the recursive errors and the combobox wouldn't show. I could not figure it out. I finally tried something out of the blue and it worked. I am not sure why but this is what worked for those searching.
    My widget was subclassed from a QDialog so I thought the proper method to return was @return QDialog::eventFilter(object, event); @
    But that didn't work. Here is what I have to make it work:
    In .h file
    @bool eventFilter (QObject*, QEvent*);@

    In constructor
    @MainPage::MainPage(QWidget *parent) :
    QDialog(parent), //note: the base class is QDialog...correct?
    ui(new Ui::MainPage)
    {
    ui->cmbName->installEventFilter(this);
    ui->cmbStreet->installEventFilter(this)@
    }
    Method:

    @bool MainPage::eventFilter (QObject *object, QEvent *event)
    {

    int row =addressModel->rowCount();
    
    if (!row ==0)
    {
    
    
        if (object== ui->cmbName)
        {
              if (event->type()==QEvent::MouseButtonPress)
              {
                int test=QMessageBox::warning(this,"Table loaded", "If you select a new Name, \n "
                                          "a new table will be loaded. \n"
                                           "Do you want to load a new table?",
                                           QMessageBox::Yes | QMessageBox::No );
                    if (test==QMessageBox::No)
                    {
                        ui->txtName->setFocus();
                        return true;
                    }
                    else
                    {
                        ui->txtName->setText("");
                        setLapTable("FName = ''"); //this resets the table view to a blank table
                        return QObject::event( event); // note: Return QObject! This change made it work
                    }
              }
    
        }
        else if(object==ui->cmbStreet)
        {
              if (event->type()==QEvent::MouseButtonPress)
              {
                int test=QMessageBox::warning(this,"Table loaded", "If you select a new street, \n "
                                          "a new table will be loaded. \n"
                                           "Do you want to load a new table?",
                                           QMessageBox::Yes | QMessageBox::No );
                    if (test==QMessageBox::No)
                    {
                        ui->txtLineDescription->setFocus();
                        return true;
                    }
                    else
                    {
                        ui->txtName->setText("");
                        setLapTable("FName = ''"); //this resets the table view to a blank table
                        return QObject::event( event); // note..Return QObject! This change made it work
                    }
              }
        return QObject::event( event);
       }
    
    return QObject::event( event);
    

    }
    return QObject::event( event); //I thought you would return to the base class.QDialog?
    }@
    This works as it should!
    Thanks for your help!



  • Returning QObject::event() is definiteley wrong! Don't do this! You might end up in an endlessloop too!

    Return QDialog::eventFilter(). If that is going to create an endless loop, you should evaluate why!

    Also, I suspect the QMessageBox to mess up things. A whole bunch of events is generated during its execution and until the flow of control returns to the event filter.

    Some other remarks:

    First:
    @
    if (!row ==0)
    // you know of
    if(row != 0)
    @

    Second:
    Check at first for the event type. You do net need the row if you are not dealing with a mouse press event:

    @
    bool MainPage::eventFilter (QObject *object, QEvent *event) {
    if(event->type() != QEvent::MouseButtonPress)
    return QDialog::eventFilter(object, event);

    int row = addressModel->rowCount();
    if(row == 0)
    return QDialog::eventFilter(object, event);

    if(object == ui->cmbName) {
    // do your work here
    } else if(object == ui->cmbStreet) {
    // do your work here
    }

    // just in case we fall through
    return QDialog::eventFilter(object, event);
    }
    @



  • Thanks,
    It works like it should, and your code is more condensed! I will get it figured out eventually! Thanks for all the help!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.