怎么样在 virtualenv 里面使用 PyQt

PyQt 现在还不支持从pip下载安装,习惯virtualenv开发的时候会比较麻烦,总不能为了一个 PyQt 就放弃了virtualenv吧。

有多种方案可以实现这个目标。我这里从源代码开始安装 PyQt,好像是不同的项目可以使用不同的版本。编译的时候我只编译了QtCore, QtGui, QtWidgets三个模块。

具体的步骤与命令看正文吧。

类型:Python,C++ & Qt4,创建时间:五月 5, 2017, 10:23 a.m. 点击查看完整内容。。。
谈谈 Qt 程序安装包的大小,以及简要打包指南

经常看到网上有些论调说 Qt 程序无比庞大,甚至拿 .NET 程序来比,说 Qt 程序打包以后跟 .NET 安装包差不多大。由此影响了很多人对 Qt 的选择。我觉得有必要对此做一些澄清——

显然这个说法是错误的!!

很容易理解,虽然 Qt 提供了很多组件,但并非所有的组件都会被程序使用,也并非所有的组件都需要打包到程序安装包里面。以 Qt 5.7 为例,一个可以正常使用的 helloworld.exe 程序未压缩不过 20M,使用 lzma 压缩算法,压缩率 25%,压缩完才 6M!

那盛传的 Qt 上百 M 的容量又是怎么回事呢?

这事从头说起。

类型:C++ & Qt4,创建时间:三月 2, 2017, 7:52 p.m. 点击查看完整内容。。。
Windows GDI加速的详细支持情况

自从 Vista 以后,Windows 对于 GDI 加速发生了很大的变化。简单地说,在 Vista 出现时,为了支持 Desktop Window Manager(DWM), GDI 是不加速的。为啥 DWM 不支持 GDI 加速呢。因为 DWM 是使用 DirectX 进行渲染的,它调用 DirectX 将显存里面的两个窗口内容合并渲染出来。不幸地是在 WINXP 的架构里面,GDI 是和 DirectX 分开的,两者没有一致的内存空间。所以 DWM 只好舍弃 GDI 的所有加速功能,让 GDI 程序把内容渲染到内存区域,再把这块内存复制到显存里面进行渲染。Windows 7 做了一些改进,要求驱动程序支持一个特殊的内存区域,能够让 GPU 和 CPU 都同时读取到的内存区域。GDI操作都写入到这个内存区域。这样的话,就不需要再做一次复制了,而且因为那个内存区域也是 GPU 可读的,于是 GDI 又可以支持 GPU 加速了。然而 Windows 7 并不像以前的 GDI 那样子,要求显卡驱动程序支持各种 2D 加速命令,而只要支持 BltBit, AlphaBlend 等几个命令就够了。

类型:C++ & Qt4,创建时间:一月 23, 2017, 1:20 p.m. 点击查看完整内容。。。
为什么不要使用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. 点击查看完整内容。。。