qt的文件夹路径最长能多少

时间: 2025-08-08 12:12:59 浏览: 3
<think>在Qt中,文件夹路径的最大长度限制主要取决于底层操作系统和文件系统,而不是Qt本身。Qt作为一个跨平台框架,其文件操作功能(如QDir、QFile等)会遵循操作系统的路径长度限制。以下是不同平台的限制情况: 1. **Windows平台**: - 在Windows API中,路径的最大长度通常为$260$个字符(包括终止空字符)[^1]。这被称为`MAX_PATH`限制。 - 从Windows 10版本1607开始,可以通过启用长路径支持来解除此限制(需在注册表或应用程序清单中设置),允许路径长度最多约$32767$个字符[^1]。 - Qt默认使用`MAX_PATH`限制,但如果应用程序启用了长路径支持,则Qt也能处理更长的路径。 2. **Linux/macOS等Unix-like系统**: - 路径长度限制由文件系统决定。例如: - `ext4`文件系统:路径最大长度为$4096$个字符。 - `XFS`文件系统:路径最大长度为$4096$个字符。 - 实际限制可能受内核配置影响,但通常远大于Windows的默认限制。 ### Qt中的注意事项 - **QFile/QDir的兼容性**: Qt的文件操作类(如`QFile::open()`)在内部会调用操作系统API,因此路径长度受上述限制约束。若路径过长,操作将失败并返回错误(如`false`)。 - **跨平台处理建议**: - 避免创建过深的目录结构。 - 在Windows上,可通过`\\?\`前缀访问长路径(需手动添加),例如: ```cpp QString longPath = R"(\\?\D:\very\long\path\...)"; QFile file(longPath); ``` 但此方式需谨慎使用,且路径必须为绝对路径[^1]。 ### 检测路径长度的方法 在Qt中,可通过以下方式检查路径是否超长: ```cpp bool isPathValid(const QString &path) { #ifdef Q_OS_WIN // Windows默认限制260字符(含驱动器符和终止符) const int maxPath = 260; #else const int maxPath = 4096; // 常见Unix-like系统的保守值 #endif return path.length() < maxPath; } ``` > **注意**:实际限制可能因系统配置而异,此方法仅为近似判断。 ### 相关优化 - 使用相对路径缩短字符串长度。 - 在Windows中启用长路径支持(需应用程序和系统共同配置)[^1]。 §§相关问题§§ 1. Qt如何在不同平台处理文件路径的编码差异? 2. 如何在Qt中安全地拼接文件路径以避免长度超限? 3. Qt是否提供检测文件系统路径长度限制的API?</think>在Qt中,**文件夹路径的最大长度限制主要取决于操作系统而非Qt框架本身**。以下是关键信息总结: ### 1. **Windows平台限制** - 默认最大路径长度:$260$个字符(含盘符和终止符) - 可通过注册表或程序清单启用扩展路径支持,将限制提升至约$32767$个字符: ```cpp // 启用长路径的示例(需应用程序清单) <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> </windowsSettings> </application> ``` - Qt的`QDir`和`QFile`在启用长路径支持后可处理超长路径[^1][^3] ### 2. **Linux/macOS平台限制** - 文件系统决定上限(通常远高于Windows): - `ext4`文件系统:$4096$字符 - `XFS`文件系统:$4096$字符 - Qt直接使用系统API,无额外限制[^4] ### 3. **Qt的路径处理特性** - **跨平台差异**:Qt的`QDir`、`QFile`等类会自动适配当前操作系统的路径限制 - **路径拼接建议**:使用`QDir::toNativeSeparators()`避免手动拼接错误: ```cpp QString path = QDir::toNativeSeparators("D:/documents/"+subdir); ``` - **临界检测**:可通过`QFileInfo::exists()`验证路径有效性: ```cpp if(QFileInfo::exists(veryLongPath)) { // 路径有效 } ``` ### 4. **最佳实践** 1. **避免深层嵌套**:减少目录层级深度 2. **使用短路径映射**(Windows): ```cpp QString shortPath = QDir("C:/very/long/path").absolutePath(); ``` 3. **启用长路径支持**(Windows应用程序) 4. **动态检查长度**: ```cpp #ifdef Q_OS_WIN const int MAX_PATH_LEN = 260; // 未启用扩展时 #else const int MAX_PATH_LEN = 4096; #endif if(myPath.length() > MAX_PATH_LEN) { qWarning() << "路径超长!"; } ``` > ⚠️ **注意**:Qt自身不添加额外限制,但超出系统限制的操作(如`QDir::rename()`)会返回`false`[^3][^4]。建议在关键操作前验证路径长度。
阅读全文

相关推荐

