Content Table

Windows 设置 JDK 的默认编码

现在 Java 开发项目编码一般都是 UTF-8,Windows 下 JDK 的默认编码是 GB2312,可把其默认值设置为 UTF-8:

  1. 右键点击计算机 > 属性 > 高级系统设置 > 高级 > 环境变量 > 系统变量: 设置 JAVA_TOOL_OPTIONS 的值为 -Dfile.encoding=UTF-8
  2. 命令行显示 UTF-8 字符: 执行 chcp 65001,设置命令行的属性,选择字体 Lucida Console(不要选择点阵字体)。如果要换回 GBK 执行 chcp 936 ,再把字体改成点阵字体即可。

SQL Server 导出 CSV 和 XML

导出 XML

1
bcp "select * from tableName FOR XML AUTO, ROOT('Root')" queryout C:/x.xml -S(local) -T -r -c
  • -w: 使用 UTF-16 编码,小端
  • -c: 使用 GBK 编码
  • -c -C6501: 使用 UTF-8 编码,但是有的计算机上不支持
  • -T: 本机使用 -T 表示可信连接,如果是访问其他机器使用 -U user -P pwd 输入用户名和密码

注意:

  • 如果不使用 -r,则导出的 XML 每行最多有 2033 个字符,会破坏 XML,用了 -r 后就没有换行符了,整个 XML 的内容在同一行。
  • 内容中的 & 等特殊字符不会被转义就直接放到属性值里了,此时用 XML 库解析会出错。

导出 CSV

1
sqlcmd -S localhost -d dbName -E -o "csvFile.csv" -Q "set nocount on; select * from tableName" -W -w 999 -s ","
  • -W: remove trailing spaces from each individual field
  • -s",": sets the column seperator to the comma (,)
  • -w 999: sets the row width to 999 chars(this will need to be as wide as the longest row or it will wrap to the next line)
  • -U: username
  • -P: password
  • -h-1: removes column name headers from the result
  • set nocount on: 输出时不显示 XXX 行受到影响的统计信息

注意:sqlcmd 导出为 CSV 文件时,如果列中有逗号,那么导出的 CSV 文件会被破坏,还没找到好办法。

参考 How to export data as CSV format from SQL Server using sqlcmd?

iTerm ssh 自动登录

使用 ssh 远程登录时:

  1. 输入 ssh root@host-ip
  2. 输入密码

每次都重复这样的操作,不仅麻烦,还要记忆好多东西,为了解决这个问题,借助 iTerm2 Profile 可以实现 ssh 自动登录:

  1. 编写 expect 脚本
  2. 使用此脚本创建 Profile
  3. 使用此 Profile 打开新标签页

MySQL 导入导出 SQL 文件

创建数据库

1
CREATE DATABASE IF NOT EXISTS databaseName DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

导入 SQL 文件

MySQL 可以使用 GUI 客户端导入 SQL 文件,此外在命令行下有常用下面 2 种方式导入 SQL 文件(先要创建好数据库)

  • 使用 mysql 导入
    • 命令中不带密码
      1. ./mysql -uroot -p 数据库名 < 导入的文件名.sql
      2. 输入密码
    • 命令中带密码: ./mysql -uroot -p$password 数据库名 < 导入的文件名.sql
  • 使用 source 导入
    1. ./mysql -uroot -p
    2. 输入密码
    3. use databaseName
    4. source 导入的文件名.sql

导出 SQL 文件

导出有 2 种: 导出数据库(包含建表语句和表中的数据),导出表结构(只有建表语句)

  • 导出数据库: mysqldump -uusername -p 数据库名 > 导出的文件名.sql
  • 导出表结构: mysqldump -uusername -p -d 数据库名 > 导出的文件名.sql

自定义 Widget 使用 QSS

相信很多同学继承如 QWidget,QPushButton 等实现自定义的控件后,发现在此控件上 QSS 不生效了,这不行啊,设置背景、边框、字体等如果没有 QSS 那就太麻烦了。其实在自定义控件上启用 QSS 非常简单,只要调用一下 setAttribute(Qt::WA_StyledBackground) 即可,就像下面这样

1
2
3
4
Widget::Widget(QWidget *parent) : QWidget(parent) {
this->setAttribute(Qt::WA_StyledBackground); // 启用 QSS
this->setStyleSheet("border: 2px solid red; background: pink; border-radius: 10px;"); // 设置 QSS
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <QApplication>
#include "Widget.h"

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

QWidget window;

Widget customWidget(&window);
customWidget.setGeometry(20, 20, 100, 100);

window.resize(300, 300);
window.show();

return app.exec();
}

如上在 main() 函数中使用自定义控件 Widget,QSS 生效了,去掉 this->setAttribute(Qt::WA_StyledBackground) 后 QSS 就没效果了:

按下鼠标拖动窗口

QWidget 已经实现了在标题栏按下鼠标移动窗口的功能,但是当实现无边框窗口时,因为没有了标题栏,移动窗口的功能就需要我们自己实现了,不过也不复杂,主要是处理鼠标的按下、移动、松开三个事件:

  • 按下鼠标:记录此时鼠标的全局坐标和窗口左上角的坐标,并且设置鼠标为按下状态
  • 移动鼠标:鼠标按下时移动鼠标,计算此时鼠标和鼠标按下时的位移差,加上按下鼠标时窗口左上角的坐标得到窗口新的坐标,移动窗口到此坐标
  • 松开鼠标:设置鼠标为未按下状态

