为什么不要使用QThread

之前我在Python中使用线程的技巧一文中提到,尽量不要在 PyQt 里面使用QThread,但是当时没有给出示例程序。这里补上一个会引起Python崩溃的代码。其实主要原因是QThread被强制关闭的时候会引起Python崩溃。

代码请见正文。

类型:Python,C++ & Qt4,创建时间:一月 6, 2015, 5:08 p.m. 点击查看完整内容。。。
使用Qt作为C++的标准库

目前大家都使用cpp的stl和boost作为cpp的标准库。不过个人觉得stl和boost用起来不是很爽。不如用Qt。

很多人觉得Qt这个库太大,而且如果不打算使用Qt的GUI也需要安装一大堆没用的dll。其实这都不是问题,我们可以在编译Qt的时候使用no-gui选项,告诉Qt我们只对它的非GUI部分感兴趣,避免编译与安装大量无用的Qt组件。最终我们可以得到一个

请点击进入正文,查看详细的操作步骤。

类型:C++ & Qt4,创建时间:七月 31, 2014, 11:17 a.m. 点击查看完整内容。。。
介绍一下KDE/Qt程序的QtCurve皮肤

推荐KDE的程序皮肤(样式)QtCurve。它可以同时支持KDE3、KDE4和GNOME环境的皮肤,方便我们统一整个Linux桌面的外观。还支持非常多的细节调整,让我们每个人都可以有自己独特的外观。

因为它支持在Windows下编译,我在Besteam里面也应用了QtCurve。非常好用!

类型:LinuxApp,C++ & Qt4,创建时间:五月 7, 2012, 7:48 p.m. 点击查看完整内容。。。
Python中使用线程的技巧

这篇长文介绍了Python如何创建一个新线程,以及几个注意事项:

  1. 创建线程的两种方法,以及它们的正确写法。
  2. 创建线程的第三种写法。
  3. 如何“强制地”结束一个线程。
  4. 不要在线程里面import新模块
  5. 不要使用PyQt的QThread类。
  6. 不要在非主线程里面使用QObject的对象。
类型:Python,C++ & Qt4,创建时间:三月 14, 2012, 8:59 p.m. 点击查看完整内容。。。
在PyQt里面如何做长操作时同时更新GUI

经常看到有人问在PyQt里面如何做长操作的时候更新GUI。我自己总结了几种方案,以及他们的适用场合:

  1. 使用processEvents(),适合每次处理时间短的情况,缺点是CPU占用可能很大。
  2. 用threading启动工作线程,再用QMetaObject.invokeMethod()向主线程传递跨线程的信息。
  3. QProgressDialog。有同步与异步两种方法。
  4. 把长的操作分为小的操作,适合每次处理都是异步的或者很复杂的情况。

第四种方案不常用,但是当你的处理过程本身是异步的时候就会用到。第一、第二方案都不错,优点是灵活。第三种方案也常见,优点是模态对话框阻止用户的其它操作,可以随时取消。缺点是一个对话框挡在那里,相当地影响用户体验。

类型:Python,C++ & Qt4,创建时间:一月 17, 2012, 3:53 p.m. 点击查看完整内容。。。
在Windows下使用KDE的Phase样式

KDE的Phase样式是我最喜欢的样式之一。我觉得它看起来非常稳重、简洁,很适合作为日常工作的默认样式。原来以为它只能工作在KDE环境,直到半年前看KDE代码的时候才发现它原来是一个Qt样式,因此,开发Windows软件的时候完全也可以使用它。

在Windows下编译Phase样式很简单,只要把Phase的三个文件复制过来,假定放在D:\phase下。然后编写这么一个工程文件:

#phase.pro
TEMPLATE = lib
CONFIG += plugin
HEADERS += bitmaps.h phasestyle.h
SOURCES += phasestyle.cpp

接下来Qt程序编译标准步骤:

h:\phase> qmake
h:\phase> mingw32-make release

编译成功后,可以在h:\phase\release下找到phase.dll文件,复制到Qt的$QTDIR\plugins\styles目录后就可以使用Phase样式了。

类型:C++ & Qt4,创建时间:一月 1, 2012, 6:01 p.m. 点击查看完整内容。。。
PyQt里让文本编辑器自动滚动