import time import pickle import pyperclip import pandas as pd from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains from selenium.common.exceptions import NoSuchElementException, TimeoutException def init_driver(): options = Options() options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--headless=new') options.page_load_strategy = 'eager' service = Service( ChromeDriverManager(path="/drivers/cache", version="115.0.5790.110").install(), quiet=True ) return webdriver.Chrome(service=service, options=options) # 定义cookie保存路径 cookie_path = "/Users/lili/PyCharmMiscProject/测试包/cookies.pkl" driver_path = "/Users/lili/Downloads/chromedriver" #打开chrome浏览器,打开目的浏览器地址 driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.maximize_window() driver = init_driver() driver.get("https://csc-dm-web-cn-v.huolala.cn/bot-manage") try: # 尝试加载已保存的cookies with open(cookie_path, 'rb') as f: cookies = pickle.load(f) # 添加cookies到当前浏览器会话 for cookie in cookies: # 处理可能的域名问题 if 'expiry' in cookie: del cookie['expiry'] driver.add_cookie(cookie) # 重新加载页面使cookie生效 driver.get("https://csc-dm-web-cn-v.huolala.cn/bot-manage") # 验证是否登录成功 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "indenpendentBot")) ) print("已使用保存的登录状态自动登录") except (FileNotFoundError, TimeoutException): # 如果没有保存的cookie或自动登录失败,则进行扫码登录 print("请使用飞书扫码登录...") # 等待扫码登录完成,最多等待60秒 WebDriverWait(driver, 60).until( # 方法1:外层用单引号,内层用双引号(推荐) EC.presence_of_element_located((By.ID, "indenpendentBot")) ) print("登录成功") # 保存登录状态的cookies cookies = driver.get_cookies() with open(cookie_path, 'wb') as f: pickle.dump(cookies, f) print("已保存登录状态,下次可自动登录") #登录成功后,找寻目标元素并点击 try: dropdown_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, '//*[@id="root"]/div[1]/section/div/main/div/div[1]/div/div[2]/div[1]/div/div[1]/div')) ) dropdown_btn.click() print("下拉按钮已点击,选项列表展开") except TimeoutException: print("未找到下拉按钮或无法点击,请检查XPath") try: # 用contains匹配文本 option_by_text = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, '//div[contains(text(), "产研收司机业务线")]')) ) option_by_text.click() print("进入产研收司机业务线页面") except TimeoutException: print("未找到产研收司机业务线") try: print("开始查找id:848的目标并点击") # 等待元素可点击(最长等待15秒,根据页面加载速度调整) target_element = WebDriverWait(driver, 15).until( EC.element_to_be_clickable( (By.XPATH, '//*[@id="botManage"]/div/div[2]/div[10]/div[2]/div[13]/a/div/div[1]/div/div/div/span[2]')) ) # 点击元素进入新页面 target_element.click() print("已找到id为848的bot,点击进入新页面") except TimeoutException: print("未找到id为848的bot,请检查XPath是否正确") #进入到页面后找到目标元素“系统管理”,点击打开下来页面 try: dropdown_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, '//*[@id="root"]/div[1]/section/aside/div/div[2]/ul/li[2]')) ) dropdown_btn.click() print("下拉按钮已点击,选项列表展开") except TimeoutException: print("未找到下拉按钮或无法点击,请检查XPath") #找到测试页面并点击 try: option_by_text = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.XPATH, '//span[contains(text(), "测试页面")]')) ) driver.execute_script("arguments[0].click();", option_by_text) print("已打开测试页面") except TimeoutException: print("未找到测试页面") except Exception as e: print(f"点击元素时发生错误:{str(e)}") #在页面中找到输入框 报错了:import sys; print('Python %s on %s' % (sys.version, sys.platform)) /Users/lili/PyCharmMiscProject/.venv/bin/python -X pycache_prefix=/Users/lili/Library/Caches/JetBrains/PyCharm2025.1/cpython-cache /Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pydev/pydevd.py --multiprocess --qt-support=auto --client 127.0.0.1 --port 51720 --file /Users/lili/PyCharmMiscProject/测试包/练习.py 已连接到 pydev 调试器(内部版本号 251.26927.90)Traceback (most recent call last): File "/Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pydev/pydevd.py", line 1570, in _exec pydev_imports.execfile(file, globals, locals) # execute the script ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "/Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile exec(compile(contents+"\n", file, 'exec'), glob, loc) ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/lili/PyCharmMiscProject/测试包/练习.py", line 38, in <module> driver = init_driver() File "/Users/lili/PyCharmMiscProject/测试包/练习.py", line 25, in init_driver ChromeDriverManager(path="/drivers/cache", version="115.0.5790.110").install(), ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: ChromeDriverManager.__init__() got an unexpected keyword argument 'path' python-BaseException