拖拽鼠标画矩形

经常看到有同学问:如何实现用鼠标拖拽出一个矩形区域的效果,类似 QQ 截图的那个矩形区域?

很简单的一个问题,主要是处理鼠标的按下、移动、松开三个事件:

  • 按下鼠标:用一个变量标记要开始拖拽出矩形了,并记录按下的位置作为矩形的左上角顶点
  • 移动鼠标:按下时移动鼠标,鼠标的当前位置作为矩形新的右下角的顶点,每次移动事件发生时都要重新画一次矩形,因为矩形变了
  • 松开鼠标:清除拖拽矩形的标志,松开鼠标后,移动鼠标时就不要再改变矩形了

Qt Creator 简介

工欲善其事,必先利其器,顺手的开发工具,能够让我们事半功倍,Qt 开发,推荐使用 Qt Creator,因为在 Qt Creator 中集成了很多实用的功能,不需要切换到其他软件就能使用

  • 编写代码(废话)
  • 使用 UI Designer 进行可视化的布局界面
  • 搜索帮助文档
  • 学习自带的例子
  • Ctrl + K 快速搜索、定位

接下来就简单的介绍下我个人觉得 Qt Creator 使用中比较重要的地方。

线程池 QThreadPool

创建线程需要向系统申请资源,线程切换时操作系统会切换线程上下文,可能会从用户态切换到内核态,当有很多线程时,频繁地切换线程会导致消耗大量的 CPU 以及内核资源,真正用于计算的资源就减少了,反而会降低程序的效率。线程并不是越多越好,线程池的作用是管理、复用、回收一组线程,控制线程的数量,避免频繁的创建和销毁线程而浪费资源。

Qt 中的线程池类为 QThreadPool,每一个 Qt 程序都有一个全局的线程池,调用 QThreadPool::globalInstance() 得到,它默认最多创建 8 个线程,如果想改变最大线程数则调用 setMaxThreadCount() 进行修改,调用 activeThreadCount() 查看线程池中当前活跃的线程数。

使用线程池挺简单的,定一个任务类例如叫 Task,继承 QRunnable 并实现虚函数 run(),Task 的对象作为 QThreadPool::start() 的参数就可以了,线程池会自动的在线程中调用 Task 的 run() 函数,异步执行。线程池中的 QRunnable 对象太多时并不会为立即为每一个 QRunnable 对象创建一个线程,而是让它们排队执行,同时最多有 maxThreadCount() 个线程并行执行。

提交给线程池的 QRunnable 对象在它的 run() 函数执行完后会被自动 delete 掉,如果不想线程池删除它,在调用线程池的 start() 前调用 setAutoDelete(false) 即可。

创建使用动态链接库

想一想大多数时候我们的项目是不是所有代码都会放在同一个工程中?人少的时候问题不大,但当项目越来越大,开发人员越来越多,会发觉开发、管理能让人窒息,大家都绞在一起,出问题时互相推诿责任,各自有理,这时如果按照功能模块进行分组各自开发,以库的形式提供给其他人使用,就能够最大限度的并行开发,提高工作效率,而且项目的模块也很清晰,责任一目了然,此外使用动态链接库后还能够按模块升级,编译的速度也更快。下面就介绍怎么在工程中创建和使用动态链接库。

Windows 中叫动态链接库 (Dynamic Link Library: .dll),Linux 中叫共享库 (Shared Library: .so),Mac 下后缀为 .dylib,这几种叫法实际指的是一种类型的库,这里都统称为动态链接库吧。

理解动态链接库需要理解符号的概念,符号包含函数、变量或者类,分为公有符号和私有符号:

  • 公有符号: 在其他程序或者库使用的符号,需要根据用途使用宏进行标记:

    • Q_DECL_EXPORT: 编译为动态链接库时符号要标记为 Q_DECL_EXPORT,表明是导出的符号
    • Q_DECL_IMPORT: 在调用动态链接库时符号要标记为 Q_DECL_IMPORT,表明是导入的符号
  • 私有符号: 除了公有符号外的其他符号,在此库之外不应该被访问,不需要进行标记

    建议: 不要在头文件中声明私有符号。

符号上的标记 Q_DECL_EXPORTQ_DECL_IMPORT 不能同时存在,为了在导出和导入时使用同一个头文件, 头文件中包含下面的宏,编译时根据条件使用不同的宏就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <QtCore/qglobal.h>

// 根据条件定义 LIB 为不同的宏
#if defined(BUILD_LIB)
#define LIB Q_DECL_EXPORT
#else
#define LIB Q_DECL_IMPORT
#endif

// 使用 LIB 修饰符号,编译时会根据编译条件替换为 Q_DECL_EXPORT 或者 Q_DECL_IMPORT
class LIB Calculator {
...
};

为了达到了在导出和导入时使用同一个头文件的目的:

  • 生成动态链接库工程的 pro 文件中添加 DEFINES += BUILD_LIB,在编译的时候 LIB 就会被替换为 Q_DECL_EXPORT
  • 使用动态链接库工程的 pro 文件中千万不要加 DEFINES += BUILD_LIB,编译的时候发现没有定义 BUILD_LIB,则 LIB 就会被替换为 Q_DECL_IMPORT