难用的 dash
#!/bin/dash
WBCHR_PIDS=$(pgrep wubi98-char)
string="${WBCHR_PIDS}"
echo $WBCHR_PIDS
if [ ${string}"abc" = "abc" ]
then
echo "wubi98-char没有活动的进程"
else
echo "检测到 wubi98-char 正在运行,即将杀掉!"
kill $WBCHR_PIDS
fi
if [ -d /opt/wubi98-char ]
then
echo "wubi98-char 文件夹存在,将会删了它!"
sudo sudo rm -rf /opt/wubi98-char
fi
if [ -f /usr/share/applications/wubi98-char.desktop ]
then
echo "wubi98-char 快捷方式存在,将会删了它!"
sudo sudo rm -rf /usr/share/applications/wubi98-char.desktop
fi
只能以这种方式,才可以不报错。
if
条件句中,合并字符串,否则空值时报错。dash
不支持常规逻辑运算符。debian
全用dash
了,想换bash
需要用户额外操作,这对资源库从简从易的原则来说,非常蛋疼。
上述 ${string}
是排查过程中额外引入的量,主要是想看看进程 PID
及其操控,神特么有两个进程,最后用 pgrep
搞定。 ${string}
本无必要,仅此说明。
QT 中的指针
98五笔小精灵,已编译发布 Linux
版本,其中源码里有个函数是这样:
void MainWindow::openPic(const QString & num){
QString sPicPath = ":/pic/" + num + ".jpg";
QString toDir = QDir::currentPath();
picTest = new QFile(toDir + "/" + num + ".jpg");
bool ok = picTest->exists();
if(!ok){
QFile my_file(sPicPath);
my_file.copy(toDir + "/" + num + ".jpg");
picTest->setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner);
}
sPicPath = toDir+"/" + num + ".jpg";
if(QSysInfo::productType()=="windows"){
sPicPath = sPicPath.replace("/", "\\");
}
QDesktopServices::openUrl(QUrl::fromLocalFile(sPicPath)); //For Linux;
}
在 windows
下, 转义成 \\
风格的路径是保险的,在 Linux
下,这样的路径又是无效的。
只好加了个判断,不同系统,分流写法。
这里还有个坑,权限在 Linux
下,设成 QFileDevice::ReadOwner | QFileDevice::WriteOwner
是保险的。按 Qt
的帮助文档,出现了权限问题。
还有一个坑,就是内存泄漏:如果每次都生成一个新的 QFile
对象,就会出现内存泄漏。
研究了一下,给程序预设了一个 QFile
指针,用指针来管理对象,成功堵住内存泄漏的风险。
同样的思路,用指针来管理绑定函数:
void MainWindow::connectBtnToUI(){
connect(ui->lineEditForSearch,&QLineEdit::returnPressed,this,&MainWindow::lineEditForSearch_returnPressed);
void (MainWindow::*pr_clicked[5])()={
&MainWindow::btnSearchChar_clicked,
&MainWindow::btnOpenPIC1_clicked,
&MainWindow::btnOpenPIC2_clicked,
&MainWindow::btnOpenWeb1_clicked,
&MainWindow::btnOpenWeb2_clicked,
};
QPushButton * pr[5]={
ui->btnSearchChar,
ui->btnOpenPIC1,
ui->btnOpenPIC2,
ui->btnOpenWeb1,
ui->btnOpenWeb2,
};
for (int i = 0;i<5;i++) {
connect(pr[i],&QAbstractButton::clicked,this,pr_clicked[i]);
}
}
单实例判断
#if defined Q_OS_LINUX //for linux
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
bool checkOnly()
{
const char filename[] = "/tmp/lockfile_wubi98-char_202307";
int fd = open (filename, O_WRONLY | O_CREAT , 0644);
int flock = lockf(fd, F_TLOCK, 0 );
if (fd == -1) {
perror("open lockfile/n");
return false;
}
if (flock == -1) {
perror("lock file error/n");
return false;
}
return true;
}
#endif
一时间,newReader
和 wubi98-char
不可共存,想了一下,原来是 lockfile
同名了。
回头想想,weasel-tool
的代码还有很多可以优化的地方。不过 c++
最大的好处是,即使用最平庸的写法,也能获得优异的性能。
64位兼容
QStringList MainWindow::getWindowsRegedit(){
QStringList RimeDirList_64;
QStringList RimeDirList_32;
bool ok1 =false;
bool ok2 =false;
/************* 64位 ↓ ***************/
QString strReg_64 = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Rime\\Weasel\\";
QSettings* setting_64 = new QSettings(strReg_64, QSettings::NativeFormat);
QString appDir_64 = setting_64->value("WeaselRoot").toString();
if(appDir_64.contains(":")){
qDebug() << "检测到64位程序存在!" << '\n';
ok1 = true;
RimeDirList_64.append(appDir_64);
}
/************* 64位 ↑ ***************/
/************* 32位 ↓ ***************/
QString strReg_32 = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Rime\\Weasel\\";
QSettings* setting_32 = new QSettings(strReg_32, QSettings::NativeFormat);
QString appDir_32 = setting_32->value("WeaselRoot").toString();
if(appDir_32.contains(":")){
qDebug() << "检测到32位程序存在!" << '\n';
ok2 = true;
RimeDirList_32.append(appDir_32);
}
/************* 32位 ↑ ***************/
QString strReg = "HKEY_CURRENT_USER\\Software\\Rime\\Weasel\\";
QSettings* setting = new QSettings(strReg, QSettings::NativeFormat);
auto appDir = setting->value("RimeUserDir").toString();
qDebug() << "获取到的 RimeUserDir 键值是: " << appDir << '\n';
if(!appDir.contains(":")){
appDir=(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
appDir.replace("weasel-tool","Rime");
qDebug() << "修改后 RimeUserDir 键值是: " << appDir <<'\n';
}
RimeDirList_64.append(appDir);
RimeDirList_32.append(appDir);
if(ok1 && !ok2){
return RimeDirList_64;
}
if(ok2 && !ok1){
return RimeDirList_32;
}
if(ok1 && ok2){
//检测到32位版本与64位版本同时存在
QString strInfo = "请选择接管的版本";
QMessageBox msgBox;
msgBox.setText(strInfo);
msgBox.setWindowTitle("选择版本");
msgBox.setInformativeText("检测到系统中32位版本与64位版本同时存在,如果关掉该弹窗,默认选择32位。");
msgBox.addButton("接管 32 位版本",QMessageBox::AcceptRole);
msgBox.addButton("接管 64 位版本",QMessageBox::RejectRole);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::AcceptRole:{
return RimeDirList_32;
}
case QMessageBox::RejectRole:{
return RimeDirList_64;
}
default:{
return RimeDirList_32;}
}
}
return {};
}
weasel-tool
处理的对象数据量较少,结构较为单一,没有全面采用面向对象的方式。我其实很想把 rimetool
整个重写,但是眼下没有时间,等后面有机会,来个大整合。
本次玩 Qt
的一个感觉就是 QHash
真他娘的好用啊!
- 一个典型的遍历
QHash<QString, QString>::const_iterator i = userData.cbegin();
while (i != userData.cend()) {
qDebug() << i.key() << " 的值是: " << i.value() << '\n';
++i;
}
- 一个典型的遍历覆写
bool QDiceThread::writeQhashToYaml(const QHash<QString,QString> userDataLatest){
//把 QHASH 写入到 wubi98_ci.custom.yaml ,threadD在做这个
QFile aFile(userFileName);
if(!aFile.open(QIODevice::WriteOnly|QIODevice::Text))
return false;
QTextStream aStream(&aFile);
aStream.setAutoDetectUnicode(true);
QString str("patch:") ;
aStream << str << '\n';
QHash<QString, QString>::const_iterator i = userDataLatest.cbegin();
while (i != userData.cend()) {
qDebug() << i.key() << " 的值是: " << i.value() << '\n';
str = i.key()+":"+i.value();
aStream << str << '\n';
++i;
}
aFile.close();
return true;
}
- 一个典型的遍历检查
for(int i = 0;i <45 ; i++){
strKeyTemp = QString(" \"%1\"").arg(keyList[i]);
QHash<QString, QString>::iterator it11 = userDataTH.find(strKeyTemp);
bool ok(it11==userDataTH.end());
if (ok){
qDebug()<< "没有检测到参数项:" << strKeyTemp << '\n';
erroList.append(keyList[i]);
ok_test = false;
}
}
- 一个典型的填值
void MainWindow::comboBoxChangeTheVaule(const QString & strNEW,const int & i){
QString strKeyTemp = QString(" \"%1\"").arg(userKeyList[i]);
userDataBySelect.insert(strKeyTemp," " + strNEW);
}
- 一个典型的读值
QString str12 = QString(" \"%1\"").arg("schema/full_icon");
QHash<QString, QString>::iterator it12 = userDataBySelect.find(str12);
if(it12!=userDataBySelect.end()){
QString str = it12.value();
str = str.replace("icons/","");
str = str.replace("\"","");
ui->label_49->setText(str);
qDebug()<< it12.key() << "的值是 : " << it12.value() <<'\n';
}
for(int n=0;n< userStr.length();n++){
QString str = userStr.at(n);
str = str.trimmed();
const QChar *ptr = str.unicode();
uint singleCharUnicode = (*ptr).unicode();
QString spelling;
if(bashHash.contains(singleCharUnicode)){
spelling = bashHash.value(singleCharUnicode);
ui->textBrowser->append(str+'\t'+spelling);
}else if(extraHash.contains(singleCharUnicode)){
spelling = extraHash.value(singleCharUnicode);
ui->textBrowser->append(str+'\t'+spelling);
}else{
QMessageBox::information(this, "仅支持国标字集", "不支持超集字查询!",
QMessageBox::Ok,QMessageBox::NoButton);
}
}
QHash
的速度非常可观,尤其是将 key
经过数值化处理之后。
流式卡节点
void newDialog::autoReader(){
QStringList fFileContent;
QFile aFile(curTablePathDia);
bool overPointMark = false;//界信号
QString strTest;
QString str;
customYamlSTM.clear();
if (aFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream aStream(&aFile);
ui->label->clear();
while (!aStream.atEnd()){
strTest=aStream.readLine();
str = strTest;
qDebug() << "读条:" << str <<'\n';
if(( strTest =annotationClear(strTest)).startsWith("...")){//判断有没有到锚点
overPointMark = true;
customYamlSTM.append("...");
continue;
}
if(!overPointMark){//尚未过界
customYamlSTM.append(str);
qDebug << "读条:" << str <<'\n';
}else{//过界了
str = str.trimmed();
str = annotationClear(str);
if(!str.isEmpty()){
fFileContent.append(str); //添加到 StringList
}
}
}
aFile.close();
ui->label->setText(curTablePathDia);
iniModelFromStringList(fFileContent);//从StringList的内容初始化数据模型
}
}
有了这次编写 Qt
程序的经验,本地数据处理类的需求是完全没问题了。
带参数启动
要让一个基于 QWidget
的 Qt 程序接受带参数启动,需要在程序的 main
函数中处理命令行参数。
这通常涉及解析命令行参数,并根据这些参数执行相应的操作。以下是一个基本的步骤:
- 修改
main
函数以接收参数
Qt 的 main
函数通常如下所示:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.show();
return app.exec();
}
这里的 argc
和 argv
参数分别代表命令行参数的数量和实际参数值,使用这些参数来接收和处理命令行输入。
- 解析命令行参数
在 main
函数中,可以使用标准的 C/C++ 方法来解析 argc
和 argv
,或者使用 Qt 提供的 QCommandLineParser
类。
QCommandLineParser
解析命令行参数更加简单和直观:
使用 QCommandLineParser
的示例:
#include <QCommandLineParser>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("My Qt App");
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("source", "The source file to process.");
// 可以添加更多选项和参数...
parser.process(app);
// 检查是否提供了必需的参数,并获取它们的值...
QString sourceFilePath = parser.positionalArguments().at(0);
qDebug() << "Source file:" << sourceFilePath;
QWidget window;
window.show();
// 根据解析的参数执行其他操作...
return app.exec();
}
在这个例子中,使用 QCommandLineParser
来解析命令行参数,并检查是否提供了一个名为 source
的位置参数。
然后,我们可以根据这些参数在程序中进行相应的操作。
- 处理参数并执行操作
一旦解析了命令行参数,可以根据这些参数执行任何必要的操作。
这可能包括改变程序的行为、加载特定的文件或数据、设置配置选项等。
确保程序能够适当地响应这些参数,并在必要时向用户提供反馈。
- 测试和调试
在实现带参数启动的功能后,确保充分测试程序以验证它是否正确响应各种命令行参数。
可以通过命令行界面手动运行程序并传递不同的参数来进行测试。
此外,使用调试工具可以帮助你跟踪和解决任何潜在的问题或错误。
上述的示例中,QString sourceFilePath = parser.positionalArguments().at(0)
这行代码做了以下几件事:
a. 调用 parser.positionalArguments()
QCommandLineParser
类的 positionalArguments()
方法返回一个包含所有位置参数的 QStringList
。
位置参数是那些在命令行中按照特定顺序出现的参数,它们不是以短横线(-
)或双短横线(--
)开头的。
在上面的例子中,我们添加了一个名为 “source” 的位置参数,这意味着用户需要在启动程序时提供这个参数。
b. 使用 at(0)
访问第一个位置参数
QStringList
类的 at(int index)
方法用于访问列表中指定索引位置的元素。
因为 positionalArguments()
返回的是一个列表,所以我们需要使用 at(0)
来获取第一个(也是唯一一个,在这个例子中)位置参数的值。索引 0
表示列表中的第一个元素。谨作为示例呈现。
c. 将获取到的值赋给 sourceFilePath
最后,获取到的位置参数的值(即用户提供的 “source” 参数的值)被赋值给 QString
类型的变量 sourceFilePath
。这个变量之后可以在程序中被使用,例如,可能用于指定要打开的文件路径或其他需要基于用户输入的操作。
在实际应用中,你可能会根据需要对这个值进行进一步的处理或验证,以确保它符合你的程序的预期要求。例如,如果 source
参数应该是一个文件路径,你可能需要检查路径是否存在,或者它是否指向了一个有效的文件。