13

My QComboBox derived class is contained in a QGraphicsScene at the bottom end of the (visible) screen - but it pops up downwards, thus out of view.

How is it possible to force the popup to open upwards, not downwards?

I've tried re-implementing showPopup like this:

void MyComboBox::showPopup()
{
     QAbstractItemView *popupView = view();
     popupView->move(0,-100);
     //popupView->window->move(0,-100);
     QComboBox::showPopup();
}

The result is, that the content seems to be shifted, but not the underlying popup object.

double-beep
  • 5,031
  • 17
  • 33
  • 41
user1319422
  • 271
  • 2
  • 7

4 Answers4

14

With the information found here, I was able to get it done this way:

void SteuerQComboBox::showPopup() {
    QComboBox::showPopup();
    QWidget *popup = this->findChild<QFrame*>(); 
    popup->move(popup->x(),popup->y()-this->height()-popup->height());
}

Note that it's crucially important to call the base classes "showPopup" first.

Thanks to everybody who was reading my question and thinking about it!

Philip Allgaier
  • 3,505
  • 2
  • 26
  • 53
user1319422
  • 271
  • 2
  • 7
2

user1319422's solution isn't bad, but it has two problems.

  1. If your platform has GUI animation, the listbox will animate opening downwards, then is moved above the text box.
  2. If you disable combobox animation (or you don't have it), the call to QComboBox::showPopup() still makes the GUI element start to appear on the screen already. So, moving it there would cause it to flicker as it appears in the first place and moves to the next.

So, to address the first problem, I just switched off animation:

void MyComboBox::showPopup()
{
  bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo);
  qApp->setEffectEnabled(Qt::UI_AnimateCombo, false);

  QComboBox::showPopup();
  qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects);
}

Then, for the second problem, I moved the frame in the Show event:

bool MyComboBox::eventFilter(QObject *o, QEvent *e)
{
  bool handled = false;
  if (e->type() == QEvent::Show)
  {
    if (o == view())
    {
      QWidget *frame = findChild<QFrame*>(); 

      //For some reason, the frame's geometry is GLOBAL, not relative to the QComboBox!
      frame->move(frame->x(),
                  mapToGlobal(lineEdit()->geometry().topLeft()).y() - frame->height());
    }
  }
  /*else if other filters here*/

  if (!handled)
    handled = QComboBox::eventFilter(o, e);

  return handled;
}
Len
  • 507
  • 5
  • 19
  • It works, but (of course) only after installing an event filter on `view()` (`view()->installEventFilter(this)` in `MyComboBox::setView` or, if only the default view is used, `MyComboBox`-constructor). If `MyComboBox` is not edtiable, `lineEdit()` will return `nullptr`. Use `this` instead. – pasbi Feb 22 '20 at 21:07
1

if you want to force popup to open above only when it is out of view you can do this:

void SteuerQComboBox::showPopup() {
    QComboBox::showPopup();
    QWidget *popup = this->findChild<QFrame*>(); 

    if((popup->y() + popup->height()) > this->window()->height())
        popup->move(popup->x(),popup->y()-this->height()-popup->height());
}
r2bd
  • 13
  • 3
0

Here's a solution that does not require deriving from QComboBox, using an event filter:

  • Install the eventFilter on QComboBox view
  • Move its parent upwards, by subtracting the view's height from its y coordinate.

Event filter:

#ifndef MYEVENTFILTER_H
#define MYEVENTFILTER_H

#include <QObject>
#include <QEvent>
#include <QAbstractItemView>

class myEventFilter : public QObject
{
    Q_OBJECT

public:
    myEventFilter (QObject *parent = nullptr) {}

protected:
    bool eventFilter(QObject * obj, QEvent * event) override
    {
        if (event->type() == QEvent::Show)
        {
            QAbstractItemView *v = static_cast<QAbstractItemView*>(obj);

            v->parentWidget()->move(v->parentWidget()->x(), v->parentWidget()->pos().y()-v->height());
        }

        return QObject::eventFilter(obj, event);
    }
};

#endif // MYEVENTFILTER_H

Implementation:

QComboBox *b = new QComboBox();

QStringList s = QStringList()<<"a"<<"b"<<"c";
b->insertItems(0,s);

b->setMinimumSize(200,40);
b->show();

myEventFilter *filter = new myEventFilter();
b->view()->installEventFilter(filter);

Here's the result:

pop up