壓測工具開發實戰篇(四)——client子窗口功能

在這里插入圖片描述

你好,我是安然無虞。

文章目錄

    • 樹控件
    • 添加文件
      • 補充學習: 函數定義中循環體里的局部變量
      • 補充學習: 動態添加對象屬性
    • 刷新文件
    • 上下文菜單 (右鍵菜單)
      • 實現右鍵菜單功能
    • 編輯節點文本

在這里插入圖片描述

在學習本篇文章之前, 建議先看一下上篇介紹MDI子窗口的文章:
壓測工具開發實戰篇(三)——開發MDI子窗口功能

樹控件

接下來, 我們想在client子窗口功能中添加 展現所選目錄的代碼文件的功能, 可以采用QTreeWidget樹控件, 所展現的樣式類似于下圖:
在這里插入圖片描述

說明: 我們這里暫時只實現展示的內容都是文件的功能, 以子目錄的形式以后實現.

首先我們在 client.ui 子窗口中加入 QTreeWidget 樹控件, 并且將對象名設置為 tree_file, 像這樣:
在這里插入圖片描述
OK, 有了樹控件之后, 該怎么將獲取到的文件顯示到樹控件上呢?

其實很簡單, 只需要像這樣改寫代碼即可:

# client.pyclass Client:def __init__(self):self.ui = uiLoader.load('./ui/client.ui')self.nodeIcon = qta.icon('fa5.user', color='steelBlue')# 在樹控件上顯示文件self.list_file_on_tree()def list_file_on_tree(self):# 清除樹上所有文件self.ui.tree_file.clear()# 這里獲取的文件是完整的文件路徑名pyfiles = glob.glob(os.path.join(SI.projectPath, 'client/*.py'))# 隱藏標頭欄self.ui.tree_file.setHeaderHidden(True)# 獲取樹控件的不可見根結點root = self.ui.tree_file.invisibleRootItem()for pyf in pyfiles:# 獲取完整文件路徑名的基本文件名fname = os.path.basename(pyf)# 準備一個樹節點nodeItem = QTreeWidgetItem()# 設置節點圖標nodeItem.setIcon(0, self.nodeIcon)# 設置該節點的第一個列文本nodeItem.setText(0, fname)# 設置該節點在以前的flag基礎上, 多一個可編輯ItemIsEditablenodeItem.setFlags(nodeItem.flags() | Qt.ItemIsEditable)root.addChild(nodeItem)

這樣之后我們看:
在這里插入圖片描述
符合預期, 這里需要再補充一個功能就是: 在還沒有設置項目目錄的時候, 應該在日志輸出文本框中顯示提示信息, 并且不能打開子窗口, 只有在設置了項目目錄之后才能在點擊側邊按鈕的時候正確打開子窗口顯示對應的數據.

如果要實現上面的功能, 只需要在 main.py 文件中的 打開子窗口方法之前判斷是否已經設置了當前的項目目錄.

# main.pydef load_client_sub_win(self):# 打開子窗口之前需要先判斷是否已經設置了當前的項目目錄if not SI.projectPath:self.logInfo('請先設置項目目錄')return...

這樣實現之后, 我們看:
在這里插入圖片描述
OK, 符合預期, 繼續看接下來的內容.

添加文件

接下來我們在 client.ui 中定義工具欄, 實現 添加客戶端代碼和刷新客戶端代碼的功能:
比如我們點擊添加的動作:
在這里插入圖片描述
會自動幫我們顯示輸入對話框, 并且給出默認的文件名(并且保證文件名沒有重復):

# 設置工具欄條目圖標
self.ui.action_addone.setIcon(qta.icon('fa5.file'))
self.ui.action_refresh.setIcon(qta.icon('mdi.refresh'))
# 定義點擊工具欄action事件處理
self.ui.action_addone.triggered.connect(self.action_addone)
self.ui.action_refresh.triggered.connect(self.list_file_on_tree)

當點擊添加動作時, 會執行 action_addone 信號處理函數:

