zoukankan      html  css  js  c++  java
  • Qt QAbstractTableModel + QTableView 实现的一个图片播放列表编辑器

    目标:

    • 使用Qt Model/View的思想实现一个幻灯片播放列表编辑器. 有上移, 下移, 添加, 删除, 保存等功能. 效果如下图所示:

    Model(XmlModel)继承自 QAbstractTableModel, 根据需要实现对应的接口. 主要代码如下:

    • xmlmodel.h
    #ifndef XMLMODEL_H
    #define XMLMODEL_H
    
    #include <QAbstractTableModel>
    
    class XmlModel : public QAbstractTableModel
    {
        Q_OBJECT
    
    public:
        explicit XmlModel(QObject *parent = nullptr);
    
        // Header:
        QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    
        // Basic functionality:
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        int columnCount(const QModelIndex &parent = QModelIndex()) const override;
        Qt::ItemFlags flags (const QModelIndex &index) const override;      //控制表格特性, 是否可选中, 可编辑等.
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
        //移动行
        bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
        //拖拽, 暂未使用
        bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
        bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;        //删除行
        bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;        //插入行
        bool upRows(int row, int count, const QModelIndex &parent = QModelIndex());                     //上移一行
        bool downRows(int row, int count, const QModelIndex &parent = QModelIndex());                   //下移一行
        QList<QPair<QString, int>> getList();                                                      //获取model中全部数据, 用于导出等目的
    
    private:
        QList <QPair<QString, int>> m_data;
        QStringList  header;
    };
    
    #endif // XMLMODEL_H
    
    
    • xmlmodel.cpp
    #include "xmlmodel.h"
    #include <QDebug>
    
    XmlModel::XmlModel(QObject *parent)
        : QAbstractTableModel(parent)
    {
        header<<tr("素材(图片)")<<tr("播放时间(秒)");
    //    m_data.push_back(QPair<QString, int>("1.png", 5));
    //    m_data.push_back(QPair<QString, int>("2.png", 5));
    //    m_data.push_back(QPair<QString, int>("3.png", 5));
    //    m_data.push_back(QPair<QString, int>("4.png", 5));
    //    m_data.push_back(QPair<QString, int>("5.png", 5));
    }
    
    QVariant XmlModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        // FIXME: Implement me!    
        if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
            return header[section];
        return QAbstractTableModel::headerData(section,orientation,role);
    }
    
    int XmlModel::rowCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return 0;
        return m_data.count();
    
        // FIXME: Implement me!
    }
    
    int XmlModel::columnCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return 0;
        return 2;
    
        // FIXME: Implement me!
    }
    
    Qt::ItemFlags XmlModel::flags(const QModelIndex &index) const
    {
        if(index.column() == 1) //设置第二列可编辑
            return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
        else
            return  Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    }
    
    QVariant XmlModel::data(const QModelIndex &index, int role) const
    {
        //qDebug() << __FUNCTION__ << __LINE__ <<"index.row() == " << index.row() << "role===" << role;
        if (!index.isValid())
            return QVariant();
    
        // FIXME: Implement me!
        QPair<QString, int> da = m_data[index.row()];
        switch (role) {
        case Qt::DisplayRole:
        {
            switch (index.column()) {
            case 0:
                return da.first;
            case 1:
                return da.second;
            }
            break;
        }
        //编辑状态时的处理(如数据为int, 会自动将该单元格转为int调整格, 带上下按钮, 并屏蔽字母输入)
        case Qt::EditRole: 
        {
            switch (index.column()) {
            case 1:
                return da.second;
            }
            break;
        }
        default:
            break;
        }
    
        return QVariant();
    }
    
    bool XmlModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        //qDebug() << "index row = " << index.row() << ", index column =" << index.column() << ", role = " << role;
        if(index.isValid()&&role==Qt::EditRole){
            switch (index.column()) {
            case 0:
                m_data[index.row()].first = value.value<QString>();
                break;
            case 1:
                m_data[index.row()].second = value.value<int>();
                break;
            default:
                break;
            }
        }
        return true;
    }
    
    bool XmlModel::moveRows(const QModelIndex &srcParent, int srcRow, int count,
                            const QModelIndex &dstParent, int dstChild)
    {
        Q_UNUSED(srcParent);
        Q_UNUSED(dstParent);
        beginMoveRows(QModelIndex(), srcRow, srcRow + count - 1, QModelIndex(), dstChild);
        for(int i = 0; i<count; ++i) {
            m_data.insert(dstChild + i, m_data[srcRow]);
            int removeIndex = dstChild > srcRow ? srcRow : srcRow+1;
            m_data.removeAt(removeIndex);
        }
        endMoveRows();
        return true;
    }
    
    bool XmlModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
    {
        Q_UNUSED(parent);
        Q_UNUSED(column);
    
        if(row == -1) {
            row = rowCount();
        }
    
        return QAbstractTableModel::dropMimeData(data, action, row, 0, parent);
    }
    
    bool XmlModel::removeRows(int row, int count, const QModelIndex &parent)
    {
        Q_UNUSED(parent);
        beginRemoveRows(QModelIndex(), row, row + count - 1);
        for(int i = 0; i<count; ++i) {
            m_data.removeAt(row);
        }
        endRemoveRows();
        return true;
    }
    
    bool XmlModel::insertRows(int row, int count, const QModelIndex &parent)
    {
        Q_UNUSED(parent);
        beginInsertRows(QModelIndex(), row, row + count - 1);
        for(int i = 0; i<count; ++i) {
            m_data.insert(row, QPair <QString, int>("", 5));
        }
        endInsertRows();
        return true;
    }
    
    bool XmlModel::upRows(int row, int count, const QModelIndex &parent)
    {
        return moveRows(parent, row, count, parent, row -1);
    }
    
    bool XmlModel::downRows(int row, int count, const QModelIndex &parent)
    {
        return moveRows(parent, row, count, parent, row +2);    //因为是前插, 这里要加2
    }
    
    QList<QPair<QString, int> > XmlModel::getList()
    {
        return m_data;
    }
    
    

    View使用QTableView控件实现. 代码如下:

    • widget.h
    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include "xmlmodel.h"
    
    namespace Ui {
    class Widget;
    }
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private slots:
        void on_pushButton_add_clicked();
    
        void on_pushButton_up_clicked();
    
        void on_pushButton_down_clicked();
    
        void on_pushButton_del_clicked();
    
        void on_pushButton_open_clicked();
    
        void on_pushButton_save_clicked();
    
        void on_pushButton_saveas_clicked();
    
        void on_pushButton_close_clicked();
    
    private:
        Ui::Widget *ui;
        XmlModel * m_model;
        QString m_curFile;
        bool saveFile(QString path);
        bool closeFile();
        bool m_saveStatus;  //true 文件已保存, false 文件未保存;
    };
    
    #endif // WIDGET_H
    
    
    • widget.cpp
    #include "widget.h"
    #include "ui_widget.h"
    #include <QStandardItemModel>
    #include <xmlmodel.h>
    #include <QMessageBox>
    #include <QDebug>
    #include <QFileDialog>
    #include <QDomDocument>
    
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
        m_curFile = "";
        this->m_saveStatus = true;
        m_model = new XmlModel(this);
        ui->tableView->setModel(m_model);
        ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//所有列设置自动列宽
        ui->tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);//对第0列单独设置固定宽度
        ui->tableView->setColumnWidth(1, 100);//设置固定宽度
        ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);    //设置只能选单行
        ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);     //设置只能选中行
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    //添加
    void Widget::on_pushButton_add_clicked()
    {
        //m_model->insertRows(m_model->rowCount(), 1);
        QString strs;
        QStringList file_list, output_name;
        //"*.jpg" << "*.png" << "*.gif" << "*.jpeg"
        QStringList str_path_list = QFileDialog::getOpenFileNames(this, tr("选择素材"), tr(""), tr("图片文件(*.jpg *.png *.gif *.jpeg);"));
    
        //qDebug() << __FUNCTION__ <<__LINE__ << "str_path_list.size===" << str_path_list.size();
        if(0 == str_path_list.size())
            return;
        int curNewRow = m_model->rowCount();
        m_model->insertRows(m_model->rowCount(), str_path_list.size());
        QModelIndex index;
        for(int i = 0; i < str_path_list.size(); ++i)
        {
            index = m_model->index(curNewRow + i, 0);
            m_model->setData(index, str_path_list[i]);
            index = m_model->index(curNewRow + i, 1);
            m_model->setData(index, 5);
        }
        this->m_saveStatus = false;
    }
    
    //删除
    void Widget::on_pushButton_del_clicked()
    {
        int row = ui->tableView->currentIndex().row();
        if(nullptr == m_model || row < 0 || row > m_model->rowCount()-1)
        {
            QMessageBox::information(this,"提示","找不到操作项");
            return ;
        }
        if(nullptr == m_model || row < 0 || row > m_model->rowCount()-1)
            return;
        m_model->removeRows(row, 1);
        this->m_saveStatus = false;
    }
    
    //上移
    void Widget::on_pushButton_up_clicked()
    {
        int row = ui->tableView->currentIndex().row();
        if(nullptr == m_model || row < 0 || row > m_model->rowCount()-1)
        {
            QMessageBox::information(this,"提示","找不到操作项");
            return ;
        }
        //上移需要判断是否第一行,不移动
        if(0 == row)
        {
            QMessageBox::information(this,"提示","已经是第一行");
            return ;
        }
        m_model->upRows(row, 1);
        this->m_saveStatus = false;
    }
    
    //下移
    void Widget::on_pushButton_down_clicked()
    {
        int row = ui->tableView->currentIndex().row();
        if(nullptr == m_model || row < 0 || row > m_model->rowCount()-1)
        {
            QMessageBox::information(this,"提示","找不到操作项");
            return ;
        }
        //下移需要判断是否最后一行,不移动
        if(row == m_model->rowCount()-1)
        {
            QMessageBox::information(this,"提示","已经是最后一行");
            return ;
        }
        m_model->downRows(row, 1);
        this->m_saveStatus = false;
    }
    
    void Widget::on_pushButton_open_clicked()
    {
        if(!this->m_saveStatus)
        {
            int r = QMessageBox::question(this, "提示","已打开的文件尚未保存, 是否保存? ",QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::Yes);
            switch (r) {
            case QMessageBox::Cancel:
                return;
            case QMessageBox::Yes:
                on_pushButton_save_clicked();
                break;
            case QMessageBox::No:
                break;
            default:
                break;
            }
            closeFile();
        }
        QString str_path = QFileDialog::getOpenFileName(this, tr("选择列表文件"), tr(""), tr("列表文件(*.xspf);"));
        if(str_path.isEmpty())
            return;
        //qDebug() << "str_path === " << str_path;
        // FIXME: Implement me!
        m_curFile = str_path;
    
    }
    
    void Widget::on_pushButton_save_clicked()
    {
        if(0 == m_model->rowCount())
        {
            int r = QMessageBox::question(this, "提示","列表为空, 仍然保存吗? ",QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Ok);
            if (r != QMessageBox::Ok)
                return;
        }
        if(m_curFile.isEmpty())
        {
            QString str_path = QFileDialog::getSaveFileName(this, tr("保存列表文件"), tr(""), tr("列表文件(*.xspf);"));
            if(str_path.isEmpty())
                return;
            if(saveFile(str_path))
                QMessageBox::information(this,"提示","保存成功");
        }
        else
            if(saveFile(m_curFile))
                QMessageBox::information(this,"提示","保存成功");
    }
    
    void Widget::on_pushButton_saveas_clicked()
    {
        if(0 == m_model->rowCount())
        {
            int r = QMessageBox::question(this, "提示","列表为空, 仍然保存吗? ",QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Ok);
            if (r != QMessageBox::Ok)
                return;
        }
        QString str_path = QFileDialog::getSaveFileName(this, tr("保存列表文件"), tr(""), tr("列表文件(*.xspf);"));
        if(str_path.isEmpty())
            return;
        if(saveFile(m_curFile))
            QMessageBox::information(this,"提示","保存成功");
    }
    
    bool Widget::saveFile(QString path)
    {
        // FIXME: Implement me!
        Q_UNUSED(path);
        return true;
    }
    
    bool Widget::closeFile()
    {
        //清空model, 初始化参数
        if(m_model->rowCount() > 0)
            m_model->removeRows(0, m_model->rowCount());
        this->m_curFile = "";
        this->m_saveStatus = true;
        return true;
    }
    
    //关闭文件
    void Widget::on_pushButton_close_clicked()
    {
        if(!this->m_saveStatus)
        {
            int r = QMessageBox::question(this, "提示","文件尚未保存, 是否保存后关闭? ",QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::Yes);
            switch (r) {
            case QMessageBox::Cancel:
                return;
            case QMessageBox::Yes:
                on_pushButton_save_clicked();
                break;
            case QMessageBox::No:
                break;
            default:
                break;
            }
            closeFile();
        }
        else
        {
            closeFile();
        }
    
    }
    
    
    • widget.ui
    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Widget</class>
     <widget class="QWidget" name="Widget">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>792</width>
        <height>494</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>素材编辑工具</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
       <property name="spacing">
        <number>20</number>
       </property>
       <property name="leftMargin">
        <number>20</number>
       </property>
       <property name="topMargin">
        <number>20</number>
       </property>
       <property name="rightMargin">
        <number>20</number>
       </property>
       <property name="bottomMargin">
        <number>20</number>
       </property>
       <item>
        <widget class="QTableView" name="tableView"/>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_4">
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout">
           <item>
            <widget class="QPushButton" name="pushButton_open">
             <property name="text">
              <string>打开列表</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="pushButton_save">
             <property name="text">
              <string>保存</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="pushButton_saveas">
             <property name="text">
              <string>另存为</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="pushButton_close">
             <property name="text">
              <string>关闭文件</string>
             </property>
            </widget>
           </item>
          </layout>
         </item>
         <item>
          <spacer name="horizontalSpacer">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout_3">
           <item>
            <widget class="QPushButton" name="pushButton_add">
             <property name="text">
              <string>添加素材</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="pushButton_del">
             <property name="text">
              <string>删除素材</string>
             </property>
            </widget>
           </item>
          </layout>
         </item>
         <item>
          <spacer name="horizontalSpacer_2">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout_2">
           <item>
            <widget class="QPushButton" name="pushButton_up">
             <property name="text">
              <string>上移</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="pushButton_down">
             <property name="text">
              <string>下移</string>
             </property>
            </widget>
           </item>
          </layout>
         </item>
        </layout>
       </item>
      </layout>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>
    
    • main.cpp
    #include "widget.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
    
        return a.exec();
    }
    
  • 相关阅读:
    c#读取excel导入到数据库中
    查找同一个表中某个字段中有相同的数据
    Url传值编码UrlEncode和解码UrlDecode
    FOR ALL ENTRIES IN
    设置自动增长列从哪里开始增长
    More than 100 ABAP Interview Faq's(1)
    使用__FILE__和__LINE__
    Windows API封装:LoadLibrary/FreeLibrary
    Singleton模式笔记
    C++接口着色技术
  • 原文地址:https://www.cnblogs.com/linkyip/p/13498598.html
Copyright © 2011-2022 走看看