记录一些在 windows 下将数据从 mysql 导入到 excel 过程中遇到的坑。首先,这一操作有两种方案:

  1. 在 navicat 中进行操作,将数据写入到 excel 文件中
  2. 在 excel 中进行操作,将数据从 mysql 服务器写入文件

第一种方案同事说在数据量很大的时候导出速度很慢,原因不明,我猜测是因为 navicat 使用的 POI 技术实现的功能,没有深究。第二种方案就我进行测试的方案,这种方案至少在速度方面有很明显的提升,下面列一下测试过程中遇到的坑:

  1. 只能使用 windows 的 office excel,wps 的表格有奇怪的问题无法解决(后来发现是因为系统中没有安装 Visual C++ Redistributable Packages for Visual Studio 2013
  2. 安装 Mysql ODBC 驱动时要注意 excel 的软件位数,如果 excel 是 32 位的,那么 Mysql ODBC 驱动也必须是 32 位的
  3. 64 位系统不显示 32 位的 ODBC 驱动,需要手动打开 C:\\Windows\\sysWOW64\\odbcad32.exe 程序添加连接
  4. 导入数据时出现错误:MySQL client ran out of memory,调整系统中的数据源(ODBC)中的连接选项,找到并勾选”Do not cache result”之类的选项,应该可以解决

转自:http://www.jctrans.com/tool/gjym.htm

国际域名缩写

国际域名缩写 国家或地区 Countries and Regions
AD 安道尔共和国 Andorra
AE 阿拉伯联合酋长国 United Arab Emirates
AF 阿富汗 Afghanistan
AG 安提瓜和巴布达 Antigua and Barbuda
AI 安圭拉岛 Anguilla
AL 阿尔巴尼亚 Albania
AM 亚美尼亚 Armenia
AO 安哥拉 Angola
AR 阿根廷 Argentina
AT 奥地利 Austria
AU 澳大利亚 Australia
AZ 阿塞拜疆 Azerbaijan
BB 巴巴多斯 Barbados
BD 孟加拉国 Bangladesh
BE 比利时 Belgium
BF 布基纳法索 Burkina-faso
BG 保加利亚 Bulgaria
BH 巴林 Bahrain
BI 布隆迪 Burundi
BJ 贝宁 Benin
BL 巴勒斯坦 Palestine
BM 百慕大群岛 Bermuda Is.
BN 文莱 Brunei
BO 玻利维亚 Bolivia
BR 巴西 Brazil
BS 巴哈马 Bahamas
BW 博茨瓦纳 Botswana
BY 白俄罗斯 Belarus
BZ 伯利兹 Belize
CA 加拿大 Canada
CF 中非共和国 Central African Republic
CG 刚果 Congo
CH 瑞士 Switzerland
CK 库克群岛 Cook Is.
CL 智利 Chile
CM 喀麦隆 Cameroon
CN 中国 China
CO 哥伦比亚 Colombia
CR 哥斯达黎加 Costa Rica
CS 捷克 Czech
CU 古巴 Cuba
CY 塞浦路斯 Cyprus
CZ 捷克 Czech Republic
DE 德国 Germany
DJ 吉布提 Djibouti
DK 丹麦 Denmark
DO 多米尼加共和国 Dominica Rep.
DZ 阿尔及利亚 Algeria
EC 厄瓜多尔 Ecuador
EE 爱沙尼亚 Estonia
EG 埃及 Egypt
ES 西班牙 Spain
ET 埃塞俄比亚 Ethiopia
FI 芬兰 Finland
FJ 斐济 Fiji
FR 法国 France
GA 加蓬 Gabon
GB 英国 United Kiongdom
GD 格林纳达 Grenada
GE 格鲁吉亚 Georgia
GF 法属圭亚那 French Guiana
GH 加纳 Ghana
GI 直布罗陀 Gibraltar
GM 冈比亚 Gambia
GN 几内亚 Guinea
GR 希腊 Greece
GT 危地马拉 Guatemala
GU 关岛 Guam
GY 圭亚那 Guyana
HK 香港特别行政区 Hongkong
HN 洪都拉斯 Honduras
HT 海地 Haiti
HU 匈牙利 Hungary
ID 印度尼西亚 Indonesia
IE 爱尔兰 Ireland
IL 以色列 Israel
IN 印度 India
IQ 伊拉克 Iraq
IR 伊朗 Iran
IS 冰岛 Iceland
IT 意大利 Italy
JM 牙买加 Jamaica
JO 约旦 Jordan
JP 日本 Japan
KE 肯尼亚 Kenya
KG 吉尔吉斯坦 Kyrgyzstan
KH 柬埔寨 Kampuchea (Cambodia )
KP 朝鲜 North Korea
KR 韩国 Korea
KT 科特迪瓦共和国 Republic of Ivory Coast
KW 科威特 Kuwait
KZ 哈萨克斯坦 Kazakstan
LA 老挝 Laos
LB 黎巴嫩 Lebanon
LC 圣卢西亚 St.Lucia
LI 列支敦士登 Liechtenstein
LK 斯里兰卡 Sri Lanka
LR 利比里亚 Liberia
LS 莱索托 Lesotho
LT 立陶宛 Lithuania
LU 卢森堡 Luxembourg
LV 拉脱维亚 Latvia
LY 利比亚 Libya
MA 摩洛哥 Morocco
MC 摩纳哥 Monaco
MD 摩尔多瓦 Moldova, Republic of
MG 马达加斯加 Madagascar
ML 马里 Mali
MM 缅甸 Burma
MN 蒙古 Mongolia
MO 澳门 Macao
MS 蒙特塞拉特岛 Montserrat Is
MT 马耳他 Malta
MU 毛里求斯 Mauritius
MV 马尔代夫 Maldives
MW 马拉维 Malawi
MX 墨西哥 Mexico
MY 马来西亚 Malaysia
MZ 莫桑比克 Mozambique
NA 纳米比亚 Namibia
NE 尼日尔 Niger
NG 尼日利亚 Nigeria
NI 尼加拉瓜 Nicaragua
NL 荷兰 Netherlands
NO 挪威 Norway
NP 尼泊尔 Nepal
NR 瑙鲁 Nauru
NZ 新西兰 New Zealand
OM 阿曼 Oman
PA 巴拿马 Panama
PE 秘鲁 Peru
PF 法属玻利尼西亚 French Polynesia
PG 巴布亚新几内亚 Papua New Cuinea
PH 菲律宾 Philippines
PK 巴基斯坦 Pakistan
PL 波兰 Poland
PR 波多黎各 Puerto Rico
PT 葡萄牙 Portugal
PY 巴拉圭 Paraguay
QA 卡塔尔 Qatar
RO 罗马尼亚 Romania
RU 俄罗斯 Russia
SA 沙特阿拉伯 Saudi Arabia
SB 所罗门群岛 Solomon Is
SC 塞舌尔 Seychelles
SD 苏丹 Sudan
SE 瑞典 Sweden
SG 新加坡 Singapore
SI 斯洛文尼亚 Slovenia
SK 斯洛伐克 Slovakia
SL 塞拉利昂 Sierra Leone
SM 圣马力诺 San Marino
SN 塞内加尔 Senegal
SO 索马里 Somali
SR 苏里南 Suriname
ST 圣多美和普林西比 Sao Tome and Principe
SV 萨尔瓦多 EI Salvador
SY 叙利亚 Syria
SZ 斯威士兰 Swaziland
TD 乍得 Chad
TG 多哥 Togo
TH 泰国 Thailand
TJ 塔吉克斯坦 Tajikstan
TM 土库曼斯坦 Turkmenistan
TN 突尼斯 Tunisia
TO 汤加 Tonga
TR 土耳其 Turkey
TT 特立尼达和多巴哥 Trinidad and Tobago
TW 台湾省 Taiwan
TZ 坦桑尼亚 Tanzania
UA 乌克兰 Ukraine
UG 乌干达 Uganda
US 美国 United States of America
UY 乌拉圭 Uruguay
UZ 乌兹别克斯坦 Uzbekistan
VC 圣文森特岛 Saint Vincent
VE 委内瑞拉 Venezuela
VN 越南 Vietnam
YE 也门 Yemen
YU 南斯拉夫 Yugoslavia
ZA 南非 South Africa
ZM 赞比亚 Zambia
ZR 扎伊尔 Zaire
ZW 津巴布韦 Zimbabwe

最近 coding 的 pages 服务越来越没法用了,不是自动部署失败就是博客无法访问,而且访问速度很慢,估计再过段时间 coding 就不再提供免费的 pages 服务了。但是如果将域名解析到 github 上,百度又无法抓取(github 不允许),思来想去还是放在自己的服务器上吧。

不过放在自己的服务器上有两个问题:

  • 没有备案不能使用 dnspod 的 301 重定向把 www 的域名重定向到顶级域名
  • 博客更新后不能自动部署

我的博客是个基于 hexo 的静态博客,可以使用 nginx 来提供 web 服务,同时 nginx 可以配置根据域名来重定向,这样就解决了第一个问题,博客内容就使用 git 仓库,注意这个仓库不是 markdown 的原始文件,而是经过 hexo 根据生成后的 html 站点,如果之前在 github 上部署过,在 github 上的仓库的名字应该会是 <UserName>.github.io 或者在 coding 上部署过那仓库名则是 coding 下跟用户名相同的仓库。以后更新了博客依然推送到 github 或者 coding 上,在自己的服务器上 clone 下仓库,然后使用 github 或者 coding 的 webhooks 功能更新自己服务器上的仓库,这样就解决了自己服务器上的博客不能自动部署更新的问题。

下面的例子是基于 github 的,coding 的操作类似,不过鉴于 coding 提供的服务不太稳定的问题,建议博客仓库托管也不要放在 coding 上了。

克隆博客仓库到服务器上

这一步很简单,跟克隆普通 git 仓库一样,我把仓库放在了 /opt/hexo-blog 下,如果不放在这个路径下,那后面的内容中的相应路径也要修改。

以我在 github 下的博客仓库为例,注意执行命令的用户权限,后面的命令都假设服务器上的账户是 root:

1
git clone git@github.com:listenerri/listenerri.github.io.git /opt/hexo-blog

这里使用了 ssh 协议的仓库地址,这需要将服务器的 ssh public key 部署在自己的 github 账户中,如果不想部署使用 https 协议的地址也可以,不过可能会需要输入用户名和密码,这会影响到后面 python 脚本中的功能,建议还是用 ssh 协议的地址,部署个 key 又不麻烦。

阅读全文 »

目前我遇到的两种需要指定 git 使用的 ssh 秘钥的场景是:

  • git 服务地址不同(一个公司,一个 github)
  • 同一个git 服务地址但账户不同

下面分开介绍。

服务地址不同

这估计是大多数人遇到的场景,同一个笔记本电脑,有时需要向公司的 git 服务推送代码,有时要向 github 推送代码,当然也可选择在两个不同的服务器上部署自己相同的公钥,但当无法做到这一情况时就要给 git 或者说给 ssh 命令指定哪个服务地址使用哪个秘钥。

首先生成两套秘钥即四个文件,两对公、私秘钥,具体的生成方法这里就不赘述了,不过要注意在生成第二套私钥时需要指定文件名称,否则将覆盖默认秘钥,一般秘钥文件存储在 ~/.ssh 目录下:

1
2
3
4
id_rsa
id_rsa.pub
id_rsa_company
id_rsa_company.pub

.pub 结尾的文件为公钥,需要将其部署在服务端,比如 id_rsa.pub 部署在 github 上,id_rsa_company.pub 部署在公司的服务上,id_rsa 则是 ssh 命令也是 git 命令使用的默认的私钥,当秘钥文件生成完成后使用以下命令将用于公司的私钥添加到 ssh-agent 中:

1
ssh-add ~/.ssh/id_ras_company

添加完成后可以查看已经添加过的私钥:

阅读全文 »

quilt 是一个管理补丁的工具,它使用了栈的概念来管理多个补丁,其管理的补丁一般被放在 patches 目录下,在这个目录下除了补丁文件还有一个 series 文件,这个文件中列出的补丁文件才是真正被打入项目的补丁文件,同时补丁文件在 series 文件中出现的顺序也是它们在栈中的顺序,没有包含在 series 文件中的补丁文件将不会被 quilt 管理,当新增或移除补丁时 quilt 会自动更新 series 文件不需要手动管理。

接触到这个命令是因为要给一个 deb 软件包打上我自己的补丁,但是 quilt 命令的 man 手册比较混乱,没有将相关的子命令放在一起进行介绍,下面将根据子命令之间的相关性总结下用到的和与之相关的子命令。

applied-unapplied

  • applied:列出包含在 series 文件中且已经应用(生效)了的补丁
  • unapplied:列出包含在 series 文件中但没有应用(生效)的补丁

diff

指定一个被打过补丁的项目文件并根据其变动生成一个 diff 文件,如果指定了补丁则只把指定补丁对项目文件的改动生成到 diff 文件,当同一个项目文件被多个补丁改动过,这个参数就有用了,如果不指定补丁则默认使用最顶层的补丁,如果被打过补丁的项目文件也没有指定,则将包含指定补丁或最顶层的补丁导致的所有项目文件变动。

files

列出最顶层的一个或指定的补丁改动了的所有项目文件

patches

列出所有改动了指定的项目文件的补丁

阅读全文 »

QThread 官方文档中介绍了的两种用法:

  • worker-object
  • subclass

worker-object

引用官方文档中的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// worker 类
// 声明了一个信号,一个槽
class Worker : public QObject
{
Q_OBJECT

public slots:
void doWork(const QString &parameter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}

signals:
void resultReady(const QString &result);
};

// 控制器类
// 运行中主线程,用于管理 worker 对象和 QThread 对象
// 也声明了一个信号,一个槽
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};

