Qt QAbstractTableModel + QTableView 實現的一個圖片播放列表編輯器
阿新 • • 發佈:2020-08-13
目標:
使用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();
}