tkinter绘制组件(36)——树状图

news2025/1/6 20:29:42

tkinter绘制组件(36)——树状图

  • 引言
  • 布局
    • 函数结构
    • 内容数据格式
  • 整体框架
    • 绘制元素与重绘宽度
    • 标识元素
    • 展开与闭合
    • 完整函数代码
  • 效果
    • 测试代码
    • 最终效果
  • github项目
  • pip下载
  • 结语

引言

TinUI的第38个元素控件,也是TinUI-4.0-添加的第一个组件,代表着TinUI已经补齐tkinter原生的所有控件,可基本替代tkinter控件类型(至少是类型)。

我应该是第一个在tkinter画布上搞定树状图的冤种,真的好耗时间,代码量和TinUI的滚动条(scrollbar)差不多,但是好多细节都要处理,下面会讲。隔壁customtkinter还没有这个打算,目前正在做表格和标签栏视图,当然,后面应该也会跟上。

本来我也没打算用TinUI实现树状图,但是所有原生控件就剩它一个,而且树状图可以在分级数据显示中发挥重要作用,最终还是提上日程,在TinUI-4.0-中发布。


布局

函数结构

    def add_treeview(self,pos:tuple,fg='#1a1a1a',bg='#f3f3f3',onfg='#1a1a1a',onbg='#eaeaea',oncolor='#3041d8',signcolor='#8a8a8a',width=200,height=300,font='微软雅黑 12',content=(('one',('1','2','3')),'two',('three',('a',('b',('b1','b2','b3')),'c')),'four'),command=None):#树状图   
    '''
    pos-位置
    fg-文本颜色
    bg-背景色
    onfg-选中文本颜色
    onbg-选中背景色
    oncolog-选中标识色
    signcolor-滚动条提示色
    width-宽度
    height-高度
    font-字体
    content=(
    a,
    (b,(b1,b2,b3)),
    (c,(c1,(c2,(c2-1,c2-2)),c3)),
    d,
    )-内容
    command-回调函数,接受选中元素id层级从属列表
    '''

内容数据格式

真的丧心病狂~~~~

因为涉及到层级关系,本来考虑到使用字典格式,但是突然发现,又不是每一个元素都有子级,这种json格式纯属画蛇添足。

那么就只能采取比较原始的多维元组格式了。

横着写如果没有语法高亮真的不知道在写什么,所以下面以分行写为例:

