39、国际化与网络编程:PyQt 应用开发指南

国际化与网络编程: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 应用程序。

内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预测功能。同时展望了多模态标注、主动学习边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理DOM操作的稳定性,在实践中理解浏览器扩展的安全机制性能优化策略。
基于Gin+GORM+Casbin+Vue.js的权限管理系统是一个采用前后端分离架构的企业级权限管理解决方案,专为软件工程和计算机科学专业的毕业设计项目开发。该系统基于Go语言构建后端服务,结合Vue.js前端框架,实现了完整的权限控制和管理功能,适用于各类需要精细化权限管理的应用场景。 系统后端采用Gin作为Web框架,提供高性能的HTTP服务;使用GORM作为ORM框架,简化数据库操作;集成Casbin实现灵活的权限控制模型。前端基于vue-element-admin模板开发,提供现代化的用户界面和交互体验。系统采用分层架构和模块化设计,确保代码的可维护性和可扩展性。 主要功能包括用户管理、角色管理、权限管理、菜单管理、操作日志等核心模块。用户管理模块支持用户信息的增删改查和状态管理;角色管理模块允许定义不同角色并分配相应权限;权限管理模块基于Casbin实现细粒度的访问控制;菜单管理模块动态生成前端导航菜单;操作日志模块记录系统关键操作,便于审计和追踪。 技术栈方面,后端使用Go语言开发,结合Gin、GORM、Casbin等成熟框架;前端使用Vue.js、Element UI等现代前端技术;数据库支持MySQL、PostgreSQL等主流关系型数据库;采用RESTful API设计规范,确保前后端通信的标准化。系统还应用了单例模式、工厂模式、依赖注入等设计模式,提升代码质量和可测试性。 该权限管理系统适用于企业管理系统、内部办公平台、多租户SaaS应用等需要复杂权限控制的场景。作为毕业设计项目,它提供了完整的源码和论文文档,帮助学生深入理解前后端分离架构、权限控制原理、现代Web开发技术等关键知识点。系统设计规范,代码结构清晰,注释完整,非常适合作为计算机相关专业的毕业设计参考或实际项目开发的基础框架。 资源包含完整的系统源码、数据库设计文档、部署说明和毕
(Kriging_NSGA2)克里金模型结合多目标遗传算法求最优因变量及对应的最佳自变量组合研究(Matlab代码实现)内容概要:本文研究了克里金(Kriging)模型多目标遗传算法(NSGA-II)相结合的方法,用于求解最优因变量及其对应的最佳自变量组合。通过构建克里金代理模型近似复杂的目标函数,有效降低了计算成本,并利用NSGA-II算法进行多目标优化,实现了在多个相互冲突的目标之间寻找帕累托最优解。文中详细阐述了克里金模型的构建过程、超参数估计方法以及NSGA-II算法的集成方式,最后通过Matlab代码实现该方法,并应用于实际案例中验证其有效性。; 适合人群:具备一定数学建模和优化理论基础,熟悉Matlab编程,从事工程优化、数据分析或相关领域研究的科研人员及研究生。; 使用场景及目标:①解决高维、非线性、计算代价高昂的多目标优化问题;②在缺乏显式函数表达式的仿真或实验系统中,利用代理模型加速优化进程;③获取最优性能指标(因变量)的同时确定对应的最佳设计参数(自变量组合)。; 阅读建议:建议读者结合文中提供的Matlab代码,深入理解克里金模型的构造交叉验证方法,掌握NSGA-II算法的关键操作,如非支配排序和拥挤距离计算,并通过实际案例调试程序,加深对代理模型辅助优化流程的整体把握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值