热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用wxPython创建“目录树”(5)

1.使用wxPython创建“目录树”1.1.实现过程1.1.1.设置树的样式1.1.2.设置图标1.1.3.获取当前工作区的所有文件,并创建结点1.1.4.目录最小、最大化1.1


  • 1. 使用 wxPython 创建“目录树”

    • 1.1. 实现过程

      • 1.1.1. 设置树的样式

      • 1.1.2. 设置图标

      • 1.1.3. 获取当前工作区的所有文件,并创建结点

      • 1.1.4. 目录最小、最大化

      • 1.1.5. 实现右键菜单操作

      • 1.1.6. 及时地更新目录树内容

      • 1.1.7. 对结点进行排序



    • 1.2. 完整代码

      • 1.2.1. 目录树

      • 1.2.2. 测试类

      • 1.2.3. 相关图标



    • 1.3. 相关参考





wxPython 自带的 wx.GenericDirCtrl 可以实现目录树的效果,但是同 vscode 的目录树对比,是丑了一些,而且虽然指定了目录,但是仍把磁盘翻了个遍。

# 使用 wx.GenericDirCtrl 控件
import wx
import os
class TestFrame(wx.Frame):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.dir_tree = wx.GenericDirCtrl(self, -1, dir=os.getcwd())
if __name__ == '__main__':
app = wx.App()
frm = TestFrame(None)
frm.Show()
app.MainLoop()














GenericDirCtrlvscode
)


这里使用 wx.TreeCtrl 里来设计出类似 vscode 的目录树,先看下最终的效果图:


1.1. 实现过程


1.1.1. 设置树的样式

默认的 TreeCtrl 控件的样式和上图的 GenericDirCtrl 是一样的,这里我们需要这种样式:

self.tree = wx.TreeCtrl(self, -1,
style = wx.TR_DEFAULT_STYLE # 默认样式
|wx.TR_TWIST_BUTTONS # 结点使用 >/v 而不是 +/-
|wx.TR_NO_LINES # 不绘制结点之间的连线
)

1.1.2. 设置图标

结点的图标都是得自己设置的(调用 tree.SetItemImage()),否则默认是没有图标的。

另外,文件夹与文件之间的图标不同,不同扩展名的文件图标也不同。

这里用 Images.py 保存图标的数据,如获取名为 “file_type_python” 的图标的位图为

bmp = Images.file_type_python.GetBitmap()
# 下面是等价的
# bmp = getattr(Iamges, "file_type_python").GetBitmap()

Ext2IconDict.py 中包含一个字典 IconMap.Ext2IconDict,字典的 key 代表的是文件的扩展名,如 “png,py” 等,字典的 value 代表着“相应图标的名字”,如 "file_type_python",

Ext2IcOnDict= {
# ...
"py" : "file_type_python",
# ...
}

这样就可以由扩展名来获取相应图标的位图了。关于如何生成 Images.py 可以参考 使用 wx.tools.img2py (4)。

bmp = getattr(Iamges, IconMap.Ext2IconDict['py']).GetBitmap()

1.1.3. 获取当前工作区的所有文件,并创建结点

参考下面代码的 self.InitTree(),一般都是使用递归的思路。这一部分主要参考了 wxPython in action Chapter 15 的相关内容。


1.1.4. 目录最小、最大化

使用 AuiManager 可以实现子窗口的最大化、最小化等操作。

import wx
import wx.lib.agw.aui as aui
class TestFrame(wx.Frame):

def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.SetTitle("目录树")
self.SetSize(800, 600)
self.dir_tree = DirectoryTree(self, size=(200, 600))
self.txt = wx.TextCtrl(self, -1, value="I'm 002",workspace").
Left().Layer(1).Position(1).CloseButton(True).MaximizeButton(True).MinimizeButton(True))
self._mgr.AddPane(self.txt, aui.AuiPaneInfo().CenterPane())

# 记得要 Update
self._mgr.Update()

目录树的最小化、最大化如下图所示:

min_max_tree


1.1.5. 实现右键菜单操作

使用 Window.PopupMenu() 可以实现弹出菜单,对应的事件为 wx.EVT_CONTEXT_MENU

