Qt浅谈之三十一数据表格(QTableWidget)

一、简介

        QTableWidget是QT对话框设计中常用的显示数据表格的控件,QTableWidget单元格数据是QTableWidgetItem对象来实现的,整个表格都需要用逐个单元格对象QTableWidgetItem构建起来。


二、详解

1、代码

(1)table.h

  1. #ifndef TABLE_H
  2. #define TABLE_H
  3. #include <QtGui>
  4. class Table : public QTableWidget
  5. {
  6. Q_OBJECT
  7. public:
  8. Table(QWidget *parent = 0);
  9. ~Table();
  10. void setColumnValue(const int &columnSum, const QStringList &header); //set header value
  11. void setHeaderWidth(const int &index, const int &width); //set header and column widhth for each index
  12. void setHeaderHeight(const int &height); //set header height
  13. void addRowValue(const int &height, const QStringList &value, const QIcon &fileIcon);
  14. void setRowH(const int &index, const int &height);
  15. void setItemFixed(bool flag);
  16. bool getSelectedRow(QList <int> &rowList);
  17. protected:
  18. void contextMenuEvent(QContextMenuEvent *event);
  19. QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
  20. void keyPressEvent(QKeyEvent *event);
  21. private:
  22. void createActions();
  23. private slots:
  24. void slotItemEntered(QTableWidgetItem *item);
  25. void slotActionRename();
  26. void slotItemSelectionChanged();
  27. private:
  28. int tableWidth;
  29. int tableHeight;
  30. QList <int>rowHeghtList;
  31. QList <int>rowWidthList;
  32. QMenu *popMenu;
  33. QAction *actionName;
  34. QAction *actionSize;
  35. QAction *actionType;
  36. QAction *actionDate;
  37. QAction *actionOpen;
  38. QAction *actionDownload;
  39. QAction *actionFlush;
  40. QAction *actionDelete;
  41. QAction *actionRename;
  42. QAction *actionCreateFolder;
  43. QTableWidgetItem *rightClickedItem;
  44. QMap <QTableWidgetItem *, QString>fileMap;
  45. bool dupFlag;
  46. };
  47. // custom item delegate class
  48. class NoFocusDelegate : public QStyledItemDelegate
  49. {
  50. protected:
  51. void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
  52. };
  53. #endif // TABLE_H
