pyqt在python里是啥_Python 中文处理系列之PyQt(中)

本篇讨论PyQt4中的中文处理问题。

Qt中的QString与QByteArray之间的关系,近似等同于Python 2.x中的unicode与str的关系,以及Python 3.x中的str与bytes之间的关系。PyQt提供了Qt类型(包括C/C++类型)与Python原始类型之间的隐式转换。而PyQt4、PyQt5、Python 2.x、Python 3.x的隐式转换方法都不尽相同,由此产生了4种排列组合的情况。本文讨论其中的两种:PyQt4 + Python 2.x 以及 PyQt4 + Python 3.x

1. PyQt4 + Python 2.x

1.1 QByteArray与Python类型互转

QByteArray这个类型其实和中文等国际字符没有什么太大的关系。和Qt一样,PyQt4中的QByteArray也是保存8位的原始码流。通常情况下我们会从一个IO设备中获得这样的码流。

当我们得到一个QByteArray后,它可能有以下几种去处:自己发生一些简单的运算(例如,取长度、截断等)

提供给一个需要QByteArray的场合

提供给一个需要QString的场合

提供给一个需要str的场合

提供给一个需要unicode的场合

对于情况1和2,在此不必多说。

对于情况3,这属于Qt部分的内容,可参阅QTextCodec这个类。简单的说,你必须知道这个QByteArray实例的字符编码方式,以便于拿到正确的解码。

对于情况4,由于PyQt4在QByteArray内部实现了__str__方法,所以str()作用于QByteArray是没有问题的。

from PyQt4.QtCore import *

f = QFile('test_in.txt')

if f.open(QIODevice.ReadOnly):

hello_text = f.readAll()

f.close()

with open('text_out.txt','w') as f2:

hello_text_2 = hello_text + " end"

hello_text_3 = "before" + hello_text

print type(hello_text_2)

print type(hello_text_3)

f2.write(hello_text_3)

f2.write(hello_text_2)

f2.write(hello_text.join(['1','2'])) #error

f2.write(str(hello_text).join(['1','2']))为什么hello_text_2和hello_text_3都是QByteArray类型而不是Python的str类型?这是因为PyQt4的QByteArray实现了__add__和__radd__。

对于情况5,同样需要知道QByteArray类型变量的编码方式,才能用Python的codecs模块解码。

from PyQt4.QtCore import *

import codecs

f = QFile('test_in.txt')

if f.open(QIODevice.ReadOnly):

hello_text = f.readAll()

f.close()

with codecs.open('text_out.txt',encoding='utf-8',mode='w') as f2:

f2.write(codecs.decode(hello_text,'gb2312'))

代码中,f2.write()需要一个Python的unicode类型。使用codecs.decode函数对hello_text进行解码,其中存在QByteArray到str的隐式转换。最终会得到一个以UTF-8编码的『你好』文件。

反过来,当一个PyQt的函数需要QByteArray的时候,如何将一个Python类型赋予它呢?规则是这样的:对于str类型和只含有ASCII的unicode类型,直接使用或通过QByteArray()构造函数

对于unicode类型,要使用codecs进行编码。

# -*- coding: utf-8 -*-

from PyQt4.QtCore import *

import codecs

f = QFile('test_out.txt')

text1 = u'hello'

text2 = u'你好'

if f.open(QIODevice.WriteOnly):

f.write(text1)

f.write(text2) #error

f.write(codecs.encode(text2,'utf-8'))

f.close()

这段代码需要保存为无BOM的UTF-8。实际上,从QByteArray到Python环境,首先是要做str()的强制转换的,而str()强制转换并不改变QByteArray内部的数据。

1.2 QString与Python类型互转

QString类型与Python的str类型并不直接对应,而是对应于类型unicode。所以,当把一个Python类型传送给需要QString类型的函数时,首先应该考虑该类型是否能够顺利的转为unicode类型。

对于只含有ASCII字符的str类型实例,其与unicode类型的转换是自由的。所以对于不涉及国际字符的情况,大可放心的在PyQt下使用Python的str。一旦涉及国际字符,则需要知道该字符的编码,并使用codecs模块进行解码。

例如,有一个文本文件保存了GB2312编码的『你好』,我们用Python的IO来读取这个文件,并使用MessageBox显示出来。

from PyQt4.QtCore import *

from PyQt4.QtGui import *

import codecs

import sys

app = QApplication(sys.argv)

with open('test_in.txt') as f:

hello_text = f.read()

f.close()

QMessageBox.information(

None,codecs.decode(hello_text,'gb2312'),

codecs.decode(hello_text,'gb2312')

)