检查以下代码,提供优化建议: import os import sys import json import time import wave import librosa import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QTextEdit, QFileDialog, QProgressBar, QGroupBox, QTabWidget, QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox ) from PyQt5.QtCore import QThread, pyqtSignal, Qt from pydub import AudioSegment from transformers import pipeline from pyannote.audio import Pipeline import torch import whisper import speech_recognition as sr # 设置环境变量(用户需要替换为自己的Hugging Face Token) os.environ['HUGGINGFACE_TOKEN'] = 'your_hugging_face_token_here' # 模型路径配置 MODEL_DIR = "./models" WHISPER_MODEL_PATH = os.path.join(MODEL_DIR, "whisper-small") SENTIMENT_MODEL_PATH = os.path.join(MODEL_DIR, "Erlangshen-Roberta-110M-Sentiment") PYANNOTE_MODEL_PATH = os.path.join(MODEL_DIR, "pyannote-speaker-diarization-3.0") # 确保模型目录存在 os.makedirs(MODEL_DIR, exist_ok=True) class AnalysisWorker(QThread): """后台分析线程""" progress_updated = pyqtSignal(int, str) analysis_completed = pyqtSignal(dict) error_occurred = pyqtSignal(str) def __init__(self, audio_files, keyword_file): super().__init__() self.audio_files = audio_files self.keyword_file = keyword_file self.keywords = {} self.running = True def run(self): try: # 加载关键词 self.load_keywords() # 加载模型 self.progress_updated.emit(5, "加载模型中...") self.load_models() results = [] total_files = len(self.audio_files) for idx, audio_file in enumerate(self.audio_files): if not self.running: return file_name = os.path.basename(audio_file) self.progress_updated.emit(int((idx/total_files)*90 + 5), f"分析文件: {file_name}") # 分析单个文件 result = self.analyze_audio(audio_file) results.append(result) # 生成报告 self.progress_updated.emit(95, "生成分析报告...") report = self.generate_report(results) # 完成 self.progress_updated.emit(100, "分析完成!") self.analysis_completed.emit(report) except Exception as e: self.error_occurred.emit(str(e)) def load_keywords(self): """从Excel文件加载关键词""" try: df = pd.read_excel(self.keyword_file) self.keywords = { "opening": df["开场白关键词"].dropna().tolist(), "closing": df["结束语关键词"].dropna().tolist(), "forbidden": df["服务禁语"].dropna().tolist() } self.progress_updated.emit(2, f"加载关键词: {len(self.keywords['opening'])}个开场白, " f"{len(self.keywords['closing'])}个结束语, " f"{len(self.keywords['forbidden'])}个禁语") except Exception as e: raise Exception(f"加载关键词文件失败: {str(e)}") def load_models(self): """加载所有需要的模型""" # 加载语音识别模型 self.whisper_model = whisper.load_model("small", download_root=MODEL_DIR) # 加载情感分析模型 self.sentiment_model = pipeline( "text-classification", model=SENTIMENT_MODEL_PATH, tokenizer=SENTIMENT_MODEL_PATH ) # 加载说话人分离模型 self.pyannote_pipeline = Pipeline.from_pretrained( "pyannote/speaker-diarization-3.0", use_auth_token=os.environ['HUGGINGFACE_TOKEN'] ) def convert_audio_format(self, audio_file): """将音频转换为WAV格式(如果需要)""" if not audio_file.lower().endswith('.wav'): output_file = os.path.splitext(audio_file)[0] + '.wav' audio = AudioSegment.from_file(audio_file) audio.export(output_file, format="wav") return output_file return audio_file def get_audio_duration(self, audio_file): """获取音频时长""" with wave.open(audio_file, 'r') as wav_file: frames = wav_file.getnframes() rate = wav_file.getframerate() duration = frames / float(rate) return duration def analyze_audio(self, audio_file): """分析单个音频文件""" result = { "file_name": os.path.basename(audio_file), "duration": 0, "opening_found": False, "closing_found": False, "forbidden_found": False, "agent_sentiment": [], "customer_sentiment": [], "agent_speech_rate": 0, "agent_clarity": 0, "agent_volume": 0, "problem_solved": False } try: # 转换音频格式 wav_file = self.convert_audio_format(audio_file) # 获取音频时长 result["duration"] = self.get_audio_duration(wav_file) # 说话人分离 diarization = self.pyannote_pipeline(wav_file) # 识别第一个说话人(默认客服) agent_segments = [] customer_segments = [] for turn, _, speaker in diarization.itertracks(yield_label=True): # 假设第一个说话人是客服 if speaker == "SPEAKER_00": agent_segments.append((turn.start, turn.end)) else: customer_segments.append((turn.start, turn.end)) # 如果没有识别到说话人,使用默认分割 if not agent_segments and not customer_segments: mid = result["duration"] / 2 agent_segments = [(0, mid)] customer_segments = [(mid, result["duration"])] # 识别语音转文本 recognizer = sr.Recognizer() with sr.AudioFile(wav_file) as source: audio_data = recognizer.record(source) # 识别客服语音 agent_text = "" for start, end in agent_segments: segment = audio_data.get_segment(start*1000, end*1000) try: text = recognizer.recognize_google(segment, language="zh-CN") agent_text += text + " " except: pass # 识别客户语音 customer_text = "" for start, end in customer_segments: segment = audio_data.get_segment(start*1000, end*1000) try: text = recognizer.recognize_google(segment, language="zh-CN") customer_text += text + " " except: pass # 关键词检查 result["opening_found"] = any(keyword in agent_text for keyword in self.keywords["opening"]) result["closing_found"] = any(keyword in agent_text for keyword in self.keywords["closing"]) result["forbidden_found"] = any(keyword in agent_text for keyword in self.keywords["forbidden"]) # 情感分析 if agent_text: agent_sentiments = self.sentiment_model(agent_text, top_k=None) result["agent_sentiment"] = [{"label": s["label"], "score": s["score"]} for s in agent_sentiments] if customer_text: customer_sentiments = self.sentiment_model(customer_text, top_k=None) result["customer_sentiment"] = [{"label": s["label"], "score": s["score"]} for s in customer_sentiments] # 客服语音分析 y, sr = librosa.load(wav_file) agent_audio = [] for start, end in agent_segments: start_sample = int(start * sr) end_sample = int(end * sr) agent_audio.extend(y[start_sample:end_sample]) if agent_audio: agent_audio = np.array(agent_audio) # 计算语速(音节/秒) syllables = len(agent_text) / 2 # 简单估算 speaking_time = sum(end - start for start, end in agent_segments) result["agent_speech_rate"] = syllables / speaking_time if speaking_time > 0 else 0 # 计算清晰度(频谱质心) spectral_centroids = librosa.feature.spectral_centroid(y=agent_audio, sr=sr)[0] result["agent_clarity"] = np.mean(spectral_centroids) # 计算平均音量 result["agent_volume"] = np.mean(librosa.amplitude_to_db(np.abs(agent_audio))) # 问题解决率(简化逻辑) solution_keywords = ["解决", "完成", "处理", "安排", "办理"] result["problem_solved"] = any(keyword in agent_text for keyword in solution_keywords) # 清理临时文件 if wav_file != audio_file: os.remove(wav_file) return result except Exception as e: self.error_occurred.emit(f"分析文件 {audio_file} 时出错: {str(e)}") return None def generate_report(self, results): """生成分析报告""" # 过滤掉失败的分析 valid_results = [r for r in results if r is not None] if not valid_results: return {"excel_path": "", "chart_path": ""} # 创建报告目录 report_dir = os.path.join(os.getcwd(), "质检报告") os.makedirs(report_dir, exist_ok=True) timestamp = time.strftime("%Y%m%d%H%M%S") # 生成Excel报告 excel_path = os.path.join(report_dir, f"质检报告_{timestamp}.xlsx") self.generate_excel_report(valid_results, excel_path) # 生成图表报告 chart_path = os.path.join(report_dir, f"图表报告_{timestamp}.png") self.generate_chart_report(valid_results, chart_path) return { "excel_path": excel_path, "chart_path": chart_path, "results": valid_results } def generate_excel_report(self, results, output_path): """生成Excel格式的报告""" df = pd.DataFrame(results) # 处理情感分析数据 df['客服情感'] = df['agent_sentiment'].apply( lambda x: max(x, key=lambda s: s['score'])['label'] if x else '未知' ) df['客户情感'] = df['customer_sentiment'].apply( lambda x: max(x, key=lambda s: s['score'])['label'] if x else '未知' ) # 选择需要的列 report_df = df[[ "file_name", "duration", "opening_found", "closing_found", "forbidden_found", "客服情感", "客户情感", "agent_speech_rate", "agent_clarity", "agent_volume", "problem_solved" ]] # 重命名列 report_df.columns = [ "文件名", "通话时长(秒)", "开场白合规", "结束语合规", "使用禁语", "客服情感", "客户情感", "客服语速(音节/秒)", "客服清晰度", "客服平均音量(dB)", "问题解决" ] # 保存Excel report_df.to_excel(output_path, index=False) def generate_chart_report(self, results, output_path): """生成图表报告""" # 创建图表 fig, axes = plt.subplots(3, 2, figsize=(15, 15)) fig.suptitle('外呼录音质检分析报告', fontsize=16) # 1. 开场白合规率 opening_compliance = sum(1 for r in results if r['opening_found']) / len(results) * 100 axes[0, 0].bar(['合规', '不合规'], [opening_compliance, 100-opening_compliance], color=['green', 'red']) axes[0, 0].set_title('开场白合规率') axes[0, 0].set_ylabel('百分比(%)') # 2. 客服情感分布 agent_sentiments = [max(r['agent_sentiment'], key=lambda s: s['score'])['label'] for r in results if r['agent_sentiment']] sentiment_counts = pd.Series(agent_sentiments).value_counts() axes[0, 1].pie(sentiment_counts, labels=sentiment_counts.index, autopct='%1.1f%%') axes[0, 1].set_title('客服情感分布') # 3. 客户情感分布 customer_sentiments = [max(r['customer_sentiment'], key=lambda s: s['score'])['label'] for r in results if r['customer_sentiment']] sentiment_counts = pd.Series(customer_sentiments).value_counts() axes[1, 0].pie(sentiment_counts, labels=sentiment_counts.index, autopct='%1.1f%%') axes[1, 0].set_title('客户情感分布') # 4. 客服语速分布 speech_rates = [r['agent_speech_rate'] for r in results if r['agent_speech_rate'] > 0] axes[1, 1].hist(speech_rates, bins=10, color='skyblue', edgecolor='black') axes[1, 1].set_title('客服语速分布') axes[1, 1].set_xlabel('语速(音节/秒)') axes[1, 1].set_ylabel('频次') # 5. 问题解决率 problem_solved = sum(1 for r in results if r['problem_solved']) / len(results) * 100 axes[2, 0].bar(['已解决', '未解决'], [problem_solved, 100-problem_solved], color=['blue', 'orange']) axes[2, 0].set_title('问题解决率') axes[2, 0].set_ylabel('百分比(%)') # 6. 使用禁语情况 forbidden_found = sum(1 for r in results if r['forbidden_found']) axes[2, 1].bar(['使用禁语', '未使用禁语'], [forbidden_found, len(results)-forbidden_found], color=['red', 'green']) axes[2, 1].set_title('禁语使用情况') axes[2, 1].set_ylabel('数量') plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.savefig(output_path) plt.close() def stop(self): """停止分析""" self.running = False class CallQualityApp(QMainWindow): """主应用程序窗口""" def __init__(self): super().__init__() self.setWindowTitle("外呼电话录音质检分析系统") self.setGeometry(100, 100, 1000, 700) # 初始化变量 self.audio_files = [] self.keyword_file = "" self.worker = None # 创建UI self.init_ui() def init_ui(self): """初始化用户界面""" main_widget = QWidget() main_layout = QVBoxLayout() # 创建标签页 tab_widget = QTabWidget() main_layout.addWidget(tab_widget) # 分析页面 analysis_tab = QWidget() tab_widget.addTab(analysis_tab, "分析") analysis_layout = QVBoxLayout(analysis_tab) # 文件选择区域 file_group = QGroupBox("文件选择") file_layout = QVBoxLayout() # 音频文件选择 audio_layout = QHBoxLayout() self.audio_label = QLabel("未选择音频文件") audio_btn = QPushButton("选择音频文件/文件夹") audio_btn.clicked.connect(self.select_audio) audio_layout.addWidget(audio_btn) audio_layout.addWidget(self.audio_label) file_layout.addLayout(audio_layout) # 关键词文件选择 keyword_layout = QHBoxLayout() self.keyword_label = QLabel("未选择关键词文件") keyword_btn = QPushButton("选择关键词文件") keyword_btn.clicked.connect(self.select_keyword_file) keyword_layout.addWidget(keyword_btn) keyword_layout.addWidget(self.keyword_label) file_layout.addLayout(keyword_layout) file_group.setLayout(file_layout) analysis_layout.addWidget(file_group) # 按钮区域 btn_layout = QHBoxLayout() self.start_btn = QPushButton("开始分析") self.start_btn.clicked.connect(self.start_analysis) self.stop_btn = QPushButton("停止分析") self.stop_btn.clicked.connect(self.stop_analysis) self.stop_btn.setEnabled(False) self.clear_btn = QPushButton("清空") self.clear_btn.clicked.connect(self.clear_all) btn_layout.addWidget(self.start_btn) btn_layout.addWidget(self.stop_btn) btn_layout.addWidget(self.clear_btn) analysis_layout.addLayout(btn_layout) # 进度条 self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_label = QLabel("准备就绪") analysis_layout.addWidget(self.progress_bar) analysis_layout.addWidget(self.progress_label) # 输出区域 output_group = QGroupBox("输出信息") output_layout = QVBoxLayout() self.output_text = QTextEdit() self.output_text.setReadOnly(True) output_layout.addWidget(self.output_text) output_group.setLayout(output_layout) analysis_layout.addWidget(output_group) # 结果页面 result_tab = QWidget() tab_widget.addTab(result_tab, "结果") result_layout = QVBoxLayout(result_tab) # 结果表格 self.result_table = QTableWidget() self.result_table.setColumnCount(11) self.result_table.setHorizontalHeaderLabels([ "文件名", "通话时长(秒)", "开场白合规", "结束语合规", "使用禁语", "客服情感", "客户情感", "客服语速", "客服清晰度", "客服音量", "问题解决" ]) self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) result_layout.addWidget(self.result_table) # 图表区域 self.chart_label = QLabel() self.chart_label.setAlignment(Qt.AlignCenter) result_layout.addWidget(self.chart_label) main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) def select_audio(self): """选择音频文件或文件夹""" options = QFileDialog.Options() files, _ = QFileDialog.getOpenFileNames( self, "选择音频文件", "", "音频文件 (*.mp3 *.wav *.amr *.m4a);;所有文件 (*)", options=options ) if files: self.audio_files = files self.audio_label.setText(f"已选择 {len(files)} 个音频文件") self.output_text.append(f"已选择 {len(files)} 个音频文件") def select_keyword_file(self): """选择关键词文件""" options = QFileDialog.Options() file, _ = QFileDialog.getOpenFileName( self, "选择关键词文件", "", "Excel文件 (*.xlsx *.xls);;所有文件 (*)", options=options ) if file: self.keyword_file = file self.keyword_label.setText(os.path.basename(file)) self.output_text.append(f"已选择关键词文件: {os.path.basename(file)}") def start_analysis(self): """开始分析""" if not self.audio_files: QMessageBox.warning(self, "警告", "请先选择音频文件!") return if not self.keyword_file: QMessageBox.warning(self, "警告", "请先选择关键词文件!") return # 更新UI状态 self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.output_text.append("开始分析...") # 创建并启动工作线程 self.worker = AnalysisWorker(self.audio_files, self.keyword_file) self.worker.progress_updated.connect(self.update_progress) self.worker.analysis_completed.connect(self.analysis_finished) self.worker.error_occurred.connect(self.handle_error) self.worker.start() def stop_analysis(self): """停止分析""" if self.worker: self.worker.stop() self.worker = None self.output_text.append("分析已停止") self.progress_label.setText("分析已停止") self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def clear_all(self): """清空所有内容""" self.audio_files = [] self.keyword_file = "" self.audio_label.setText("未选择音频文件") self.keyword_label.setText("未选择关键词文件") self.output_text.clear() self.progress_bar.setValue(0) self.progress_label.setText("准备就绪") self.result_table.setRowCount(0) self.chart_label.clear() def update_progress(self, value, message): """更新进度""" self.progress_bar.setValue(value) self.progress_label.setText(message) self.output_text.append(message) def analysis_finished(self, report): """分析完成""" self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) # 显示结果 self.output_text.append(f"分析完成! 报告已保存到: {report['excel_path']}") self.output_text.append(f"图表已保存到: {report['chart_path']}") # 在结果表格中显示数据 self.show_results(report['results']) # 显示图表 self.show_chart(report['chart_path']) def handle_error(self, error_message): """处理错误""" self.output_text.append(f"错误: {error_message}") self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) QMessageBox.critical(self, "错误", error_message) def show_results(self, results): """在表格中显示结果""" self.result_table.setRowCount(len(results)) for row, result in enumerate(results): # 获取客服主要情感 agent_sentiment = max(result['agent_sentiment'], key=lambda s: s['score'])['label'] if result['agent_sentiment'] else "未知" customer_sentiment = max(result['customer_sentiment'], key=lambda s: s['score'])['label'] if result['customer_sentiment'] else "未知" # 填充表格数据 self.result_table.setItem(row, 0, QTableWidgetItem(result['file_name'])) self.result_table.setItem(row, 1, QTableWidgetItem(f"{result['duration']:.2f}")) self.result_table.setItem(row, 2, QTableWidgetItem("是" if result['opening_found'] else "否")) self.result_table.setItem(row, 3, QTableWidgetItem("是" if result['closing_found'] else "否")) self.result_table.setItem(row, 4, QTableWidgetItem("是" if result['forbidden_found'] else "否")) self.result_table.setItem(row, 5, QTableWidgetItem(agent_sentiment)) self.result_table.setItem(row, 6, QTableWidgetItem(customer_sentiment)) self.result_table.setItem(row, 7, QTableWidgetItem(f"{result['agent_speech_rate']:.2f}")) self.result_table.setItem(row, 8, QTableWidgetItem(f"{result['agent_clarity']:.2f}")) self.result_table.setItem(row, 9, QTableWidgetItem(f"{result['agent_volume']:.2f}")) self.result_table.setItem(row, 10, QTableWidgetItem("是" if result['problem_solved'] else "否")) def show_chart(self, chart_path): """显示图表""" if os.path.exists(chart_path): pixmap = plt.imread(chart_path) # 这里简化处理,实际应用中可能需要调整图像大小 self.chart_label.setPixmap(pixmap) else: self.output_text.append(f"未找到图表文件: {chart_path}") if __name__ == "__main__": # 检查环境变量 if 'HUGGINGFACE_TOKEN' not in os.environ or os.environ['HUGGINGFACE_TOKEN'] == 'your_hugging_face_token_here': print("请设置有效的Hugging Face Token!") print("1. 访问 https://huggingface.co/settings/tokens 获取Token") print("2. 在代码中设置环境变量") sys.exit(1) app = QApplication(sys.argv) window = CallQualityApp() window.show() sys.exit(app.exec_())