class TestFrame(wx.Frame):
def __init__(self, *args, **kw):
# ...
self.Bind(wx.EVT_CONTEXT_MENU, self.OnTreeRightUp) # 右键弹出式菜单
# ...
def OnTreeRightUp(self, event):
# ...
menu = wx.Menu()
menuitem = menu.Append(-1, "Send")
self.Bind(wx.EVT_MENU, self.OnSend, menuitem)
menu.Append(-1, "(new folder)")
menu.Append(-1, "(new file)")
menu.Append(-1, "(copy)")
menu.Append(-1, "(paste)")
menu.Append(-1, "(cut)")
self.PopupMenu(menu)
menu.Destroy()
# ...

选中文件,右键点击 Send,可将文件名发送到右侧文字框:

left_pop_menu


1.1.6. 及时地更新目录树内容

每当离开应用窗口时,都可能会改变工作目录。所以更新目录树的时机为"鼠标重新点击应用程序",对应的事件为 wx.EVT_ACTIVATE,需要注意的是这个得绑定在 wx.Frame 类里,否则不起作用。

为了简化思路,每当重新点击窗口时,要先判断前后的工作目录是否有发生变化,有变化才刷新树(刷新指的是:删除之前的所有子结点,再根据新目录重新创建结点),不然每次点击就刷新


1.1.7. 对结点进行排序

对结点进行排序,需要重写 TextCtrl.OnCompareItems(self, item1, item2),然后在实例调用 SortChildren()


class MyTreeCtrl(wx.TreeCtrl):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)

def OnCompareItems(self, item1, item2):
"""重写 OnCompareItems
data = [0, 文件夹名] /
data = [1, 文件名]
"""
data1 = self.GetItemData(item1)
data2 = self.GetItemData(item2)
if data1[0] > data2[0]:
return 1
elif data1[0] return -1
else:
if data1[1].lower() > data2[1].lower():
return 1
elif data1[1].lower() return -1
else:
return 0
# ...
def InitTree(self):
"""初始化树"""

# ...
# 根结点展开
self.tree.Expand(self.root_id)
# 对子结点排序
self.tree.SortChildren(self.root_id)

排完序的运行结果:

my_tree_dir_sort


1.2. 完整代码


1.2.1. 目录树



点击查看代码


import wx
import glob
import os
import wx.lib.agw.aui as aui
import Images
from IconMap import Ext2IconDict
class MyTreeCtrl(wx.TreeCtrl):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)

def OnCompareItems(self, item1, item2):
"""重写 OnCompareItems
data = [0, 文件夹名] /
data = [1, 文件名]
"""
data1 = self.GetItemData(item1)
data2 = self.GetItemData(item2)
if data1[0] > data2[0]:
return 1
elif data1[0] return -1
else:
if data1[1].lower() > data2[1].lower():
return 1
elif data1[1].lower() return -1
else:
return 0

class DirectoryTree(wx.Window):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.work_path = os.getcwd() # 默认为当前的工作区目录
# 创建树
self.tree = MyTreeCtrl(self, -1,
style = wx.TR_DEFAULT_STYLE # 默认样式
|wx.TR_TWIST_BUTTONS # 结点使用 >/v 而不是 +/-
|wx.TR_NO_LINES # 不绘制结点之间的连线
)
# 初始化图像列表
self.InitImageList()
# 初始化树
self.InitTree()
# 布局
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.tree, 1, wx.EXPAND|wx.LEFT|wx.BOTTOM, 2)
self.SetSizer(sizer)
def InitImageList(self):
"""初始化图像列表"""
# 创建一个图像列表
il = wx.ImageList(16, 16)
# 扩展名到图标 ID 的字典
self.ext_map_imageId = {}
for ext in Ext2IconDict:
# 文件扩展名 to imageID
bmp = getattr(Images, Ext2IconDict[ext]).GetBitmap()
self.ext_map_imageId[ext] = il.Add(bmp)
# 单独添加几种特殊情况
self.ext_map_imageId['default_file'] = il.Add(getattr(Images, 'default_file').GetBitmap())
self.ext_map_imageId['default_folder'] = il.Add(getattr(Images, 'default_folder').GetBitmap())
self.ext_map_imageId['default_folder_opened'] = il.Add(getattr(Images, 'default_folder_opened').GetBitmap())
# 将图像分配给树
self.tree.AssignImageList(il)

