QT快速学习笔记——在组件之上显示另一个组件

Parenting system介绍

这是我要介绍的QT程序的第一个重要的概念——Parenting system,原谅我不知道怎么翻译=_=。

这个机制的实现由QObject类提供,该类是QT中绝大部分组件的基类。QT中类的关系简图如下:

可以看到,我们在上一节中所使用的QPushButton组件也继承自QObject类,所以该组件也支持Parenting system机制。

代码演示

接下来我将通过一段代码来演示Parenting system机制的作用:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton *button1 = new QPushButton("Hello World");
    // 实例化button2,在构造函数的第二个参数中填入button1的地址
    // 此时button2就是button1的子组件
    QPushButton *button2 = new QPushButton("Hi", button1);

    // 只需要显示父组件,其下的子组件就会一同显示
    // 另外子组件也不需要单独调用delete(),他会随着父组件一起被释放
    button1->show();

    return a.exec();
}

运行后显示是这样的:

我们将窗体拉大,变成如下:

解读

可以看到,button2是显示在了button1的上面。这就很实际了,因为我们常见的Windows窗体都是在一个组件上叠加其他组件来显示的,例如:

每一个颜色的框框代表一个组件(没有把所有组件都标记出来,只摘取部分说明问题)。可以发现这样一个很复杂的窗体,就是一个面积很大的组件上面无限层次的叠加小组件来最终实现的。

而维护这种组件之间的父子关系的方法就是我们本篇文章所介绍的——Parenting system机制。

但是我们刚刚的程序还存在一个问题,就是父组件button1是一个按钮,试想一下如果把button1换成一个显示空白的组件是不是会更好一些?相关内容将在下一篇文章中详述。

QT快速学习笔记——编写第一个QT程序

刚新建完的QT项目包含五个文件,分别是:

  • main.cpp
  • mainwindow.cpp
  • mainwindow.h
  • mainwindow.ui
  • untitled.pro

尝试运行一下这个默认项目,运行的方法如下所示。

运行起来的程序:

是一个标题为MainWindow的空窗体,里面什么也没有。即便如此,这个窗体的代码对于初学者而言依然过于复杂,接下来我们删除掉窗体显示相关代码,只保留QT程序最基本的框架,随后再一点一点向里面填充内容。

删掉除了main.cpp和untitled.pro之外的其他文件。最后整个项目变成这样:

我们看main.cpp,这个文件的初始内容如下:

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

下面简单修改一下这个文件,删除掉对mainwindow.h文件的引用和对MainWindow类的实例化相关代码。我们在之前看到的那个标题为MainWindow的窗体实际上就是这个类生成的。删除后的代码如下所示:

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    return a.exec();
}

现在看到的就是一个QT程序的最基本框架了。下面我们填充一个按钮到这个QT程序里,让按钮显示Hello World。

#include <QApplication>
#include <QPushButton> // QPushButton这个类提供按钮组件

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 实例化一个按钮,这里传给构造函数的值会被设置为按钮显示的文字
    // 除此之外还可以通过按钮类提供的setText方法来设置,后面会讲
    QPushButton *button = new QPushButton("Hello World"); 
    button->show(); // 调用show方法,显示组件。

    return a.exec();
}

之后运行程序就可以看到第一个QT程序跑起来了~他可能很简陋,但战略意义远大于战术意义。

QT快速学习笔记——开发环境部署及项目创建

QT开发环境安装

在下面的地址中找到适合你的操作系统的最新的安装包,下载之。

http://download.qt.io/archive/qt/

安装步骤就是一路点击“下一步”,直到出现如下界面的时候需要手工勾选一下,只勾选我标记出来的选项即可。

随后继续下一步,直到安装完成。

创建一个新项目

QT自带的开发工具叫QT Creator,是一个C++ IDE,你也可以使用微软的VS来开发QT,这里只介绍QT Creator。

QT安装好后QT Creator的快捷方式并不会创建在系统桌面上,需要你去开始菜单里找。

QT Creator启动后长这个样子:

创建项目的流程是:文件->新建文件或项目,然后调出如下界面,按照我选的选一下,之后一路无脑下一步。选的过程中可以简单看看界面的文字介绍了解一下这一步大概做了哪些工作,实在搞不懂的也别去纠结,该懂的以后自然会懂~

这样一个QT项目就创建好了,如下所示:

QT快速学习笔记——导语

前言

