PyQt5 数据在字节类型的信号发射期间丢失
Posted
技术标签:
【中文标题】PyQt5 数据在字节类型的信号发射期间丢失【英文标题】:PyQt5 data lost during signal emit in bytes type 【发布时间】:2017-02-23 13:44:12 【问题描述】:我正在使用 PyQt5 和 Python 3 显示通过串行端口从嵌入式设备接收到的一些数据。 PySerial 库用于处理串行通信。由于 PySerial 的读取函数的返回类型是 bytes
,所以我使用线程来发出 bytes
类型的接收数据。有趣的是,字节b'\x00'
和之后的所有字节在发射-接收周期中都丢失了。但是,如果将发射信号的类型设置为QByteArray
,则数据是正确发射和接收的。
下面是重现问题的 MWE:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtCore, QtWidgets
class Example(QtWidgets.QWidget):
new_bytes = QtCore.pyqtSignal(bytes)
new_qba = QtCore.pyqtSignal('QByteArray')
bytes_to_emit = b'\x01\x00\x02'
def __init__(self, parent=None):
super(Example, self).__init__()
self.init_ui()
self.setWindowTitle('Emit Bytes Bug')
def init_ui(self):
emit_label = QtWidgets.QLabel('Data to emit')
data_label = QtWidgets.QLabel(self.bytes_to_emit.hex())
emit_bytes_button = QtWidgets.QPushButton('Emit bytes')
emit_qba_button = QtWidgets.QPushButton('Emit QByteArray')
self.rev_bytes_edit = QtWidgets.QLineEdit()
self.rev_qba_edit = QtWidgets.QLineEdit()
clear_button = QtWidgets.QPushButton('Clear receive')
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(emit_label, 0, 0)
grid_layout.addWidget(data_label, 0, 1)
grid_layout.addWidget(emit_bytes_button, 1, 0)
grid_layout.addWidget(self.rev_bytes_edit, 1, 1)
grid_layout.addWidget(emit_qba_button, 2, 0)
grid_layout.addWidget(self.rev_qba_edit, 2, 1)
grid_layout.addWidget(clear_button, 3, 0)
self.setLayout(grid_layout)
emit_bytes_button.clicked.connect(self.on_bytes_button)
emit_qba_button.clicked.connect(self.on_qba_button)
self.new_bytes.connect(self.update_byte_receive)
self.new_qba.connect(self.update_qba_receive)
clear_button.clicked.connect(self.on_clear_button)
def update_byte_receive(self, data):
data_str = data.hex()
self.rev_bytes_edit.insert(data_str)
def update_qba_receive(self, data):
tmp_bytes = bytes(data)
data_str = tmp_bytes.hex()
self.rev_qba_edit.insert(data_str)
def on_bytes_button(self):
self.new_bytes.emit(self.bytes_to_emit)
def on_qba_button(self):
self.new_qba.emit(self.bytes_to_emit)
def on_clear_button(self):
self.rev_bytes_edit.clear()
self.rev_qba_edit.clear()
def main():
app = QtWidgets.QApplication(sys.argv)
form = Example()
form.show()
app.exec_()
if __name__ == '__main__':
main()
要发出的数据是b'\x01\x00\x02'
,点击'Emit bytes'按钮会发出'bytes'类型的数据,并在按钮右侧的lineedit中显示接收到的数据。同样,单击“Emit QByteArray”按钮将发出QByteArray
类型的数据,并在按钮对应的lineedit 上显示接收到的数据。 “清除接收”按钮只是清除行编辑。
虽然我已经知道替代的工作解决方案,即使用 QByteArray
类型发出字节信号,但我想知道在发出信号时首选哪种信号类型。如果需要发出的数据已经在 bytes
类型中,那么使用 Python 3 的原生 bytes
类型是否会更快?还是 PyQt5 的 QByteArray
更接近底层 C++ 代码?任何建议将不胜感激。
Python3 版本:3.5
PyQt5 版本:5.7
提前致谢
【问题讨论】:
我使用的是 Python 3.4 和 PySide 1.2.2,这段代码运行良好。 @HashSplat 这可能是由于PyQt
和PySide
在python
和C++
之间处理类型转换的方式不同
@user3419537。没有。它对我使用 Python-3.6 和 PyQt-5.7.1 也非常有效,而且真的没有理由不这样做。 update_byte_receive
收到的类型是<class 'bytes'>
,和预期的一样,而且值没有被截断。如果 OP 有什么不同,那一定是由于他们正在使用的特定版本的 SIP 和/或 PyQt5 中的错误。
@user3419537。 PyQt5 仅针对 Python 3 进行了全面测试,因此就 Python 2 而言,所有的赌注都没有了。但是,如果您使用 Python bytearray
,则该示例应该可以工作。
@Daniel。在皮下,PyQt4 和 PyQt5 在某些方面有很大的不同。我建议你尝试使用 PyQt-5.7.1。我很确定最近修复了一个与此相关的错误。 PS:我测试了 SIP-4.19 和 SIP-4.19.1。
【参考方案1】:
您可能想查看 Signals and Slots 的 PyQt 文档
当发出信号时,如果可能,所有参数都将转换为 C++ 类型。
和Python Strings, Qt Strings and Unicode
对于 Python v3,默认情况下会完成以下转换。
1234563 或实现buffer
协议的 Python 对象。如果
Qt
需要char
(或const
版本),则PyQt5
将接受与char *
相同的类型,并且还要求单个 提供了字符。如果
Qt
需要signed char *
或unsigned char *
(或const
版本),那么PyQt5
将接受bytes
。如果
Qt
需要signed char
或unsigned char
(或const
版本),那么PyQt5
将接受长度为1 的bytes
。如果
Qt
需要QString
,那么PyQt5
将接受str
、仅包含ASCII
字符的bytes
、QByteArray
或None
。如果
Qt
期望QByteArray
,那么PyQt5
也将接受bytes
。如果
Qt
需要QByteArray
,那么PyQt5
也将接受仅包含Latin-1
字符的str
。
如您所见,PyQt
很乐意将bytes
对象转换为几种C++/Qt
类型。它怎么知道你想要QByteArray
?没有。
如果您在调试器中停止代码并查看new_bytes
信号,您将看到该信号已被赋予QString
类型(至少这是我系统上python2.7 下发生的情况)。如果您熟悉C
字符串的工作原理,您就会知道字节x/00
表示字符串的结尾,而与缓冲区大小无关。这就是您的输出在 `x/00 之后终止的原因。
无论哪种方式,python 对象都需要转换为C++
类型。用QtCore.pyqtSignal('QByteArray')
声明信号会明确告诉PyQt
你想要哪种类型。
【讨论】:
大部分内容并不真正相关,因为在示例中,信号是在 Python 中定义和发出的,而不是在 Qt/C++ 中。这很重要,例如,当发出 Qt/C++ 一无所知的 Python 特定对象(例如tuple
)时。对于这些情况,根本不进行任何转换 - 实际上,会发出一个指针。使用 Python 3 而不是 Python 2 进行测试也很重要。对于后者,您需要使用 Python bytearray
而不是 bytes
才能获得正确的行为。
在 python3 的 bytes
和 PyQt5 的 QByteArray
之间是否有偏好发送信号的类型,由发送方和接收方的预期数据类型 bytes
提供?
@Daniel。从表面上看,如果您收到bytes
,那么同时发送bytes
似乎更高效,因为它避免了与QByteArray
之间的转换。然而,布丁的证据在吃,你知道timeit module在哪里,对吧?我的预感是,除非您发送大量数据或每秒发出数百万个信号或类似情况,否则它不会产生什么影响。以上是关于PyQt5 数据在字节类型的信号发射期间丢失的主要内容,如果未能解决你的问题,请参考以下文章