content=(
a-#1,
(b-#1,(b1-#2,b2-#2,b3-#2)),
(c-#1,(c1-#2,(c2-#2,(c2.1-#3,c2.2-#3)),c3-#2)),
d-#1
)

字母后面的-#n表示层级。

基本就是(一级,(一级,(下一级)),一级)的嵌套格式。

整体框架

整个treeview的绘制控件和滚动条绑定,是这次任务里最简单的部分。

        nowid=None
        fln=TinUINum()#用于寻找父级关系,目前效率比较低,之后考虑优化
        frame=BasicTinUI(self,bg=bg)#主显示框,显示滚动条
        box=BasicTinUI(frame,bg=bg,width=width,height=height)#显示选择内容
        box.place(x=12,y=12)
        cavui=self.create_window(pos,window=frame,width=width+24,height=height+24,anchor='nw')
        uid='treeview'+str(cavui)
        self.addtag_withtag(uid,cavui)
        yscro=frame.add_scrollbar((width+12,12),widget=box,height=height,bg=bg,color=signcolor,oncolor=signcolor)#纵向
        xscro=frame.add_scrollbar((12,height+12),widget=box,height=width,direction='x',bg=bg,color=signcolor,oncolor=signcolor)#横向
        #id为back的uid
        items=dict()#元素对象{id:(text,back,[sign]),...}
        items_dict=dict()#链接关系(下一级){id:(id1,id2,id3,...),id2:(id2-1,id2-2,...),id-new:(...)...}
        box.add_back((0,0,0,0),linew=0)

最后一行保证box里存在初始元素。

绘制元素与重绘宽度

这是本次第二简单的部分,可以从之前的listview中搬运一点过来。

首先就是元素绘制,因为有嵌套关系,所以就是循环解包,碰到一个多元组就解一个,确保所有元素在items里的顺序。此外,考虑到存在父子级关系,所以在items_dict中只保留一级关联,且为从上至下(从左到右)的顺序。

        def endy():
            return box.bbox('all')[-1]
        def add_item(padx=5,texts:tuple=(),father_id=None):#添加元素
            child_id=[]
            for text in texts:
                y=endy()+3
                if type(text)==str:#单极
                    te=box.create_text((padx+15,y),text=text,font=font,fill=fg,anchor='nw')
                    back=box.add_back((),tuple([te]),fg=bg,bg=bg,linew=3)
                    items[back]=(te,back)
                else:#存在子级
                    sign=box.create_text((padx,y),text='▽',font='Consolas 13',fill=signcolor,anchor='nw')#▷
                    te=box.create_text((padx+15,y),text=text[0],font=font,fill=fg,anchor='nw')
                    back=box.add_back((),tuple((sign,te)),fg=bg,bg=bg,linew=3)
                    items[back]=(te,back,sign)
                    add_item(padx+15,text[1],back)
                    box.tag_bind(sign,'<Button-1>',lambda event,s=sign,cid=back:close_view(s,cid))
                box.tag_bind(back,'<Enter>',lambda event,_id=back:buttonin(_id))
                box.tag_bind(back,'<Leave>',lambda event,_id=back:buttonout(_id))
                box.tag_bind(back,'<Button-1>',lambda event,_id=back:click(_id))
                box.tag_bind(te,'<Enter>',lambda event,_id=back:buttonin(_id))
                box.tag_bind(te,'<Leave>',lambda event,_id=back:buttonout(_id))
                box.tag_bind(te,'<Button-1>',lambda event,_id=back:click(_id))
                child_id.append(back)
            if father_id!=None:#存在父级
                items_dict[father_id]=tuple(child_id)

最后一行代码就是对父组件的反向链接,确保所有可能存在的下一级组件均包含在内。这样做有一个好处,那就是即便是直接多层嵌套,也可以直接记录往上一级的所有节点元素,直到第一级。当然,这样的逻辑分解,其性能因电脑配置而异。接下来还会有一个比较依赖电脑性能和元素数量的操作。

对于重绘宽度,在table,listview,listbox等元素控件已经积累了经验,直接上代码:

        #...
        maxwidth=bbox[2]
        if maxwidth<width-14:maxwidth=width-14
        for i in items.keys():
            old_coords=box.coords(i)
            old_coords[0]=old_coords[6]=6.0
            old_coords[2]=old_coords[4]=6+maxwidth
            box.coords(i,old_coords)
        #...

这里使用到了coords方法,因为我是直接使用back元素最为背景。

标识元素

treeview的标识元素和listview是一样的,但区别是每次选定后的位置重绘方法不一样。

因为listview的元素是不变的,因此使用元素序号直接计算标识元素位置。但是treeview不一样,鬼知道使用者点成什么样子。所以,只能每次控件更新后计算相应内容所在的位置,移动标识元素。

当然,这还涉及到当选定元素被折叠,或者展开元素中存在选中元素,都会涉及到额外的标识元素位置和可见性更改,但是这会在下一小节提到,本节只包括常规的标识元素更换。

        def buttonin(cid):
            if cid!=nowid:
                box.itemconfig(cid,fill=onbg,outline=onbg)
        def buttonout(cid):
            if cid!=nowid:
                box.itemconfig(cid,fill=bg,outline=bg)
        def click(cid):
            nonlocal nowid
            box.itemconfig(line,state='normal')
            box.itemconfig(nowid,fill=bg,outline=bg)#原来的
            box.itemconfig(cid,fill=onbg,outline=onbg)#现在的
            nowid=cid#互换次序
            posi=box.bbox(nowid)[1]
            box.moveto(line,1,posi+linew/5)
            if command!=None:
                fln.father_link=[cid]#父级关系
                find_father_link(fln,cid)
                command(fln.father_link[::-1])#[父级, 子1级, 子2级...]

这里可以看到command接受的是一个列表。

加入我选定的元素id是12,它的从属结构为1->3->12,那么列表就是[1,3,12]

其中的find_father_link函数就是一个耗时操作,也就是根据子级寻找上一层级,直到第一层。目前我只能想到穷举法,或者是我只来得及写这种方法,如果有更好的反向寻找父级,可以向TinUI的GitHub项目中提供issue。这个函数内容如下:

        def find_father_link(fln,cid):#获取元素父级关系
            for i in items_dict:
                if cid in items_dict[i]:
                    fln.father_link.append(i)
                    find_father_link(fln,i)

展开与闭合

这个内容是最烦人的,因为有很多级,只能够通过内部循环的方法进行界面操作。

我试过以下几种操作逻辑:

  1. 闭合后再展开原各级隐藏与否照旧。

  2. 闭合后所有子级,包括跨级,关闭,展开只展开直接下一级。

  3. 同【2】,但展开时展开所有子级,包括跨级。

但是,经过一次次测试,不管其它方法能不能成功,总之,我只实现了方法【2】,毕竟这是以目前最简单的方法实现较合理的功能。这是一种权衡(确信🧐。

闭合:

        def close_view(sign,cid):#闭合
            if box.itemcget(sign,'text')=='▷':#避免重复关闭节点
                return
            #重新绑定sign元素的相应方法
            box.tag_bind(sign,'<Button-1>',lambda event:open_view(sign,cid))
            box.itemconfig(sign,text='▷')
            #获取所有子元素,包括跨级子元素
            cids=get_cids(cid)
            move='move'+str(cid)#单层管理命名元素
            for i in cids:
                #print(box.itemcget(items[i][0],'text'))
                for uid in items[i]:
                    box.addtag_withtag(move,uid)#绑定移动tag标志
                if i in items_dict:#如果这也是一个节点元素,关闭节点
                    close_view(items[i][-1],i)#内部关闭
            bbox=box.bbox(move)
            box.itemconfig(move,state='hidden')#关闭节点
            index=tuple(items.keys()).index(cids[-1])+1#获取节点位置信息
            if index!=len(items.keys()):
                height=bbox[3]-bbox[1]#获取移动模块高度
                for i in tuple(items.keys())[index:]:
                    for uid in items[i]:#移动所有下方元素内容
                        box.move(uid,0,-height)#移动下方元素
            box.dtag(move)
            if nowid in cids:#标识元素控制
                box.itemconfig(line,state='hidden')#如果选中项被折叠
            else:
                click(nowid)#重新绘制位置,包括标识符
            box.config(scrollregion=box.bbox('all'))

看注释,这里的注释比TinUI源文件详细很多。

展开:

        def open_view(sign,cid):#展开
            if box.itemcget(sign,'text')=='▽':
                return
            box.tag_bind(sign,'<Button-1>',lambda event:close_view(sign,cid))
            box.itemconfig(sign,text='▽')
            cids=items_dict[cid]
            move='move'+str(cid)#单层管理命名元素
            for i in cids:#只展开一层
                for uid in items[i]:
                    box.addtag_withtag(move,uid)
            box.itemconfig(move,state='normal')
            bbox=box.bbox(move)
            if bbox==None:return
            index=tuple(items.keys()).index(cids[-1])+1
            if index!=len(items.keys()):
                height=bbox[3]-bbox[1]#获取移动模块高度
                for i in tuple(items.keys())[index:]:
                    for uid in items[i]:
                        box.move(uid,0,height)
            box.dtag(move)
            if nowid in cids:#重新显示标识元素
                click(nowid)
            box.config(scrollregion=box.bbox('all'))

虽然这两段看起来简单,但是测试的时候还是费了一段时间。

完整函数代码

    def add_treeview(self,pos:tuple,fg='#1a1a1a',bg='#f3f3f3',onfg='#1a1a1a',onbg='#eaeaea',oncolor='#3041d8',signcolor='#8a8a8a',width=200,height=300,font='微软雅黑 12',content=(('one',('1','2','3')),'two',('three',('a',('b',('b1','b2','b3')),'c')),'four'),command=None):#树状图
        '''
        content=(
        a,
        (b,(b1,b2,b3)),
        (c,(c1,(c2,(c2-1,c2-2)),c3)),
        d,
        )
        '''
        def buttonin(cid):
            if cid!=nowid:
                box.itemconfig(cid,fill=onbg,outline=onbg)
        def buttonout(cid):
            if cid!=nowid:
                box.itemconfig(cid,fill=bg,outline=bg)
        def click(cid):
            nonlocal nowid
            box.itemconfig(line,state='normal')
            box.itemconfig(nowid,fill=bg,outline=bg)#原来的
            box.itemconfig(cid,fill=onbg,outline=onbg)#现在的
            nowid=cid#互换次序
            posi=box.bbox(nowid)[1]
            box.moveto(line,1,posi+linew/5)
            if command!=None:
                fln.father_link=[cid]#父级关系
                find_father_link(fln,cid)
                command(fln.father_link[::-1])#[父级, 子1级, 子2级...]
        def find_father_link(fln,cid):#获取元素父级关系
            for i in items_dict:
                if cid in items_dict[i]:
                    fln.father_link.append(i)
                    find_father_link(fln,i)
        def endy():
            return box.bbox('all')[-1]
        def add_item(padx=5,texts:tuple=(),father_id=None):#添加元素
            child_id=[]
            for text in texts:
                y=endy()+3
                if type(text)==str:#单极
                    te=box.create_text((padx+15,y),text=text,font=font,fill=fg,anchor='nw')
                    back=box.add_back((),tuple([te]),fg=bg,bg=bg,linew=3)
                    items[back]=(te,back)
                else:#存在子级
                    sign=box.create_text((padx,y),text='▽',font='Consolas 13',fill=signcolor,anchor='nw')#▷
                    te=box.create_text((padx+15,y),text=text[0],font=font,fill=fg,anchor='nw')
                    back=box.add_back((),tuple((sign,te)),fg=bg,bg=bg,linew=3)
                    items[back]=(te,back,sign)
                    add_item(padx+15,text[1],back)
                    box.tag_bind(sign,'<Button-1>',lambda event,s=sign,cid=back:close_view(s,cid))
                box.tag_bind(back,'<Enter>',lambda event,_id=back:buttonin(_id))
                box.tag_bind(back,'<Leave>',lambda event,_id=back:buttonout(_id))
                box.tag_bind(back,'<Button-1>',lambda event,_id=back:click(_id))
                box.tag_bind(te,'<Enter>',lambda event,_id=back:buttonin(_id))
                box.tag_bind(te,'<Leave>',lambda event,_id=back:buttonout(_id))
                box.tag_bind(te,'<Button-1>',lambda event,_id=back:click(_id))
                child_id.append(back)
            if father_id!=None:#存在父级
                items_dict[father_id]=tuple(child_id)
        def get_cids(cid):
            cids=[]
            if cid in items_dict:
                for i in items_dict[cid]:
                    cids.append(i)
                    ccids=get_cids(i)
                    cids+=ccids
            return cids
        def open_view(sign,cid):#展开
            if box.itemcget(sign,'text')=='▽':
                return
            box.tag_bind(sign,'<Button-1>',lambda event:close_view(sign,cid))
            box.itemconfig(sign,text='▽')
            cids=items_dict[cid]
            move='move'+str(cid)#单层管理命名元素
            for i in cids:#只展开一层
                for uid in items[i]:
                    box.addtag_withtag(move,uid)
            box.itemconfig(move,state='normal')
            bbox=box.bbox(move)
            if bbox==None:return
            index=tuple(items.keys()).index(cids[-1])+1
            if index!=len(items.keys()):
                height=bbox[3]-bbox[1]#获取移动模块高度
                for i in tuple(items.keys())[index:]:
                    for uid in items[i]:
                        box.move(uid,0,height)
            box.dtag(move)
            if nowid in cids:#重新显示标识元素
                click(nowid)
            box.config(scrollregion=box.bbox('all'))
        def close_view(sign,cid):#闭合
            if box.itemcget(sign,'text')=='▷':
                return
            box.tag_bind(sign,'<Button-1>',lambda event:open_view(sign,cid))
            box.itemconfig(sign,text='▷')
            cids=get_cids(cid)
            move='move'+str(cid)#单层管理命名元素
            for i in cids:
                #print(box.itemcget(items[i][0],'text'))
                for uid in items[i]:
                    box.addtag_withtag(move,uid)
                if i in items_dict:
                    close_view(items[i][-1],i)
            bbox=box.bbox(move)
            box.itemconfig(move,state='hidden')
            index=tuple(items.keys()).index(cids[-1])+1
            if index!=len(items.keys()):
                height=bbox[3]-bbox[1]#获取移动模块高度
                for i in tuple(items.keys())[index:]:
                    for uid in items[i]:
                        box.move(uid,0,-height)
            box.dtag(move)
            if nowid in cids:#标识元素控制
                box.itemconfig(line,state='hidden')
            else:
                click(nowid)#重新绘制位置
            box.config(scrollregion=box.bbox('all'))
        def bindview(event):
            if event.state==0:
                box.yview_scroll(int(-1*(event.delta/120)), "units")
            elif event.state==1:
                box.xview_scroll(int(-1*(event.delta/120)), "units")
        nowid=None
        fln=TinUINum()#用于寻找父级关系,目前效率比较低,之后考虑优化
        frame=BasicTinUI(self,bg=bg)#主显示框,显示滚动条
        box=BasicTinUI(frame,bg=bg,width=width,height=height)#显示选择内容
        box.place(x=12,y=12)
        cavui=self.create_window(pos,window=frame,width=width+24,height=height+24,anchor='nw')
        uid='treeview'+str(cavui)
        self.addtag_withtag(uid,cavui)
        yscro=frame.add_scrollbar((width+12,12),widget=box,height=height,bg=bg,color=signcolor,oncolor=signcolor)#纵向
        xscro=frame.add_scrollbar((12,height+12),widget=box,height=width,direction='x',bg=bg,color=signcolor,oncolor=signcolor)#横向
        #id为back的uid
        items=dict()#元素对象{id:(text,back,[sign]),...}
        items_dict=dict()#链接关系(下一级){id:(id1,id2,id3,...),id2:(id2-1,id2-2,...),id-new:(...)...}
        box.add_back((0,0,0,0),linew=0)
        add_item(5,content)
        #重绘宽度
        bbox=box.bbox(tuple(items.keys())[0])#第一个元素高度-4
        linew=bbox[3]-bbox[1]
        line=box.create_line((1,linew/3,1,linew*2/3),fill=oncolor,width=3,capstyle='round')
        #重绘宽度
        maxwidth=bbox[2]
        if maxwidth<width-14:maxwidth=width-14
        for i in items.keys():
            old_coords=box.coords(i)
            old_coords[0]=old_coords[6]=6.0
            old_coords[2]=old_coords[4]=6+maxwidth
            box.coords(i,old_coords)
        allback=self.add_back((),tuple([cavui]),fg=bg,bg=bg,linew=3)
        self.addtag_withtag(uid,allback)
        box.config(scrollregion=box.bbox('all'))
        box.move(line,0,-linew-height)
        box.itemconfig(line,state='hidden')
        box.bind('<MouseWheel>',bindview)
        return items,items_dict,box,uid

效果

测试代码

def test12(cid):
    for i in cid:
        print(trvbox.itemcget(trvl[i][0],'text')+'/',end='')
    print('') 
if __name__=='__main__':
    #...
    trvl,_,trvbox,_=b.add_treeview((1220,1300),command=test12)
    #...

最终效果

在这里插入图片描述


github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

现在TinUI已经补齐了所有的tkinter原生控件。4.0版本还会包含基础image控件与scrollbar的平滑滚动功能,或许吧。

🔆tkinter创新🔆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rsync下行同步+inotify实时同步介绍和部署

一、Rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09; 是一个开源的快速备份工具&#xff0c;可以在不同主机之间镜像同步整个目录树&#xff0c;支持增量备份&#xff0c;并保持链接和权限&#xff0c;且采用优化的同步算法&#xff0c;传输前执行压缩&#xf…

2022世界杯结果预测,简单AI模型最有效?附代码!

2022世界杯冠军是谁&#xff1f;本文将为你揭晓一个利用简单AI模型得到的靠谱预测。 许多人称足球为“不可预测的比赛”&#xff0c;因为一场足球比赛有不同的因素可以改变最终比分。 这是真的……在某种程度上。 北大出版社&#xff0c;人工智能原理与实践 人工智能和数据科…

面试官:什么是伪共享,如何避免?

本文已收录到 GitHub AndroidFamily&#xff0c;有 Android 进阶知识体系&#xff0c;欢迎 Star。技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 加入 Android 交流群。 前言 大家好&#xff0c;我是小彭。 在前面的文章里&#xff0c;我们聊到了 CPU 的高速缓存机制。…

【electron】判断当前进程是否是开发环境的五种方案(获取一些资源的路径)

文章目录导读需求开发环境判断是否是开发环境方案一&#xff1a;location.protocol方案二&#xff1a;全局变量__static方案三&#xff1a;全局变量process.resourcesPath方案四&#xff1a;全局变量process.env.WEBPACK_DEV_SERVER方案五&#xff1a;app.isPackaged获取配置目…

OS-process

process 什么是进程 进程是被加载到内存中、正在运行的程序&#xff1b;多个进程可能对于同一个程序、一个正在运行的OS中会有多个进程 进程是程序的一次执行过程&#xff0c;是操作系统分配资源的基本单位 作业等同于进程 进程的布局&#xff1a; 每个进程都有一个不同的…

内网渗透中最常见的十种漏洞分析总结

【环境搭建资料、工具包、全套视频…等籽料】私信聆取 以下信息是根据2020年和2019年为全球各种中型组织和企业完成的60多个渗透测试报告汇总而来的&#xff0c;在跳转到列表之前&#xff0c;让我们简要介绍一下全面的测试方法。 一、测试方法 目的是使用白盒(灰盒)方法在现场…

spirngboot项目.mvn/wrapper/maven-wrapper.properties‘ does not exist.

rm -rf ~ 命令后&#xff0c;项目出现一些问题&#xff0c;如下 执行命令&#xff1a; ./mvnw clean package -am -pl bistoury-dist -P$PROFILR -Dmaven.test.skip -Denforcer.skiptrue 报异常&#xff1a; starting to build bistoury agent Exception in thread "ma…

[附源码]java毕业设计同德佳苑物业管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

SpringBoot数据层解决方案/ 内置持久化解决方案JdbcTemplate使用/内置数据库H2使用以及RedisNoSql使用

写在前面&#xff1a; 继续记录自己的SpringBoot学习之旅&#xff0c;这次是SpringBoot应用相关知识学习记录。若看不懂则建议先看前几篇博客&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 3.4 数据层解决方案 3.4.1 使用项目 新建空项目&a…

redis的事务

Redis的事务&#xff1a;一次事务操作&#xff0c;改成功的成功&#xff0c;该失败的失败。 提交事务 如果遇见错误 成功的成功&#xff0c;失败的失败&#xff0c;不会回滚 先开启事务&#xff0c;执行一些列的命令&#xff0c;但是命令不会立即执行&#xff0c;会被放在一…

web分享会

文章目录前言平台1. 在线练习平台2. 自己搭建靶场3. CTF真题平台分享前言 个人的学习过程仅供参考。 个人情况 我是大二进的工作室&#xff0c;算是比较晚的了&#xff0c;工作室的师兄人也很好&#xff0c;我有问题也会去找他们。我是一位女生&#xff0c;非常欢迎师妹们加入&…

从购买云服务器开始到成功部署网站并且可以正常外网访问整个体系流程(从0到1的过程)

准备工作&#xff1a; 购买云服务器(阿里云或腾讯云都行)&#xff0c;过程一样。购买域名&#xff0c;并做解析处理&#xff0c;通过域名访问网站(后面需要自行备案)。 云服务器一些基本配置处理&#xff1a; 用密码登录方式登录root用户到云服务器上 登录成功后&#xff0c…

MHDNet

发现一种性能更优的目标检测网络&#xff0c;不同的输入分辨率下&#xff0c;目标分布和检测头之间存在不同的匹配关系。基于这些有指导意义的发现&#xff0c;作者提出了一种基于检测头和目标分布之间匹配的轻量化交通目标检测网络&#xff0c;称为MHDNet。该模型在BDD100K数据…

【k8s】3、kubeadm安装k8s集群

文章目录一、环境部署1、关闭防火墙规则、关闭selinux、关闭swap交换分区2、修改主机名、DNS解析3、调整内核参数二、所有节点安装Docker三、安装k8s集群1、所有节点配置K8S源2、所有节点安装kubeadm、kubelet和kubectl3、部署K8S集群3.1 初始化操作&#xff08;master节点操作…

【虚幻引擎UE】UE4/UE5 环境构建插件推荐及使用介绍

一、Ultra Dynamic Sky&#xff08;天气插件&#xff09; 支持天气和昼夜变换&#xff0c;包含音效&#xff0c;可以预设也可以动态切换。内置照明解决方案&#xff0c;太阳、月亮和天空照明均与天空同步&#xff0c;并随时间变化。 天气包含&#xff1a;晴天、2D晴天、雨天、…

进程的通信 - 命名管道

命名管道概述 命名管道&#xff08;Named Pipes&#xff09;&#xff0c;顾名思义&#xff0c;一个有名字的管道。命名管道的名字主要是用于确保多个进程访问同一个对象。命名管道不仅可以在同一台计算机之间传输数据&#xff0c;甚至能在跨越一个网络的不同计算机的不同进程之…

Zbrush 导出置换 然后导入vray 在 3ds max 和 maya 设置

注&#xff1a;方法很多&#xff0c;这个只是个人学习总结&#xff0c;如果不合适&#xff0c;可另外学习其他方法&#xff01; 第一步&#xff1a; Zbrush 导出置换和法线设置&#xff1a; 第二步&#xff1a; 3Ds max 设置方式建议用exr格式&#xff08;由于导出的时候…

科目一过关技巧

口诀 3让6违——题目中看到“6分”选“违”字&#xff1b;看到“违”字选6分&#xff08;“违”法停车的违字除外&#xff0c;选9分&#xff09;&#xff1b;看到“让”就3分红高蓝低——红色圈圈最高&#xff0c;蓝色最低虚可实禁——车辆可以压虚线&#xff0c;不能压实线&a…

Windows11 配置Cuda cuDNN Pytorch环境

文章目录1 安装CudaNote 安装失败的情况2 安装cuDNN3 安装Anaconda4 安装Pytorch5 使用Pycharm进行验证6 结束1 安装Cuda 进入 developer cuda: https://developer.nvidia.com/ 之后自定义路径进行安装即可 必装CUDA&#xff0c;其他情况自己选择 自定义路径 Document与Deve…

Python表白比心

本文介绍运用Python中的turtle库控制函数画比心图。    文章目录一、效果展示二、代码详解1 导入库2 播放音乐3 画手4 定义画心的函数5 定义写名字的函数并实现动态画心一、效果展示 在介绍代码之前&#xff0c;先来看下本文的实现效果。 可以参考Pinstaller(Python打包为exe…