在 worker 类中的槽函数是真正在子线程中工作的内容,而 worker 那个信号被发出时则表示工作完成,控制器中与之连接的槽函数的作用就是处理 worker 对象在完成工作之后使用其信号发出来的数据,控制器中的那个信号被发出时则是命令 worker 对象开始工作。因此需要使用下面的代码让 worker 对象在线程中开始工作:

1
2
3
4
5
// Note: 不要在主线程中以调用 worker->doWork() 函数的方式让 worker 开始工作
// 否则将会导致主线程阻塞在这个调用上,只能使用信号-槽的方式:

Controller *controller = new Controller;
emit controller->operate("some string");

看起来 worker 对象和 QThread 的对象并没有什么关系,那么 worker 对象是怎么在后台工作的呢?关键的一句代码是:

1
worker->moveToThread(&workerThread);

其中moveToThread方法是在 QObject 类中定义的,这意味着所有继承了 QObject 类的对象都可以调用这个方法来改变与自身关联的线程,使用下面的代码可以将 worker 对象再转移回主线程中:

阅读全文 »

最近公司需要补充一些项目的文档,我负责的几个项目中比较有实用价值的是这个 dde-dock 插件的开发入门教程,这里是转载,原发在 dde-dock 项目源码中。

插件的工作原理

