У меня есть собственный QDialog с набором пользовательских ползунков (т.е. QWidgets, каждый из которых состоит из ползунка и соответствующей метки) на нем. По сути, этот диалог используется для изменения координат объекта в 3D-сцене путем настройки ползунков для каждого измерения.
В настоящее время сам QDialog хранит указатель на объект сцены, который он изменяет. Таким образом, слот, который заботится о движении объекта, когда ползунок излучает valueChanged
также является частью класса QDialog. Поскольку он не может знать, какой ползунок был перемещен, функция перемещения (довольно неэффективно) просто проходит по всем ползункам в диалоговом окне, собирает их значения и назначает новую конфигурацию трехмерному объекту.
В идеале, только перемещение, которое изменилось, должно быть переназначено при перемещении ползунка. Поэтому я попытался использовать QSignalMapper для идентификации каждого ползунка с помощью числового индекса. Это потребует возможности отправить valueChanged
сигнал с двумя параметрами: один идентифицирует ползунок отправителя, а второй сам задает новое значение. К сожалению, как я узнал Вот, QSignalMapper не может этого сделать.
Другой способ получить функциональность, которую я хочу, — это использовать sender()
метод. Но, согласно документации, это плохая практика — она нарушает принцип модульности.
Я могу подумать о нескольких других решениях: разрешить пользовательскому классу ползунка сохранять родительский диалог (кажется, плохо в том же сказать, что sender()
плохо), или, возможно, даже сохраните сам подвижный объект как статический член пользовательского класса ползунка вместо (не статически / как сейчас) в общем диалоге.
Какой из этих подходов, если таковые имеются, будет лучшим путем? Какие альтернативы я должен рассмотреть?
Возможное решение — подключить сигнал QSlider sliderReleased()
, выдается, когда пользователь отпускает ползунок с помощью мыши, с помощью QSignalMapper map()
и сохранить идентификатор ползунка с указателем на некоторый список. Когда значение зависло, QDialog может выдать другой сигнал с информацией об идентификаторе слайдера и новом значении.
QSignalMapper *mapper = new QSignalMapper(this);
connect(slider_0, SIGNAL(sliderReleased()), mapper, SLOT(map()));
mapper->setMapping(slider_0, 0);
tab_s[0] = slider_0;
connect(slider_1, SIGNAL(sliderReleased()), mapper, SLOT(map()));
mapper->setMapping(slider_1, 1);
tab_s[1] = slider_1;
connect(slider_2, SIGNAL(sliderReleased()), mapper, SLOT(map()));
mapper->setMapping(slider_2, 2);
tab_s[2] = slider_2;connect(mapper, SIGNAL(mapped(int)),
this, SLOT(checkSlider(int)));
и в каком-то слоте:
void SomeDialog::checkSlider(int id)
{
emit valueChanged(id, tab_s[id]->value());
}
Идеальным решением будет подкласс QSlider
и переиздать valueChanged()
сигнал с добавленными параметрами (ось x / y / z). Скажем MySlider
который строит с заданным индексом оси (0/1/2):
class MySlider : public QSlider
{
Q_OBJECT
public:
MySlider(int axis, QWidget *parent);
signals:
void valueChanged(int axis, int value);
private slots:
void reemitValueChanged(int value);
private:
int m_axis;
};
MySlider::MySlider(int axis, QWidget *parent)
: QSlider(parent)
{
m_axis = axis;
connect(this, SIGNAL(valueChanged(int)),
this, SLOT(reemitValueChanged(int)));
}
void MySlider::reemitValueChanged(int value)
{
emit valueChanged(m_axis, value);
}
MySlider
перехватывает valueChanged(int)
сигнал от QSlider
и испускает собственный сигнал valueChanged(int,int)
с индексом оси (0/1/2). Ты можешь использовать MySlider
в приложении сейчас:
for (int i=0; i<3; i++) {
sliders[i] = new MySlider(i, this);
connect(sliders[i], SIGNAL(valueChanged(int,int)),
this, SIGNAL(updateScene(int,int)));
}
Конечно, вам придется расположить эти ползунки в макете или что-то в этом роде. Вотэто источник этого подхода.
Я думаю, используя sender()
метод в порядке.
Я бы написал что-то вроде этого:
enum Axes
{
axisX,
axisY,
axisZ
}
class MyDialog : public QDialog
{
...
signals:
void valueChanged(int value, int type);
}
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent)
{
...
connect(xSlider, SIGNAL(valueChanged(int)),
this, SLOT(onSliderChange(int));
connect(ySlider, SIGNAL(valueChanged(int)),
this, SLOT(onSliderChange(int));
connect(zSlider, SIGNAL(valueChanged(int)),
this, SLOT(onSliderChange(int));
}
void MyDialog::onSliderChange(int value)
{
int axis = -1;
QSlider *slider = dynamic_cast<QSlider*>(sender());
if (slider == xSlider)
{
axis = axisX;
}
else if (slider == ySlider)
{
axis = axisY;
}
else if (slider == zSlider)
{
axis = axisZ;
}
else
{
qWarning() << "Wrong sender";
}
if (axis != -1)
{
emit valueChanged(value, axis);
}
}
//signalMapper.h
//--------------
#ifndef SIGNALMAPPER_H
#define SIGNALMAPPER_H
#include <QWidget>
#include <QGridLayout>
#include <QSlider>
#include <QLabel>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
QGridLayout* m_pGridLayoutMain;
QLabel* m_pLabel;
private slots:
void setLabelText(QWidget *pWidget);
};
#endif // SIGNALMAPPER_H//signalMapper.cpp
//----------------
#include "signalMapper.h"#include <QSignalMapper>
#include <QString>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setMinimumSize(400, 200);
QSignalMapper* pSignalMapper = new QSignalMapper(this);
m_pGridLayoutMain = new QGridLayout(this);
m_pGridLayoutMain->setContentsMargins(10, 10, 10, 10);
m_pGridLayoutMain->setSpacing(10);
m_pLabel = new QLabel(this);
m_pLabel->setMinimumSize(150, 20);
m_pLabel->setAlignment(Qt::AlignCenter);
m_pLabel->setFrameStyle(QFrame::Box | QFrame::Sunken);
m_pGridLayoutMain->addWidget(m_pLabel, 0, 0);
for(int i=1; i < 10; i++)
{
QSlider* pSlider = new QSlider(this);
QString strObjName = "Slider " + QString().setNum(i);
pSlider->setObjectName(strObjName);
pSlider->setMinimum(0);
pSlider->setMaximum(100);
pSlider->setSingleStep(1);
pSlider->setOrientation(Qt::Horizontal);
pSlider->setValue(35);
connect(pSlider, SIGNAL(valueChanged(int)), pSignalMapper, SLOT(map()));
pSignalMapper->setMapping(pSlider, pSlider);
m_pGridLayoutMain->addWidget(pSlider, i, 0);
}
connect(pSignalMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setLabelText(QWidget*)));
}
Widget::~Widget()
{
}
void Widget::setLabelText(QWidget *pWidget)
{
QSlider* pSlider = dynamic_cast<QSlider*>(pWidget);
if(pSlider)
{
qDebug("Success");
m_pLabel->setText(pSlider->objectName()+" value changed to "+QString().setNum(pSlider->value()));
}
else
{
qDebug("Failure");
}
}