exit(app.exec_())看起来好麻烦的样子。能不能让解码过程一次就设好呢?答案是肯定的。我们只需要利用Qt的QTextCodec进行一次全局的设置,并将Python的str类型直接赋给需要QString作为参数的函数。

from PyQt4.QtCore import *

from PyQt4.QtGui import *

import sys

QTextCodec.setCodecForCStrings(QTextCodec.codecForName("GB2312"))

app = QApplication(sys.argv)

with open('test_in.txt') as f:

hello_text = f.read()

f.close()

QMessageBox.information(

None,hello_text,

hello_text

)

exit(app.exec_())反之,从Qt拿到的QString如何赋给Python环境?用unicode()转换一下就行了。当然,如果你清楚地知道QString变量里只含有ASCII码,那么str()也是可以的。当然,你可以借用QTextCodec.fromUnicode把QString转换成QByteArray,然后再从QByteArray转换成Python所需要的类型。

总结:在PyQt4+Python 2.x中,QByteArray和str是一致的,都是原始的8位码流;QString和unicode是一致的,都是可以存储国际字符的变量。可以利用Qt的QTextCodec或者Python的codecs进行国际字符的编解码。我们可以用一张图来表示:

2. PyQt4 + Python 3.x

2.1 QByteArray与Python类型互转

在Python 3.x中,QByteArray与bytes类型一一对应。从Qt读文件再用Python写入的例子变成了这样:

from PyQt4.QtCore import *

f = QFile('test_in.txt')

if f.open(QIODevice.ReadOnly):

hello_text = f.readAll()

f.close()

with open('text_out.txt','w') as f2:

f2.write(bytes(hello_text).decode("gb2312"))

Python的codecs模块依然有效,故可以书写Python 2.x/3.x兼容的代码。

from PyQt4.QtCore import *

import codecs

f = QFile('test_in.txt')

if f.open(QIODevice.ReadOnly):

hello_text = f.readAll()

f.close()

with codecs.open('text_out.txt',encoding='utf-8',mode='w') as f2:

f2.write(codecs.decode(hello_text,'gb2312'))

这是因为codecs.decode在Python 2.x和Python 3.x对『原始数据』(即第一个参数)做了合理的处理。

而Qt中需要QByteArray的场合,可以用bytes类型予以代入。

# -*- coding: utf-8 -*-

from PyQt4.QtCore import *

f = QFile('test_out.txt')

text1 = 'hello'

text2 = '你好'

if f.open(QIODevice.WriteOnly):

f.write(text1)

f.write(text2) #error

f.write(text2.encode('utf-8'))

f.close()

这段代码需要保存为无BOM的UTF-8。当然,利用codecs也可以写出Python 2.x/3.x兼容的代码。

2.2 QString与Python类型互转

在Python 3.x中,QString类型『消失』了。声明一个QString类型的变量将引发QString类无法找到的错误。

凡需要QString的Qt函数,可以直接赋以str,而返回QString的Qt函数均返回类型str。

最后,由于只含有ASCII的bytes类型与str类型有自动编解码的关系,在这种情况下bytes类型和QString类型也是互通的。

总结图如下:

3. 自由选择2.x和3.x风格的QString

Python 3.x 中去掉了QString,带来了很多方便。我们希望在Python 2.x 中也能用到这么方便的隐式转换;但Python 3.x 的QString带来代码的向下兼容问题,而且QString自身丰富的处理函数也消失了,故有时候我们想在Python 3.x 中能用到显式的QString。

使用PyQt4自带的sip模块,能够任意的选择2.x和3.x风格的QString实现。命令是

import sip

sip.setapi('QString',1) # 1 = Python 2.x; 2 = Python 3.x

from PyQt4.QtCore import *

...

setapi('QString',x)只能运行一次,由于import PyQt4的过程中会尝试重设sip.setapi,故setapi必须在import PyQt4之前进行。

如果在Python 2.x 环境下选择了 sip.setapi('QString',2),那么QString类会消失,取而代之的是Python自带的unicode类;如果在Python 3.x 环境下选择了 sip.setapi('QString',1),那么QString类会出现,并与str类形成等价关系。

4.其他字符串的表示法

在PyQt4中:凡以char *,unsigned char *表示的'\0'结尾字符串,均与QByteArray对待的形式相同。

凡以char,unsigned char表示的字符,视同长度为1的QByteArray

凡以QChar表示的字符,如果QString存在,视同长度为1的QString;如果QString不存在,那么QChar也不存在。

(未完待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值