使用多个滑块在自定义QWidget中映射QSlider :: valueChanged信号

时间:2013-09-30 16:53:48

标签: c++ qt qwidget modularity qslider

我有一个自定义QDialog,上面有一组自定义滑块(即QWidgets,每个滑块都包含一个滑块和一个相关的标签)。实质上,此对话框用于通过调整每个维度的滑块来更改3D场景中对象的坐标。

目前,QDialog本身存储一个指向它修改的场景对象的指针。因此,当滑块发出valueChanged时,处理对象移动的槽也是QDialog类的一部分。由于无法知道哪个滑块被移动,因此移动功能(相当低效)只是遍历对话框中的所有滑块,收集它们的值,并为3D对象分配新配置。

理想情况下,只有在移动滑块时才需要重新分配更改的尺寸。所以我尝试使用QSignalMapper来识别带有数字索引的每个滑块。这需要能够发送带有两个参数的valueChanged信号:一个标识发送方滑块,另一个给出新值。不幸的是,当我学习here时,QSignalMapper无法做到这一点。

获得我想要的功能的另一种方法可能是使用sender()方法。但是,根据文档,这是不好的做法 - 它违反了模块化原则。

我可以想到其他一些解决方案:允许自定义滑块类存储其父对话框(看起来很糟糕,sender()是坏的),或者甚至可以将可移动对象本身存储为自定义滑块类的静态成员,而不是整个对话框中的(非静态/现在)。

这些方法中哪一个(如果有的话)是最好的方式?我应该考虑哪些替代方案?

4 个答案:

答案 0 :(得分:1)

可能的解决方案是连接QSlider信号sliderReleased(),当用户使用鼠标释放滑块时发出,使用QSignalMapper map()并在某些列表中存储带指针的滑块id。当值挂起时,QDialog可以发出另一个信号,其中包含滑块ID和新值的信息。

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());
}

答案 1 :(得分:1)

理想的解决方案是子类QSlider并使用添加的参数(x / y / z轴)重新发出valueChanged()信号。假设使用给定轴索引(0/1/2)构造MySlider

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)的{​​{1}}信号,并使用轴索引(0/1/2)发出自己的信号QSlider。您现在可以在应用程序中使用valueChanged(int,int)

MySlider

当然,你必须在布局或其他东西中安排这些滑块。 Here是这种方法的来源。

答案 2 :(得分:0)

我认为使用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);
    }
}

答案 3 :(得分:0)

//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");
    }
}