zip

大家在看

recommend-type

金蝶EAS通过套打模板实现后台生成PDF文件.docx

在EAS开发中,如果需要合同调用套打模板自动生成PDF进行档案归档备份,可通过后台服务器代码进行开发实现;
recommend-type

复盛压缩机选型软件.rar )

此款为官方专用,简单的压缩机可以选择。SRL型的没有,暂时不能使用请谨慎选择
recommend-type

基于边折叠的网格快速简化

Fast mesh simplification via edge collapsing This project contains an implementation of a "multiple choice" mesh simplfication algorithm. Over a number of iterations a random fraction of the total edges in the supplied mesh are processed with a subset of these processed edges collapsed (the lowest scoring collapses win when a collision occurs). The only non-standard dependency is the qef_simd.h single file header which you can find in my "qef" project, a version is also included here.
recommend-type

20201107-为rvv-llvm添加一个intrinsic-廖春玉1

3. multiclass signed_binary_v_vv_vx { 4. multiclass Binary<string name,/ string
recommend-type

一种低噪声便携式的心电监测仪设计

便携式监护仪小型方便,结构简单,性能稳定,可以随身携带,可由电池供电,一般用于非监护室及外出抢救病人的监护。心血管疾病是人类生命的最主要威胁之一,而心电(Electrocardiogram,ECG信号是诊断心血管疾病的主要依据,因此实时监测病人心电活动,设计自动采集病人心电信号的便携式系统具有重要意义。本文为人体日常生活方便,设计了导联电极脱落检测电路,防止运动输入电极脱落。

最新推荐

recommend-type

有限公司成立合作协议书.doc

有限公司成立合作协议书.doc
recommend-type

mpu9650STM32f103c8t6例程

mpu9650STM32f103c8t6例程
recommend-type

1754823429242.jpeg

1754823429242.jpeg
recommend-type

工业自动化领域欧姆龙Sysmac Studio NJ101-1000与R88D-KN01H伺服控制系统实现

欧姆龙Sysmac Studio环境下NJ101-1000控制器与R88D-KN01H伺服电机的编程实现方法。首先概述了硬件特点及其应用场景,接着深入探讨了输入信号(如使能输入、点动控制、回原点模式)、输出信号(如使能状态、故障信息、速度和位置反馈)的具体配置方式。文中还重点讲解了如何利用Sysmac Studio提供的打包块功能简化编程流程,确保各模式间的互斥性和安全性,并给出了简单示例代码展示点动正转功能的实现。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些希望深入了解欧姆龙伺服系统编程的人士。 使用场景及目标:适用于需要对NJ101-1000和R88D-KN01H系列伺服进行精准控制的应用场合,如生产线自动化、机器人运动控制等。目的是帮助读者掌握伺服系统的配置技巧,提高工作效率和产品质量。 其他说明:文中强调了编程过程中需要注意的关键点,比如不同模式之间的切换逻辑、速度设定的选择依据等,有助于读者更好地理解和应用相关技术。同时提醒读者,在实际项目中应根据具体需求灵活调整参数设置。
recommend-type

已解密-技术服务协议.docx

已解密-技术服务协议.docx
recommend-type

Python程序TXLWizard生成TXL文件及转换工具介绍

### 知识点详细说明: #### 1. 图形旋转与TXL向导 图形旋转是图形学领域的一个基本操作,用于改变图形的方向。在本上下文中,TXL向导(TXLWizard)是由Esteban Marin编写的Python程序,它实现了特定的图形旋转功能,主要用于电子束光刻掩模的生成。光刻掩模是半导体制造过程中非常关键的一个环节,它确定了在硅片上沉积材料的精确位置。TXL向导通过生成特定格式的TXL文件来辅助这一过程。 #### 2. TXL文件格式与用途 TXL文件格式是一种基于文本的文件格式,它设计得易于使用,并且可以通过各种脚本语言如Python和Matlab生成。这种格式通常用于电子束光刻中,因为它的文本形式使得它可以通过编程快速创建复杂的掩模设计。TXL文件格式支持引用对象和复制对象数组(如SREF和AREF),这些特性可以用于优化电子束光刻设备的性能。 #### 3. TXLWizard的特性与优势 - **结构化的Python脚本:** TXLWizard 使用结构良好的脚本来创建遮罩,这有助于开发者创建清晰、易于维护的代码。 - **灵活的Python脚本:** 作为Python程序,TXLWizard 可以利用Python语言的灵活性和强大的库集合来编写复杂的掩模生成逻辑。 - **可读性和可重用性:** 生成的掩码代码易于阅读,开发者可以轻松地重用和修改以适应不同的需求。 - **自动标签生成:** TXLWizard 还包括自动为图形对象生成标签的功能,这在管理复杂图形时非常有用。 #### 4. TXL转换器的功能 - **查看.TXL文件:** TXL转换器(TXLConverter)允许用户将TXL文件转换成HTML或SVG格式,这样用户就可以使用任何现代浏览器或矢量图形应用程序来查看文件。 - **缩放和平移:** 转换后的文件支持缩放和平移功能,这使得用户在图形界面中更容易查看细节和整体结构。 - **快速转换:** TXL转换器还提供快速的文件转换功能,以实现有效的蒙版开发工作流程。 #### 5. 应用场景与技术参考 TXLWizard的应用场景主要集中在电子束光刻技术中,特别是用于设计和制作半导体器件时所需的掩模。TXLWizard作为一个向导,不仅提供了生成TXL文件的基础框架,还提供了一种方式来优化掩模设计,提高光刻过程的效率和精度。对于需要进行光刻掩模设计的工程师和研究人员来说,TXLWizard提供了一种有效的方法来实现他们的设计目标。 #### 6. 系统开源特性 标签“系统开源”表明TXLWizard遵循开放源代码的原则,这意味着源代码对所有人开放,允许用户自由地查看、修改和分发软件。开源项目通常拥有活跃的社区,社区成员可以合作改进软件,添加新功能,或帮助解决遇到的问题。这种开放性促进了技术创新,并允许用户根据自己的需求定制软件。 #### 7. 压缩包子文件的文件名称列表 文件名称列表中的“txlwizard-master”可能指的是TXLWizard项目的主版本库或主分支。这个名称表明了这是项目源代码的中心点,其他开发者会从这个主分支拉取代码进行合作开发或部署。以“-master”结尾通常是版本控制系统中表示主要开发线路的常见约定,例如Git中的master(现在更常被称为main)分支。 通过这些知识点的详细解释,我们可以看到TXLWizard不仅是一个用于生成TXL文件的工具,它还整合了一系列的功能,使得电子束光刻掩模的设计工作更为高效和直观。同时,作为一个开源项目,它能够借助社区的力量不断进步,为用户带来更多的便利和创新。
recommend-type

【创新图生成:扣子平台的技术前沿与创新思维】:引领图像生成技术的新潮流

# 1. 扣子平台的图像生成技术概述 在当今数字化时代,图像生成技术正成为推动技术创新和社会变革的关键力量。扣子平台正是这一技术革新的代表。本章将对扣子平台的图像生成技术进行一个全面概述,探究它如何将理论技术转化为实际应用,并带来显著的社会影响。 扣子平台运用了先进的图像生成技术,将现实世界和数字空间无缝融合,打造了沉浸式的视觉体验。其技术的核心是通过高度复杂的算法模型,快速生成高质
recommend-type

海康威视机器视觉工程师考核

<think>好的,我现在需要帮助用户了解海康威视机器视觉工程师的考核内容和要求。根据用户提供的引用资料,特别是引用[1]和[2],里面提到了考核素材包分为初级和中级,涵盖理论、算法、应用案例等。首先,我要整理这些信息,确保结构清晰,符合用户要求的格式。 接下来,我需要确认素材包的具体内容,比如初级和中级的不同点。引用[2]提到初级包含基础理论、算法实现和实际案例,中级则增加复杂算法和项目分析。这部分需要分点说明,方便用户理解层次。 另外,用户可能想知道如何准备考核,比如下载素材、学习顺序、模拟考核等,引用[2]中有使用说明和注意事项,这部分也要涵盖进去。同时要注意提醒用户考核窗口已关闭,
recommend-type

Linux环境下Docker Hub公共容器映像检测工具集

在给出的知识点中,我们需要详细解释有关Docker Hub、公共容器映像、容器编排器以及如何与这些工具交互的详细信息。同时,我们会涵盖Linux系统下的相关操作和工具使用,以及如何在ECS和Kubernetes等容器编排工具中运用这些检测工具。 ### Docker Hub 和公共容器映像 Docker Hub是Docker公司提供的一项服务,它允许用户存储、管理以及分享Docker镜像。Docker镜像可以视为应用程序或服务的“快照”,包含了运行特定软件所需的所有必要文件和配置。公共容器映像指的是那些被标记为公开可见的Docker镜像,任何用户都可以拉取并使用这些镜像。 ### 静态和动态标识工具 静态和动态标识工具在Docker Hub上用于识别和分析公共容器映像。静态标识通常指的是在不运行镜像的情况下分析镜像的元数据和内容,例如检查Dockerfile中的指令、环境变量、端口映射等。动态标识则需要在容器运行时对容器的行为和性能进行监控和分析,如资源使用率、网络通信等。 ### 容器编排器与Docker映像 容器编排器是用于自动化容器部署、管理和扩展的工具。在Docker环境中,容器编排器能够自动化地启动、停止以及管理容器的生命周期。常见的容器编排器包括ECS和Kubernetes。 - **ECS (Elastic Container Service)**:是由亚马逊提供的容器编排服务,支持Docker容器,并提供了一种简单的方式来运行、停止以及管理容器化应用程序。 - **Kubernetes**:是一个开源平台,用于自动化容器化应用程序的部署、扩展和操作。它已经成为容器编排领域的事实标准。 ### 如何使用静态和动态标识工具 要使用这些静态和动态标识工具,首先需要获取并安装它们。从给定信息中了解到,可以通过克隆仓库或下载压缩包并解压到本地系统中。之后,根据需要针对不同的容器编排环境(如Dockerfile、ECS、Kubernetes)编写配置,以集成和使用这些检测工具。 ### Dockerfile中的工具使用 在Dockerfile中使用工具意味着将检测工具的指令嵌入到构建过程中。这可能包括安装检测工具的命令、运行容器扫描的步骤,以及将扫描结果集成到镜像构建流程中,确保只有通过安全和合规检查的容器镜像才能被构建和部署。 ### ECS与Kubernetes中的工具集成 在ECS或Kubernetes环境中,工具的集成可能涉及到创建特定的配置文件、定义服务和部署策略,以及编写脚本或控制器来自动执行检测任务。这样可以在容器编排的过程中实现实时监控,确保容器编排器只使用符合预期的、安全的容器镜像。 ### Linux系统下的操作 在Linux系统下操作这些工具,用户可能需要具备一定的系统管理和配置能力。这包括使用Linux命令行工具、管理文件系统权限、配置网络以及安装和配置软件包等。 ### 总结 综上所述,Docker Hub上的静态和动态标识工具提供了一种方法来检测和分析公共容器映像,确保这些镜像的安全性和可靠性。这些工具在Linux开发环境中尤为重要,因为它们帮助开发人员和运维人员确保他们的容器映像满足安全要求。通过在Dockerfile、ECS和Kubernetes中正确使用这些工具,可以提高应用程序的安全性,减少由于使用不安全的容器镜像带来的风险。此外,掌握Linux系统下的操作技能,可以更好地管理和维护这些工具,确保它们能够有效地发挥作用。
recommend-type

【扣子平台图像艺术探究:理论与实践的完美结合】:深入学习图像生成的艺术

# 1. 图像艺术的理论基础 艺术领域的每一个流派和技巧都有其理论基础。在图像艺术中,理论基础不仅是对艺术表现形式的认知,也是掌握艺术创作内在逻辑的关键。深入理解图像艺术的理论基础,能够帮助艺术家们在创作过程中更加明确地表达自己的艺术意图,以及更好地与观众沟通。 图像艺术的理论