QT开发GIF截屏工具的问题记录

网友投稿 280 2022-11-29

QT开发GIF截屏工具的问题记录

项目地址:​​log 重定向问题。

描述:在QML中的log print成功输出所有信息,CPP中的打印不能显示文件名,行号,函数名

看了帮助文档中的例子 QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) 安装我们自己的handler后可以重定向

#include #include #include void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){ QByteArray localMsg = msg.toLocal8Bit(); switch (type) { case QtDebugMsg: fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtInfoMsg: fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); abort(); }}int main(int argc, char **argv){ qInstallMessageHandler(myMessageOutput); QApplication app(argc, argv); ... return app.exec();}

我使用在自己的项目中,源代码如下

...int main(){//...qInstallMessageHandler( LogBase );//...return 0;}

结果log 重定向,QML成功打印所有信息,CPP中的打印不能显示文件名,行号,函数名

[Debug ((null), 0, (null))] LogInit finished.[Warning (qrc:/main.qml, 72, (null))] qrc:/main.qml:72:9: QML Button: Cannot anchor to an item that isn't a parent or sibling. [Debug (qrc:/main.qml, 30, onPressed)] onPressed: (613.9609375, 507.06640625)

在查看源码后,发现QMessageLogContext有两种构造方式。

class QMessageLogContext{ Q_DISABLE_COPY(QMessageLogContext)public: Q_DECL_CONSTEXPR QMessageLogContext() : version(2), line(0), file(Q_NULLPTR), function(Q_NULLPTR), category(Q_NULLPTR) {} Q_DECL_CONSTEXPR QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) : version(2), line(lineNumber), file(fileName), function(functionName), category(categoryName)

在我们的工程中,CPP类下的QMessageLogContext构造使用了第一种方式,QML下的的QMessageLogContext构造使用了第二种方式。我想,这和应用程序的启动相关,本工程使用QQmlApplicationEngine启动。 我们可以通过修改message handler函数来保证文件名,行号,函数名的输出:

const QMessageLogContext &context = QMessageLogContext( __FILE__, __LINE__, __FUNCTION__, NULL ), const QString &msg = QString("") ) {

Qt Quick设置icon

在pro文件中加上:

macx{ message("compile for mac os x") ICON = Images/logo.icns}

重新生成Makeifle,再make。

双击mac程序,程序在移动文件时提示没有权限。

双击app启动程序,我一开始猜想是Finder进程启动app进程。使用pstree可以查看相关的进程派生关系。可以使用brew安装pstree。 查询Finder相关的分支:

➜ Images git:(master) ✗ ps aux |grep Finderweiyang 284 0.0 0.5 4751940 45756 ?? R 14Jul18 9:12.45 /System/Library/CoreServices/Finder.app/Contents/MacOS/Finderweiyang 27974 0.0 0.0 4267752 576 s002 R+ 9:07AM 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn Finder➜ Images git:(master) ✗ pstree -p 284-+= 00001 root /sbin/launchd \--= 00284 weiyang /System/Library/CoreServices/Finder.app/Contents/MacOS/Find

用类似的方法,查询双击Gifer.app后的进程派生关系。

➜ Images git:(master) ✗ ps aux |grep Giferweiyang 28107 0.2 0.6 4474736 46468 ?? S 9:08AM 0:01.01 /Users/weiyang/pro/Gifer/Gifer.app/Contents/MacOS/Giferweiyang 28129 0.0 0.0 4267752 532 s002 R+ 9:09AM 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn Gifer➜ Images git:(master) ✗ pstree -p 28107-+= 00001 root /sbin/launchd \--= 28107 weiyang /Users/weiyang/pro/Gifer/Gifer.app/Contents/MacOS/Gifer

原来,是launchd启动app的。 那么命令行启动的进程派生又是怎么样子的呢?

➜ Images git:(master) ✗ ps aux |grep Giferweiyang 28395 0.0 0.0 4267752 640 s002 R+ 9:16AM 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn Giferweiyang 28342 0.0 0.6 4476844 46860 s001 S+ 9:15AM 0:00.70 ./Gifer➜ Images git:(master) ✗ pstree -p 28342-+= 00001 root /sbin/launchd \-+= 17185 weiyang /Applications/iTerm.app/Contents/MacOS/iTerm2 \-+= 17249 weiyang /Applications/iTerm.app/Contents/MacOS/iTerm2 --server lo \-+= 17250 root login -fp weiyang \-+= 17251 weiyang -zsh \--= 28342 weiyang ./Gifer

命令行启动的进程是由终端进程启动的。

在源码中,我使用了​​QDir::current().absolutePath()​​​定位程序的路径,但他返回的是​​/​​。进一步查看该函数的解释

[static] QDir QDir::current() Returns the application’s current directory. The directory is constructed using the absolute path of the current directory

再看看QCoreApplication::applicationDirPath()的解释

[static] QString QCoreApplication::applicationDirPath() Returns the directory that contains the application executable. For example, if you have installed Qt in the C:\Qt directory, and you run the regexp example, this function will return “C:/Qt/examples/tools/regexp”.

在程序启动起来后QDir QDir::current()返回的application’s current directory是不确定的。 如果是双击app启动,返回值是​​​/​​​;如果是命令行启动,返回值是可执行文件的路径,也就和​​QCoreApplication::applicationDirPath()​​​一样了。 后者是我们需要的函数。

使用system运行命令的问题

假设我使用system运行这样一条命令: ​​​ffmpeg -threads 1 -y -r 70 -i /Users/weiyang/pro/Gifer/Gifer.app/Contents/MacOS/Pictures/out%d.png -final_delay 300 /Users/weiyang/pro/Gifer/Gifer.app/Contents/MacOS/Pictures/output.gif​​​ 再这样判断:

; if( -1 == ret || 127 == ret ) { tmp = cmdStr + strerror(errno); qWarning() << tmp; QMessageBox::information( this, tr("Combine Gif Failed"), tmp, QMessageBox::Ok ); return false;

判断依据来自于mac上的帮助文档

RETURN VALUES The system() function returns the exit status of the shell as returned by waitpid(2), or -1 if an error occurred when invoking fork(2) or waitpid(2). A return value of 127 means the execution of the shell failed.

system的工作大致是这样的: 父进程fork子进程 父进程等待子进程 在子进程中执行字符串所表述的命令 返回执行结果。

我将源码改成:

system( cmdStr.toStdString().c_str() ); // create sub qCritical("system error!"); } else { qDebug("ret is not -1, exit ret value = [0x%x]", ret); //0x7f00 // call shell script and finish successfully. if (WIFEXITED(ret)) // WIFEXITED is a macro { if (0 == WEXITSTATUS(ret)) // return value of shell script run. { qDebug("run shell script successfully."); } // 0x7f = 127 else { qCritical("run shell script fail, script exit code: %d, reason: %s", WEXITSTATUS(ret), strerror(errno)); } } else { qCritical("exit ret = [%d]", WEXITSTATUS(ret)); } }

当我在shell终端中命令行方式启动后,没有问题。但如果是双击Gifer.app就会是这样的结果:

[Debug (windowgrabber.cpp, 159, bool WindowGrabber::combineImages())] ret is not -1, exit ret value = [0x7f00][Critical (windowgrabber.cpp, 171, bool WindowGrabber::combineImages())] run shell script fail, script exit code: 127, reason: No such file or

一样的命令,结果却是不同的。 我找到了ffmpeg的具体位置,然后在system的命令中也给出ffmpeg的绝对路径,问题就不存在了。也就是说,在双击启动app的方式下,系统找不到ffmpeg。 在双击启动app的方式下,PATH的值是

/usr/bin:/bin:/usr/sbin:/sbin

此结果在代码中添加​​system("echo $PATH > ~/code/txt");​​​可以检测出来。 这和我在​​​~/.bashrc​​​下的设置是不一样的。 新的PATH值刚好对应

/usr/include/paths.h67:#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"/usr/libexec/security-checksystem12:export PATH=/usr/bin:/bin:/usr/sbin:/sbin/usr/local/Homebrew/bin/brew71: PATH="/usr/bin:/bin:/usr/sbin:/sbin"

system的源码为:

int system(const char * cmdstring){ pid_t pid; int status; if( cmdstring == NULL ) { return 1; } if( (pid = fork()) < 0 ) { status = -1; } else if( pid == 0 ) { // execl create new process to replace old process execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); // if above statement execute successfully, the following function would not work. _exit(127); } else { while( waitpid(pid, &status, 0) < 0 ) { if( errno != EINTR ) { status = -1; break; } } } return

execlp搜索目录默认值是paths.h中的​​_PATH_DEFPATH​​​。也就是前面提到过的​​/usr/bin:/bin:/usr/sbin:/sbin​​

For execlp() and execvp(), search path is the path specified in the environment by PATH variable. If this variable is not specified, the default path is set according to the ​​​_PATH_DEFPATH​​​ definition in ​​​​​, which is set to “/usr/bin:/bin”. For execvP(), the search path is specified as an argument to the function. In addition, certain errors are treated specially.

故,在​​/usr/bin/​​下建立一个软连接即可。

➜ ~ sudo ln -s

怎样使得dmg安装包自带Application文件夹

之前打包都是使用QT的打包功能,macdeployqt或windeployqt,但是生成的dmg有一个缺点,他没有Application文件夹图标生成。每次都需要拖动到Finder中的Application。 我在​​​看到了一位大神Linus Unnebäck 的回答,他的方案简单易懂。

brew install nodenpm install

创建spec.json

{ "title": "Gifer Installer", "icon": "Images/logo.icns", "background": "Images/back.png", "icon-size": 80, "contents": [ { "x": 100, "y": 120, "type": "file", "path": "Gifer.app"}, { "x": 400, "y": 120, "type": "link", "path": "/Applications"} ], "window": { "size": { "width": 500, "height": 300} }}

生成dmg: ​​​appdmg spec.json Gifer.dmg​​​ 效果:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:shell编程 (3) —— 调试
下一篇:POST方法给@RequestBody传参数失败的解决及原因分析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~