def InitTree(self):
"""初始化树"""
# 获取工作目录所有子文件和子目录
self.all_files = self.GetAllFileFrom(self.work_path)
# 设置根目录
if self.tree.GetCount() <1:
self.root_id = self.tree.AddRoot(self.all_files[0], data=[0, self.all_files[0]])
else:
self.root_id = self.tree.GetRootItem()
# 清楚所有子结点
self.tree.DeleteChildren(self.root_id)
# 递归添加子节点
self.AddTreeNodes(self.root_id, self.all_files[1])
# 根结点展开
self.tree.Expand(self.root_id)
# 对子结点排序
self.tree.SortChildren(self.root_id)
def GetAllFileFrom(self, path):
"""递归获取包括该目录及其子文件、子目录所有文件,
生成一个“树状列表”,如: [root, [sub-list]]
[root, [
item1,
[item2, [
item21, item22, item23
],
item3,
]
]
"""
sub_list = []
for file_name in glob.iglob(os.path.join(path, "*")):
if os.path.isdir(file_name):
sub_list.append(self.GetAllFileFrom(file_name))
else:
file_name_without_root = file_name.split('\\')[-1]
sub_list.append(file_name_without_root)
root = path.split('\\')[-1]
return [root, sub_list]
def CompareTreeList(self, plist, qlist):
"""比较两个树状列表"""
if len(plist) != len(qlist):
return False
res = True
for p,q in zip(plist, qlist):
if type(p) == str and type(q) ==str:
if p != q:
return False
elif type(p) == list and type(q) == list:
res = self.CompareTreeList(p, q)
else:
return False
return res
def AddTreeNodes(self, parentItem, items):
"""递归添加树结点
Args:
parentItem ([treeItemID]): [description]
items ([list]): [description]
"""
for item in items:
if type(item) == str:
newItem = self.tree.AppendItem(parentItem, item, data=[1, item])
# 设置数据图像
ext = item.split('.')[-1] # 扩展名
if ext in self.ext_map_imageId:
self.tree.SetItemImage(newItem, self.ext_map_imageId[ext], which=wx.TreeItemIcon_Normal)
else:
self.tree.SetItemImage(newItem, self.ext_map_imageId['default_file'], which=wx.TreeItemIcon_Normal)
else:
newItem = self.tree.AppendItem(parentItem, item[0], data=[0, item[0]])
# 设置结点的图像(文件夹)
self.tree.SetItemImage(newItem, self.ext_map_imageId['default_folder'], which=wx.TreeItemIcon_Normal)
self.tree.SetItemImage(newItem, self.ext_map_imageId['default_folder_opened'], which=wx.TreeItemIcon_Expanded)
# 递归调用
self.AddTreeNodes(newItem, item[1])
def IsDirChange(self):
"""判断当前工作区目录是否修改"""
tmp_list = self.GetAllFileFrom(self.work_path)
return not self.CompareTreeList(tmp_list, self.all_files)
def GetSelectionItem(self):
"""返回被选中的结点"""
return self.tree.GetFocusedItem()
def Unselect(self):
"""取消选中的结点"""
self.tree.Unselect()
def GetItemData(self, id):
"""返回指定结点的data"""
return self.tree.GetItemData(id)


1.2.2. 测试类



点击查看代码

class TestFrame(wx.Frame):

def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.SetTitle("目录树")
self.SetSize(800, 600)
self.dir_tree = DirectoryTree(self, size=(200, 600))
self.txt = wx.TextCtrl(self, -1, value="I'm 002",workspace").
Left().Layer(1).Position(1).CloseButton(True).MaximizeButton(True).MinimizeButton(True))
self._mgr.AddPane(self.txt, aui.AuiPaneInfo().CenterPane())