(2)table.cpp
  1. #include "table.h"
  2. Table::Table(QWidget *parent)
  3. : QTableWidget(parent)
  4. , rightClickedItem(NULL)
  5. , dupFlag(false)
  6. {
  7. rowHeghtList.clear();
  8. rowWidthList.clear();
  9. fileMap.clear();
  10. this->setMouseTracking(true);
  11. //setWindowTitle(tr("table"));
  12. horizontalHeader()->setDefaultSectionSize(100);
  13. verticalHeader()->setDefaultSectionSize(30); //设置默认行高
  14. tableWidth = 100;
  15. tableHeight = 30;
  16. horizontalHeader()->setClickable(false); //设置表头不可点击(默认点击后进行排序
  17. QFont font = horizontalHeader()->font(); //设置表头字体加粗
  18. font.setBold(true);
  19. horizontalHeader()->setFont(font);
  20. horizontalHeader()->setStretchLastSection(true); //设置充满表宽度
  21. horizontalHeader()->setMovable(false); //表头左右互换
  22. //verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
  23. setFrameShape(QFrame::NoFrame); //设置无边框
  24. //setShowGrid(false); //设置不显示格子线
  25. verticalHeader()->setVisible(false); //设置垂直头不可见
  26. setSelectionMode(QAbstractItemView::ExtendedSelection); //可多选(Ctrl、Shift、 Ctrl+A都可以)
  27. setSelectionBehavior(QAbstractItemView::SelectRows); //设置选择行为时每次选择一行
  28. setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑
  29. setStyleSheet("selection-background-color:lightblue;"); //设置选中背景色
  30. //horizontalHeader()->setStyleSheet("QHeaderView::section{background:skyblue;}"); //设置表头背景色
  31. //setStyleSheet("background: rgb(56,56,56);alternate-background-color:rgb(48,51,55);selection-background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(56,56,56),stop:1 rgb(76,76,76));"); //设置选中背景色
  32. horizontalHeader()->setStyleSheet("QHeaderView::section{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(134, 245, 99, 255),stop:0.5 rgba(134, 148, 99, 255),stop:1 rgba(115, 87, 128, 255));color:rgb(25, 70, 100);padding-left: 1px;border: 1px solid #FFFF00;}"); //设置表头背景色
  33. setAlternatingRowColors(true);
  34. //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  35. //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  36. //设置水平、垂直滚动条样式
  37. horizontalScrollBar()->setStyleSheet("QScrollBar{background:transparent; height:12px;}"
  38. "QScrollBar::handle{background:lightgray; border:2px solid transparent; border-radius:5px;}"
  39. "QScrollBar::handle:hover{background:gray;}"
  40. "QScrollBar::sub-line{background:transparent;}"
  41. "QScrollBar::add-line{background:transparent;}");
  42. verticalScrollBar()->setStyleSheet("QScrollBar{background:transparent; width:12px;}"
  43. "QScrollBar::handle{background:lightgray; border:2px solid transparent; border-radius:5px;}"
  44. "QScrollBar::handle:hover{background:gray;}"
  45. "QScrollBar::sub-line{background:transparent;}"
  46. "QScrollBar::add-line{background:transparent;}");
  47. // set the item delegate to your table widget
  48. setItemDelegate(new NoFocusDelegate()); //虚线边框去除
  49. //setFocusPolicy(Qt::NoFocus); //去除选中虚线框
  50. horizontalHeader()->setHighlightSections(false); //点击表时不对表头行光亮(获取焦点)
  51. createActions();
  52. setItemFixed(false);
  53. connect(this, SIGNAL(itemEntered(QTableWidgetItem*)), this , SLOT(slotItemEntered(QTableWidgetItem*)));
  54. connect(this, SIGNAL(itemSelectionChanged()), this , SLOT(slotItemSelectionChanged()));
  55. //this->resize(600, 600);
  56. }
  57. Table::~Table()
  58. {
  59. }
  60. void Table::setColumnValue(const int &columnSum, const QStringList &header)
  61. {
  62. setColumnCount(columnSum); //设置列数
  63. this->setHorizontalHeaderLabels(header); //设置列的标签
  64. }
  65. void Table::setHeaderWidth(const int &index, const int &width)
  66. {
  67. horizontalHeader()->resizeSection(index,width);
  68. if (rowWidthList.count() <= index + 1) {
  69. rowWidthList.append(width);
  70. }
  71. else {
  72. rowWidthList[index+1] = width;
  73. }
  74. tableWidth = 0;
  75. for(int index = 0; index < rowWidthList.count(); index++)
  76. tableWidth += rowWidthList.at(index);
  77. resize(tableWidth, tableHeight);
  78. }
  79. void Table::setHeaderHeight(const int &height)
  80. {
  81. horizontalHeader()->setFixedHeight(height); //设置表头的高度
  82. if (rowHeghtList.isEmpty()) {
  83. rowHeghtList.append(height);
  84. }
  85. else {
  86. rowHeghtList[0] = height;
  87. }
  88. tableHeight = 0;
  89. for(int index = 0; index < rowHeghtList.count(); index++)
  90. tableHeight += rowHeghtList.at(index);
  91. resize(tableWidth, tableHeight);
  92. }
  93. void Table::addRowValue(const int &height, const QStringList &value, const QIcon &fileIcon)
  94. {
  95. int row_count = rowCount(); //获取表单行数
  96. insertRow(row_count); //插入新行
  97. setRowHeight(row_count, height);
  98. for (int index = 0; index < columnCount(); index++) {
  99. QTableWidgetItem *item = new QTableWidgetItem;
  100. if (index == 0) {
  101. item->setIcon(fileIcon);
  102. item->setTextAlignment(Qt::AlignVCenter | Qt::AlignLeft);
  103. fileMap.insert(item, value.at(index));
  104. }
  105. else {
  106. item->setTextAlignment(Qt::AlignCenter);
  107. }
  108. item->setText(value.at(index));
  109. setItem(row_count, index, item);
  110. }
  111. rowHeghtList.append(height);
  112. tableHeight += height;
  113. resize(tableWidth, tableHeight);
  114. }
  115. void NoFocusDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex &index) const
  116. {
  117. QStyleOptionViewItem itemOption(option);
  118. if (itemOption.state & QStyle::State_HasFocus)
  119. itemOption.state = itemOption.state ^ QStyle::State_HasFocus;
  120. QStyledItemDelegate::paint(painter, itemOption, index);
  121. }
  122. void Table::setRowH(const int &index, const int &height)
  123. {
  124. setRowHeight(index, height);
  125. if (rowHeghtList.count() <= index + 1) {
  126. rowHeghtList.append(height);
  127. }
  128. else {
  129. rowHeghtList[index+1] = height;
  130. }
  131. tableHeight = 0;
  132. for(int index = 0; index < rowHeghtList.count(); index++)
  133. tableHeight += rowHeghtList.at(index);
  134. resize(tableWidth, tableHeight);
  135. }
  136. void Table::createActions()
  137. {
  138. popMenu = new QMenu();
  139. actionName = new QAction(this);
  140. actionSize = new QAction(this);
  141. actionType = new QAction(this);
  142. actionDate = new QAction(this);
  143. actionOpen = new QAction(this);
  144. actionDownload = new QAction(this);
  145. actionFlush = new QAction(this);
  146. actionDelete = new QAction(this);
  147. actionRename = new QAction(this);
  148. actionCreateFolder = new QAction(this);
  149. actionOpen->setText(tr("打开"));
  150. actionDownload->setText(tr("下载"));
  151. actionFlush->setText(tr("刷新"));
  152. actionDelete->setText(tr("删除"));
  153. actionRename->setText(tr("重命名"));
  154. actionCreateFolder->setText(tr("新建文件夹"));
  155. actionName->setText(tr("名称"));
  156. actionSize->setText(tr("大小"));
  157. actionType->setText(tr("项目类型"));
  158. actionDate->setText(tr("修改日期"));
  159. actionFlush->setShortcut(QKeySequence::Refresh);
  160. connect(actionRename, SIGNAL(triggered()), this, SLOT(slotActionRename()));
  161. }
  162. void Table::contextMenuEvent(QContextMenuEvent *event)
  163. {
  164. popMenu->clear();
  165. QPoint point = event->pos();
  166. rightClickedItem = this->itemAt(point);
  167. if(rightClickedItem != NULL) {
  168. popMenu->addAction(actionDownload);
  169. popMenu->addAction(actionFlush);
  170. popMenu->addSeparator();
  171. popMenu->addAction(actionDelete);
  172. popMenu->addAction(actionRename);
  173. popMenu->addSeparator();
  174. popMenu->addAction(actionCreateFolder);
  175. QMenu *sortStyle = popMenu->addMenu(tr("排序"));
  176. sortStyle->addAction(actionName);
  177. sortStyle->addAction(actionSize);
  178. sortStyle->addAction(actionType);
  179. sortStyle->addAction(actionDate);
  180. popMenu->exec(QCursor::pos());
  181. event->accept();
  182. }
  183. }
  184. QModelIndex Table::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
  185. {
  186. //重写移动光标事件,当存在编辑项的时候,让光标永远位于当前项(编辑项),否则返回父类
  187. if(rightClickedItem && rightClickedItem->row() >= 0) {
  188. return currentIndex();
  189. }
  190. else {
  191. return QTableWidget::moveCursor(cursorAction, modifiers);
  192. }
  193. }
  194. void Table::keyPressEvent(QKeyEvent *event)
  195. {
  196. if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
  197. QTableWidgetItem *item = currentItem();
  198. if (item) {
  199. closePersistentEditor(item);
  200. openPersistentEditor(item);
  201. slotItemSelectionChanged();
  202. dupFlag = false;
  203. }
  204. }
  205. }
  206. void Table::slotItemSelectionChanged()
  207. {
  208. //关闭编辑项
  209. if (rightClickedItem && dupFlag == false) {
  210. int editRow = rightClickedItem->row();
  211. QTableWidgetItem *item = this->item(editRow, 0);
  212. QMap <QTableWidgetItem *, QString>::iterator it;
  213. for (it = fileMap.begin(); it != fileMap.end(); ++it) {
  214. if (it.key() != item) {
  215. if (it.value() == item->text()) {
  216. dupFlag = true;
  217. }
  218. }
  219. }
  220. if (dupFlag == false) {
  221. this->closePersistentEditor(item);
  222. rightClickedItem = NULL;
  223. }
  224. else {
  225. QMessageBox::critical(this,tr("错误提示"),tr("文件重名"), tr("确定"));
  226. setCurrentItem(item);
  227. }
  228. }
  229. else {
  230. dupFlag = false;
  231. }
  232. }
  233. void Table::setItemFixed(bool flag)
  234. {
  235. if (flag == true)
  236. horizontalHeader()->setResizeMode(QHeaderView::Fixed);
  237. else
  238. horizontalHeader()->setResizeMode(QHeaderView::Interactive);
  239. }
  240. bool Table::getSelectedRow(QList <int> &rowList)
  241. {
  242. //多选并获取所选行
  243. QList <QTableWidgetItem *> items = this->selectedItems();
  244. int itemCount = items.count();
  245. if(itemCount <= 0) {
  246. return false;
  247. }
  248. for (int index = 0; index < itemCount; index++) {
  249. int itemRow = this->row(items.at(index));
  250. rowList.append(itemRow);
  251. }
  252. return true;
  253. }
  254. void Table::slotItemEntered(QTableWidgetItem *item)
  255. {
  256. if(!item)
  257. return;
  258. QString name = item->text();
  259. if (name.isEmpty())
  260. return;
  261. QToolTip::showText(QCursor::pos(), name);
  262. }
  263. void Table::slotActionRename()
  264. {
  265. //获得当前节点并获取编辑名称
  266. if (rightClickedItem) {
  267. int editRow = rightClickedItem->row();
  268. QTableWidgetItem *item = this->item(editRow, 0); //编辑的行号及第一列
  269. this->setCurrentCell(editRow, 0);
  270. this->openPersistentEditor(item); //打开编辑项
  271. this->editItem(item);
  272. }
  273. }
