【xlwings api语言参考】Range.Show 方法

xlwings API的Range.Show方法用于滚动活动窗口,将指定的单元格区域移到视野中。此操作适用于单个单元格所在的活动文档。应用此方法时,需先定义一个表示Range对象的变量rng。

功能:
滚动当前活动窗口中的内容以将指定区域移到视图中。 此区域必须由活动文档中的单个单元格组成。

语法:
rng.Show()
rng是一个表示 Range 对象的变量。

点这里看视频课程

在这里插入图片描述

下面代码用pyqt6封装,显示封装后的全部代码以及打包exe方法 1、是否可以用 pyinstaller --noconsole D:\pythonProject-wangwang\打包exe\测试-小琳.py import xlwings as xw import os import sys from pathlib import Path import logging from datetime import datetime import tkinter as tk from tkinter import filedialog # 配置日志记录 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) logger = logging.getLogger(__name__) def select_folder(): """让用户选择文件夹""" root = tk.Tk() root.withdraw() # 隐藏主窗口 folder_path = filedialog.askdirectory( title='请选择包含审批效率明细报表的文件夹', initialdir=os.path.expanduser('~/Desktop') ) if not folder_path: logger.error("未选择文件夹,程序退出") sys.exit(1) return folder_path def get_excel_files(folder_path): """获取指定格式的10个Excel文件""" folder = Path(folder_path) if not folder.exists(): raise FileNotFoundError(f"文件夹不存在: {folder_path}") # 查找审批效率明细报表_001到_010的文件 files = [] for i in range(1, 11): file_num = f"{i:03d}" # 格式化为三位数,如001 file_pattern = f"审批效率明细报表_{file_num}.xls*" matched_files = list(folder.glob(file_pattern)) if not matched_files: logger.warning(f"未找到文件: {file_pattern}") continue # 取第一个匹配的文件(忽略临时文件) valid_files = [f for f in matched_files if not f.name.startswith('~$')] if valid_files: files.append(valid_files[0]) if len(files) < 10: logger.warning(f"只找到 {len(files)}/10 个文件") return sorted(files) # 按文件名排序 def process_files(config): """处理所有文件并提取目标公司数据""" app = None wb_output = None output_file = Path(config['file_path']) / config['output_file'] try: # 初始化Excel应用 app = xw.App(visible=False) app.display_alerts = False # 禁用警告提示 # 创建输出工作簿(如果存在则先删除) if output_file.exists(): output_file.unlink() wb_output = app.books.add() ws_output = wb_output.sheets[0] ws_output.name = "审批表S09" # 设置默认行高和列宽 ws_output.api.Rows.RowHeight = 14 # 固定行高14 ws_output.api.Columns.ColumnWidth = 16 # 固定列宽16 # 添加标题行(第一次写入) is_first_file = True total_matching_rows = 0 # 处理每个文件 for file_path in get_excel_files(config['file_path']): logger.info(f"正在处理文件: {file_path.name}") try: wb = app.books.open(file_path) ws = wb.sheets[0] # 假设数据在第一个工作表 # 获取所有数据 last_row = ws.range('A1').end('down').row data_range = ws.range(f'A1:Z{last_row}') data = data_range.value if not data or len(data) < 2: logger.warning(f"{file_path.name} 没有有效数据") continue headers = data[0] col_idx = 24 # Y列是第25列,索引为24 # 筛选数据并记录要删除的行号 matching_data = [] rows_to_delete = [] for i, row in enumerate(data[1:], start=2): # 从第2行开始 if not row or len(row) <= col_idx: continue cell_value = str(row[col_idx]).strip() if row[col_idx] else "" if cell_value == config['target_company']: matching_data.append(row) rows_to_delete.append(i) # 写入输出文件 if matching_data: if is_first_file: output_range = ws_output.range('A1').expand('table') output_range.value = [headers] + matching_data # 设置标题行格式 header_range = ws_output.range('1:1') header_range.font.bold = True header_range.row_height = 14 is_first_file = False else: last_row = ws_output.range('A1').end('down').row ws_output.range(f'A{last_row+1}').value = matching_data # 设置新增行的行高 new_rows = len(matching_data) for row in range(last_row+1, last_row+1+new_rows): ws_output.range(f'{row}:{row}').row_height = 14 total_matching_rows += len(matching_data) logger.info(f"从 {file_path.name} 提取到 {len(matching_data)} 行数据") # 删除原表中的匹配行 if rows_to_delete: for row_num in sorted(rows_to_delete, reverse=True): ws.range(f'{row_num}:{row_num}').delete(shift='up') # 保存修改后的原文件 wb.save() logger.info(f"已从 {file_path.name} 删除 {len(rows_to_delete)} 行数据") wb.close() except Exception as e: logger.error(f"处理文件 {file_path.name} 时出错: {e}") continue # 保存结果 if total_matching_rows > 0: # 添加处理时间标记 last_row = ws_output.range('A1').end('down').row time_range = ws_output.range(f'A{last_row+1}') time_range.value = [ [f"数据提取时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"], [f"共提取 {total_matching_rows} 行数据"] ] time_range.resize(2,1).row_height = 14 time_range.font.italic = True time_range.font.color = (100, 100, 100) # 确保所有列宽为16(覆盖自动调整) ws_output.api.Columns.ColumnWidth = 16 wb_output.save(output_file) logger.info(f"所有数据已保存至: {output_file}") return 0 else: logger.warning("没有找到任何匹配数据") return 1 except Exception as e: logger.error(f"处理失败: {str(e)}", exc_info=True) return 1 finally: try: if wb_output: wb_output.close() if app: app.quit() except Exception as e: logger.warning(f"清理资源时出错: {e}") if __name__ == "__main__": # 让用户选择文件夹 selected_folder = select_folder() logger.info(f"已选择文件夹: {selected_folder}") config = { 'file_path': selected_folder, 'target_company': "上海旺旺食品集团有限公司", 'output_file': "审批表S09.xlsx" } sys.exit(process_files(config))
08-26
<think>我们正在处理一个任务:将现有的使用tkinter编写的Python脚本迁移到PyQt6,并封装界面,然后使用PyInstaller打包成exe文件。 步骤: 1. 将tkinter代码迁移到PyQt6 2. 使用PyInstaller打包为exe 首先,我们需要了解tkinter和PyQt6在界面设计上的主要区别: - tkinter是Python自带的GUI库,而PyQt6是第三方库,功能更强大,但语法不同。 - 两者在事件处理、控件布局、信号与槽机制等方面有显著差异。 迁移步骤: 1. 分析原有tkinter界面的组件和布局,设计对应的PyQt6界面。 2. 将tkinter中的组件替换为PyQt6中的等效组件(例如:Button, Label等)。 3. 重新实现事件处理(tkinter中的command回调,在PyQt6中通过信号与槽实现)。 4. 调整布局(PyQt6提供了多种布局管理器,如QVBoxLayout, QHBoxLayout, QGridLayout等)。 然后,使用PyInstaller打包: 1. 安装PyInstaller。 2. 使用命令行或编写spec文件进行打包。 注意:在迁移过程中,由于两个库的差异,可能需要重构部分代码逻辑。 下面我们详细说明。 一、迁移tkinter到PyQt6 假设我们有一个简单的tkinter程序: ```python import tkinter as tk def on_button_click(): label.config(text="Button Clicked!") root = tk.Tk() root.title("Tkinter Example") label = tk.Label(root, text="Hello, Tkinter!") label.pack(pady=10) button = tk.Button(root, text="Click Me", command=on_button_click) button.pack(pady=10) root.mainloop() ``` 对应的PyQt6代码: ```python import sys from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("PyQt6 Example") # 创建中央部件和布局 central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 标签 self.label = QLabel("Hello, PyQt6!") layout.addWidget(self.label) # 按钮 self.button = QPushButton("Click Me") self.button.clicked.connect(self.on_button_click) # 连接信号与槽 layout.addWidget(self.button) def on_button_click(self): self.label.setText("Button Clicked!") if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) ``` 关键点: - PyQt6使用类来组织窗口,通常继承自QMainWindow或QWidget。 - 布局管理器用于排列控件(这里使用QVBoxLayout)。 - 事件处理:通过信号与槽机制(如button.clicked.connect(...))。 二、使用PyInstaller打包 1. 安装PyInstaller(如果尚未安装): ``` pip install pyinstaller ``` 2. 打包命令: ``` pyinstaller --onefile --windowed your_script.py ``` 参数说明: --onefile:将所有文件打包成一个可执行文件。 --windowed:对于GUI程序,不显示控制台窗口(如果是命令行程序则省略)。 3. 打包完成后,在dist目录下找到生成的exe文件。 注意:如果程序包含图标等资源文件,需要额外处理。可以使用--add-data参数添加资源文件。 三、可能遇到的问题 1. 路径问题:在打包后,程序的工作目录可能改变,因此访问资源文件(如图片)时建议使用绝对路径或通过sys._MEIPASS(在打包后PyInstaller会创建临时文件夹存放资源)。 在PyQt6中,可以使用以下方式处理资源路径: ```python import sys import os def resource_path(relative_path): """ 获取资源的绝对路径。打包到exe时,PyInstaller会创建临时文件夹存储资源 """ if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath("."), relative_path) # 使用示例 icon_path = resource_path("icon.ico") ``` 2. 打包时如果缺少某些模块,可以手动添加(使用--hidden-import)。 3. 对于PyQt6,可能需要添加额外的钩子(hook)文件,但PyInstaller通常能自动处理。 四、高级封装 如果原有脚本是业务逻辑和界面混合的,建议将业务逻辑与界面分离,这样迁移会更清晰。 例如,将核心逻辑提取到一个单独的类或模块中,然后在PyQt6界面中调用这些逻辑。 总结: 1. 迁移:将tkinter组件替换为PyQt6组件,重新设计布局,使用信号与槽连接事件。 2. 打包:使用PyInstaller打包,注意处理资源文件和路径问题。 下面提供相关问题:</think>### 如何将 Tkinter 脚本迁移到 PyQt6 并打包为 EXE #### **迁移步骤(Tkinter → PyQt6)** 1. **组件映射替换** - Tkinter 组件 → PyQt6 等效组件: - `Label` → `QLabel` - `Button` → `QPushButton` - `Entry` → `QLineEdit` - `Text` → `QTextEdit` - `Frame` → `QFrame` 或布局管理器(如 `QVBoxLayout`) - 示例: ```python # Tkinter 原代码 import tkinter as tk root = tk.Tk() label = tk.Label(root, text="Hello Tkinter") label.pack() # PyQt6 等效 from PyQt6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget app = QApplication([]) window = QWidget() layout = QVBoxLayout() label = QLabel("Hello PyQt6") layout.addWidget(label) window.setLayout(layout) window.show() app.exec() ``` 2. **事件处理重构** - Tkinter 的 `command=` 回调 → PyQt6 的 **信号与槽机制**: ```python # Tkinter button = tk.Button(root, text="Click", command=on_click) # PyQt6 button = QPushButton("Click") button.clicked.connect(self.on_click) # 需在类中定义 on_click 方法 ``` 3. **布局管理** - 使用 PyQt6 的布局管理器(`QHBoxLayout`, `QVBoxLayout`, `QGridLayout`)替代 Tkinter 的 `pack()`/`grid()`: ```python layout = QVBoxLayout() layout.addWidget(QLabel("Top")) layout.addWidget(QPushButton("Bottom")) window.setLayout(layout) ``` 4. **主循环调整** - 将 `tk.mainloop()` 替换为 PyQt6 的 `app.exec()`。 #### **封装界面(PyQt6 最佳实践)** 1. **采用面向对象结构** 将窗口封装为类,继承自 `QMainWindow` 或 `QWidget`: ```python from PyQt6.QtWidgets import QMainWindow, QApplication class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("PyQt6 App") self.setup_ui() def setup_ui(self): # 在此添加界面组件 pass if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec() ``` 2. **资源文件处理** - 图标/图片:使用 Qt 资源系统(`.qrc` 文件)或直接加载: ```python from PyQt6.QtGui import QIcon self.setWindowIcon(QIcon("icon.png")) ``` #### **打包为 EXE(PyInstaller)** 1. **安装依赖** ```bash pip install pyinstaller pyqt6 ``` 2. **基本打包命令** ```bash pyinstaller --onefile --windowed --name MyApp main.py ``` - `--onefile`:生成单个 EXE 文件 `--windowed`:隐藏命令行窗口(GUI 程序必需) `--name`:指定输出文件名 3. **处理常见问题** - **资源路径问题**: 使用 `sys._MEIPASS` 访问打包后的资源: ```python import sys, os def resource_path(relative_path): if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, relative_path) return os.path.abspath(relative_path) # 使用示例 icon_path = resource_path("icon.ico") ``` - **隐藏终端窗口**: 确保使用 `--windowed` 参数,或在代码开头添加: ```python import sys sys.argv.append("--noconsole") # 仅适用于部分打包工具 ``` 4. **高级配置(可选)** - 添加图标:`--icon=app.ico` - 包含数据文件:`--add-data "src;dest"` #### **完整迁移示例** 假设原 Tkinter 脚本: ```python import tkinter as tk def on_click(): label.config(text="Clicked!") root = tk.Tk() label = tk.Label(root, text="Hello") label.pack() button = tk.Button(root, text="OK", command=on_click) button.pack() root.mainloop() ``` 迁移后的 PyQt6 代码(`main.py`): ```python import sys from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("PyQt6 App") self.setup_ui() def setup_ui(self): layout = QVBoxLayout() self.label = QLabel("Hello") self.button = QPushButton("OK") self.button.clicked.connect(self.on_click) layout.addWidget(self.label) layout.addWidget(self.button) self.setLayout(layout) def on_click(self): self.label.setText("Clicked!") if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) ``` 打包命令: ```bash pyinstaller --onefile --windowed --name MyApp --icon=app.ico main.py ``` ### 注意事项 1. **事件循环差异**: PyQt6 严格禁止在非主线程更新 UI,而 Tkinter 相对宽松。 2. **样式自定义**: PyQt6 支持 CSS 样式表(`widget.setStyleSheet("color: red;")`),比 Tkinter 更灵活。 3. **兼容性测试**: 迁移后需测试 PyQt6 特有的信号/槽、多线程等行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DataLab

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值