def action_addone(self):# 獲得合適的初始名字for i in range(1, 1000):filename = f'client_{i}.py'if not os.path.exists(os.path.join(self.thisFolderPath, filename)):breakwhile True:# 輸入對話框 - 返回值分別是輸入數據 和 是否點擊了 OK按鈕filename, okPressed = QInputDialog.getText(self.ui, "請輸入文件名字", "文件名: ",QLineEdit.Normal, filename)filename = filename.strip()if not okPressed:returnfilepath = os.path.join(self.thisFolderPath, filename)if os.path.exists(filepath):QMessageBox.warning(self.ui, "錯誤", f"文件{filepath}已經存在, 請重新輸入!!!")else:break# 直接創建一個新文件open(filepath, 'w', encoding='utf-8')parentItem = self.ui.tree_file.invisibleRootItem()# 準備一個樹節點nodeItem = QTreeWidgetItem()# 保存原始文件名 - 添加樹節點對象的動態屬性nodeItem._original_filename = filenamenodeItem.setIcon(0, self.nodeIcon)nodeItem.setText(0, filename)nodeItem.setFlags(nodeItem.flags() | Qt.ItemIsEditable)# 添加到樹的不可見根結點下面, 成為第一層節點parentItem.addChild(nodeItem)

補充學習: 函數定義中循環體里的局部變量

問題是: 上面的代碼中 for循環里 filename 的作用域, 它為什么可以作為 .getText(self.ui, "請輸入文件名字", "文件名: ",QLineEdit.Normal, filename)中的參數filename, 不是出了for循環的作用域了嗎?

在 for 循環中,filename 是在循環體內部被賦值的, 根據 Python 的作用域規則,如果一個變量在某個代碼塊(如循環體、if 塊等)中被賦值,那么它會被提升到整個函數的作用域中,而不僅僅局限于該代碼塊.

隨意對于整個函數來說:

雖然 filename 是在 for 循環中被首次賦值,但由于它是在函數 action_addone 的作用域內被賦值的,因此它是一個局部變量,可以在整個函數內部被訪問.

補充學習: 動態添加對象屬性

在上面的代碼中, 我們在 創建樹節點的時候, 緊接著就為這個新建的樹節點添加屬性_original_filename, 保留原始文件名, 為了方便后面修改文件名.

我們知道, 本身 QTreeWidgetItem 類沒有定義 _original_filename 屬性, 我們可以像上面那樣直接賦值為其實例添加新的屬性, 但是要注意的是最好在新建節點的時候就加上, 防止后面忘記.

所以代碼就這樣:

# 準備一個樹節點
nodeItem = QTreeWidgetItem()
# 保存原始文件名 - 添加樹節點對象的動態屬性
nodeItem._original_filename = filename

這樣做的好處是:

  • 當用戶點擊某個節點時,你可以通過 item._original_filename 獲取到該節點對應的原始文件名.
  • 在處理文件操作(如打開文件、刪除文件等)時,可以直接使用 _original_filename 屬性來獲取文件名,而不需要再從其他地方查找.

刷新文件

定義刷新信號處理方法就很簡單了, 直接列出樹控件上的所有文件節點即可.

def list_file_on_tree(self):# 清除樹上所有文件self.ui.tree_file.clear()# 這里獲取的文件是完整的文件路徑名pyfiles = glob.glob(os.path.join(SI.projectPath, 'client/*.py'))# 隱藏標頭欄self.ui.tree_file.setHeaderHidden(True)# 獲取樹控件的不可見根結點root = self.ui.tree_file.invisibleRootItem()for pyf in pyfiles:# 獲取完整文件路徑名的基本文件名fname = os.path.basename(pyf)# 樹控件需要通過節點來組織和顯示數據,而不是直接顯示文件路徑# 準備一個樹節點nodeItem = QTreeWidgetItem()# 保存原始文件名 - 添加樹節點對象的動態屬性nodeItem._original_filename = fname# 設置節點圖標nodeItem.setIcon(0, self.nodeIcon)# 設置該節點的第一個列文本nodeItem.setText(0, fname)# 設置該節點在以前的flag基礎上, 多一個可編輯ItemIsEditablenodeItem.setFlags(nodeItem.flags() | Qt.ItemIsEditable)root.addChild(nodeItem)

上下文菜單 (右鍵菜單)

現在我們還需要實現右鍵樹節點時的刪除操作:
在這里插入圖片描述
這就要補充學習上下文菜單 的知識了:

