Несколько сигналов для одного слота
Для моего графического интерфейса я хотел бы иметь две пары кнопок, которые прокручивают вверх и вниз по прокрутке. Первый набор кнопок должен работать, скажем, 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++ сингл =
знак означает присвоение, для использования сравнения равенства =='. And
sender` - это функция.
Ваш подход должен работать, если он реализован правильно:
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;
}