Несколько сигналов для одного слота

Для моего графического интерфейса я хотел бы иметь две пары кнопок, которые прокручивают вверх и вниз по прокрутке. Первый набор кнопок должен работать, скажем, scrollarea1, а второй набор кнопок должен работать на scrollarea2. Виджеты, которые я помещаю в область прокрутки, называются viewport1 и viewport2. Поскольку оба набора кнопок должны выполнять одно и то же (прокрутка вверх и вниз), я подумал, что сделаю два слота с именами scrollUp и scrollDown, которые будут обрабатывать прокрутку для обоих наборов кнопок. К сожалению, я не могу сделать эту работу и мне нужна помощь. Я пробовал следующее:

QPushButton up;
QPushButton down;
QPushButton up2;
QPushButton down2;

connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&down,SIGNAL(clicked()),&down,SLOT(scrollDown()));
connect(&down2,SIGNAL(clicked()),&down,SLOT(scrollDown()));

void MainWindow::scrollDown()
{
QScrollArea area;
QWidget view;

if((QPushButton) &sender = down)
{
    area=scrollArea;
    view=viewport;
}
if((QPushButton) &sender = down2)
{
    area=scrollArea;
    view=viewport;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos+15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}

void MainWindow::scrollUp()
{
QScrollArea area;
QWidget view;

if((QPushButton) &sender = up)
{
    area=scrollArea;
    view=viewport;
}
if((QPushButton) &sender = up2)
{
    area=scrollArea2;
    view=viewport2;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos-15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}

Но это не работает по нескольким причинам. Я также попытался дать слоту некоторые аргументы, что-то вроде:

connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea1,viewport1)));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea2,viewport2)));

Но опять же, безуспешно. Кто-нибудь может мне помочь?

4 ответа

Решение

Прежде всего, "это не работает" ничего не значит, и вам трудно помочь, если вы не скажете, какие ошибки вы получаете. Тогда есть несколько проблем.

Все производные классы QObject не копируются, это означает, что вы не можете сделать

QWidget a;
QWidget b;
b = a; // Wrong

Вы должны использовать указатели (или, возможно, ссылки).

QWidget a;
QWidget * b = new QWidget(...);
QWidget * c;
c = & a; // Ok
c = b; // Ok

Тогда ваш connect звонки ошибаются:

connect(&up, SIGNAL(clicked()), &up, SLOT(scrollUp()));

Третий аргумент - это объект, у которого есть слот. up QPushButton, он не имеет scrollUp() Слот, это ваше главное окно, которое делает:

connect(&up, SIGNAL(clicked()), this, SLOT(scrollUp()));

(поскольку connect вызывается в конструкторе MainWindow this указывает на текущий объект MainWindow).

Также в C++ сингл = знак означает присвоение, для использования сравнения равенства =='. Andsender` - это функция.

Ваш подход должен работать, если он реализован правильно:

class MainWindow: public QWidget
{
    QScrollArea * scroll1;
    QScrollArea * scroll2;
    QWidget * view1;
    QWidget * view2;
    QPushButton * up1;
    QPushButton * up2;
    QPushButton * down1;
    QPushButton * down2;

public:
    MainWindow()
    {
        // Here initialize member variables.
        ...

        connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp()));
        connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp()));
        connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown()));
        connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown()));
    }

public slots:
    void scrollDown()
    {
        QScrollArea * area;
        QWidget * view;

        if(qobject_cast<QPushButton>(sender()) == down1) {
            area = & scroll1;
            view = & view1;
        } else if(qobject_cast<QPushButton>(sender()) == down2) {
            area = & scroll2;
            view = & view2;
        } else {
            // Error.
        }

        // Now `area` and `view` point to the right widgets.
        ...
    }

    void scrollUp()
    {
        // The same as before.
    }
};

Другой подход заключается в извлечении фактических инструкций прокрутки в отдельную функцию:

class MainWindow: public QWidget
{
    // Same variables as before
    ...

public:
    MainWindow()
    {
        // Here initialize member variables.
        ...

        connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp1()));
        connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp2()));
        connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown1()));
        connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown2()));
    }