(3)tablewidget.h
  1. #ifndef TABLEWIDGET_H
  2. #define TABLEWIDGET_H
  3. #include "table.h"
  4. class TableWidget : public QWidget
  5. {
  6. Q_OBJECT
  7. public:
  8. TableWidget(QWidget *parent = 0);
  9. ~TableWidget();
  10. private:
  11. bool ScanFile(const QString & path);
  12. private:
  13. Table *table;
  14. };
  15. #endif // TABLEWIDGET_H
(4)tablewidget.cpp
  1. #include "tablewidget.h"
  2. TableWidget::TableWidget(QWidget *parent)
  3. : QWidget(parent)
  4. {
  5. QTextCodec*codec = QTextCodec::codecForName("utf8");
  6. QTextCodec::setCodecForLocale(codec);
  7. QTextCodec::setCodecForCStrings(codec);
  8. QTextCodec::setCodecForTr(codec);
  9. setWindowTitle(tr("文件浏览"));
  10. table = new Table(this);
  11. QStringList header;
  12. header <<tr("文件名")<<tr("最后更改日期")<<tr("类型")<<tr("大小");
  13. table->setColumnValue(4, header);
  14. table->setHeaderWidth(0, 200);
  15. table->setHeaderWidth(1, 150);
  16. table->setHeaderWidth(2, 100);
  17. table->setHeaderWidth(3, 100);
  18. table->setHeaderHeight(30);
  19. //table->setRowH(0, 200);
  20. ScanFile(QApplication::applicationDirPath());
  21. // table->setRowHeight(46);
  22. resize(800, 800);
  23. }
  24. TableWidget::~TableWidget()
  25. {
  26. }
  27. //Qt实现遍历文件夹和文件目录
  28. bool TableWidget::ScanFile(const QString &path)
  29. {
  30. QDir dir(path);
  31. if (!dir.exists())
  32. return false;
  33. // dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
  34. // QFileInfoList list = dir.entryInfoList();
  35. // //QStringList list = dir.entryList();
  36. // for(int index = 0; index < list.count(); index++) {
  37. // QFileInfo fileInfo = list.at(index);
  38. // if (fileInfo.isDir()) {
  39. // ScanFile(fileInfo.filePath());
  40. // }
  41. // else {
  42. // qDebug() << "----------" << fileInfo.absoluteFilePath();
  43. // }
  44. // }
  45. QDirIterator dirIterator(path, QDir::Dirs | QDir::Files | QDir::NoSymLinks| QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
  46. while(dirIterator.hasNext()) {
  47. dirIterator.next();
  48. QFileInfo fileInfo = dirIterator.fileInfo();
  49. QString filePath = fileInfo.absoluteFilePath();
  50. QFileIconProvider iconProvider;
  51. QIcon icon;
  52. if (fileInfo.isDir()) { //获取指定文件图标
  53. icon = iconProvider.icon(QFileIconProvider::Folder);
  54. }
  55. else {
  56. icon = iconProvider.icon(fileInfo);
  57. }
  58. QFileIconProvider icon_provider;
  59. QString typeFile = icon_provider.type(fileInfo);
  60. table->addRowValue(30, QStringList() << filePath <<fileInfo.lastModified().toString("yyyy-MM-dd hh:mm:ss")
  61. <<typeFile<<QString::number(fileInfo.size()/1024.0, 'f', 2)+"KB", icon);
  62. }
  63. return true;
  64. }
(5)main.cpp
  1. #include "tablewidget.h"
  2. #include <QApplication>
  3. int main(int argc, char *argv[])
  4. {
  5. QApplication a(argc, argv);
  6. TableWidget w;
  7. w.show();
  8. return a.exec();
  9. }
(6)运行结果

(7)总结
        根据网上博客修改了:鼠标点击的选项会出现虚框、创建右键菜单、获取文件图标类型、右键编辑时Tab键的处理和重命名文件重名后表格更新的、递归扫描文件夹。

2、QTableWidget控件属性

一、禁止编辑表格
在默认情况下,表格里的字符是可以更改的。
比如双击一个单元格,就可以修改原来的内容,如果想禁止用户的这种操作,让这个表格对用户只读,可以这样:
ui.qtablewidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
二、 设置表格为选择整行
  1. /*设置表格为整行选中*/
  2. ui.qtablewidget->setSelectionBehavior(QAbstractItemView::SelectRows);
三、设置单个选中和多个选中
单个选中意味着每次只可以选中一个单元格,多个就是相当于可以选择”一片“那种模式。
  1. /*设置允许多个选中*/
  2. ui.qtablewidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
四、表格表头的显示与隐藏
对于水平或垂直方向的表头,如果不想显示可以用以下方式进行(隐藏/显示)设置:
  1. ui.qtablewidget->verticalHeader()->setVisible(true);
  2. ui.qtablewidget->horizontalHeader()->setVisible(false);
五、设置具体单元格中字体的对齐方式
ui.qtablewidget->item(0, 0)->setTextAlignment(Qt::AlignHCenter);
六、设置具体单元格中字体格式
  1. ui.qtablewidget->item(1, 0)->setBackgroundColor(QColor(0,60,10));
  2. ui.qtablewidget->item(1, 0)->setTextColor(QColor(200,111,100));
  3. ui.qtablewidget->item(1, 0)->setFont(QFont("Helvetica"));
七、设置具体单元格的值
ui.qtablewidget->setItem(1, 0, new QTableWidgetItem(str));
八、把QTableWidgetItem对象内容转换为QString
QString str =ui.qtablewidget->item(0, 0)->data(Qt::DisplayRole).toString();
九、具体单元格中添加控件
  1. QComboBox *comBox = new QComboBox();
  2. comBox->addItem("F");
  3. comBox->addItem("M");
  4. ui.qtablewidget->setCellWidget(0,3,comBox);
十、合并单元格
  1. //合并单元格的效果
  2. ui.qtablewidget->setSpan(2, 2, 3, 2);
  3. //第一个参数:要改变的单元格行数
  4. //第二个参数:要改变的单元格列数
  5. //第三个参数:需要合并的行数
  6. //第四个参数:需要合并的列数
十一、具体单元格中插入图片
ui.qtablewidget->setItem(3, 2, new QTableWidgetItem(QIcon("images/music.png"), "Music"));
十二、设置显示网格
ui.qtablewidget->setShowGrid(true);//显示表格线
十三、设置滚动条
ui.qtablewidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//去掉水平滚动条
十四、设置列标签
  1. //初始化界面
  2. QStringList HStrList;
  3. HStrList.push_back(QString("name"));
  4. HStrList.push_back(QString("id"));
  5. HStrList.push_back(QString("age"));
  6. HStrList.push_back(QString("sex"));
  7. HStrList.push_back(QString("department"));
  8. //设置行列数(只有列存在的前提下,才可以设置列标签)
  9. int HlableCnt = HStrList.count();
  10. ui.qtablewidget->setRowCount(10);
  11. ui.qtablewidget->setColumnCount(HlableCnt);
  12. //设置列标签
  13. ui.qtablewidget->setHorizontalHeaderLabels(HStrList);
十五、设置行和列的大小设为与内容相匹配
  1. ui.qtablewidget->resizeColumnsToContents();
  2. ui.qtablewidget->resizeRowsToContents();
十六、设置字体
ui.qtablewidget->setFont(font);   //设置字体
十七、 获取某一单元格的内容
QString strText = ui.qtablewidget->item(0, 0)->text();

3、QTableWidget美化

QSS样式表(根据需求修改颜色):
  1. QTableWidget
  2. {
  3. background: rgb(56,56,56);
  4. alternate-background-color:rgb(48,51,55);
  5. selection-background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(56,56,56),stop:1 rgb(66,66,66));
  6. }
  1. QHeaderView::section
  2. {
  3. background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(46,46,46),stop:1 rgb(56,56,56));
  4. color: rgb(210,210,210);
  5. padding-left: 4px;border: 1px solid #383838;
  6. }
  1. QScrollBar:vertical
  2. {
  3. border: 0px solid grey;
  4. background: transparent;
  5. width: 15px;
  6. margin: 22px 0 22px 0;
  7. }
  8. QScrollBar::handle:vertical
  9. {
  10. background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(46,46,46),stop:1 rgb(66,66,66));
  11. min-height: 20px;
  12. }
  13. QScrollBar::add-line:vertical
  14. {
  15. border: 0px solid grey;
  16. background: rgb(66,66,66);
  17. height: 20px;
  18. subcontrol-position: bottom;
  19. subcontrol-origin: margin;
  20. }
  21. QScrollBar::sub-line:vertical
  22. {
  23. border: 0px solid grey;
  24. background: rgb(56,56,56);
  25. height: 20px;
  26. subcontrol-position: top;
  27. subcontrol-origin: margin;
  28. }
  29. QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
  30. {
  31. border: 0px solid grey;
  32. width: 3px;
  33. height: 3px;
  34. background: rgb(46,46,46);
  35. }
  36. QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
  37. {
  38. background: none;
  39. }

四、总结

(1)源码中绝大部分的功能都没实现,Table也没进行完整的封装,可根据自己的需求修改代码。
(2)本代码的总结参考了网友的博客,在此感谢。
(3)源码已经打包上传到csdn上,可登录下载(http://download.csdn.net/detail/taiyang1987912/8938815)。
(4)若有建议,请留言,在此先感谢! 

猜你喜欢

转载自blog.csdn.net/u013934107/article/details/80938052