现在的位置: 首页 > 综合 > 正文

Qt Pixelator Example 看看看~

2017年12月16日 ⁄ 综合 ⁄ 共 9400字 ⁄ 字号 评论关闭

Pixelator也是个自定义委托的例子,这是个挺有意思的例子,好好看看!

这个例子展示了怎么用自定义委托去修改标准视图外观。为了完成这个任务,我们需要实现下面的组件。

1. 一个在图片做为数据项的情况下表示每个像素的模型,在这模型里,每个项都包含一个亮度值一致的像素。

2. 一个自定义委托,它使用被模型提供的信息去表示每个作为白底黑圆的像素,这些圆的半径和它像素点的黑度一致。

意思就是1,我们要实现一个自定义的模型,模型里的项是亮度一致的像素。2,自定义一个委托用使用自定义的模型提供的像素项,让黑色的半径来表示像素点的灰度值。程序截图如下:

 

程序包含三个主要类,MainWindow主窗口,ImageModel,PixelDelegate。

int main(int argc, char *argv[])
{
    Q_INIT_RESOURCE(images);

    QApplication app(argc, argv);
    MainWindow window;
#if defined(Q_OS_SYMBIAN)
    window.showMaximized();
#else
    window.show();
#endif
    window.openImage(":/images/qt.png");  // 初始化图
    return app.exec();
}
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

    void openImage(const QString &fileName);

public slots:
    void chooseImage();     // 选择图片槽
    void printImage();      // 打印图片槽
    void showAboutBox();    // 显示aboutBox槽
    void updateView();      // 更新视图槽

private:
    ImageModel *model;       // 模型指针
    QAction *printAction;    // 打印动作指针
    QString currentPath;     // 打开选择图片时用的路径
    QTableView *view;        // 主窗口的表视图指针
};
//! [0]
MainWindow::MainWindow()
{
//! [0]
    currentPath = QDir::homePath();       // home路径,就是用户文件夹位置
    model = new ImageModel(this);         // 创建ImageModel自定义模型

    QWidget *centralWidget = new QWidget; // MainWindow中心的主部件,将其他部件都放在它里面

//! [1]
    view = new QTableView;              // 创建表视图
    view->setShowGrid(false);           // 不显示表的格子
    view->horizontalHeader()->hide();   // 隐藏横向表头
    view->verticalHeader()->hide();     // 隐藏纵向表头
    view->horizontalHeader()->setMinimumSectionSize(1);  // 横向表头最小值
    view->verticalHeader()->setMinimumSectionSize(1);    // 纵向表头最小值
    view->setModel(model);              // 为表视图设置模型:ImageModel的实例
//! [1]

//! [2]
    PixelDelegate *delegate = new PixelDelegate(this);  // 实例化自定义的委托
    view->setItemDelegate(delegate);                    // 为表视图设置委托
//! [2]

//! [3]
    // 表视图下方的label和spinBox
    QLabel *pixelSizeLabel = new QLabel(tr("Pixel size:"));
    QSpinBox *pixelSizeSpinBox = new QSpinBox;
    pixelSizeSpinBox->setMinimum(4);   // 最小最大和初始值
    pixelSizeSpinBox->setMaximum(32);
    pixelSizeSpinBox->setValue(12);
//! [3]

    // 创建菜单了

    // 文件菜单和打开文件
    QMenu *fileMenu = new QMenu(tr("&File"), this);
    QAction *openAction = fileMenu->addAction(tr("&Open..."));
    openAction->setShortcuts(QKeySequence::Open);

    // 打印
    printAction = fileMenu->addAction(tr("&Print..."));
    printAction->setEnabled(false);
    printAction->setShortcut(QKeySequence::Print);

    // 退出
    QAction *quitAction = fileMenu->addAction(tr("E&xit"));
    quitAction->setShortcuts(QKeySequence::Quit);

    // help菜单和about
    QMenu *helpMenu = new QMenu(tr("&Help"), this);
    QAction *aboutAction = helpMenu->addAction(tr("&About"));

    menuBar()->addMenu(fileMenu);
    menuBar()->addSeparator();
    menuBar()->addMenu(helpMenu);

    // 连接菜单的各项信号槽
    connect(openAction, SIGNAL(triggered()), this, SLOT(chooseImage()));
    connect(printAction, SIGNAL(triggered()), this, SLOT(printImage()));
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAboutBox()));
//! [4]
    // spinBox的值改变必须和委托里setPixlSize、窗体显示的图里保持同步
    connect(pixelSizeSpinBox, SIGNAL(valueChanged(int)),
            delegate, SLOT(setPixelSize(int)));
    connect(pixelSizeSpinBox, SIGNAL(valueChanged(int)),
            this, SLOT(updateView()));