public slots:
    void scrollDown(QScrollArea * area, QWidget * view)
    {
        // Here you scroll over `area` and `view`.
    }

    void scrollDown1()
    {
        scrollDown(scroll1, area1);
    }

    void scrollDown2()
    {
        scrollDown(scroll2, area2);
    }

    // Again, the same for `scrollUp`.
};

В вашем коде есть несколько ошибок:

  • О отправителе сигнала: нет QObject называется "sender"но метод QObject * QObject::sender() const; который возвращает указатель на отправителя сигнала.
  • Если условия: вы применяете QPushButton** в QPushButton ((QPushButton) &sender) и вы не сравниваете эту вещь с вашими кнопками up(2) а также down(2),
  • В ваших связях между слотами и сигналами: scrollUp а также scrollDown Слоты не принадлежат к QPushButton класс, но к вашему MainWindow учебный класс.

Наконец, вы должны написать что-то вроде этого:

connect(&up,    SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&up2,   SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&down,  SIGNAL(clicked()), this, SLOT(scrollDown()));
connect(&down2, SIGNAL(clicked()), this, SLOT(scrollDown()));

void MainVindow::scrollDown() {
    // [...]

    QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender()); 
    // QPushButton * senderButton = (QPushButton *) this->sender(); works too

    if (senderButton == &down) {
        // [...]
    }

    if (senderButton == &down2) {
        // [...]
    }

    // [...]
}

void MainVindow::scrollUp() {
    // [...]

    QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender());
    // QPushButton * senderButton = (QPushButton *) this->sender(); works too

    if (senderButton == &up) {
        // [...]
    }

    if (senderButton == &up2) {
        // [...]
    }
    // [...]
}

Прежде всего, это псевдокод. Он не скомпилируется, но должен содержать необходимую информацию.

Я считаю, что эта проблема может быть наиболее элегантно решена с помощью класса QSignalMapper. Это позволяет беспараметрическим сигналам от нескольких отправителей подключаться к одному слоту.

В шапке напишите примерно так:

class QSignalMapper;

class MainWindow : public QMainWindow
{

public:
  void init();

public slots:
  void handleScrollButtons(int id);

private:
enum { ScrollUp1, ScrollDown1, ScrollUp2, ScrollDown2 } // just makes it more convenient to use

QSignalMapper *m_scrollbuttonhandler;
}

В исходном файле напишите что-то вроде этого

#include <QSignalMapper>

void MainWindow::init()
{
  m_scrollbuttonhandler = new QSignalMapper(this);
  m_scrollbuttonhandler->setMapping(scrollup1button, ScrollUp1);
  m_scrollbuttonhandler->setMapping(scrolldown1button, ScrollDown1);
  m_scrollbuttonhandler->setMapping(scrollup2button, ScrollUp2);
  m_scrollbuttonhandler->setMapping(scrolldown2button, ScrollDown2);
  connect(scrollup1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
  connect(scrolldown1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
  connect(scrollup2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
  connect(scrolldown2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));

  connect(m_scrollbuttonhandler, SIGNAL(mapped(int)), this, SLOT(handleScrollButtons(int)));
}

void MainWindow::handleScrollButtons(int id)
{
  switch (id)
  {
    case ScrollUp1:
    // stuff to do for scrollup1button
    case ScrollDown1:
    // stuff to do for scrolldown1button
    case ScrollUp2:
    // stuff to do for scrollup2button
    case ScrollDown2:
    // stuff to do for scrolldown2button
  }
}

Прежде всего, у слота не может быть никаких других аргументов, кроме сигнальных рук. Clicked не имеет аргументов, и поэтому слот не может иметь аргументов.

Я думаю, что это самый простой способ проверить, имеет ли фокус scrollArea 1 или 2, и решить, из какого из них следует двигаться.

Я также думаю, что в вашем коде есть ошибка. Не должно ли это:

if((QPushButton) &sender = down2)
{
    area=scrollArea;
    view=viewport;
}

Будь таким:

if((QPushButton) &sender = down2)
{
    area=scrollArea2;
    view=viewport2;
}
Другие вопросы по тегам