1

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

目前,QDialog 本身存储一个指向它修改的场景对象的指针。因此,当滑块发射时负责对象移动的插槽valueChanged也是 QDialog 类的一部分。因为它无法知道哪个滑块被移动了,所以移动函数(相当低效)只是循环遍历对话框中的所有滑块,收集它们的值,然后为 3D 对象分配一个新配置。

理想情况下,只有在移动滑块时才需要重新分配已更改的维度。所以我尝试使用 QSignalMapper 来识别每个带有数字索引的滑块。这将需要能够发送valueChanged带有两个参数的信号:一个标识发送方滑块,另一个提供新值本身。不幸的是,正如我在这里学到的,QSignalMapper 不能这样做。

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

我可以想到其他几个解决方案:允许自定义滑块类存储其父对话框(在同样的说法中似乎sender()很糟糕),或者甚至可以将可移动对象本身存储为自定义滑块类的静态成员在整个对话框中(非静态/现在这样)。

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

4

4 回答 4

1

理想的解决方案是使用添加参数(x/y/z 轴)QSlider对信号进行子类化并重新发射。valueChanged()让我们说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)来自的信号,并以轴索引 (0/1/2)QSlider发出自己的信号。valueChanged(int,int)您现在可以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)));
}

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

于 2013-09-30T22:53:03.133 回答
1

可能的解决方案是连接 QSlider 信号sliderReleased(),当用户用鼠标释放滑块时发出,与 QSignalMappermap()并在某个列表上存储带有指针的滑块 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());
}
于 2013-09-30T18:37:47.903 回答
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);
    }
}
于 2013-09-30T18:22:38.933 回答
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");
    }
}
于 2014-02-19T10:00:29.640 回答