【Py】去掉字典列表中某几个key的一种方法

本文介绍了如何利用Python的map和filter函数从字典构成的列表中移除指定的key。通过for循环遍历列表,结合map的__delitem__方法和filter的__contains__方法,可以高效地实现这一操作。虽然在map函数外层套用了list,其具体作用可能不明确,但这是为了确保返回可迭代的结果。
部署运行你感兴趣的模型镜像

今天在对字典构成的列表数据结构进行处理的时候,需要移除字典中的某几个key。可以使用map+filter的方式进行处理。

for item in data:
    list(map(item.__delitem__, filter(item.__contains__,l)))

在这里插入图片描述
但为什么要在map函数外套一层list,还不得而知

参考:https://stackoverflow.com/a/30351294/7151777

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>我们有一个操作ADAM4017设备的类,现在需要使用Qt Designer设计一个UI界面,然后将其与代码整合。 步骤: 1. 使用Qt Designer设计一个简单的界面,包含: - 一个按钮用于“打开串口” - 一个按钮用于“发送命令” - 一个按钮用于“读取并解析数据” - 一个文本显示区域(比如QTextEdit或QPlainTextEdit)用于显示读取到的数据和解析结果 2. 将设计好的.ui文件转换为.py文件(例如,使用pyuic5工具) 3. 创建一个主程序,将ADAM4017类与UI界面进行整合,并实现事件处理。 4. 注意:由于串口操作是阻塞的,为了避免界面卡死,可能需要使用多线程(但根据用户代码,目前我们先按照单线程实现,后续可以优化)。 设计思路: 在UI类中,我们将实例化一个ADAM4017对象,然后将按钮的点击事件连接到该对象的方法上,并将结果显示在文本区域。 但是,注意用户代码中的ADAM4017类在实例化时已经设置了串口参数(如COM200),我们可以暂时不考虑在界面上设置这些参数,但也可以预留。 由于用户代码中已经有一个ADAM4017类,我们将在这个基础上构建。 我们按照以下步骤进行: 步骤1:设计UI(使用Qt Designer) 创建一个窗体,包含三个按钮和一个文本显示框。 步骤2:将.ui文件转换成.py文件(假设为ui_adam4017.py) 步骤3:编写主程序,整合UI和ADAM4017类。 由于用户代码中已经有一个ADAM4017类,我们需要在UI界面中调用它。 注意:用户代码中的ADAM4017类在__main__中已经有一个简单的测试,我们将这个测试转换为UI事件。 以下是主程序的示例代码: 我们创建一个名为MainWindow的类,它继承自我们转换的UI类,并包含ADAM4017的实例。 代码结构如下: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox import serial import time from ui_adam4017 import Ui_MainWindow # 假设转换后的UI文件为ui_adam4017.py # 这里应该包含用户原来的ADAM4017类(略作修改,将print改为返回字符串,或者我们通过信号传递信息,但简单起见,我们直接更新界面) class ADAM4017: ... # 原来的类定义,这里我们稍作修改,比如将一些打印改为返回字符串,以便在界面上显示 class MyMainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MyMainWindow, self).__init__() self.setupUi(self) self.adam = ADAM4017() # 创建ADAM4017实例 # 连接按钮的点击事件 self.btn_open.clicked.connect(self.open_serial) self.btn_send.clicked.connect(self.send_command) self.btn_parse.clicked.connect(self.parse_data) def open_serial(self): # 调用ADAM4017的open_serial方法 success = self.adam.open_serial() if success: self.text_output.append("成功打开串口") else: self.text_output.append("打开串口失败") def send_command(self): # 发送命令 self.adam.send_command() self.text_output.append("已发送命令") def parse_data(self): # 读取并解析数据 result = self.adam.parse_adam4017_data() # 这个方法会返回一个字典 # 显示结果 if result: self.text_output.append("解析结果:") for key, value in result.items(): if key == 'all_channels': # 单独处理all_channels,因为它是一个列表 self.text_output.append(f"{key}: {', '.join(value)}") else: self.text_output.append(f"{key}: {value}") else: self.text_output.append("解析失败") if __name__ == "__main__": app = QApplication(sys.argv) window = MyMainWindow() window.show() sys.exit(app.exec_()) ``` 但是,我们需要修改原来的ADAM4017类中的一些方法,使其返回字符串而不是打印,或者我们仍然保留打印,同时将信息发送到UI界面。这里我们修改为通过返回或者修改方法使其将信息返回。 然而,在parse_adam4017_data方法中,它调用了read_data,而read_data中也有打印。我们可以修改这些方法,使其返回状态字符串,或者我们在UI类中捕获打印的内容(这需要重定向标准输出,比较复杂)。因此,我们选择修改ADAM4017类,使其方法返回操作结果和状态。 但是,考虑到时间,我们简单处理:在ADAM4017类中,我们保留原来的打印(用于调试),同时我们将关键信息返回给调用者(UI),以便显示在界面上。 另外,注意parse_adam4017_data方法中调用了read_data,而read_data会返回None,所以我们需要确保在读取数据失败时也能处理。 由于原代码中parse_adam4017_data方法已经返回一个字典,我们可以直接使用。但是,原方法中打印了各通道详情,这些信息我们也想在UI上显示。我们可以修改parse_adam4017_data方法,将原本打印的信息作为字符串返回,或者我们可以在UI中格式化输出。 这里我们选择在UI中格式化输出,所以parse_adam4017_data方法返回的字典我们直接展示。 但是,原方法中有一个print("各通道数据详情(从通道0开始)")以及每个通道的打印,这些我们可以在UI中通过遍历字典来实现。 所以,我们不必修改ADAM4017类中的parse_adam4017_data方法,只需要在UI中显示它返回的字典即可。 但是,注意原方法中使用了print,这会在控制台打印,而不会在UI显示。所以,我们可以修改这个方法去掉print,或者将这些信息整合到返回的字典中。这里为了简单,我们就不修改ADAM4017类,而是将打印信息重定向到UI。但是重定向标准输出比较麻烦,因此我们修改ADAM4017类。 修改建议:将原来的打印语句改为返回一个包含更多信息的字符串,或者我们将这些信息记录在类内部的一个日志属性中,然后由UI读取。但这样改动较大。 考虑到时间,我们修改parse_adam4017_data方法,让它返回两个一个是解析的字典,另一个是描述字符串(包含各通道详情)。但这样会改变返回结构,需要调整UI。 另一种做法:我们在UI中模拟打印这些信息,即根据返回的字典自己构造一个字符串。 由于我们在UI的parse_data方法中已经遍历了字典,那么原来parse_adam4017_data方法中打印的“各通道数据详情”就可以在UI中自己构造。 所以,我们修改UI中的parse_data方法: self.text_output.append("各通道数据详情(从通道0开始):") for i, chunk in enumerate(result['all_channels']): self.text_output.append(f"通道{i}: {chunk}") 然后,我们还可以显示返回字典中的其他键(除了all_channels)?其实我们返回的字典中,all_channels已经包含了所有通道,其他键(channel_0等)其实是冗余的。所以我们可以只显示all_channels,或者显示所有。 为了与原程序保持一致,我们可以显示所有通道,以及单独提取的channel_2和channel_5。 但是,在parse_data方法中,我们只显示all_channels的详细信息,其他键可以不用显示(因为all_channels已经包含)。 因此,在UI的parse_data方法中,我们可以这样: if result: self.text_output.append("各通道数据详情(从通道0开始):") all_channels = result['all_channels'] for i, chunk in enumerate(all_channels): self.text_output.append(f"通道{i}: {chunk}") # 还可以单独显示某个通道,如果需要的话 # 例如:self.text_output.append(f"通道2: {result['channel_2']}") 但是,原方法返回的字典中还有channel_0, channel_1等,它们和all_channels中的内容是重复的。所以我们也可以不显示整个字典,只显示all_channels。 因此,我们修改ADAM4017类的parse_adam4017_data方法,让它只返回一个包含所有通道的列表(即all_channels)?但为了保持接口不变,我们还是返回字典,但UI中可以选择显示哪些。 考虑到简单,我们就在UI中显示all_channels的每个通道,然后也显示字典中其他的单独通道(如果有需要的话,比如用户可能只关心某几个通道)。 但是,原字典中还有channel_2和channel_5,它们是从all_channels中提取出来的,所以重复了。所以我们在UI中可以不显示这些重复的内容,只显示all_channels。 所以,我们修改UI中的parse_data方法: if result: self.text_output.append("各通道数据详情(从通道0开始):") all_channels = result['all_channels'] for i, chunk in enumerate(all_channels): self.text_output.append(f"通道{i}: {chunk}") # 如果你想单独显示通道2和5,可以这样: if result['channel_2'] is not None: self.text_output.append(f"单独提取的通道2: {result['channel_2']}") if result['channel_5'] is not None: self.text_output.append(f"单独提取的通道5: {result['channel_5']}") 这样,界面就会显示所有通道,以及单独提取的两个通道(如果存在)。 但是,原ADAM4017类中,parse_adam4017_data方法在调用read_data时可能返回None,然后整个方法就没有返回字典,而是继续执行后面的代码。如果read_data返回None,那么hex_data就是None,然后对None进行切片会导致异常。所以我们需要修改parse_adam4017_data方法,使其在read_data返回None时返回None。 原parse_adam4017_data方法: def parse_adam4017_data(self): hex_data=self.read_data() # 如果read_data返回None,那么hex_data为None data_start=6 chunks = [hex_data[i:i+4] for i in range(data_start, len(hex_data), 4)] # 这里会出错 ... 因此,我们需要修改: def parse_adam4017_data(self): hex_data = self.read_data() if hex_data is None: print("没有读取到数据") return None ... 同样,在read_data方法中,如果读取失败,返回None。 我们修改后的ADAM4017类(部分修改)如下: def read_data(self): try: time.sleep(0.1) if self.ser.in_waiting > 0: self.data = self.ser.read(21) print(f"成功收到数据:{self.data.hex()}") return self.data.hex() else: print("没有数据可读") return None except Exception as e: print(f"读取数据失败:{e}") return None def parse_adam4017_data(self): hex_data = self.read_data() if hex_data is None: print("没有数据可供解析") return None data_start=6 # 注意:hex_data是字符串,每个字节用两个字符表示,所以每4个字符(即两个字节)是一个通道的数据 # 因此,hex_data的长度应该是42(21字节*2) # 检查数据的长度 if len(hex_data) < 42: print(f"数据长度不足,只有{len(hex_data)}个字符,期望42个字符") return None chunks = [hex_data[i:i+4] for i in range(data_start, len(hex_data), 4)] # 打印各通道详情(在控制台) print("各通道数据详情(从通道0开始):") for i, chunk in enumerate(chunks): print(f"通道{i}: {chunk}") channel_2_data = chunks[2] if len(chunks) > 2 else None channel_5_data = chunks[5] if len(chunks) > 5 else None return { 'channel_0': chunks[0], 'channel_1': chunks[1], 'channel_2': channel_2_data, 'channel_3': chunks[3], 'channel_4': chunks[4], 'channel_5': channel_5_data, 'all_channels': chunks } 这样,我们就修改了可能的错误。 步骤4:设计UI文件(使用Qt Designer) 我们设计一个简单的界面,包含: 三个按钮:btn_open, btn_send, btn_parse 一个文本编辑器:text_output(使用QPlainTextEdit或QTextEdit) 保存为adam4017.ui。 然后,使用命令将.ui文件转换为.py文件: pyuic5 -o ui_adam4017.py adam4017.ui 步骤5:编写主程序,将上述的MyMainWindow类与转换的UI类结合。 注意:由于串口操作可能会阻塞GUI,所以如果遇到界面无响应,可能需要使用多线程。但在这个简单示例中,我们假设读取很快。 下面提供一个完整的主程序代码,其中包含了修改后的ADAM4017类(与原来基本一致,只做了上述小修改)。 注意:由于原来的ADAM4017类中,open_serial方法中设置了串口参数为固定(COM200, 9600, 2秒超时),所以我们在UI中没有提供设置串口参数的控件。如果需要,可以增加。 完整代码: 文件1:adam4017_ui_main.py(主程序) 文件2:ui_adam4017.py(由Qt Designer生成的界面代码) 由于内容较长,我们将两个文件分开。 首先,生成ui_adam4017.py(通过Qt Designer设计并转换,这里不展示转换后的代码,因为每个人设计可能不同,但下面给出一个简单的设计示例)。 然后,在主程序中,我们整合。 这里假设ui_adam4017.py已经存在,并且其中包含一个名为Ui_MainWindow的类。 然后,我们编写主程序。 以下是主程序的完整代码(包含修改后的ADAM4017类): </think># ADAM4017串口数据读取工具 - Qt UI界面实现 下面是根据您的代码设计的Qt UI界面实现。这个界面允许用户操作ADAM4017设备的串口通信和数据读取功能。 ## 完整代码实现 ```python import sys import serial import time from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QLabel, QComboBox, QGroupBox, QStatusBar) from PyQt5.QtCore import Qt, QTimer class ADAM4017: def __init__(self, port='COM200', baudrate=9600, timeout=1): self.port = port self.baudrate = baudrate self.timeout = timeout self.ser = None self.data = None def open_serial(self): try: self.ser = serial.Serial( port=self.port, baudrate=self.baudrate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=self.timeout ) if self.ser.is_open: return True, f"成功打开串口 {self.port}" else: return False, f"串口无法打开 {self.port}" except serial.SerialException as e: return False, f"串口打开失败: {e}" def close_serial(self): if self.ser and self.ser.is_open: self.ser.close() return True, f"已关闭串口 {self.port}" return False, "串口未打开" def send_command(self): if self.ser and self.ser.is_open: try: self.ser.write(b"\x02\x03\x00\x00\x00\x08\x44\x3F") return True, "命令发送成功" except Exception as e: return False, f"命令发送失败: {e}" return False, "串口未打开,无法发送命令" def read_data(self): try: if self.ser and self.ser.is_open: time.sleep(0.1) if self.ser.in_waiting > 0: self.data = self.ser.read(21) return True, self.data.hex() else: return False, "没有收到数据" else: return False, "串口未打开" except Exception as e: return False, f"读取数据失败: {e}" def parse_adam4017_data(self): success, result = self.read_data() if not success: return False, result hex_data = result if len(hex_data) < 42: # 21字节 * 2个十六进制字符 return False, f"数据长度不足: 收到{len(hex_data)}字符, 需要42字符" data_start = 6 chunks = [hex_data[i:i+4] for i in range(data_start, len(hex_data), 4)] # 确保有足够的通道数据 if len(chunks) < 6: return False, f"通道数据不足: 解析到{len(chunks)}个通道, 需要至少6个" result = { 'channel_0': chunks[0], 'channel_1': chunks[1], 'channel_2': chunks[2], 'channel_3': chunks[3], 'channel_4': chunks[4], 'channel_5': chunks[5], 'all_channels': chunks } return True, result class ADAM4017UI(QMainWindow): def __init__(self): super().__init__() self.adam = ADAM4017() self.initUI() def initUI(self): self.setWindowTitle('ADAM4017串口数据读取工具') self.setGeometry(300, 300, 800, 600) # 创建主窗口部件 main_widget = QWidget() main_layout = QVBoxLayout() main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) # 串口配置区域 config_group = QGroupBox("串口配置") config_layout = QHBoxLayout() self.port_combo = QComboBox() # 添加常见COM端口 self.port_combo.addItems([f"COM{i}" for i in range(1, 21)]) self.port_combo.setCurrentText('COM200') self.baudrate_combo = QComboBox() self.baudrate_combo.addItems(['9600', '19200', '38400', '57600', '115200']) self.baudrate_combo.setCurrentText('9600') config_layout.addWidget(QLabel("端口:")) config_layout.addWidget(self.port_combo) config_layout.addWidget(QLabel("波特率:")) config_layout.addWidget(self.baudrate_combo) self.open_btn = QPushButton("打开串口") self.open_btn.clicked.connect(self.toggle_serial) config_layout.addWidget(self.open_btn) config_group.setLayout(config_layout) main_layout.addWidget(config_group) # 控制按钮区域 btn_layout = QHBoxLayout() self.send_btn = QPushButton("发送命令") self.send_btn.clicked.connect(self.send_command) self.send_btn.setEnabled(False) btn_layout.addWidget(self.send_btn) self.read_btn = QPushButton("读取数据") self.read_btn.clicked.connect(self.read_data) self.read_btn.setEnabled(False) btn_layout.addWidget(self.read_btn) self.parse_btn = QPushButton("解析数据") self.parse_btn.clicked.connect(self.parse_data) self.parse_btn.setEnabled(False) btn_layout.addWidget(self.parse_btn) main_layout.addLayout(btn_layout) # 数据显示区域 data_group = QGroupBox("数据输出") data_layout = QVBoxLayout() self.data_display = QTextEdit() self.data_display.setReadOnly(True) self.data_display.setFontFamily("Courier New") data_layout.addWidget(self.data_display) data_group.setLayout(data_layout) main_layout.addWidget(data_group, 1) # 使用拉伸因子1使其占据更多空间 # 状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("就绪") # 设置定时器自动读取数据 self.read_timer = QTimer() self.read_timer.timeout.connect(self.read_data) self.auto_read = False # 自动读取复选框 auto_layout = QHBoxLayout() self.auto_read_cb = QLabel("自动读取: 未激活") auto_layout.addWidget(self.auto_read_cb) self.toggle_auto_btn = QPushButton("启用自动读取") self.toggle_auto_btn.clicked.connect(self.toggle_auto_read) self.toggle_auto_btn.setEnabled(False) auto_layout.addWidget(self.toggle_auto_btn) main_layout.addLayout(auto_layout) # 日志区域 log_group = QGroupBox("日志") log_layout = QVBoxLayout() self.log_display = QTextEdit() self.log_display.setReadOnly(True) self.log_display.setMaximumHeight(100) log_layout.addWidget(self.log_display) log_group.setLayout(log_layout) main_layout.addWidget(log_group) def toggle_serial(self): """切换串口状态(打开/关闭)""" if self.adam.ser and self.adam.ser.is_open: success, message = self.adam.close_serial() self.log(message) self.status_bar.showMessage(message) self.open_btn.setText("打开串口") self.send_btn.setEnabled(False) self.read_btn.setEnabled(False) self.parse_btn.setEnabled(False) self.toggle_auto_btn.setEnabled(False) self.toggle_auto_btn.setText("启用自动读取") self.auto_read_cb.setText("自动读取: 未激活") self.read_timer.stop() else: # 获取UI中的配置 port = self.port_combo.currentText() baudrate = int(self.baudrate_combo.currentText()) self.adam.port = port self.adam.baudrate = baudrate success, message = self.adam.open_serial() self.log(message) self.status_bar.showMessage(message) if success: self.open_btn.setText("关闭串口") self.send_btn.setEnabled(True) self.read_btn.setEnabled(True) self.parse_btn.setEnabled(True) self.toggle_auto_btn.setEnabled(True) def send_command(self): """发送命令到设备""" success, message = self.adam.send_command() self.log(message) self.status_bar.showMessage(message) def read_data(self): """从设备读取数据""" success, message = self.adam.read_data() if success: self.data_display.clear() self.data_display.append("原始数据(十六进制):") self.data_display.append(message) # 格式化显示二进制数据 hex_str = message formatted = " ".join([hex_str[i:i+2] for i in range(0, len(hex_str), 2)]) self.data_display.append("\n格式化显示:") self.data_display.append(formatted) self.log(f"成功读取数据: {len(message)}字节") else: self.log(f"读取失败: {message}") self.status_bar.showMessage(message) def parse_data(self): """解析读取到的数据""" success, result = self.adam.parse_adam4017_data() if not success: self.log(f"解析失败: {result}") self.status_bar.showMessage(f"解析失败: {result}") return self.data_display.clear() self.data_display.append("解析结果:") self.data_display.append(f"通道 0: {result['channel_0']}") self.data_display.append(f"通道 1: {result['channel_1']}") self.data_display.append(f"通道 2: {result['channel_2']}") self.data_display.append(f"通道 3: {result['channel_3']}") self.data_display.append(f"通道 4: {result['channel_4']}") self.data_display.append(f"通道 5: {result['channel_5']}") self.data_display.append("\n所有通道数据:") for i, chunk in enumerate(result['all_channels']): self.data_display.append(f"通道 {i}: {chunk}") self.log("数据解析成功") self.status_bar.showMessage("数据解析成功") def toggle_auto_read(self): """切换自动读取模式""" if self.auto_read: self.read_timer.stop() self.auto_read = False self.toggle_auto_btn.setText("启用自动读取") self.auto_read_cb.setText("自动读取: 未激活") self.log("自动读取已禁用") else: self.read_timer.start(1000) # 1秒间隔 self.auto_read = True self.toggle_auto_btn.setText("停止自动读取") self.auto_read_cb.setText("自动读取: 激活中 (1秒/次)") self.log("自动读取已启用") def log(self, message): """添加日志信息""" timestamp = time.strftime("%H:%M:%S") self.log_display.append(f"[{timestamp}] {message}") # 自动滚动到底部 scrollbar = self.log_display.verticalScrollBar() scrollbar.setValue(scrollbar.maximum()) if __name__ == "__main__": app = QApplication(sys.argv) main_window = ADAM4017UI() main_window.show() sys.exit(app.exec_()) ``` ## 界面功能说明 这个UI界面实现了以下功能: 1. **串口配置区域**: - 可选择COM端口(COM1-COM20) - 可选择波特率(9600-115200) - 打开/关闭串口按钮 2. **控制按钮区域**: - 发送命令:发送设备指令 - 读取数据:从设备读取原始数据 - 解析数据:解析读取到的通道数据 - 自动读取:定时自动读取数据(1秒间隔) 3. **数据显示区域**: - 显示原始十六进制数据 - 格式化显示二进制数据 - 解析后的通道数据显示(通道0-5) 4. **日志区域**: - 显示所有操作的状态信息 - 带时间戳记录所有操作 5. **状态栏**: - 显示当前操作状态 ## 使用说明 1. 选择串口和波特率(默认COM200,9600) 2. 点击"打开串口"按钮连接设备 3. 使用按钮发送命令、读取数据、解析数据 4. 可启用自动读取功能定时获取数据 5. 日志区域显示所有操作记录 ## 设计特点 1. **用户友好界面**:所有功能清晰分组,操作直观 2. **错误处理**:完善的错误处理和状态反馈 3. **数据格式化**:原始数据格式化显示,便于阅读 4. **自动读取**:可选定时自动读取功能 5. **详细日志**:完整记录所有操作和状态变化 ## 运行要求 - Python 3.x - PyQt5 (`pip install PyQt5`) - pyserial (`pip install pyserial`) 这个UI界面紧密集成了您的ADAM4017类,提供了图形化的操作方式,使设备操作更加直观和方便[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值