勇哥学习了十来天的QT,边学边写了个小练习。放上来做为一些知识点的备查。
这个小程序的功能有:
磁盘读取歌曲名列表
双击歌曲名调用外部播放器播放
可以支持首拼方法模糊搜索歌曲
控件的右键菜单
功能很简单,但是用到了一些常用的Qt知识点,包括:
QListWidget控件
QTabWidget控件
QPushButton控件
QLineEdit控件
控件相关的信号与槽
SIGNAL和SLOT宏
foreach
try...catch...
QSettings类
QFile类
QTextStream类
QCoreApplication类
QString类
QStringList类
QProcess类
tuple
qDebug
QMap
QMenu
QAction
vistual assist插件
Qt Designer
下面是一些知识点相关的贴子:
磁盘文件操作
控件右键菜单
汉字转拼音
启动外部程序
异常处理
配置文件读写
下面贴上业务逻辑的主要代码:
work.h
#pragma once #pragma execution_character_set("utf-8") #include <QSettings> #include "Config.h" #include <tuple> class Work { public: //歌曲列表类型 static enum MusicListTypeEnum { allMusic, selMusic, //搜索结果列表,用于全部歌曲标签 searchMusicAll, //搜索结果列表,用于选中的歌曲标签 searchMusicSel }; private: //全部歌曲的音乐列表,<歌曲名,歌曲文件名路径串> QMap<QString, QString> musicListAll; //选中歌曲的音乐列表,<歌曲名,歌曲文件名路径串> QMap<QString, QString> musicListSelect; //全部歌曲名称的拼音字典 QMap<QString, QString> musicAllPyList; //选择歌曲名称的拼音字典 QMap<QString, QString> musicSelPyList; Config* config; private: //更新歌曲的拼音(全部的歌曲) void updateMusicAllPy(); //更新歌曲的拼音(选中的歌曲) void updateMusicSelPy(); public: Work(Config* config); ~Work(); //读取音乐列表 void readMusicList(Work::MusicListTypeEnum musictype); //按歌曲名或者简拼搜索歌曲 QMap<QString, QString> searchMuslicList(QString name, Work::MusicListTypeEnum musictype); //取歌曲列表(枚举:allMusic,selMusic) QMap<QString, QString> getMusicList(Work::MusicListTypeEnum musictype); //添加歌曲到选中的歌单 void addMusic(std::tuple<QString, QString > data); //删除选中歌单的歌(不能删除全部歌单中的歌) void delMusic(QString musicName); //取歌曲名拼音列表(全部歌曲) QMap<QString, QString> getMusicAllPyList(); //取歌曲名拼音列表(选中的歌曲) QMap<QString, QString> getMusicSelPyList(); //删除缓存的歌曲列表 void delMusicListFile(Work::MusicListTypeEnum musictype); };
work.cpp
#include "Work.h" #include "CdiskFile.h" #include "Cchineseletterhelper.h" Work::Work(Config* config) { this->config = config; } Work::~Work() { } void Work::readMusicList(Work::MusicListTypeEnum musictype) { QString allmusicPath = config->get("mtvconfig", "allMusic").toString(); QString selmusicPath = config->get("mtvconfig", "selMusic").toString(); QString mtvPath = config->get("mtvconfig", "mtvDir").toString(); if (musictype == Work::MusicListTypeEnum::selMusic){ QString txt = CdiskFile::readAllTxt(selmusicPath); if (musicListSelect.count() < 2){ QFile f1; if (f1.exists(selmusicPath)){ //如果selMusic.txt存在由磁盘缓存文件重建内存表 musicListSelect = CdiskFile::QMapLoad(selmusicPath); } f1.close(); updateMusicSelPy(); } return; } else{ try { QString txt = CdiskFile::readAllTxt(allmusicPath); if (musicListAll.count() < 2){ QFile f1; if (f1.exists(allmusicPath)){ //如果allMusic.txt存在由磁盘缓存文件重建内存表 musicListAll = CdiskFile::QMapLoad(allmusicPath); } else{ //如果allMusic.txt不存在则读磁盘文件名重建内存表 QStringList list1 = CdiskFile::getFileNames(mtvPath, QStringList() << "*.mkv"); foreach(QString m, list1){ musicListAll.insert(m, mtvPath + "/" + m); } } updateMusicAllPy(); CdiskFile::QMapSave(allmusicPath, musicListAll); } return; } catch (...) { } } } QMap<QString, QString> Work::searchMuslicList(QString name, Work::MusicListTypeEnum musictype) { QMap<QString, QString> map; if (musictype == Work::MusicListTypeEnum::allMusic){ auto v= musicListAll.find(name); if (v != musicListAll.end()){ map.insert(v.key(), v.value()); } } else{ auto v = musicListSelect.find(name); if (v != musicListSelect.end()){ map.insert(v.key(), v.value()); } } return map; } QMap<QString, QString> Work::getMusicList(Work::MusicListTypeEnum musictype) { if (musictype == Work::MusicListTypeEnum::allMusic){ return musicListAll; } else{ return musicListSelect; } } void Work::addMusic(std::tuple<QString ,QString > data) { QString selmusicPath = config->get("mtvconfig", "selMusic").toString(); if (!musicListSelect.contains(std::get<0>(data))){ musicListSelect.insert(std::get<0>(data), std::get<1>(data)); CdiskFile::QMapSave(selmusicPath, musicListSelect); } } void Work::delMusic(QString musicName) { QString selmusicPath = config->get("mtvconfig", "selMusic").toString(); if (musicListSelect.contains(musicName)){ musicListSelect.remove(musicName); CdiskFile::QMapSave(selmusicPath, musicListSelect); } } void Work::updateMusicAllPy() { foreach(QString m, musicListAll.keys()){ musicAllPyList.insert(m, Cchineseletterhelper::GetFirstLetters(m)); } } void Work::updateMusicSelPy() { foreach(QString m, musicListSelect.keys()){ musicSelPyList.insert(m, Cchineseletterhelper::GetFirstLetters(m)); } } QMap<QString, QString> Work::getMusicAllPyList(){ return musicAllPyList; } QMap<QString, QString> Work::getMusicSelPyList(){ return musicSelPyList; } void Work::delMusicListFile(MusicListTypeEnum musictype) { QFile file; auto fname1 = config->get("mtvconfig", "allMusic").toString(); auto fname2 = config->get("mtvconfig", "selMusic").toString(); try { if (musictype == Work::MusicListTypeEnum::allMusic){ if (file.exists(fname1)){ QFile::remove(fname1); } } else{ if (file.exists(fname2)){ QFile::remove(fname2); } } } catch (...) { } }
QtForOpencv.h
#pragma once #pragma execution_character_set("utf-8") #include <QtWidgets/QWidget> #include "ui_QtForOpencv.h" #include <QLabel> #include <QCoreApplication> #include <QSettings> #include "Config.h" #include "Work.h" #include "CdiskFile.h" #include "CcsvHelper.h" #include <QTabWidget> #include <QListWidget> #include <QMenu> class QtForOpencv : public QWidget { Q_OBJECT public: QtForOpencv(QWidget *parent = Q_NULLPTR); ~QtForOpencv(); private: Ui::QtForOpencvClass ui; Config* config; Work* work; QMenu * allMusicMenu; QMenu * selMusicMenu; QAction * updateMusicAction; QAction * addMusicAction; QAction * delMusicAction; //更新音乐列表 void updateMusicList(QMap<QString,QString> data, Work::MusicListTypeEnum type); //注册UI信号与槽 void ConnectUiEvent(); private slots: void OnBtnUpdateMusic(); void OnBtnAddSelMusic(); void OnBtnDelMusic(); void OnLineEditSearchMusic(const QString data); void OnTabChange(int index); void OnListwAllMusic(QListWidgetItem * item1); void OnAllMusicListClick(QListWidgetItem*); void OnAllMusicListPlay(QListWidgetItem*); void OnSelMusicListClick(QListWidgetItem*); void OnSelMusicListPlay(QListWidgetItem*); void OnUpdateMenuAction(const QPoint &pos); public: QLabel* imgLabel; };
QtForOpencv.cpp
#include "QtForOpencv.h" #include <opencv2\opencv.hpp> #include <opencv2\core.hpp> #include <opencv2\imgcodecs.hpp> #include <opencv2\imgproc.hpp> #include <QPushButton> #include <qdebug.h> #include <QProcess> #include <QMessageBox> #include "Cchineseletterhelper.h" using namespace cv; using namespace std; QtForOpencv::QtForOpencv(QWidget *parent) : QWidget(parent) { ui.setupUi(this); ConnectUiEvent(); allMusicMenu = new QMenu(this); selMusicMenu = new QMenu(this); updateMusicAction = new QAction("刷新歌曲",this); addMusicAction = new QAction("添加到选中的歌曲", this); delMusicAction = new QAction("删除歌曲", this); allMusicMenu->addAction(updateMusicAction); allMusicMenu->addAction(addMusicAction); selMusicMenu->addAction(delMusicAction); connect(updateMusicAction, &QAction::triggered, [=]{ qDebug() << "updateMusicAction"; OnBtnUpdateMusic(); }); connect(addMusicAction, &QAction::triggered, [=]{ qDebug() << "addMusicAction"; OnBtnAddSelMusic(); }); connect(delMusicAction, &QAction::triggered, [=]{ qDebug() << "delMusicAction"; OnBtnDelMusic(); }); // 给控件设置上下文菜单策略 ui.listwAllMusic->setContextMenuPolicy(Qt::CustomContextMenu); //鼠标右键点击控件时会发送一个void QWidget::customContextMenuRequested(const QPoint &pos)信号 //给信号设置相应的槽函数 //以slot函数方式connect connect(ui.listwAllMusic, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnUpdateMenuAction(const QPoint&))); //以Lambada表达式方法connect ui.listwSelMusic->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui.listwSelMusic, &QListWidget::customContextMenuRequested, [=](const QPoint &pos) { qDebug() << pos; selMusicMenu->exec(QCursor::pos()); }); //初始化配置 QString inifilePath = QCoreApplication::applicationDirPath() + "/myTest.ini"; config = new Config(inifilePath); config->set("mtvconfig", "allMusic", QCoreApplication::applicationDirPath()+"/allmusic.txt"); config->set("mtvconfig", "selMusic", QCoreApplication::applicationDirPath() + "/selmusic.txt"); config->set("mtvconfig", "mtvDir", "e:/music"); config->set("mtvconfig", "playerDir", "E:/Program Files/SPlayer/splayer.exe"); //初始化工作逻辑 work = new Work(config); work->readMusicList(Work::MusicListTypeEnum::allMusic); work->readMusicList(Work::MusicListTypeEnum::selMusic); ui.tabmain->setCurrentIndex(0); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::allMusic), Work::MusicListTypeEnum::allMusic); auto s = sizeof(double); } void QtForOpencv::OnUpdateMenuAction(const QPoint &pos){ qDebug() << pos;//参数pos用来传递右键点击时的鼠标的坐标,这个坐标一般是相对于控件左上角而言的 allMusicMenu->exec(QCursor::pos()); } void QtForOpencv::ConnectUiEvent() { //更新歌单 connect(ui.btnUpdateMusic, SIGNAL(clicked()), this, SLOT(OnBtnUpdateMusic())); //添加到选中的歌单 connect(ui.btnAddSelMusic, SIGNAL(clicked()), this, SLOT(OnBtnAddSelMusic())); //删除歌曲(仅选中的歌单) connect(ui.btnDelMusic, &QPushButton::clicked, this, &QtForOpencv::OnBtnDelMusic); //歌曲搜索框内容改变 connect(ui.lineEditSearchMusic, SIGNAL(textChanged(const QString)), this, SLOT(OnLineEditSearchMusic(const QString))); //tab标签被点中 connect(ui.tabmain, SIGNAL(tabBarClicked(int)), this, SLOT(OnTabChange(int))); //全部歌曲列表项目被单击或双击运行 connect(ui.listwAllMusic, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnAllMusicListPlay(QListWidgetItem*))); connect(ui.listwAllMusic, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(OnAllMusicListClick(QListWidgetItem*))); //选中的歌曲列表项目被单击或双击运行 connect(ui.listwSelMusic, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnSelMusicListPlay(QListWidgetItem*))); connect(ui.listwSelMusic, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(OnSelMusicListClick(QListWidgetItem*))); } void QtForOpencv::OnBtnUpdateMusic() { //刷新歌曲 qDebug() << "OnBtnUpdateMusic"; work->delMusicListFile(Work::MusicListTypeEnum::allMusic); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::allMusic), Work::MusicListTypeEnum::allMusic); } void QtForOpencv::OnBtnAddSelMusic() { //添加到选中的歌曲 qDebug() << "OnBtnAddSelMusic"; std::tuple<QString,QString> data; QString musicName = ui.listwAllMusic->selectedItems()[0]->text(); QString musicPath = config->get("mtvconfig", "mtvDir").toString() + "/" + musicName; data = make_tuple(musicName, musicPath); work->addMusic(data); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::selMusic), Work::MusicListTypeEnum::selMusic); QMessageBox::about(NULL, "信息", "添加成功!"); } void QtForOpencv::OnBtnDelMusic() { qDebug() << "OnBtnDelMusic"; //删除选中歌曲列表中的歌曲 if (ui.tabmain->currentIndex() > 0){ QString musicName = ui.listwSelMusic->selectedItems()[0]->text(); work->delMusic(musicName); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::selMusic), Work::MusicListTypeEnum::selMusic); QMessageBox::about(NULL, "信息", "已经删除!"); } } void QtForOpencv::OnTabChange(int index) { qDebug() << "tab..." << index ; switch (index) { case 0: ui.btnAddSelMusic->setEnabled(true); ui.btnUpdateMusic->setEnabled(true); ui.btnDelMusic->setEnabled(false); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::allMusic), Work::MusicListTypeEnum::allMusic); break; case 1: ui.btnAddSelMusic->setEnabled(false); ui.btnUpdateMusic->setEnabled(true); ui.btnDelMusic->setEnabled(true); updateMusicList(work->getMusicList(Work::MusicListTypeEnum::selMusic), Work::MusicListTypeEnum::selMusic); break; default: break; } } void QtForOpencv::OnLineEditSearchMusic(const QString data) { qDebug() << data; if (data.length() > 0){ //QString firstLetters = Cchineseletterhelper::GetFirstLetters(""); QMap<QString, QString> map1; if (ui.tabmain->currentIndex() == 0){ auto list = work->getMusicAllPyList(); auto path = config->get("mtvconfig", "mtvDir").toString(); for (QMap<QString, QString>::iterator i = list.begin(); i != list.end(); ++i){ if (i.value().indexOf(data.toUpper()) >= 0){ map1.insert(i.key(), path + "/" + i.key()); } } if (map1.count() > 0){ updateMusicList(map1, Work::MusicListTypeEnum::searchMusicAll); } } else if (ui.tabmain->currentIndex() == 1){ auto list = work->getMusicSelPyList(); auto path = config->get("mtvconfig", "mtvDir").toString(); for (QMap<QString, QString>::iterator i = list.begin(); i != list.end(); ++i){ if (i.value().indexOf(data.toUpper()) >= 0){ map1.insert(i.key(), path + "/" + i.key()); } } if (map1.count() > 0){ updateMusicList(map1, Work::MusicListTypeEnum::searchMusicSel); } } } else{ if (ui.tabmain->currentIndex() == 0){ updateMusicList(work->getMusicList(Work::MusicListTypeEnum::allMusic), Work::MusicListTypeEnum::allMusic); } else if (ui.tabmain->currentIndex() == 1){ updateMusicList(work->getMusicList(Work::MusicListTypeEnum::selMusic), Work::MusicListTypeEnum::selMusic); } } } void QtForOpencv::OnListwAllMusic(QListWidgetItem * item1) { qDebug() << "OnListwAllMusic"; } void QtForOpencv::updateMusicList(QMap<QString, QString> data, Work::MusicListTypeEnum type) { QStringList list1; foreach(QString m, data.keys()){ list1.append(m); } if (data.count() < 1) return; if (type == Work::MusicListTypeEnum::allMusic || type == Work::MusicListTypeEnum::searchMusicAll){ ui.listwAllMusic->clear(); ui.listwAllMusic->addItems(list1); } else if (type == Work::MusicListTypeEnum::selMusic || type == Work::MusicListTypeEnum::searchMusicSel){ ui.listwSelMusic->clear(); ui.listwSelMusic->addItems(list1); } } void QtForOpencv::OnAllMusicListClick(QListWidgetItem* item) { //qDebug() << "OnAllMusicListClick"; } void QtForOpencv::OnAllMusicListPlay(QListWidgetItem* item) { //播放歌曲 qDebug() << "OnAllMusicListPlay"; auto mtvdir = config->get("mtvconfig", "mtvDir").toString(); auto txt = item->text(); auto playDir = config->get("mtvconfig", "playerDir").toString(); auto pro = new QProcess(this); pro->start(playDir, QStringList(mtvdir+"/"+txt)); pro->waitForStarted(); } void QtForOpencv::OnSelMusicListClick(QListWidgetItem* item) { //qDebug() << "OnSelMusicListClick"; } void QtForOpencv::OnSelMusicListPlay(QListWidgetItem* item) { qDebug() << "OnSelMusicListPlay"; OnAllMusicListPlay(item); } QtForOpencv::~QtForOpencv() { delete config; delete work; }
Config.h
#pragma once #pragma execution_character_set("utf-8") #include <QVariant> #include <QSettings> class Config { public: Config(QString filename=""); virtual ~Config(); //保存配置项(节点名,键名,键值) void set(QString, QString, QVariant); //读取配置项(节点名,键名) QVariant get(QString, QString); private: QString m_qstrfilename; QSettings * m_psetting; };
Config.app
#include "Config.h" #include <QCoreApplication> #include <QSettings> #include <QDebug> Config::Config(QString filename) { if (filename.isEmpty()) { m_qstrfilename = QCoreApplication::applicationDirPath() + "/config.ini"; } else { m_qstrfilename = filename; } m_psetting = new QSettings(m_qstrfilename, QSettings::Format::IniFormat); qDebug() << m_qstrfilename; } Config::~Config() { delete m_psetting; m_psetting = 0; } void Config::set(QString qstrNodeName, QString qstrKeyName, QVariant qvarValue) { /* /mtvconfig/allMusic */ m_psetting->setValue(QString("/%1/%2").arg(qstrNodeName).arg(qstrKeyName), qvarValue); } QVariant Config::get(QString qstrNodeName, QString qstrKeyName) { QVariant qvar = m_psetting->value(QString("/%1/%2").arg(qstrNodeName).arg(qstrKeyName)); return qvar; }
CdiskFile.h
#pragma once #pragma execution_character_set("utf-8") #include <QString> #include <QMap> #include "CcsvHelper.h" #include "qdir.h" class CdiskFile { public: CdiskFile(); virtual ~CdiskFile(); //读取文本文件全部内容(文件名带路径) static QString readAllTxt(const QString filepath); //取指定目录下全部符合扩展名的文件名列表,递归搜索。(目录路径,扩展名列表) static QStringList getFileNames(const QString filepath,QStringList filters); //保存QMap<QString, QString>的数据到磁盘文件。(文件名路径,map数据) static bool QMapSave(const QString fileNamePath, QMap<QString, QString> mapdata); //从磁盘文件读取内容到QMap<QString, QString>。(文件名路径,文件名) static QMap<QString, QString> QMapLoad(const QString fileNamePath); //由文件名路径串拆分出文件名与目录串 static std::tuple<QString, QString> splitFileNamePath(const QString filenamepath); };
CdiskFile.cpp
#include "CdiskFile.h" #include <QFile> #include <QDir> #include "qtextstream.h" CdiskFile::CdiskFile() { } CdiskFile::~CdiskFile() { } QString CdiskFile::readAllTxt(const QString filepath) { try { QFile file(filepath); file.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray ary = file.readAll(); file.close(); return QString(ary); } catch (...) { throw; } } QStringList CdiskFile::getFileNames(const QString filepath, QStringList filters) { QStringList list1; QDir dir(filepath); list1 = dir.entryList(filters, QDir::Files | QDir::AllEntries, QDir::Name); return list1; } bool CdiskFile::QMapSave(const QString fileNamePath, QMap<QString, QString> mapdata) { auto tup=CdiskFile::splitFileNamePath(fileNamePath); QString filepath = std::get<0>(tup); QString filename = std::get<1>(tup); QDir dir; if (!dir.exists(filepath)){ if (!dir.mkpath(filepath)) return false; } QString tmp; QList<QString> list2; foreach(auto m,mapdata.keys()){ list2.clear(); list2.append(m); list2.append(mapdata[m]); tmp.append(CcsvHelper::createCSVRow(list2)+"\n"); } try{ QFile file(filepath + "/" + filename); file.open(QIODevice::WriteOnly | QIODevice::Text); file.write(tmp.toUtf8()); file.close(); return true; } catch (...){ return false; } } QMap<QString, QString> CdiskFile::QMapLoad(const QString fileNamePath) { QMap<QString, QString> map; auto tup = CdiskFile::splitFileNamePath(fileNamePath); QString filepath = std::get<0>(tup); QString filename = std::get<1>(tup); QFile file(filepath + "/" + filename); if (file.open(QIODevice::ReadOnly | QIODevice::Text)){ QTextStream in(&file); in.setCodec("UTF-8"); auto res = in.readAll(); auto list1 = res.split("\n"); foreach(QString m, list1){ auto ary1 = CcsvHelper::explainCSVRow(m); if (ary1.length() == 2) map.insert(ary1[0], ary1[1]); else { int k1 = 0; } } } return map; } std::tuple<QString, QString> CdiskFile::splitFileNamePath(const QString filenamepath) { std::tuple<QString, QString> tup; int pos1=filenamepath.lastIndexOf('/'); QString strPath = filenamepath.left(pos1 + 1); QString strFileName = filenamepath.right(filenamepath.count() - pos1 - 1); tup = std::make_tuple(strPath, strFileName); return tup; }
这款点歌的小程序勇哥还写过其它语言版本的,放这里给各位把玩一下吧:
Python版本的:
《Python Tkinter 学习成果:点歌软件music》
plc+单片机+c#版本:
《欧姆龙cp1h常用指令学习(十四)练习篇一:红外线遥控点歌》
C#版本的:
再早之前,还有一个易语言版本写的,怕被各位鄙视,还是不要放上来了吧。
这个小程序配置采用ini文件,其实相对于xml和C#的对象序列化来生成配置文件来看,是相当的原始。
而且现在也流行用PropertyGrid之类的属性流览控件来设置配置项,我想把这个程序也改造成这样的。
(属性浏览控件指的就是Qt Designer或者vs里面用到的控件属性设置面板)
等勇哥搞出来了会另起一个贴子登出来。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