# 记得要 Update
self._mgr.Update()
# 绑定事件
self.Bind(wx.EVT_ACTIVATE, self.OnActive) # 每当鼠标重新点击此窗口,检查更新目录
self.Bind(wx.EVT_CONTEXT_MENU, self.OnTreeRightUp) # 右键弹出式菜单
def OnActive(self, event):
if event.GetActive():
print("On Active!" )
if self.dir_tree.IsDirChange():
print("Refresh dir-tree!")
self.dir_tree.InitTree()
def OnTreeRightUp(self, event):
id = self.dir_tree.GetSelectionItem()
self.msg = ""
if id.IsOk():
self.msg = self.dir_tree.GetItemData(id)
print(self.msg)
menu = wx.Menu()
menuitem = menu.Append(-1, "Send")
self.Bind(wx.EVT_MENU, self.OnSend, menuitem)
menu.Append(-1, "(new folder)")
menu.Append(-1, "(new file)")
menu.Append(-1, "(copy)")
menu.Append(-1, "(paste)")
menu.Append(-1, "(cut)")
self.PopupMenu(menu)
menu.Destroy()
# 记得取消当前选择
self.dir_tree.Unselect()
def OnSend(self, event):
self.txt.AppendText("\nClick: "+self.msg)

if __name__ == '__main__':
app = wx.App()
frm = TestFrame(None)
frm.Show()
app.MainLoop()


1.2.3. 相关图标



  • IconMap.py:https://gitee.com/iam002/sketch/blob/dev/IconMap.py



  • Images.py:https://gitee.com/iam002/sketch/blob/dev/Images.py




1.3. 相关参考



  • https://docs.wxpython.org/



    • wx.GenericDirCtrl

    • wx.TreeCtrl

    • wx.lib.agw.aui

    • Window.PopupMenu()

    • wx.TextCtrl



  • wxPython in Action by Noel Rappin, Robin Dunn, Chapter 10 and Chapter 15



    • 蓝奏云下载: https://wwe.lanzoup.com/b01oz824f ,密码:g7np

    • 配套代码:https://github.com/freephys/wxPython-In-Action



  • 图标来源:https://github.com/vscode-icons/vscode-icons



  • 使用 wx.tools.img2py(4)





推荐阅读
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom&amp;quot;echarts&amp;quot;;4、如果用到map(地图),还 ... [详细]
  • 本文通过基准测试(Benchmark)对.NET Core环境下Thrift和HTTP客户端的微服务通信性能进行对比分析。基准测试是一种评估系统或组件性能的方法,通过运行一系列标准化的测试来衡量其表现。 ... [详细]
  • WPF项目学习.一
    WPF项目搭建版权声明:本文为博主初学经验,未经博主允许不得转载。一、前言记录在学习与制作WPF过程中遇到的解决方案。使用MVVM的优点是数据和视图分离,双向绑定,低耦合,可重用行 ... [详细]
  • 本文探讨了 Canvas 元素在不同尺寸设置下出现变形失真的原因,并详细解释了 HTML 尺寸和 CSS 尺寸的区别及其对视觉效果的影响。 ... [详细]
  • Java设计模式详解:解释器模式的应用与实现
    本文详细介绍了Java设计模式中的解释器模式,包括其定义、应用场景、优缺点以及具体的实现示例。通过音乐解释器的例子,帮助读者更好地理解和应用这一模式。 ... [详细]
  • 本文介绍了如何使用Python爬取妙笔阁小说网仙侠系列中所有小说的信息,并将其保存为TXT和CSV格式。主要内容包括如何构造请求头以避免被网站封禁,以及如何利用XPath解析HTML并提取所需信息。 ... [详细]
  • python模块之正则
    re模块可以读懂你写的正则表达式根据你写的表达式去执行任务用re去操作正则正则表达式使用一些规则来检测一些字符串是否符合个人要求,从一段字符串中找到符合要求的内容。在 ... [详细]
  • Iwouldliketohaveatooltopdisplayedonatextboxunderacertainsituation.我希望在特定情况下在文本框中显示工具栏 ... [详细]
  • 本文详细解析了ASP.NET 2.0中的Callback机制,不仅介绍了基本的使用方法,还深入探讨了其背后的实现原理。通过对比Atlas框架,帮助读者更好地理解和应用这一机制。 ... [详细]
  • 2022年2月 微信小程序 app.json 配置详解:启用调试模式
    本文将详细介绍如何在微信小程序的 app.json 文件中启用调试模式(debug),并通过实际案例展示其配置方法和应用场景。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 开发日志:高效图片压缩与上传技术解析 ... [详细]
author-avatar
鱼儿37度半_795
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有