“上下文菜單”指的是在某個特定的上下文(如樹節點)中,通過鼠標右鍵點擊而彈出的菜單. 這種菜單通常包含與當前上下文相關的操作選項,例如在樹形控件中,右鍵點擊某個節點可能會彈出一個菜單,提供對該節點進行操作的選項,如“添加子節點”、“刪除節點”、“重命名節點”等.

實現右鍵菜單功能

1.設置上下文菜單策略:

通過調用setContextMenuPolicy(Qt.CustomContextMenu)方法,將樹形控件的上下文菜單策略設置為自定義模式.

這意味著菜單的顯示和內容將由用戶自己定義,而不是使用默認的系統菜單.

2.連接信號與槽:

通過customContextMenuRequested.connect(self.show_context_menu_onfiletree),將樹形控件的customContextMenuRequested信號連接到一個自定義的槽函數show_context_menu_onfiletree.

當用戶右鍵點擊樹形控件時,會觸發這個信號,進而調用槽函數來顯示自定義的上下文菜單.

# 設置樹控件上下文策略 - 也可以在 Qt Designer 中設置
self.ui.tree_file.setContextMenuPolicy(Qt.CustomContextMenu)
# 定義信號處理方法
self.ui.tree_file.customContextMenuRequested.connect(self.show_context_menu_on_filetree)

我們來實現這個右鍵樹控件時觸發信號調用的槽函數:

def show_context_menu_on_filetree(self, position):"""右鍵樹控件菜單"""tree = self.ui.tree_file# 獲取當前用戶點選的節點curItem = tree.currentItem()# 沒有當前選中節點if not curItem:print('沒有選中節點,返回')return# 創建 上下文菜單 和 菜單項Actionmenu = QMenu(tree)action_delnode = QAction("刪除")action_delnode.triggered.connect(self.action_delnode)menu.addAction(action_delnode)# 在鼠標點擊處展示上下文菜單menu.exec_(tree.mapToGlobal(position))

我們發現觸發上面的刪除動作會執行 action_delnode 方法:

# 右鍵菜單的刪除節點方法 - 注意別忘了刪除電腦中的文件
def action_delnode(self, position):tree = self.ui.tree_file# 獲取當前用戶點選的節點currentItem = tree.currentItem()# 真正的在電腦中把文件刪掉filepath = os.path.join(self.thisFolderPath, currentItem.text(0))try:os.remove(filepath)except:pass# 在Qt界面上把文件刪掉# 找到該節點的父節點parentItem = currentItem.parent()# 如果沒有父節點, 就是不可見的父節點if not parentItem:parentItem = tree.invisibleRootItem()# 刪除該節點parentItem.removeChild(currentItem)

編輯節點文本

如果我們要實現 雙擊樹節點 可以編輯節點文本, 首先需要在創建節點的時候, 設置節點的flag為: 可編輯 ItemIsEditable

# 準備一個樹節點
nodeItem = QTreeWidgetItem()
# 保存原始文件名 - 添加樹節點對象的動態屬性
nodeItem._original_filename = filenamenodeItem.setIcon(0, self.nodeIcon)
nodeItem.setText(0, filename)# 設置該節點在以前的flag基礎上,多一個可編輯 ItemIsEditable
nodeItem.setFlags(nodeItem.flags() | Qt.ItemIsEditable)

樹節點文本被編輯時會觸發ItemChanged信號:

# 樹節點文本被編輯后會觸發ItemChanged信號
self.ui.tree_file.itemChanged.connect(self.item_changed)

對這個信號進行處理:

def item_changed(self, item, column):"""對文件重命名"""new_name = item.text(column)# 這個時候就用到了之前為樹節點添加的屬性_original_filename屬性,保存原文件名original_name = item._original_filenameoriginal_path = os.path.join(self.thisFolderPath, original_name)new_path = os.path.join(self.thisFolderPath, new_name)# 檢查新文件名是否已經存在if os.path.exists(new_path):QMessageBox.warning(self.ui, "錯誤", f"文件{new_path}已經存在, 請重新輸入!!!")# 注意需要將名字改為原來的名字item.setText(column, original_name)else:# 如果文件名合法, 更新原始文件名屬性item._original_filename = new_name# 進行文件重命名操作os.rename(original_path, new_path)
遇見安然遇見你,不負代碼不負卿。
謝謝老鐵的時間,咱們下篇再見!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77061.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77061.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77061.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PyTorch使用(4)-張量拼接操作

