tkinter drawing component (23) -- list box

Posted by run_GMoney on Sat, 26 Feb 2022 03:50:18 +0100

introduction

Similar to the drop-down box (combo box) combobox of TinUI, the list box also displays a number of selectable text segments to the user, calling the response function after the user clicks the selection, and passing the selected parameters, that is, the selected text segment text. So are these two components contradictory? Of course not.

Application scenario of combobox:

  • Number of finite elements

  • Short text length

To sum up, it is small and fine. The application scenario of listbox:

  • Many options

  • The length and format of the optional text vary greatly, generally long text or complex format text

  • Occupied position fixed

Therefore, the two components are actually complementary. In addition, TinUI has completed the drawing of the scroll bar, which lays a foundation for the creation of these components with variable field of view.

layout

Function structure

def add_listbox(self,pos:tuple,width:int=200,height:int=200,font='Microsoft YaHei 12',data=('a','b','c'),bg='#f2f2f2',fg='black',activebg='#e9e9e9',sel='#b4bbea',anchor='nw',command=None):#Draw list box
    '''
    pos::Starting position
    width::width
    height::height
    font::typeface
    data::Unary element set, optional prompt text data
    bg::Background color
    fg::Text and scroll bar colors
    activebg::The background color of the response mouse
    sel::Background color when selected
    anchor::align orientation 
    command::The callback function must accept a parameter, the selected text
    '''

Component framework

Because we need to use the scroll bar, we use two basic tin UI components nested.

        frame=BasicTinUI(self,bg=bg)#Main display box, displaying scroll bar
        box=BasicTinUI(frame,bg=bg,width=width,height=height)#Show selection
        box.place(x=12,y=12)

Bind scroll bar

It's just like using tkinter native scroll bar, but it's easier. You only need to bind it once. This is also equivalent to an official example of TinUI scroll bar binding "extra components".

        uid=self.create_window(pos,window=frame,width=width+24,height=height+24,anchor=anchor)
        frame.add_scrollbar((width+12,12),widget=box,height=height,bg=bg,color=fg,oncolor=fg)#portrait
        frame.add_scrollbar((12,height+12),widget=box,height=height,direction='x',bg=bg,color=fg,oncolor=fg)#transverse

Logic different from combobox

You can think of it as an improvement of my logic level. In listbox, we will use independent data structure to judge the different response of each option to the event.

This variable is as follows:

choices={}#'a':[a_text,a_back,is_sel:bool]

That is, take the text content as the key to correspond to the values of text object, background box object and selected status respectively.

Add optional elements

Redrawing techniques similar to combobox and table will not be repeated here. Please see those two articles for details.

        maxwidth=0#Maximum width
        for i in data:
            end=box.bbox('all')
            end=5 if end==None else end[-1]
            text=box.create_text((5,end+7),text=i,fill=fg,font=font,anchor='nw')
            bbox=box.bbox(text)
            twidth=bbox[2]-bbox[0]
            maxwidth=twidth if twidth>maxwidth else maxwidth
            back=box.create_rectangle((3,bbox[1]-2,bbox[2]+2,bbox[3]+2),width=0,fill=bg)
            box.tkraise(text)
            choices[i]=[text,back,False]
            box.tag_bind(text,'<Enter>',lambda event,text=i : in_mouse(text))
            box.tag_bind(text,'<Leave>',lambda event,text=i : out_mouse(text))
            box.tag_bind(text,'<Button-1>',lambda event,text=i : sel_it(text))
            box.tag_bind(back,'<Enter>',lambda event,text=i : in_mouse(text))
            box.tag_bind(back,'<Leave>',lambda event,text=i : out_mouse(text))
            box.tag_bind(back,'<Button-1>',lambda event,text=i : sel_it(text))
        if maxwidth<width:#Judge the maximum width
            maxwidth=width
        repaint_back()#Repaint 

The redrawing function is as follows:

        def repaint_back():
            for v in choices.values():
                bbox=box.coords(v[1])
                box.coords(v[1],3,bbox[1]-2,5+maxwidth+2,bbox[3]+2)

Don't forget to bind the visual range.

        bbox=box.bbox('all')
        box.config(scrollregion=bbox)
        def set_y_view(event):
            box.yview_scroll(int(-1*(event.delta/120)), "units")
        box.bind('<MouseWheel>',set_y_view)

Binding style

Because the above has bound the event response of each canvas object to the mouse, the bound function is directly indicated here.