插件是一种在不需要改动并重新编译主程序本身的情况下去扩展主程序功能的一种机制。\
dde-dock 插件是根据 Qt 插件标准所开发的共享库文件(so),通过实现 Qt 的插件标准和 dde-dock 提供的接口,共同完成 dde-dock 的功能扩展。\
可以通过以下链接查看关于 Qt 插件更详细的介绍:

https://wiki.qt.io/Plugins\
https://doc.qt.io/qt-5/plugins-howto.html

dde-dock 插件加载流程

在 dde-dock 启动时会跑一个线程去检测目录/usr/lib/dde-dock/plugins下的所有文件,并检测是否是一个正常的动态库文件,如果是则尝试加载。尝试加载即检测库文件的元数据,插件的元数据定义在一个 JSON 文件中,这个后文会介绍,如果元数据检测通过就开始检查插件是否实现了 dde-dock 指定的接口,这一步也通过之后就会开始初始化插件,获取插件提供的控件,进而将控件显示在任务栏上。

接口列表

这里先列出 dde-dock 都提供了哪些接口,可作为一个手册查看,注意,为 dde-dock 编写插件并不是要实现所有接口,这些接口提供了 dde-dock 允许各种可能的功能,插件开发者可以根据自己的需求去实现自己需要的接口。后续的插件示例也将会用到这里列出的部分接口。