文章目錄 張量拼接操作1. torch.cat 函數的使用1.1. torch.cat 定義1.2. 語法1.3. 關鍵規則 1.4. 示例代碼1.4.1. 沿行拼接(dim0)1.4.2. 沿列拼接(dim1)1.4.3. 高維拼接(dim2) 1.5. 錯誤場景分析1.5.1. 維度…

linux命令之yes(Linux Command Yes)

linux命令之yes 簡介與功能 yes 命令在 Linux 系統中用于重復輸出一行字符串,直到被殺死(kill)。該命令最常見的用途是自動化控制腳本中的交互式命令,以便無需用戶介入即可進行連續的確認操作。 用法示例 基本用法非常簡單&am…

《算法筆記》10.3小節——圖算法專題->圖的遍歷 問題 B: 連通圖

題目描述 給定一個無向圖和其中的所有邊&#xff0c;判斷這個圖是否所有頂點都是連通的。 輸入 每組數據的第一行是兩個整數 n 和 m&#xff08;0<n<1000&#xff09;。n 表示圖的頂點數目&#xff0c;m 表示圖中邊的數目。如果 n 為 0 表示輸入結束。隨后有 m 行數據…

使用Prometheus監控systemd服務并可視化

實訓背景 你是一家企業的運維工程師&#xff0c;需將服務器的systemd服務監控集成到Prometheus&#xff0c;并通過Grafana展示實時數據。需求如下&#xff1a; 數據采集&#xff1a;監控所有systemd服務的狀態&#xff08;運行/停止&#xff09;、資源占用&#xff08;CPU、內…

OpenCV--圖像邊緣檢測

在計算機視覺和圖像處理領域&#xff0c;邊緣檢測是極為關鍵的技術。邊緣作為圖像中像素值發生急劇變化的區域&#xff0c;承載了圖像的重要結構信息&#xff0c;在物體識別、圖像分割、目標跟蹤等眾多應用場景中發揮著核心作用。OpenCV 作為強大的計算機視覺庫&#xff0c;提供…

Rollup詳解

Rollup 是一個 JavaScript 模塊打包工具&#xff0c;專注于 ES 模塊的打包&#xff0c;常用于打包 JavaScript 庫。下面從它的工作原理、特點、使用場景、配置和與其他打包工具對比等方面進行詳細講解。 一、 工作原理 Rollup 的核心工作是分析代碼中的 import 和 export 語句…

Chapter 7: Compiling C++ Sources with CMake_《Modern CMake for C++》_Notes