为什么称之为“快速学习笔记”?是因为我本人学东西就是一个“莽”字,啥也不会的时候就横冲直撞观其大略习其思想,瞅的差不多了直接融入到项目里应用,再在开发过程中对细节问题随时查缺补漏。当然这种学习方法并不适合所有人,有的自律性强耐得住寂寞的人就比较喜欢稳扎稳打,从基础开始一点点学。

哦,对了——你可能想问学习QT是不是要精通C++才行?我必须告诉你并不是,因为我本人就是个C++白痴(约等于什么也不会的那种),按照我的学习思路,C++的语法是到用到的时候才会去查手册的,并不会在学习QT时提前去学。当然,这样做的前提是,你必须拥有其他至少一门OOP编程语言的使用经验,否则还是先简单学习下C++再来看这个系列的文章吧。

QT简介

QT是一个C++的开发库,里面包括多线程、网络、GUI等等的模块。很多人把QT当成是一个GUI库其实是片面了,因为QT更像一个大杂烩,GUI只是其功能的一部分。

QT学习资料

除了本系列文章之外你还可以参考官方的学习手册。其实我是很推荐你看看官方手册的,因为我所写的是我按照我的理解方式总结出来的,也许你自己翻一遍官方手册会有不一样的感悟呢。英文不好没关系,我也是借着谷歌翻译看的=_=

QT官方入门手册(推荐初学者阅读)

https://wiki.qt.io/Qt_for_Beginners

QT官方完全开发手册(推荐做项目的时候随用随查)

https://doc.qt.io/qt-5/reference-overview.html

QT的应用案例

列举四个比较有名的项目

  • 国产Deepin操作系统的DDE桌面环境
  • KDE桌面环境
  • WPS办公软件
  • HBuilderX前端开发者工具

如何从YouTube上扒等效柱状投影的3D全景视频

导语

这段时间接了个项目,是给WordPress编写全景视频播放插件,客户的视频来源是YouTube。

但是我发现了一个很有意思的问题:

做过全景视频的同学应该一眼就看出来这种不是常见的视频格式。

事实上,这个是谷歌最新研发的 “等角度立方体贴图 (EAC) ”,这种格式目前只有YouTube可以正常解析。

当然,YouTube也提供了常见的“等效柱状投影”的全景视频,下面看获取方法。

YouTube上等效柱状投影全景视频的获取方法

核心思想就是改变浏览器的User Agent,让YouTube识别不出你所用的浏览器,这样他就不能确定你的浏览器是否支持解析“等角度立方体贴图”格式的视频,所以会返回最普通的“等效柱状投影”的视频。这样即便是直接展示视频的原始样式而不做全景播放处理也依然可以勉强观看。

效果如下:

修改User Agent可以使用各个浏览器的插件实现,比如FireFox可以使用User-Agent Switcher插件,将UserAgent修改为curl或者为空都可以。之后就可以使用常用的扒视频工具从这个网页上提取链接下载了。

记录一个Laravel orm访问器不起作用的问题

前言

今天码砖的时候发现laravel orm的访问器在用户前台死活都用不了,但是管理员后台却显示正常,一时间感到很迷乱……

问题原因

经过仔细观察,原来后台是先查询然后再通过手工调用对象属性的方式显示,前台则是查询后直接将结果返回给用户。

翻了下官方手册,orm的访问器并不是在数据库查询的时候起的作用,而是在调用相应的orm对象属性的时候才会生效。

所以,想用访问器的话就不要把查询到的orm对象直接返回给客户了,中间加一层处理就好了。

附加

根据访问器的工作原理可知,假如数据库查询时限制了输出的字段,那么在访问器中被限制输出的字段是没办法用$this->field或$this->attributes[‘field’]来索引的。

JB家的IDE在Linux下无法保存FTP/数据库的密码的解决方案

前言

事情的缘由是这样的——某天我开开心心的打开computer想要码一会砖,然后万恶的Phpstorm一直在询问我sftp的登录用户名和密码。即便我输入的时候选中了记住密码,在程序重启之后这货又会再次询问。之后发现Datagrip也犯了一样的毛病,一直询问我数据库登录信息……

这里必须要吐槽一下JB家IDE的脑残设计——明知道是缺少依赖软件包,为啥就不能直接在前端抛一个错误呢?搞得我还得一点一点的翻程序日志,我太难了=_=…

错误原因

