[Solved] PushButton Focus problem in a non-linear Wizard



  • Hi,

    I’m trying to make a non-linear wizard aplication which will works only with a keyboard. I have all the pages created in a línear way and I have problems trying to make it non linar.

    On the first page (WPrincipal) I have 3 buttons (b1, b2 and b3). Each one sends you to a different page(WP1, WP2 and WP3).

    @
    int WPIni::nextId() const
    {
    if (b1->hasFocus()) //This doesn’t work unless I force it with b1->setFocus() before
    return BaseWizard::PM1;
    else if (b2->hasFocus())
    return BaseWizard::PM2;
    else if (b3->hasFocus())
    return BaseWizard::PM3;
    }
    @
    Where BaseWizard is the QWizard and it has declared:

    @enum {PMIni, PM1,PM2,PM3}@

    @
    setPage(PMIni, page0);
    setPage(PM1, page1);
    setPage(PM2r, page2);
    setPage(PM3, page3);

    setStartId(PMIni);
    @

    The question is: If I put something like that, it works and changes to the right WizardPage I want

    @
    int WPIni::nextId() const
    {
    return BaseWizard::PM1;
    }
    @

    But if I put he code with the ifs (the first code) then it does nothing becuse it doen’t get the ‘hasFocus()’ unless I writte before b1->setFocus() but that is not what I want because what I need is to know when the tab is on that button.
    In other words, what I need is: If I start the aplication, I tab twice to go to third button for example and then I press Enter key, I want to go to WP3.

    I don't know if maybe a connect () for signals and slots would be the answer if it is, then I don't know how to use it 'cause I tried but I didnt succeed...

    Thank you!


  • Lifetime Qt Champion

    Hi,

    Sounds like you should rather use a set of QRadioButton for that. They would fit more naturally in the work flow.



  • Hi again SGaist :)

    I need that buttons to be really big on the screen, so the occupy the 70% of it. That's why I use push buttons so the user can see a really big rectangle button.

    Also I proved to change them to QRadioButton to see if the problem dissapear with that type but not. I still have the same problem: b1->hasFocus() its always false. I use tab to go from one button to another but it seams that the tab focus don't set b1->hasFocus() a true and I don't know how to make it.


  • Lifetime Qt Champion

    Sorry I wasn't clear enough. I proposed the QRadioButton because they feel more natural when dealing with a choice and you could use their checked state to return the right page id. However in your case you can make your QPushButtons checkable, put them in a QButtonGroup and configure that button group to be exclusive. To simplify your code, you can use the addButton ( QAbstractButton * button, int id ) function where the id you give is the page id corresponding to the button, the in nextId you can simply return the QButtonGroup::checkedId() value



  • Hi,

    Ok, if I inderstood well, that is what you mean:

    WPIni.h
    @
    private:
    QPushButton *b1;
    QPushButton *b2;
    QPushButton b3;
    QButtonGroup
    BGroup;
    @

    WPIni.cpp
    @

    WPInicial::WPInicial(QWidget *parent) :
    BasePage(parent),
    b1(new QPushButton),
    b2(new QPushButton),
    b3(new QPushButton),
    BGroup(new QButtonGroup)
    {
    BGroup->addButton(b1,1);
    BGroup->addButton(b2,2);
    BGroup->addButton(b3,3);
    [...]
    }

    int WPIni::nextId() const
    {
    return this->BGroup->checkedId();
    }
    @

    If that is, the nextId always returns -1 and its like if it were the last page.

    Thank you!

    //------------------------------------------------//
    Edited: I also tried putting
    @
    setFocusProxy(b1);
    setFocusPolicy(Qt::TabFocus);
    @


  • Lifetime Qt Champion

    Did you make the buttons checkable ? Check one by default ?



  • Hi, now when you start the program you can see 3 big buttons (b1, b2, b3) and then the wizard's buttons (back, next, cancel). This buttons are grey but if the tabfocus is on them, they became blue, so when the app start, b1 is blue and if you press Tab, b1 becames grey and b2 blue.

    Now, if I set the setCheckable() property to true to b1, b2 and b3, then when the app starts b1 is blue but if you Tab, you go directly to Fnish/Next button and you can't reach buttons b2 or b3.

    I put next / finish because in that case next button becames finish button cause it detects that pae as the last one cause nextId() still returns -1)

    Maybe the problem is that I'm doing a different thing than what I need really, I don't know...

    My intention is:

    • App starts with b1 in blue. [Now you can do p.e one of this 2 options]
    • If you press Enter you go to the next page (not to next button) which is WP1.
    • If you press Tab, b1 becames grey and b2 blue (it already does that now), and then if you press Enter you go to WP2.

    I have another question... It's there any run loop? I mean, the tipically loop that is called every second or elapsedtime as the oen used in games to run the update and render loops... So as I know the code into the loop is being read every sec I can do something like:

    @
    if (Keypressed== Enter) then //if in any moment enter is pressed:
    look wthich button has the focus
    depending the button go to the WizardPage wanted.
    endif
    @

    It's an other option to do what I need maybe... It's a posssibility I just thought 'cause I need this to work and it doesn't T_T hehe

    P.D: ischecked and has focus are diferents. I need that focus leads over check, to pick the button with the tab


  • Lifetime Qt Champion

    You can use setTabOrder to setup which widget gets the focus after the current when pressing tab. And then the space key to check the button which the user select.

    However a focused widget doesn't really tell the user that he just choose something. Either he should have a visual feedback like the one QRadioButton proposes or an exclusive checkable button group.

    The event loop is already running.



  • Yes, I have already the visual effect becasue as I said, when u tab, the buttons became blue but what I don't know is how to know which is selected because hasFocus() returns( -1), BGroup->checkedId() returns (-1) and so on... -.-''

    I've solved something but now I have 2 main problems:

    • If I press P I go to the next page. That is correct. But if I press Enter, nothing happens O.o Does anyone know why??? I want it to function with Enter key (And the same with TAB Key)

    • The other problem its the same I already had. nextId() ALWAS return - 1; (Visually with tab u see the change of the focus but it seems that for the program there is no button selected)
      @
      void WPIni::keyPressEvent(QKeyEvent *event) // definition
      {
      event->accept();

      switch(event->key())
      {
      case Qt::Key_Enter:
      wizard()->next();
      break;
      case Qt::key_P:
      wizard()->next();
      break;
      }
      @

    @
    int WPIni::nextId() const
    {
    return this->BGroup->checkedId();
    }
    @

    Can I access to that event loop? So I can call a function inside him? I can't see were it is...

    [EDITED: I saw that http://qt-project.org/doc/qt-4.8/focus.html but I don't see how to implement the filter, and still have the problem htat the tab and enter keys seems to not be recognized in the switch(event- > key) ]



  • Ok, I solved the filter and tab problem or at least it seems to.

    But I have the same problem still... (I will explain it agian to make it as clear as possible because I don't really know what to do with that and I need a solution T_T)

    I have this stylesheet on buttons that make them blue with white letters when tab focus its on them or when u click them
    @
    setStyleSheet("QPushButton{background-color:#d5d5d5; font: 20pt; font-weight: bold; } QPushButton:focus{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #d5d5d5, stop: 1 #8888a8); color: white;}");
    @

    The thing is that is u click them or u use tab, their color change as it has to.
    Example:
    You are on b1 which is blue (b2 and b3 are grey).
    You tab.
    B1 becames grey, b2 blue and b3 stays grey.
    you tab or you click b3.
    b1 is grey, b2 is grey, b3 is blue.

    Well... If the program know who has to be blue... WHY CAN't I KNOW which button is?!!!!


  • Lifetime Qt Champion

    Because the focus changed before nextId is called e.g. the Next button got it.

    If you really want to work only with the focus, subclass QPushButton, emit a signal when focusIn is called then in your QWizardPage, catch that signal and update the next id value based on the sender of that signal.



  • I didn't know about that FocusIn/Out functions.

    I think I will need a middle class (BaseButton) to reimplement that functions and emit the signal I am instered to?

    What I have now is a really big mess on my mind -.-'' (I'm sorry but I'm new with Qt and all that at once exceeds me and the more things I try that I see here or in other post, the worst that works....) So I'll apreciate a lot if you can expecify that a little bit more or tell me what I am making wrong or what shold I put down and where:

    My structure:

    • class BaseWizard : public QWizard
    • class BasePage : public QWizardPage -> All WizardPages inherite from that
    • class BaseButton : public QPushButton -> New class. The QPushButton will inheritance from it to get when they are on focus
    • class WPInicial : public BasePage -> WP initial that will see the user.The 3 buttons that it have have to redirect to WP1, WP2, y WP3.

    BaseButton.cpp:
    @
    void BaseButton::focusInEvent(QFocusEvent *e)
    {
    if (e->reason() == Qt::TabFocusReason)
    {
    QPushButton::focusInEvent(e);
    emit(focussed(true));
    }
    }
    void BaseButton::focusOutEvent(QFocusEvent *e)
    {
    if (e->reason() == Qt::TabFocusReason)
    {
    QPushButton::focusOutEvent(e);
    emit(focussed(false));
    }
    }
    void BaseButton::setFocusb1(bool value)
    {
    if (value)
    setFocus();
    }
    @

    BaseButton.h

    @
    class BaseButton : public QPushButton
    {
    Q_OBJECT
    public:
    BaseButton(QWidget *parent = 0);
    ~BaseButton();

    signals:
    void focussed(bool hasFocus);
    private slots:
    void setFocusb1(bool value);

    public:
    virtual void focusInEvent(QFocusEvent *e);
    virtual void focusOutEvent(QFocusEvent *e);
    };
    @

    WPInicial.h (The buttons declaration now with basebutton)
    @
    BaseButton *_b1;
    BaseButton *_b2;
    BaseButton *_b3;
    @
    WPInicial.cpp
    @
    In the constructor:
    _b1->installEventFilter(this);
    _b2->installEventFilter(this);
    _b3->installEventFilter(this);

    connect (_b1, SIGNAL(focussed(bool)),_b1, SLOT(setFocusb1(bool)));
    connect (_b2, SIGNAL(focussed(bool)),_b2, SLOT(setFocusb1(bool)));
    connect (_b3, SIGNAL(focussed(bool)),_b3, SLOT(setFocusb1(bool)));
    

    @

    If you think that I dont need BaseButton to get the focus then tell me so I can delete it.

    Any help to fix that mess and be able to get the focus of a button will be very apreciated :) Meanwhile I'll continue with that mess...

    Thank you so much


  • Lifetime Qt Champion

    Let's simplify things a bit:

    @
    BaseButton.cpp:
    void BaseButton::focusInEvent(QFocusEvent *e)
    {
    if (e->reason() == Qt::TabFocusReason)
    {
    emit focussed();
    }
    QPushButton::focusInEvent(e);
    }

    BaseButton.h
    class BaseButton : public QPushButton
    {
    Q_OBJECT
    public:
    BaseButton(QWidget *parent = 0);
    ~BaseButton();

    signals:
    void focussed();

    protected:
    virtual void focusInEvent(QFocusEvent *e);
    };
    WPInicial.h (The buttons declaration now with basebutton)
    BaseButton *_b1;
    BaseButton *_b2;
    BaseButton *_b3;
    QSignalMapper _mapper;
    int _nextId;
    WPInicial.cpp
    In the constructor:
    _mapper.setMapping(_b1, 0);
    _mapper.setMapping(_b2, 1);
    _mapper.setMapping(_b3, 2);
    connect (_b1, SIGNAL(focussed()),&mapper, SLOT(map()));
    connect (_b2, SIGNAL(focussed()),&mapper, SLOT(map()));
    connect (_b3, SIGNAL(focussed()),&mapper, SLOT(map()));
    connect(&_mapper, SIGNAL(mapped(int)), SLOT(buttonFocussed(int)));

    void WPInicial::buttonFoccussed(int nextId)
    {
    _nextId = nextId;
    }
    @

    [edit: Corrected typos]



  • Hi SGaist, thank you for all your help and for taking the time to write it for me :) This have became an alleviation for me really big.

    I have made those changes to run it:
    (note: I'm using Qt5)

    WPinicial.h
    @public slots:
    void buttonFoccussed(int nextId);@

    WPInicial.cpp
    @ _mapper.setMapping(_b1, 0); //the compiler said addMapping didnt exists.
    _mapper.setMapping(_b2, 1);
    _mapper.setMapping(_b3, 2);@

    And then I have had to do that:

    @int WPInicial::nextId() const
    {
    if (_b1->hasFocus())
    return BaseWizard::Page1;
    else if (_b2->hasFocus())
    return BaseWizard::Page2;
    else if (_b3->hasFocus())
    return BaseWizard::Page3;
    }@

    Page1,2,3 .... its the name of every WizarPage I gave in the enum.
    I had to make that because your code never goes to WPInicial::buttonFoccussed(int nexId) function so _nextId never gets the right number...
    With the override of nextId() I have a solution for my problem but now I'm wondering why does your code never go to buttonFocussed ??? I'm trying to understand how Qt's work with the signals and your code sound totally logic for me...

    (Note: I changed buttonFoccussed to buttonFocussed so that isn't the problem)


  • Lifetime Qt Champion

    Because my emit line had a typo and a mix between QSignalMapper and QDataWidgetMapper…

    You can replace 1,2,3 with BaseWizard::Page1 and friends so you only need to return _nextId



  • Ahh!! Ok, I understood it like internals idof the function even when I saw the Class reference -.-''

    Thank you so much!


Log in to reply
 

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