国际化与网络编程:PyQt 应用开发指南
1. 国际化设置
1.1 翻译工具使用
可以通过以下两种方式开启翻译功能:
- 运行
mkpyqt.py
时添加
-t
(translate)选项。
- 运行
Make PyQt
并勾选
Translate
复选框。
开启翻译后,这两个工具会依次运行
pylupdate4
和
lrelease
。
1.2 翻译工作流程
将 Qt Linguist 应用程序和
.ts
文件交给翻译人员,让他们对字符串进行翻译。Qt Linguist 易于使用,能通过建议之前翻译过的类似短语来减少重复劳动,还会按上下文(通常是窗口类名)对翻译字符串进行分组。
使用 Qt Linguist 的具体步骤如下:
1. 运行 Qt Linguist,点击
File
->
Open
,打开
.ts
文件。
2. 点击左侧
Context
停靠窗口中的
+
符号,显示上下文中的字符串,然后点击其中一个字符串。该字符串会出现在右上角面板的
Source text
标题下。
3. 点击
Translation
标题下的区域,输入翻译内容。
4. 点击
Context
停靠窗口中相关字符串旁边的问号图标,确认该字符串翻译完成。点击图标会在问号和对勾之间切换,打勾的翻译表示已完成,
lrelease
会将其放入
.qm
文件中。
1.3 代码中的国际化处理
-
字符串处理
:确保每个用户可见的字符串使用
QObject.tr()或QApplication.translate()。对于有可替换参数的字符串,应使用QString.arg()及其编号%n参数,而不是 Python 的%运算符。 -
数字处理
:对于数字,可能需要使用
%Ln以获得正确的千位和小数点分隔符。 - 货币符号处理 :可以使用以下代码处理货币符号:
currency = QApplication.translate("Currency", "$")
将
"$"
翻译为合适的符号,如
"€"
、
"£"
、
"¥"
等。
-
日期处理
:对于日期,可以使用
QDate.toString(Qt.SystemLocaleDate)
或
QDate.toString(Qt.ISODate)
。
-
单位处理
:对于计量单位,最好提供一个合理的默认值,让用户可以通过配置对话框进行更改,或者在首次运行时让用户选择单位、默认纸张大小等。
1.4 总结
-
创建基于 HTML 的在线帮助系统相对简单,可以使用
QTextBrowser或QDesktopServices.openUrl()。而创建使用 Qt Assistant 的系统则较难设置。 -
设置应用程序进行翻译较为直接。通常使用
.pro文件列出.ts文件以及包含用户可见字符串的.ui、.py和.pyw文件,并使用pylupdate4和lrelease来更新.ts文件并生成.qm文件。也可以通过生成初始.ts文件,然后使用mkpyqt.py或Make PyQt来避免使用.pro文件。
2. 网络编程基础
2.1 Python 标准库的网络支持
Python 标准库有许多提供网络功能的模块,例如:
-
urllib2.urlopen()
可用于提供 Internet 文件的“文件句柄”,然后使用
for line in fh:
逐行读取文件。
- 可以使用
urllib.urlretrieve()
从 Internet 下载整个文件:
source = "http://www.amk.ca/files/python/crypto/" + \
"pycrypto-2.0.1.tar.gz"
target = source[source.rfind("/") + 1:]
name, message = urllib.urlretrieve(source, target)
urllib
和
urllib2
模块功能强大,支持 FTP 和 HTTP 协议,后者可使用
GET
或
POST
请求,还能使用 HTTP 代理。
urllib2
支持基本身份验证并可设置 HTTP 头。如果 Python 安装了 SSL 支持,
urllib2
还能使用 HTTPS 协议。标准库还支持许多其他网络协议,如 IMAP4、POP3、SMTP 用于电子邮件,NNTP 用于网络新闻,以及处理 cookie、XML - RPC、CGI 和创建服务器的库。大多数 Python 的网络支持基于
socket
模块,可直接用于底层网络编程。
2.2 PyQt4 的网络类
除了 Python 标准库,PyQt4 提供了自己的网络类,包括用于客户端 FTP 支持的
QFtp
和用于 HTTP 支持的
QHttp
。底层网络编程可以使用
QAbstractSocket
的子类,如
QTcpSocket
、
QTcpServer
和
QUdpSocket
,从 Qt 4.3 开始还支持
QSslSocket
。
2.3 UDP 和 TCP 协议
PyQt 提供两种不同类型的套接字:
-
UDP(User Datagram Protocol)
:由
QUdpSocket
类支持。UDP 轻量级但不可靠,不保证数据能被接收,是无连接的,数据以离散项的形式发送或接收。常用于监控连续读数的仪器,偶尔丢失读数影响不大。
-
TCP(Transmission Control Protocol)
:由
QTcpSocket
类支持。TCP 是可靠的面向连接和流的协议,可发送和接收任意数量的数据,套接字负责将数据拆分成足够小的块进行发送,并在另一端重新组装数据。客户端/服务器应用程序通常使用 TCP,因为它们需要可靠性。
2.4 示例应用:Building Services
我们将使用 Building Services 应用作为示例,该应用的服务器存储建筑物中房间的详细信息和预订日期,客户端用于预订和取消特定房间的特定日期。服务器和客户端运行在同一台机器上,使用
localhost
作为 IP 地址,端口号为 9407(端口号应大于 1023,通常在 5001 到 32767 之间,最大为 65535)。服务器可以接受两种请求:
"BOOK"
和
"UNBOOK"
,并可以做出三种响应:
"BOOK"
、
"UNBOOK"
和
"ERROR"
。所有请求和响应都以二进制数据形式发送和接收。
以下是相关变量的设置:
from PyQt4.QtNetwork import *
PORT = 9407
SIZEOF_UINT16 = 2
2.5 客户端实现
2.5.1 客户端类初始化
class BuildingServicesClient(QWidget):
def __init__(self, parent=None):
super(BuildingServicesClient, self).__init__(parent)
self.socket = QTcpSocket()
self.nextBlockSize = 0
self.request = None
这里我们创建了一个
QTcpSocket
对象用于与服务器通信,
nextBlockSize
用于确定是否接收到足够的响应数据以处理响应,
request
是一个包含请求数据的
QByteArray
对象,若没有数据要发送则为
None
。
2.5.2 信号与槽连接
self.connect(self.socket, SIGNAL("connected()"),
self.sendRequest)
self.connect(self.socket, SIGNAL("readyRead()"),
self.readResponse)
self.connect(self.socket, SIGNAL("disconnected()"),
self.serverHasStopped)
self.connect(self.socket,
SIGNAL("error(QAbstractSocket::SocketError)"),
self.serverHasError)
self.connect(self.roomEdit, SIGNAL("textEdited(QString)"),
self.updateUi)
self.connect(self.dateEdit, SIGNAL("dateChanged(QDate)"),
self.updateUi)
self.connect(self.bookButton, SIGNAL("clicked()"),
self.book)
self.connect(self.unBookButton, SIGNAL("clicked()"),
self.unBook)
self.connect(quitButton, SIGNAL("clicked()"), self.close)
前四个信号与套接字相关,用于处理连接建立、数据读取、连接断开和错误情况。其他连接与用户界面相关,用于验证输入、启用/禁用按钮、预订和取消预订房间以及关闭应用程序。
2.5.3 更新用户界面
def updateUi(self):
enabled = False
if not self.roomEdit.text().isEmpty() and \
self.dateEdit.date() > QDate.currentDate():
enabled = True
if self.request is not None:
enabled = False
self.bookButton.setEnabled(enabled)
self.unBookButton.setEnabled(enabled)
如果房间编辑框有房间号且日期编辑框的日期晚于今天,则启用预订和取消预订按钮;如果有未处理的请求,则禁用这些按钮。
2.5.4 关闭事件处理
def closeEvent(self, event):
self.socket.close()
event.accept()
当应用程序关闭时,关闭套接字并接受关闭事件。
2.5.5 预订和取消预订方法
def book(self):
self.issueRequest(QString("BOOK"), self.roomEdit.text(),
self.dateEdit.date())
def unBook(self):
self.issueRequest(QString("UNBOOK"), self.roomEdit.text(),
self.dateEdit.date())
点击
Book
或
Unbook
按钮时,调用
issueRequest()
方法发送相应请求。
2.5.6 准备请求并连接服务器
def issueRequest(self, action, room, date):
self.request = QByteArray()
stream = QDataStream(self.request, QIODevice.WriteOnly)
stream.setVersion(QDataStream.Qt_4_2)
stream.writeUInt16(0)
stream << action << room << date
stream.device().seek(0)
stream.writeUInt16(self.request.size() - SIZEOF_UINT16)
self.updateUi()
if self.socket.isOpen():
self.socket.close()
self.responseLabel.setText("Connecting to server...")
self.socket.connectToHost("localhost", PORT)
该方法准备请求字节数组,更新用户界面,关闭可能已打开的套接字,设置响应标签以告知用户正在尝试建立连接,然后调用
connectToHost()
连接到服务器。
2.5.7 发送请求
def sendRequest(self):
self.responseLabel.setText("Sending request...")
self.nextBlockSize = 0
self.socket.write(self.request)
self.request = None
连接建立后,该方法更新响应标签,设置下一个块大小为 0,将请求字节数组写入套接字,并将请求对象设置为
None
,以便准备新的请求。
2.5.8 读取响应
def readResponse(self):
stream = QDataStream(self.socket)
stream.setVersion(QDataStream.Qt_4_2)
while True:
if self.nextBlockSize == 0:
if self.socket.bytesAvailable() < SIZEOF_UINT16:
break
self.nextBlockSize = stream.readUInt16()
if self.socket.bytesAvailable() < self.nextBlockSize:
break
action = QString()
room = QString()
date = QDate()
stream >> action >> room
if action != "ERROR":
stream >> date
if action == "ERROR":
msg = QString("Error: %1").arg(room)
elif action == "BOOK":
msg = QString("Booked room %1 for %2").arg(room) \
.arg(date.toString(Qt.ISODate))
elif action == "UNBOOK":
msg = QString("Unbooked room %1 for %2").arg(room) \
.arg(date.toString(Qt.ISODate))
self.responseLabel.setText(msg)
self.updateUi()
self.nextBlockSize = 0
由于服务器的响应可能会分段返回,因此使用无限循环来确保读取完整的响应。根据响应的类型,显示相应的消息,并更新用户界面。
2.5.9 处理服务器异常
def serverHasStopped(self):
self.responseLabel.setText(
"Error: Connection closed by server")
self.socket.close()
def serverHasError(self, error):
self.responseLabel.setText(QString("Error: %1") \
.arg(self.socket.errorString()))
self.socket.close()
当服务器终止或出现网络错误时,显示错误消息并关闭套接字。
2.6 客户端工作流程总结
以下是客户端的工作流程 mermaid 流程图:
graph TD;
A[启动客户端] --> B[初始化套接字和变量];
B --> C[等待用户输入];
C --> D{用户点击 Book 或 Unbook};
D -- 是 --> E[准备请求并连接服务器];
E --> F{连接成功};
F -- 是 --> G[发送请求];
G --> H{收到响应};
H -- 是 --> I[读取响应并显示结果];
I --> C;
F -- 否 --> J[显示错误信息];
J --> C;
H -- 否 --> K{连接断开或出错};
K -- 是 --> J;
K -- 否 --> H;
D -- 否 --> C;
客户端的用户可以输入预订和取消预订请求,点击
Book
和
Unbook
按钮将请求发送到服务器,并在响应标签中查看请求结果。请求通过将
QByteArray
写入合适设置的套接字发送,响应通过
QDataStream
从套接字读取,这使我们能够直接将
QStrings
、
QDates
和其他支持数据流的类型读取到本地变量中。接下来我们将关注服务器的实现。
3. 服务器实现
3.1 服务器类初始化
class BuildingServicesServer(QTcpServer):
def __init__(self, parent=None):
super(BuildingServicesServer, self).__init__(parent)
self.rooms = {}
这里创建了一个
QTcpServer
的子类,
self.rooms
字典用于存储房间的预订信息,键为房间号,值为已预订的日期集合。
3.2 启动服务器
if __name__ == "__main__":
app = QApplication(sys.argv)
server = BuildingServicesServer()
if not server.listen(QHostAddress("localhost"), PORT):
print("Error: Could not start server")
sys.exit(1)
print("Server started on port %d" % PORT)
sys.exit(app.exec_())
在
main
函数中,创建
QApplication
实例,启动服务器并监听指定的 IP 地址和端口号。如果启动失败,打印错误信息并退出程序;成功则打印启动信息。
3.3 处理新连接
def incomingConnection(self, socketDescriptor):
socket = QTcpSocket()
if not socket.setSocketDescriptor(socketDescriptor):
return
self.connect(socket, SIGNAL("readyRead()"),
lambda: self.readRequest(socket))
self.connect(socket, SIGNAL("disconnected()"),
lambda: self.socketDisconnected(socket))
当有新的客户端连接时,
incomingConnection
方法会被调用。创建一个
QTcpSocket
对象并设置其描述符,然后连接
readyRead
和
disconnected
信号到相应的槽函数。
3.4 读取请求
def readRequest(self, socket):
stream = QDataStream(socket)
stream.setVersion(QDataStream.Qt_4_2)
while True:
if socket.bytesAvailable() < SIZEOF_UINT16:
break
nextBlockSize = stream.readUInt16()
if socket.bytesAvailable() < nextBlockSize:
break
action = QString()
room = QString()
date = QDate()
stream >> action >> room >> date
if action == "BOOK":
self.handleBookRequest(socket, room, date)
elif action == "UNBOOK":
self.handleUnbookRequest(socket, room, date)
使用
QDataStream
从套接字读取请求数据,根据请求的动作(
BOOK
或
UNBOOK
)调用相应的处理函数。
3.5 处理预订请求
def handleBookRequest(self, socket, room, date):
if room not in self.rooms:
self.rooms[room] = set()
if date in self.rooms[room]:
self.sendErrorResponse(socket, "Room already booked on this date")
else:
self.rooms[room].add(date)
self.sendSuccessResponse(socket, "BOOK", room, date)
如果房间不在
self.rooms
字典中,创建一个新的集合来存储该房间的预订日期。如果该日期已被预订,发送错误响应;否则,将日期添加到集合中并发送成功响应。
3.6 处理取消预订请求
def handleUnbookRequest(self, socket, room, date):
if room in self.rooms and date in self.rooms[room]:
self.rooms[room].remove(date)
self.sendSuccessResponse(socket, "UNBOOK", room, date)
else:
self.sendErrorResponse(socket, "Room not booked on this date")
如果房间存在且该日期已被预订,从集合中移除该日期并发送成功响应;否则,发送错误响应。
3.7 发送成功响应
def sendSuccessResponse(self, socket, action, room, date):
response = QByteArray()
stream = QDataStream(response, QIODevice.WriteOnly)
stream.setVersion(QDataStream.Qt_4_2)
stream.writeUInt16(0)
stream << action << room << date
stream.device().seek(0)
stream.writeUInt16(response.size() - SIZEOF_UINT16)
socket.write(response)
准备成功响应的字节数组,写入响应数据,设置响应大小,然后将响应发送到客户端。
3.8 发送错误响应
def sendErrorResponse(self, socket, errorMessage):
response = QByteArray()
stream = QDataStream(response, QIODevice.WriteOnly)
stream.setVersion(QDataStream.Qt_4_2)
stream.writeUInt16(0)
stream << QString("ERROR") << QString(errorMessage)
stream.device().seek(0)
stream.writeUInt16(response.size() - SIZEOF_UINT16)
socket.write(response)
准备错误响应的字节数组,写入错误信息,设置响应大小,然后将响应发送到客户端。
3.9 处理客户端断开连接
def socketDisconnected(self, socket):
socket.deleteLater()
当客户端断开连接时,调用
deleteLater
方法删除套接字对象。
3.10 服务器工作流程总结
以下是服务器的工作流程 mermaid 流程图:
graph TD;
A[启动服务器] --> B[监听指定端口];
B --> C{有新连接};
C -- 是 --> D[处理新连接];
D --> E{有数据可读};
E -- 是 --> F[读取请求];
F --> G{请求类型};
G -- BOOK --> H[处理预订请求];
G -- UNBOOK --> I[处理取消预订请求];
H --> J{预订成功};
J -- 是 --> K[发送成功响应];
J -- 否 --> L[发送错误响应];
I --> M{取消预订成功};
M -- 是 --> K;
M -- 否 --> L;
K --> E;
L --> E;
E -- 否 --> N{连接断开};
N -- 是 --> O[删除套接字];
N -- 否 --> E;
C -- 否 --> B;
服务器等待客户端的连接和请求,根据请求类型进行相应的处理,并将处理结果以二进制数据的形式发送回客户端。
4. 总结与建议
4.1 国际化总结
-
国际化设置可以使应用程序支持多种语言,提高应用的通用性和用户体验。通过使用
pylupdate4和lrelease工具以及 Qt Linguist 应用程序,可以方便地进行翻译工作。 - 在代码中,要注意使用合适的方法处理字符串、数字、货币符号、日期和单位,确保在不同语言和地区的环境下都能正确显示。
4.2 网络编程总结
- Python 标准库和 PyQt4 都提供了丰富的网络编程功能。在客户端/服务器应用程序中,TCP 协议因其可靠性而被广泛使用。
-
通过
QTcpSocket和QTcpServer类,可以方便地实现简单的客户端/服务器应用程序。在实现过程中,要注意处理连接建立、数据读取、错误处理等情况。
4.3 建议
- 国际化方面 :在开发应用程序时,尽早考虑国际化需求,将用户可见的字符串统一处理,方便后续的翻译工作。
- 网络编程方面 :对于并发处理多个客户端请求的需求,可以考虑使用多线程或异步编程的方式,提高服务器的性能和响应能力。例如,在后续的开发中,可以将服务器改造成多线程版本,避免多个客户端请求冲突时的阻塞问题。
以下是一个总结表格:
| 类别 | 要点 |
| ---- | ---- |
| 国际化 | 使用
pylupdate4
和
lrelease
工具,Qt Linguist 进行翻译;代码中正确处理字符串、数字等 |
| 网络编程 | Python 标准库和 PyQt4 提供网络功能;TCP 协议用于客户端/服务器应用;使用
QTcpSocket
和
QTcpServer
实现应用 |
| 建议 | 尽早考虑国际化;使用多线程或异步编程提高服务器性能 |
通过以上的国际化设置和网络编程实现,我们可以开发出功能丰富、用户友好且具有良好扩展性的 PyQt 应用程序。
超级会员免费看
54

被折叠的 条评论
为什么被折叠?