错误原因很简单,就是前面说的:缺少依赖包。具体缺少的是一个叫做gnome-keyring的包,这个包是用来保存各种密码的。

解决方案

安装之。以下列出常见的三个系列发行版下的安装方式,其他发行版的同学使用各自的包管理器直接安装就行,这个包通常都是集成在官方源里的。

Ubuntu/Debian

sudo apt install gnome-keyring

Fedora/CentOS

sudo yum install gnome-keyring

Arch/Manjaro

sudo pacman -S gnome-keyring

整理下我的VIM配置文件

以下是我个人使用的VIM配置文件内容,记录在这里,方便日后查阅。

"打开语法高亮
syntax on
set t_Co=256
"打开文件类型检测功能
filetype on
"不同文件类型采用不同缩进
filetype indent on
"允许使用插件
filetype plugin on
filetype plugin indent on
"关闭vi模式
set nocp
"与windows共享剪贴板
set clipboard+=unnamed
"取消VI兼容,VI键盘模式不易用
set nocompatible
"显示行号, 或set number
set nu
"历史命令保存行数
set history=100
"当文件被外部改变时自动读取
set autoread
"取消自动备份及产生swp文件
set nobackup
set nowb
set noswapfile
"允许使用鼠标点击定位
set mouse=a
"允许区域选择
set selection=exclusive
set selectmode=mouse,key
"高亮光标所在行
set cursorline
"取消光标闪烁
set novisualbell
"总是显示状态行
set laststatus=2
"状态栏显示当前执行的命令
set showcmd
"标尺功能,显示当前光标所在行列号
set ruler
"粘贴时保持格式
set paste
"高亮显示匹配的括号
set showmatch
"在搜索的时候忽略大小写
set ignorecase
"高亮被搜索的句子
set hlsearch
"在搜索时,输入的词句的逐字符高亮(类似firefox的搜索)
set incsearch
"继承前一行的缩进方式,特别适用于多行注释
set autoindent
"为C程序提供自动缩进
set smartindent
"使用C样式的缩进
set cindent
"制表符为4
set tabstop=4
set expandtab
"%retab!
"统一缩进为4
set softtabstop=4
set shiftwidth=4
"允许使用退格键,或set backspace=2
set backspace=eol,start,indent
set whichwrap+=<,>,h,l
"取消换行
set nowrap
"启动的时候不显示那个援助索马里儿童的提示
set shortmess=atI
"在被分割的窗口间显示空白,便于阅读
set fillchars=vert:\ ,stl:\ ,stlnc:\
"光标移动到buffer的顶部和底部时保持3行距离, 或set so=3
set scrolloff=3
"设定默认解码
set fenc=utf-8
set fencs=utf-8,usc-bom,euc-jp,gb18030,gbk,gb2312,cp936
"设定字体
set guifont=Courier_New:h11:cANSI
set guifontwide=新宋体:h11:cGB2312
"设定编码
set enc=utf-8
set fileencodings=ucs-bom,utf-8,chinese
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim
"自动补全
filetype plugin indent on
set completeopt=longest,menu
"自动补全命令时候使用菜单式匹配列表
set wildmenu
autocmd FileType ruby,eruby set omnifunc=rubycomplete#Complete
autocmd FileType python set omnifunc=pythoncomplete#Complete
autocmd FileType javascript set omnifunc=javascriptcomplete#CompleteJS
autocmd FileType html set omnifunc=htmlcomplete#CompleteTags
autocmd FileType css set omnifunc=csscomplete#CompleteCSS
autocmd FileType xml set omnifunc=xmlcomplete#CompleteTags
autocmd FileType java set omnifunc=javacomplete#Complet

35行C++代码实现时间戳转时间算法

程序逻辑概述

根据平年闰年计算规则,利用递归先求出年份。之后再根据固定的大小月规则和平年闰年下2月天数的不同求出月数,之后就剩下最简单的天数、小时、分钟和秒了。

这个程序我承认为了尽可能缩小行数,有的地方写的不是很规范~正可谓是为了减少行数而不择手段~

程序代码

注:去掉空行的话的确只有35行的,我并不是标题党!

#include <iostream>

int time_stamp = 28800, year = 1969, month = 0, day = 0, hour = -1, minute = -1, tmp;

auto isLeapYear = [] { return (year % 4 == 0 && year % 100 != 0) || (year % 100 == 0 && year % 400 == 0) || (year % 3200 == 0 && year % 172800 == 0); };