接口定义的文件一般在系统的如下位置:

1
2
/usr/include/dde-dock/pluginproxyinterface.h
/usr/include/dde-dock/pluginsiteminterface.h

PluginItemInterface

阅读全文 »

在 deepin linux 系统下编译 qtcreator 4.8.0 版本,可是按照官方 README 却始终编译不通过,遇到了以下几个问题,并列出了相关解决方案。当然官方的 README 上所说的编译依赖还是要装上的。

问题0

这个可能不是必须的

不要在源码目录下建立 build/build-debug/build-release 之类的构建目录, 否则会出现一些奇奇怪怪的问题, 导致编译失败

只需要直接在源码根目录下执行:

1
2
3
4
# 仅供参考
qmake
make
make install

问题1

1
2
3
4
g++ -Wl,-z,origin '-Wl,-rpath,$ORIGIN:$ORIGIN/..:$ORIGIN/../lib/qtcreator' -Wl,--no-undefined -Wl,-z,origin -Wl,-rpath,/usr/lib/llvm-7/lib -Wl,--exclude-libs,ALL -Wl,-O1 -shared -Wl,-soname,libClangFormat.so -o libClangFormat.so .obj/release-shared/clangformatconfigwidget.o .obj/release-shared/clangformatindenter.o .obj/release-shared/clangformatplugin.o .obj/release-shared/clangformatutils.o .obj/release-shared/moc_clangformatconfigwidget.o .obj/release-shared/moc_clangformatplugin.o  -L/home/ri/coding/qt-creator/lib/qtcreator -L/home/ri/coding/qt-creator/lib/qtcreator/plugins -lCppTools -lProjectExplorer -lTextEditor -lCore -lCPlusPlus -lQtcSsh -lAggregation -lExtensionSystem -lUtils -L/usr/lib/llvm-7/lib -lclangFormat -lclangToolingInclusions -lclangToolingCore -lclangRewrite -lclangLex -lclangBasic -lLLVM-7 -lQt5Widgets -lQt5Gui -lQt5Concurrent -lQt5Network -lQt5Core -lGL -lpthread  
/usr/bin/ld: 找不到 -lclangToolingInclusions
collect2: error: ld returned 1 exit status
make[3]: *** [Makefile:286:../../../lib/qtcreator/plugins/libClangFormat.so] 错误 1