Mouse in and out events:

        def in_mouse(t):
            if choices[t][2]==True:#Selected
                return
            box.itemconfig(choices[t][1],fill=activebg)
        def out_mouse(t):
            if choices[t][2]==True:#Selected
                box.itemconfig(choices[t][1],fill=sel)
            else:
                box.itemconfig(choices[t][1],fill=bg)

As you can see, we judge whether this option has been selected through the data structure. If not, we will adjust the style, otherwise we will not do any processing.

Using this method, you can avoid the tedious process of determining the style change operation through the style judgment of the canvas object.

Select action:

        def sel_it(t):
            box.itemconfig(choices[t][1],fill=sel)
            choices[t][2]=True
            for i in choices.keys():
                if i==t:
                    continue
                choices[i][2]=False
                out_mouse(i)
            if command!=None:
                command(t)

In addition to the judgment of variable data structure, other logic and operations such as selection, deselection and style recovery are the same as combobox.

Complete code function

    def add_listbox(self,pos:tuple,width:int=200,height:int=200,font='Microsoft YaHei 12',data=('a','b','c'),bg='#f2f2f2',fg='black',activebg='#e9e9e9',sel='#b4bbea',anchor='nw',command=None):#Draw list box
        def repaint_back():
            for v in choices.values():
                bbox=box.coords(v[1])
                box.coords(v[1],3,bbox[1]-2,5+maxwidth+2,bbox[3]+2)
        def in_mouse(t):
            if choices[t][2]==True:#Selected
                return
            box.itemconfig(choices[t][1],fill=activebg)
        def out_mouse(t):
            if choices[t][2]==True:#Selected
                box.itemconfig(choices[t][1],fill=sel)
            else:
                box.itemconfig(choices[t][1],fill=bg)
        def sel_it(t):
            box.itemconfig(choices[t][1],fill=sel)
            choices[t][2]=True
            for i in choices.keys():
                if i==t:
                    continue
                choices[i][2]=False
                out_mouse(i)
            if command!=None:
                command(t)
        frame=BasicTinUI(self,bg=bg)#Main display box, displaying scroll bar
        box=BasicTinUI(frame,bg=bg,width=width,height=height)#Show selection
        box.place(x=12,y=12)
        uid=self.create_window(pos,window=frame,width=width+24,height=height+24,anchor=anchor)
        frame.add_scrollbar((width+12,12),widget=box,height=height,bg=bg,color=fg,oncolor=fg)#portrait
        frame.add_scrollbar((12,height+12),widget=box,height=height,direction='x',bg=bg,color=fg,oncolor=fg)#transverse
        choices={}#'a':[a_text,a_back,is_sel:bool]
        maxwidth=0#Maximum width
        for i in data:
            end=box.bbox('all')
            end=5 if end==None else end[-1]
            text=box.create_text((5,end+7),text=i,fill=fg,font=font,anchor='nw')
            bbox=box.bbox(text)
            twidth=bbox[2]-bbox[0]
            maxwidth=twidth if twidth>maxwidth else maxwidth
            back=box.create_rectangle((3,bbox[1]-2,bbox[2]+2,bbox[3]+2),width=0,fill=bg)
            box.tkraise(text)
            choices[i]=[text,back,False]
            box.tag_bind(text,'<Enter>',lambda event,text=i : in_mouse(text))
            box.tag_bind(text,'<Leave>',lambda event,text=i : out_mouse(text))
            box.tag_bind(text,'<Button-1>',lambda event,text=i : sel_it(text))
            box.tag_bind(back,'<Enter>',lambda event,text=i : in_mouse(text))
            box.tag_bind(back,'<Leave>',lambda event,text=i : out_mouse(text))
            box.tag_bind(back,'<Button-1>',lambda event,text=i : sel_it(text))
        if maxwidth<width:
            maxwidth=width
        repaint_back()
        bbox=box.bbox('all')
        box.config(scrollregion=bbox)
        def set_y_view(event):
            box.yview_scroll(int(-1*(event.delta/120)), "units")
        box.bind('<MouseWheel>',set_y_view)
        return box,uid

effect

Test code

def test(event):
    a.title('TinUI Test')
    b.add_paragraph((50,150),'This is TinUI The event function reached by the button is echoed. In addition, the window title is also changed and the indent of the first line title is reduced')
    b.coords(m,100,5)
def test1(word):
    print(word)
def test2(event):
    ok1()
def test3(event):
    ok2()
def test4(event):
    from time import sleep
    for i in range(1,101):
        sleep(0.02)
        progressgoto(i)