int getYear(int *ptr_time_stamp) {
	++year;
	if (*ptr_time_stamp >= (isLeapYear() ? 31622400: 31536000))
		getYear(&(*ptr_time_stamp -= (isLeapYear() ? 31622400 : 31536000)));

	return year;
}

int getMonth(int *ptr_time_stamp) {
	++month;
	if (month == 2 && (*ptr_time_stamp >= (isLeapYear() ? 2505600 : 2419200)))
		getMonth(&(*ptr_time_stamp -= (isLeapYear() ? 2505600 : 2419200)));
	else if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && *ptr_time_stamp >= 2592000)
		getMonth(&(*ptr_time_stamp -= 2678400));
	else if ((month == 4 || month == 6 || month == 9 || month == 11) && *ptr_time_stamp >= 2678400)
		getMonth(&(*ptr_time_stamp -= 2592000));

	return month;
}

int getTime(int *ptr_time_stamp, int *type, int second) {
	++*type;
	if (*ptr_time_stamp >= second) 
		getTime(&(*ptr_time_stamp -= second), type, second);

	return *type;
}

int main(void) {
	std::cin >> tmp;
	time_stamp += tmp;

	std::cout << getYear(&time_stamp) << "年";
	std::cout << getMonth(&time_stamp) << "月";
	std::cout << getTime(&time_stamp, &day, 86400) << "日 ";
	std::cout << getTime(&time_stamp, &hour, 3600) << "时";
	std::cout << getTime(&time_stamp, &minute, 60) << "分" << time_stamp << "秒";

	return 0;
}

使用公安部研发的“中小网站安全防护终端”免费为网站提供WAF

注意

该软件已经停更,建议用云锁、安全狗这些防护软件

前言

“中小网站安全防护终端”是公安部第三研究所研发的一款服务器本地WAF防护类软件,其向各类站点免费开放下载,旨在提升全国的网络安全水平。

因为我的博客访客每天只有150左右,没有什么攻击价值=_=。没人攻击就没法测试这套防护软件的具体防护效果,不过反正是免费的东西,加上总是比没有好~

部署方法

注册并登录中小网站安全防护网站

登录后点击左侧的“本地防护”。这里还有个“云防护”,不用管他,实测在这篇文章发布的时候已经用不了了,据说是用的阿里的云防护基础版。

之后先下载适合你的操作系统的防护软件(本文演示使用Linux版),然后点击“服务器管理”。

新增一台服务器,填入对应的信息。需要说明的是: 备案号这里填的是公安备案号,不是ICP备案号。服务器IP栏填外网IP,物理地址是你的网卡的MAC地址。个别云厂商无法查看外网网卡MAC地址的,填内网网卡的MAC也行。填完后保存即可。

将下载到的防护软件上传到服务器上,解压并安装。命令如下:

tar -zxvf 软件压缩包的文件名
cd WebProtect-WPT
chmod 777 ./install.sh && ./install.sh

之后耐心等待安装完成即可。

安装后访问https://ip:7000,来打开WEB面板,默认账号密码为admin/admin(登录后建议更改)。

登录后会要求你输入序列号,否则是看不到功能模块的。序列号可以在中小网站安全防护网站上查看。点开服务器列表,在箭头所指的位置上能看见“本地防护安装码”,复制那个码填过去即可。如果提示码还未生成的话就要等一段时间了,我当时等了有半小时。

安装并授权后的界面

结语

遇到问题欢迎在评论区留言哦~

Guzzle遇到4xx和5xx的状态码时返回异常的解决办法

前言

写了个API商城的项目,就是向外收费提供API接口。其中实名认证相关的接口是调用的阿里云的,他们有个很操蛋的设定——如果认证失败会返回555状态码。要知道5xx可是服务器出错才会返回的,这种返回值说实话对api调用方不是很友好。我的习惯是,只要接口调用成功就应该返回2xx的状态码,然后在返回值里面加入code字段来标注错误码。

阿里的这种返回方式触发了Guzzle的异常,很头疼。

解决办法

后来耐心翻阅了一下官方手册,发现可以在new对象的时候加入http_errors=false的选项来禁止Guzzle生成异常。

具体示例代码如下:

$client = new Client(['http_errors' => false]);
$response = $client->request('GET', 'https://xxx.com/xxx', ['headers' => $post_head, 'form_params' => $post_data]);

结语

实际应用中遇到任何问题可以在评论区留言。我会协助你解决的!