报错里提到找不到 clangToolingInclusions 这个库文件, 根据 -L/usr/lib/llvm-7/lib 可知构建系统要在这个目录下找, 尝试了以下三个方法:

  1. 手动进入此目录搜索的确没有找到
  2. 故而又在 /usr/lib 目录搜索依然没有
  3. 接着使用 apt-file search clangToolingInclusions 命令搜索看是不是因为某个包没装, 结果依然没有

这就奇怪了, 难道是 debian 系的系统中没有这个库文件吗? 被改名了?

阅读全文 »

在 Qt 中实现动画的一种方便的做法就是使用 QPropertyAnimation 类, 构造 QPropertyAnimation 时设置目标 widget 和 property, 然后设置一下初始和结束的 property 值剩下的 Qt 就会帮我们做了.

常用的一个动画属性就是 “geometry”, 这个属性包含了 widget 的位置以及形状(矩形), 所以通过设置这个属性可以实现 widget 的位置和大小动画.

只是使用这个属性实现大小动画时要留意, widget 不能被设置固定的大小, 即下面这类函数不能调用, 否则 QPropertyAnimation 将无法调整目标 widget 的大小, 其中缘由细细想一下便可知道:

  • setFixedSize
  • setFixedWidth
  • setFixedHeight

但如果目的 widget 不得不设置一个初始大小的话可以调用如下这些函数:

  • setMinimumSize
  • setMaxmumSize
  • setGeometry

QPointer

QPointer在用法上跟普通的指针没有什么区别, 可以将它当做是一个普通指针一样使用. 例如:

1
2
3
4
5
6
7
8
9
void barFunc(QLabel *label) {
...
}

QPointer<QLabel> pointer;
pointer = new QLabel;

// 直接将pointer作为QLabel类型的指针传入barFunc函数作为参数
barFunc(pointer);

主要作用:

QPointer的主要功能是避免悬空指针的出现, 悬空指针是指: 指针不为空, 但是其指向的对象已经不在了. 也就是说当对象在其他地方被delete了, 而我们所持有的指向这个对象的指针依然指向那块内存地址, 而没有被置为空, 此时如果使用这个指针就会出错. 下面的QSharedPointer也有避免悬空指针的功能.

使用场景:

在多个不同地方的指针指向同一个对象, 当一个地方delete了这个对象后, 其他地方依然会使用指向这个对象的指针, 此时如果没有使用QPointer封装, if (pointer)返回的是true, 而如果使用QPointer封装, QPointer检测到对象被销毁那么if (pointer)返回的是false.

场景举例:

  • 我需要将我的一个对象以指针的形式暴露出去, 而且我会在某些情况下delete这个对象, 那我暴露出去的指针就应该使用QPointer封装一下.
  • (上面情况的另一个视角)我接收了一个指针, 但指针指向的对象会在别的地方被销毁, 那我接收这个指针时就应该使用QPointer封装一下.

QSharedPointer

阅读全文 »