def test5(result):
    b.itemconfig(scale_text,text='Current selected value:'+str(result))

if __name__=='__main__':
    a=Tk()
    a.geometry('700x700+5+5')

    b=TinUI(a,bg='white')
    b.pack(fill='both',expand=True)
    m=b.add_title((600,0),'TinUI is a modern way to show tkinter widget in your application')
    m1=b.add_title((0,680),'test TinUI scrolled',size=2,angle=24)
    b.add_paragraph((20,290),'''     TinUI Is based on tkinter Canvas development interface UI Layout scheme as tkinter Expand and TinEngine Exist with the expansion of. At present, TinUI Available for project.''',
    angle=-18)
    b.add_paragraph((20,100),'The following paragraph is to test the non parallel font display effect of the canvas TinUI Brief introduction of')
    b.add_button((250,450),'Test button',activefg='white',activebg='red',command=test,anchor='center')
    b.add_checkbutton((80,430),'allow TinUI test',command=test1)
    b.add_label((10,220),'This is made of canvas TinUI Drawn Label assembly')
    b.add_entry((250,330),350,'This is used for input',command=print)
    b.add_separate((20,200),600)
    b.add_radiobutton((50,480),300,'sky is blue, water is blue, too. So, what is your heart',('red','blue','black'),command=test1)
    b.add_link((400,500),'TinGroup knowledge base','http://tinhome.baklib-free.com/')
    b.add_link((400,530),'implement print function',print)
    _,ok1,_=b.add_waitbar1((500,220),bg='#CCCCCC')
    b.add_button((500,270),'Stop waiting for animation',activefg='cyan',activebg='black',command=test2)
    bu1=b.add_button((700,200),'Stop dot scroll bar',activefg='white',activebg='black',command=test3)[1]
    bu2=b.add_button((700,250),'nothing button 2')[1]
    bu3=b.add_button((700,300),'nothing button 3')[1]
    b.add_labelframe((bu1,bu2,bu3),'box buttons')
    _,_,ok2,_=b.add_waitbar2((600,400))
    b.add_combobox((600,550),text='How likely are you to go to Mount Everest',content=('20%','40%','60%','80%','100%','1000%'))
    b.add_button((600,480),text='Test progress bar (no event version)',command=test4)
    _,_,_,progressgoto,_,_=b.add_progressbar((600,510))
    b.add_table((180,630),data=(('a','space fans over the\nworld','c'),('you\ncan','2','3'),('I','II','have a dream, then try your best to get it!')))
    b.add_paragraph((300,850),text='Above is a table')
    b.add_onoff((600,100))
    b.add_spinbox((680,100))
    b.add_scalebar((680,50),command=test5)
    scale_text,_=b.add_label((890,50),text='Current selected value: 2')
    b.add_info((680,140),info_text='this is info widget in TinUI')
    mtb=b.add_paragraph((0,720),'Test menu (right click)')
    b.add_menubar(mtb,cont=(('command',print),('menu',test1),'-',('TinUI Text movement',test)))
    ttb=b.add_paragraph((0,800),'TinUI What can you do?')
    b.add_tooltip(ttb,'numerous')
    b.add_back(pos=(0,0),uids=(ttb,),bg='cyan')
    _,_,ok3,_=b.add_waitbar3((600,800),width=240)
    b.add_button((600,750),text='Stop ribbon wait box',command=lambda event:ok3())
    textbox=b.add_textbox((890,100),text='This is a text input box. Of course, it can't be in textbox Bind horizontal scrolling in parameters of'+'\n Line feed'*30)[0]
    textbox['wrap']='none'
    b.add_scrollbar((1095,100),textbox)
    b.add_scrollbar((890,305),textbox,direction='x')
    b.add_listbox((890,430),data=('item1','item2','item3','item4\n item4.1\n item4.2\n item4.3\n itme4.4\n item4.5','item5 and item5.1 and item5.2 and item5.3'),
    command=print)

    a.mainloop()

Final effect

github project

github project address of TinUI

pip Download

pip install tinui

Modify open source agreement

From February 2022, TinUI (including TinUIXml Technology) uses GPLv3 open source protocol. To use TinUI in your software or program, please follow the TinUI open source agreement and open source terms.

For open source terms, see github project address of TinUI.

epilogue

TinUI has launched the latest modern xml layout method - TinUIXml. Use it quickly!!!

May the world be peaceful and the motherland be strong and prosperous

🔆 tkinter innovation 🔆

Topics: Python Tkinter