//! [4]

    // 可以开始布局显示了
    QHBoxLayout *controlsLayout = new QHBoxLayout;
    controlsLayout->addWidget(pixelSizeLabel);
    controlsLayout->addWidget(pixelSizeSpinBox);
    controlsLayout->addStretch(1);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(view);
    mainLayout->addLayout(controlsLayout);
    centralWidget->setLayout(mainLayout);

    setCentralWidget(centralWidget);   // 将这个中心部件设置到MainWindow的中心

    setWindowTitle(tr("Pixelator"));
    resize(640, 480);
//! [5]
}
//! [5]
// 菜单打开时触发这个槽:选择图片
void MainWindow::chooseImage()
{
    // 用OPEN对话框获取图片
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Choose an image"), currentPath, "*");

    if (!fileName.isEmpty())
        openImage(fileName);  // 将图片打开
}

// 将图片打开
void MainWindow::openImage(const QString &fileName)
{
    QImage image;

    if (image.load(fileName)) {
        model->setImage(image);     // 为模型载入图
        if (!fileName.startsWith(":/")) {   // 修改标题
            currentPath = fileName;
            setWindowTitle(tr("%1 - Pixelator").arg(currentPath));
        }

        printAction->setEnabled(true);      // 打印设置可用
        updateView();                       // 更新视图
    }
}

void MainWindow::printImage()
{
#ifndef QT_NO_PRINTER  // 可打印情况
    if (model->rowCount(QModelIndex())*model->columnCount(QModelIndex()) // 大图片
        > 90000) {
	    QMessageBox::StandardButton answer;
	    answer = QMessageBox::question(this, tr("Large Image Size"),
            tr("The printed image may be very large. Are you sure that "
               "you want to print it?"),
            QMessageBox::Yes | QMessageBox::No);
        if (answer == QMessageBox::No)  // 放弃打印,返回函数
            return;
    }

    QPrinter printer(QPrinter::HighResolution);

    QPrintDialog *dlg = new QPrintDialog(&printer, this);   // 打印对话框
    dlg->setWindowTitle(tr("Print Image"));

    if (dlg->exec() != QDialog::Accepted)   // 放弃打印,返回函数
        return;

    QPainter painter;
    painter.begin(&printer);  // 获取打印

    int rows = model->rowCount(QModelIndex());
    int columns = model->columnCount(QModelIndex());
    int sourceWidth = (columns+1) * ItemSize;
    int sourceHeight = (rows+1) * ItemSize;

    painter.save();   // 保存状态

    double xscale = printer.pageRect().width()/double(sourceWidth);
    double yscale = printer.pageRect().height()/double(sourceHeight);
    double scale = qMin(xscale, yscale);

    // 调整painter位置适应打印页面
    painter.translate(printer.paperRect().x() + printer.pageRect().width()/2,
                      printer.paperRect().y() + printer.pageRect().height()/2);
    painter.scale(scale, scale);  // 适应页面的缩放
    painter.translate(-sourceWidth/2, -sourceHeight/2);

    QStyleOptionViewItem option;
    QModelIndex parent = QModelIndex();

    QProgressDialog progress(tr("Printing..."), tr("Cancel"), 0, rows, this); // 展示进度
    progress.setWindowModality(Qt::ApplicationModal);  // 设置成模态对话框
    float y = ItemSize/2;

    for (int row = 0; row < rows; ++row) {  // 按行数来标记进度
        progress.setValue(row);
        qApp->processEvents();        // 应用程序响应进程事件
        if (progress.wasCanceled())
            break;

        float x = ItemSize/2;

        for (int column = 0; column < columns; ++column) {
            option.rect = QRect(int(x), int(y), ItemSize, ItemSize);
            view->itemDelegate()->paint(&painter, option,   // 打印绘制
                                        model->index(row, column, parent));
            x = x + ItemSize;
        }
        y = y + ItemSize;
    }
    progress.setValue(rows);

    painter.restore();  // 恢复先前保存状态
    painter.end();

    if (progress.wasCanceled()) {   // 进度被取消
        QMessageBox::information(this, tr("Printing canceled"),
            tr("The printing process was canceled."), QMessageBox::Cancel);
    }
#else  // 不可打印情况:弹出对话框完事
    QMessageBox::information(this, tr("Printing canceled"), 
        tr("Printing is not supported on this Qt build"), QMessageBox::Cancel);
#endif
}

// 显示about对话框
void MainWindow::showAboutBox()
{
    QMessageBox::about(this, tr("About the Pixelator example"),
        tr("This example demonstrates how a standard view and a custom\n"
           "delegate can be used to produce a specialized representation\n"
           "of data in a simple custom model."));
}