前两天看txt小说的时候,手头找不到小说阅读器,就顺手写了一个。主要功能是让文字自动滚动。原理超简单,让QTextBrowser的滚动条每隔一段时间往下拉一点就可以了。好了,上代码:

vb=self.textBrowser.verticalScrollBar()
if vb.value()>=vb.maximum():
    return
vb.setValue(vb.value() + 2)

据我试验,QPlainTextEdit滚动条拉动时以行为单位,每次滚动只能下拉一行。用QTextEditQTextBrowser不会有这个问题。

类型:Python,C++ & Qt4,创建时间:一月 1, 2012, 5:54 p.m. 点击查看完整内容。。。
窗口最小化后隐藏

通常最小化的窗口会在任务栏上显示一个按钮。当用户按一下这个按钮,窗口就还原了。这是一个被大多数桌面环境,比如Windows,KDE,GNOME所采用的普遍设计。不过,因为任务栏通常只是桌面边上一小行,容纳不了很多按钮,用户通常希望把那些不常用的程序隐藏起来,只在通知栏显示一个小图标,要使用的时候再点击那个小图标恢复窗口。这种作法最典型的例子是QQ和Winamp。

最基本的思路是拦截窗口的最小化事件:

void MyWindow::changeEvent(QEvent* event)
{
    if(event->type()==QEvent::WindowStateChange)
    {
        if(windowState() & Qt::WindowMinimized)
        {
            QTimer::singleShot(0, this, SLOT(hide())); //修改这一行
            mSystemTrayIcon->show();
        }
    QMainWindow::changeEvent(event);
}
类型:C++ & Qt4,创建时间:一月 1, 2012, 5:49 p.m. 点击查看完整内容。。。
Qt窗口的删除、析构

Qt内关于窗口的删除是一个复杂的事情。接下来详细地讲一讲。

当我们创建一个窗口时,如果这个窗口是一个顶级窗口,准确地说是w.windowFlags().testFlag(Qt.Window) is True的窗口,Qt会自动附加一个Qt.WA_QuitOnClose属性。它的意思是,窗口被关闭了,qApp.lastWindowClose信号会被触发。

默认情况下,qApp接收到此信号后会退出程序,这个行为相当于在初始化程序时:

qApp.lastWindowClosed.connect(qApp.quit)

要修改这个默认行为,可以设置QApplicationquitOnLastWindowClosed属性,比如:

qApp.setQuitOnLastWindowClosed(False)

通常,用户可以点击窗口的关闭按钮关闭窗口,或者当系统关闭的时候,窗口也会被关闭。我们可以通过重载窗口的closeEvent()函数控制关闭动作,比如询问用户要不要保存文档,或者只是最小化窗口。下面是一个例子:

def closeEvent(self, event):
    if self.maybeSave():
        self.saveSettings():
        event.accept()
    else:
        event.ignore()

我们也可以在菜单里设置一个退出命令,点击它的时候调用窗口的close()函数即是关闭窗口。

关闭窗口之后,通过我们还要手动销毁这个窗口,释放它所占用的内存。对此Qt提供了一个简便的办法,只要将窗口的Qt.WA_DeleteOnClose属性设置为true,Qt会自动帮我们销毁这个窗口。

类型:Python,C++ & Qt4,创建时间:一月 1, 2012, 4:42 p.m. 点击查看完整内容。。。
使用QToolBox时,pyuic4的一个编译错误

有时候QtDesigner生成的窗口如果包含QToolBox,使用pyuic4编译的时候会出错。经过调查,我发现主要是因为QtDesigner在创建QToolBox的页时会自动生成页面的名字。可能是因为翻译的问题,页面的名字是中文的(page变成)。页面的名字会编译成python的变量名。在python3k之前,使用中文作为python的变量名显然是不合适的。所以会提示错误。

如果发现pyuic4发生错误,可以试着加入调试选项:pyuic4 -d main.ui。 可以解决很多像QToolBox那样的错误。

类型:Python,C++ & Qt4,创建时间:一月 1, 2012, 2:54 p.m. 点击查看完整内容。。。
使用Qt实现从资源管理器中拖动文件到应用程序的功能

我是EditPlus和FileZilla的用户。这两个软件都支持文件拖拽。我们可以从资源管理器中拖动一个文件到EditPlus的窗口,EditPlus就会自动打开这个文件。或者拖到FileZilla的远程窗口内,FileZilla就会自动将文件上传到服务器。我经常希望自己的软件也支持拖动的操作。

作为基本功能,大多数操作系统都各自定义了拖放所需的通信协议。Qt对此做了一个很好的封装。基本的流程是:当用户从其它应用程序拖动文件、文本、图像等到Qt程序时,Qt会自动产生一个DrapEnterEvent,应用程序可以判断数据是不是可以处理的。如果能处理,就反馈给Qt,这样的话,用户就会看到一个接受拖放的标识。待用户放开鼠标时,Qt再产生一个DropEvent。最后应用程序从事件中取出数据进行处理。

那么应用程序如何判断程序是否可以处理拖放的数据呢?很简单,数据中包含了MIME类型。MIME类型是一个用于标识二进制数据类型的Internet标准。很多朋友可能习惯通过文件的扩展名来判断数据类型,但是扩展名只有短短的几个字符,常常有歧义。通常,如果应用程序要处理文本,就判断MIME类型是不是"text/palin",如果要处理JPEG图像,就判断MIME类型是不是"image/jpeg",等等。同一段数据通常包含多种MIME类型,比如拖动富文本时,数据中可能包含纯文本、HTML文本、图像。应用程序可以选择处理所支持的数据格式。

具体如何处理从资源管理器拖动的文件呢 ?请点击查看正文。

类型:Python,C++ & Qt4,创建时间:一月 1, 2012, 8:52 p.m. 点击查看完整内容。。。
Qt程序如何用PostMessage向一个窗口输入文字

写一些监控类程序的时候,有时候会要求某个条件满足的时候就向某个窗口 写入一堆文字。我首先想到的是PostMessage。对于文本框之类的控件,可以使用WM_SETTEXT这个消息。如果不是文本控件,只好使用WM_CHAR写入一个个的文字。 基本的用法是这样子的:

PostMessage(hwnd, WM_CHAR, c, 0);

如果编译的时候定义了UNICODE。那其中的c是文字的utf-16码(TCHAR)。Qt的QChar有个unicode()方法,可以取得utf-16内码。

QString s=tr("一段中文和Enlish Text");
foreach(QChar c, s)
    PostMessage(hwnd, WM_CHAR, c.unicode(), 0);
类型:C++ & Qt4,创建时间:一月 1, 2012, 2:12 p.m. 点击查看完整内容。。。
Qt的signal/slot机制原理

signal/slot在底层会使用三种方式传递消息。参见QObject::connect()方法:

bool QObject::connect (const QObject * sender,
        const char *signal, const QObject *receiver,
        const char *method,
        Qt::ConnectionType type = Qt::AutoCompatConnection)

最后一个参数是就是传递消息的方式了,有四个取值:

  • Qt::DirectConnection
  • Qt::QueuedConnection
  • Qt::BlockingQueuedConnection
  • Qt::AutoConnection

有关这几种类型的详细信息请点击查看正文。

类型:C++ & Qt4,创建时间:一月 1, 2012, 11:49 a.m. 点击查看完整内容。。。
Python, Qt, Java的字符编码

首先定义只在本文中出现的两个基础概念:

  • 字节组。每8个位(bit)是为一个字节,多个字节成为一个字节组。
  • 文字串。现实语言的最小单位称为文字。一个英文字母就是一个文字,一个汉字也是一个文字,一个空格也是文字。

这两个概念是我生造的,主要是为了避免和现有的一大堆术语混起来。

文字串不能理解成字符串。倒是像圆这样的数学概念,比较抽象一些,只存在于头脑里。可以把文字串写在纸上,也可以记在计算机里,还可以读出来。字节组就是文字串在计算机中的表现形式。想一想,在纸上圆可以用一个公式来表达,也可以用一个图形来表达,还可以用文字描述它。文字串也一样,可以表述为多种字节组。众所周知,计算机只能处理数字,所以为了描述文字,最简单的方法就是把文字列出来,每个文字用一个数字表示。不同的国家有不同的方法来做这件事,比如美国人和英国人只有字母,还有一些符号,总共排了256个文字,叫做ascii码。中国的文字就比较多了 ,那个康熙大字典据说有40万,所以中国人排了gb2312标准,big5,gbk、还有gb18030,里面包含的文字总数各不相同。

  • 把文字串表述成字节组的方法就叫做字符编码。

显然,美国人的ascii码是不能表示中国文字的,因为它只能表示256个文字。虽然,中国的标准可以表示美国人的文字,但是却不能表示阿拉伯字符和其它一些像型文字。后来大家意识到这个问题是文化交流的障碍,于是就在一起制定了一个unicode标准。简而言之,unicode标准就是穷尽这个世界上所有的文字,给每个文字编一个数字。 和gb2312,gbk等一样,其实unicode也只是一种编码标准,只不过它能够表示所有的文字。从此,如果一个计算机系统想要支持多种语言文字,只要简单地支持unicode就可以了。在这种系统里,我们可以认为文字串就是unicocde字节组,unicode字节组就是文字串。

类型:Python,C++ & Qt4,Java,创建时间:十二月 31, 2011, 4:52 p.m. 点击查看完整内容。。。
修改qt程序的图标(for windows)

最近经常在windows写qt程序。编译完的qt程序图标是windows默认的应用程序图标,不好看。于是就想给它换一个,找了一下qt的帮助文档,里面有提到这个问题。方法也挺简单的:

首先当然要有一个图标了,呵呵。把这个图标复制到程序的主目录下,姑且名字叫myicon.ico吧。没有?可以自己用vc画一个。然后编写一个icon.rc文件。里面只有一行文字:

IDI_ICON1               ICON                    "myicon.ico"

最后,在工程的pro文件里加入一行:

RC_FILE = icon.rc

qmakemake一下,就可以发现你的应用程序拥有漂亮的图标了。

顺便说一下,做WindowBlinds的那个公司有一个IconPackager,还一个叫做IconStudio的吧。可以用来管理和生成图标文件。 不过貌似要收钱的。。

类型:C++ & Qt4,创建时间:十二月 31, 2011, 10 a.m. 点击查看完整内容。。。
传参数应该用哪种形式——值、引用、指针?

最近写C++程序经常郁闷传参数的时候应该传值、传引用还是传指针。

  • 传值 :int func(User u);
  • 传指针: int func(User* u);
  • 传引用: int func(User& u);

传值是传参数最常见的方法,相当简单,是C++的基础传参方法。如果参数是整形、字符等基础类型,用传值的方法是最快的,也是最简单的。它的缺陷是对于一些很大的对象,比如上面定义的User对象,使用传值方法时,内存开销比较大,会重新创建一个User对象。还有一个情况,如果User是多态的,这样传参数会出问题。因为C++会调用新创建的User对象的复制构造函数,而默认的复制构造函数只会把需要的属性复制到新对象里,引起所谓的切割现象。

在C++古生代,传指针是传值之外的唯一方法。与传值相比较,需要考虑内存管理的问题,在栈上分配的对象通常就不能传递指针,因为函数返回之后栈上的对象会被析构,这时的指针就会成为野指针,引用它会造成内存访问异常。即使对象是在堆上分配的,传指针里也同样要考虑分配错误等问题。不过,与传值相比,传指针对付大型对象和多态对象很有效,而且,使用指针形式传递的变量在运行过程中可以被修改。

传引用似乎结合了两者的优点。与传指针一样,传引用能很好地对付大型对象多态对象,而且相对于传指针,传引用通常是不需要考虑内存管理的问题,因为只能使用另一个已经初始化的变量来初始化引用。一切看起来似乎都很美好。不过优点往往也是缺点,引用类型的变量不适合作为类的属性,因为引用一旦初始化就不能改变其指向的对象。C++这种面向底层的语言真是让人很痛苦。

总结一下:

基础类型传值、普通的非多态的对象传引用,多态对象以及分配在堆上的对象传指针。

类型:C++ & Qt4,创建时间:十二月 30, 2011, 7:43 p.m. 点击查看完整内容。。。