Chapter 7: Compiling C Sources with CMake 1. Understanding the Compilation Process Key Points: Four-stage process: Preprocessing → Compilation → Assembly → LinkingCMake abstracts low-level commands but allows granular controlToolchain configuration (c…

5分鐘上手GitHub Copilot:AI編程助手實戰指南

引言 近年來&#xff0c;AI編程工具逐漸成為開發者提升效率的利器。GitHub Copilot作為由GitHub和OpenAI聯合推出的智能代碼補全工具&#xff0c;能夠根據上下文自動生成代碼片段。本文將手把手教你如何快速安裝、配置Copilot&#xff0c;并通過實際案例展示其強大功能。 一、…

謝志輝和他的《韻之隊詩集》:探尋生活與夢想交織的詩意世界

大家好&#xff0c;我是謝志輝&#xff0c;一個扎根在文字世界&#xff0c;默默耕耘的寫作者。寫作于我而言&#xff0c;早已不是簡單的愛好&#xff0c;而是生命中不可或缺的一部分。無數個寂靜的夜晚&#xff0c;當世界陷入沉睡&#xff0c;我獨自坐在書桌前&#xff0c;伴著…

Logo語言的死鎖

Logo語言的死鎖現象研究 引言 在計算機科學中&#xff0c;死鎖是一個重要的研究課題&#xff0c;尤其是在并發編程中。它指的是兩個或多個進程因爭奪資源而造成的一種永久等待狀態。在編程語言的設計與實現中&#xff0c;如何避免死鎖成為了優化系統性能和提高程序可靠性的關…

深入理解矩陣乘積的導數:以線性回歸損失函數為例

深入理解矩陣乘積的導數&#xff1a;以線性回歸損失函數為例 在機器學習和數據分析領域&#xff0c;矩陣微積分扮演著至關重要的角色。特別是當我們涉及到優化問題&#xff0c;如最小化損失函數時&#xff0c;對矩陣表達式求導變得必不可少。本文將通過一個具體的例子——線性…

real_time_camera_audio_display_with_animation

視頻錄制 import cv2 import pyaudio import wave import threading import os import tkinter as tk from PIL import Image, ImageTk # 視頻錄制設置 VIDEO_WIDTH = 640 VIDEO_HEIGHT = 480 FPS = 20.0 VIDEO_FILENAME = _video.mp4 AUDIO_FILENAME = _audio.wav OUTPUT_…

【Pandas】pandas DataFrame astype

Pandas2.2 DataFrame Conversion 方法描述DataFrame.astype(dtype[, copy, errors])用于將 DataFrame 中的數據轉換為指定的數據類型 pandas.DataFrame.astype pandas.DataFrame.astype 是一個方法&#xff0c;用于將 DataFrame 中的數據轉換為指定的數據類型。這個方法非常…

Johnson

理論 全源最短路算法 Floyd 算法&#xff0c;時間復雜度為 O(n)跑 n 次 Bellman - Ford 算法&#xff0c;時間復雜度是 O(nm)跑 n 次 Heap - Dijkstra 算法&#xff0c;時間復雜度是 O(nmlogm) 第 3 種算法被 Johnson 做了改造&#xff0c;可以求解帶負權邊的全源最短路。 J…

Exce格式化批處理工具詳解:高效處理,讓數據更干凈!

Exce格式化批處理工具詳解&#xff1a;高效處理&#xff0c;讓數據更干凈&#xff01; 1. 概述 在數據分析、報表整理、數據庫管理等工作中&#xff0c;數據清洗是不可或缺的一步。原始Excel數據常常存在格式不統一、空值、重復數據等問題&#xff0c;影響數據的準確性和可用…

(三十七)Dart 中使用 Pub 包管理系統與 HTTP 請求教程

Dart 中使用 Pub 包管理系統與 HTTP 請求教程 Pub 包管理系統簡介 Pub 是 Dart 和 Flutter 的包管理系統&#xff0c;用于管理項目的依賴。通過 Pub&#xff0c;開發者可以輕松地添加、更新和管理第三方庫。 使用 Pub 包管理系統 1. 找到需要的庫 訪問以下網址&#xff0c…

代碼隨想錄算法訓練營第三十五天 | 416.分割等和子集

416. 分割等和子集 題目鏈接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 文章講解&#xff1a;代碼隨想錄 視頻講解&#xff1a;動態規劃之背包問題&#xff0c;這個包能裝滿嗎&#xff1f;| LeetCode&#xff1a;416.分割等和子集_嗶哩嗶哩_bilibi…

HTTP 教程 : 從 0 到 1 全面指南 教程【全文三萬字保姆級詳細講解】

目錄 HTTP 的請求-響應 HTTP 方法 HTTP 狀態碼 HTTP 版本 安全性 HTTP/HTTPS 簡介 HTTP HTTPS HTTP 工作原理 HTTPS 作用 HTTP 與 HTTPS 區別 HTTP 消息結構 客戶端請求消息 服務器響應消息 實例 HTTP 請求方法 各個版本定義的請求方法 HTTP/1.0 HTTP/1.1 …

spring功能匯總

1.創建一個dao接口&#xff0c;實現類&#xff1b;service接口&#xff0c;實現類并且service里用new創建對象方式調用dao的方法 2.使用spring分別獲取dao和service對象(IOC) 注意 2中的service里面獲取dao的對象方式不用new的(DI) 運行測試&#xff1a; 使用1的方式創建servic…

Vue.js 實現下載模板和導入模板、數據比對功能核心實現。

在前端開發中&#xff0c;數據比對是一個常見需求&#xff0c;尤其在資產管理等場景中。本文將基于 Vue.js 和 Element UI&#xff0c;通過一個簡化的代碼示例&#xff0c;展示如何實現“新建比對”和“開始比對”功能的核心部分。 一、功能簡介 我們將聚焦兩個核心功能&…