//! [6]
// 更新视图
void MainWindow::updateView()
{
    view->resizeColumnsToContents();     // 为内容调整列和行的size
    view->resizeRowsToContents();
}
//! [6]
//! [0]
// ImageModel继承QAbstractTableModel
// 因为图片矩阵和Table最接近
// rowCount,columnCount,data,headerData都需要被使用到,需要重实现
class ImageModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    ImageModel(QObject *parent = 0);

    void setImage(const QImage &image);   // 设置图片,将图片载入

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

private:
    QImage modelImage;
};
//! [0]
//! [0]
ImageModel::ImageModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}
//! [0]

//! [1]
void ImageModel::setImage(const QImage &image)
{
    modelImage = image;
    reset();   // 模型改变,需要重新设置模型
}
//! [1]

//! [2]
int ImageModel::rowCount(const QModelIndex & /* parent */) const
{
    return modelImage.height();    // 返回模型图片的高
}

int ImageModel::columnCount(const QModelIndex & /* parent */) const
//! [2] //! [3]
{
    return modelImage.width();     // 返回模型图片的宽
}
//! [3]

//! [4]
// 返回数据:索引下的像素灰度值,这个灰度用qGray函数
QVariant ImageModel::data(const QModelIndex &index, int role) const
{
    // 这里的角色是DisplayRole
    if (!index.isValid() || role != Qt::DisplayRole)
        return QVariant();
    return qGray(modelImage.pixel(index.column(), index.row()));
}
//! [4]

//! [5]
// 委托会要获取该值
QVariant ImageModel::headerData(int /* section */,
                                Qt::Orientation /* orientation */,
                                int role) const
{
    if (role == Qt::SizeHintRole)
        return QSize(1, 1);
    return QVariant();
}
//! [5]
static const int ItemSize = 256;

//! [0]
// PixelDelegate继承QAbstractItemDelegate
// 需要支持绘制项,就要重新实现paint
// 需要重新实现sizeHint
class PixelDelegate : public QAbstractItemDelegate
{
    Q_OBJECT

public:
    PixelDelegate(QObject *parent = 0);

    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const;

    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index ) const;

public slots:
    void setPixelSize(int size);    // 设置显示点的大小

private:
    int pixelSize;
};
//! [0]
//! [0]
PixelDelegate::PixelDelegate(QObject *parent)
    : QAbstractItemDelegate(parent)
{
    pixelSize = 12;  // 保持和spinBox中的值初始化相同
}
//! [0]

//! [1]
// paint的任务就是要画pixelSize大小的黑圆,作为项
void PixelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const
{
//! [2]
    if (option.state & QStyle::State_Selected)  // 项被选中
        painter->fillRect(option.rect, option.palette.highlight());
//! [1]

//! [3]
    int size = qMin(option.rect.width(), option.rect.height()); // 取更新中高和宽的较小者作为半径
//! [3] //! [4]
    int brightness = index.model()->data(index, Qt::DisplayRole).toInt(); // 取模型的值
    double radius = (size/2.0) - (brightness/255.0 * size/2.0);  // 灰度比值对应的半径大小
    if (radius == 0.0)
        return;
//! [4]

//! [5]
    painter->save(); // 保存当前painter的状态便于恢复
//! [5] //! [6]
    painter->setRenderHint(QPainter::Antialiasing, true); // 反走样
//! [6] //! [7]
    painter->setPen(Qt::NoPen);  // 设置笔
//! [7] //! [8]
    if (option.state & QStyle::State_Selected)  // 如果被选中
//! [8] //! [9]
        painter->setBrush(option.palette.highlightedText()); // 设置绘制高亮画刷
    else
//! [2]
        painter->setBrush(option.palette.text());  // 否则设置绘制黑点画刷
//! [9]

//! [10]
    // 绘制
    painter->drawEllipse(QRectF(option.rect.x() + option.rect.width()/2 - radius,
                                option.rect.y() + option.rect.height()/2 - radius,
                                2*radius, 2*radius));
    painter->restore();  // painter恢复先前状态
}
//! [10]

//! [11]
// 返回项的大小
QSize PixelDelegate::sizeHint(const QStyleOptionViewItem & /* option */,
                              const QModelIndex & /* index */) const
{
    return QSize(pixelSize, pixelSize);
}
//! [11]

//! [12]
// 设置项的大小,作为spinBox的changevalue响应槽
void PixelDelegate::setPixelSize(int size)
{
    pixelSize = size;
}
//! [12]

 

抱歉!评论已关闭.