1. 程式人生 > >python tkinter控制元件treeview的資料列表顯示的實現_code

python tkinter控制元件treeview的資料列表顯示的實現_code

素材檔案

  1. result.txt
  2. result2.txt

result.txt檔案的資料來源是爬取貓眼電影前一百名的電影,而result2.txt檔案只不過是內容上把result.txt的內容複製幾十次,使其資料足夠多,現截選如下:

{"排名": "1", "片名": "霸王別姬", "主演": "張國榮,張豐毅,鞏俐", "上映時間": "1993-01-01(中國香港)", "評分": "9.6"}
{"排名": "2", "片名": "羅馬假日", "主演": "格利高裡·派克,奧黛麗·赫本,埃迪·艾伯特", "上映時間": "1953-09-02(美國)", "評分": "9.1"}
{"排名": "3", "片名": "肖申克的救贖", "主演": "蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓", "上映時間": "1994-10-14(美國)", "評分": "9.5"}
{"排名": "4", "片名": "這個殺手不太冷", "主演": "讓·雷諾,加里·奧德曼,娜塔莉·波特曼", "上映時間": "1994-09-14(法國)", "評分": "9.5"}
{"排名": "5", "片名": "教父", "主演": "馬龍·白蘭度,阿爾·帕西諾,詹姆斯·肯恩", "上映時間": "1972-03-24(美國)", "評分": "9.3"}
{"排名": "6", "片名": "泰坦尼克號", "主演": "萊昂納多·迪卡普里奧,凱特·溫絲萊特,比利·贊恩", "上映時間": "1998-04-03", "評分": "9.5"}
{"排名": "7", "片名": "龍貓", "主演": "日高法子,阪本千夏,糸井重裡", "上映時間": "1988-04-16(日本)", "評分": "9.2"}
{"排名": "8", "片名": "唐伯虎點秋香", "主演": "周星馳,鞏俐,鄭佩佩", "上映時間": "1993-07-01(中國香港)", "評分": "9.2"}
{"排名": "9", "片名": "千與千尋", "主演": "柊瑠美,入野自由,夏木真理", "上映時間": "2001-07-20(日本)", "評分": "9.3"}
{"排名": "10", "片名": "魂斷藍橋", "主演": "費雯·麗,羅伯特·泰勒,露塞爾·沃特森", "上映時間": "1940-05-17(美國)", "評分": "9.2"}
{"排名": "11", "片名": "亂世佳人", "主演": "費雯·麗,克拉克·蓋博,奧利維婭·德哈維蘭", "上映時間": "1939-12-15(美國)", "評分": "9.1"}

實現效果

版本一實現的程式碼:

# -*- coding: utf-8 -*-
"""
Created on Fri Jan  4 13:44:40 2019

@author: HJY
"""

import tkinter as tk
from tkinter import ttk
import re
import time

#固定
pattern = '{"排名": "(.*?)", "片名": "(.*?)", "主演": "(.*?)", "上映時間": "(.*?)", "評分": "(.*?)"}\n'
patch = re.compile(pattern)

class info():
    def __init__(self,):
        self.root = tk.Tk()
        self._setpage()
               
    def _setpage(self,):
        start= time.time()
        
        self.scrollbar = tk.Scrollbar(self.root,)
        self.scrollbar.pack(side=tk.RIGHT,fill=tk.Y)
           
        title=['1','2','3','4','5',]
        self.box = ttk.Treeview(self.root,columns=title,
                                yscrollcommand=self.scrollbar.set,
                                show='headings')
        
        self.box.column('1',width=50,anchor='center')
        self.box.column('2',width=200,anchor='center')
        self.box.column('3',width=300,anchor='center')
        self.box.column('4',width=150,anchor='center')
        self.box.column('5',width=50,anchor='center')
        
        self.box.heading('1',text='Range')
        self.box.heading('2',text='Flim Name')
        self.box.heading('3',text='Actor')
        self.box.heading('4',text='Time')
        self.box.heading('5',text='Score')
        
        self.dealline()
                
        self.scrollbar.config(command=self.box.yview)
        self.box.pack()

        end=time.time()
        tk.Label(self.root,text=end-start,fg='red').pack()
        tk.Button(self.root,text='Look',bg='green',).pack()
                
    def readdata(self,):    
        """逐行讀取檔案"""    
        
        #讀取gbk編碼檔案,需要加encoding='utf-8'
        f = open('result.txt','r',encoding='utf-8')
        line = f.readline()
        while line:
            yield line
            line = f.readline()            
        f.close()
        
    def dealline(self,):
        op = self.readdata()
        while 1:
            try:
                line = next(op)
            except StopIteration as e:
                break
            else:
                result = patch.match(line)
                self.box.insert('','end',values=[result.group(i) for i in range(1,6)])
                                
             
if __name__ == '__main__':
    op = info()
    op.root.mainloop()

首先這裡引入yield的用法,實現逐行讀取檔案,迭代器只有在每一次next()的時候才會產生下一條資料,而不需要一次性讀取整份檔案,處理檔案中的每行資料並且儲存結果,這種方式可以有效的避免面對大檔案時的處理時間以及記憶體等問題。
但這裡還是等檔案中的資料都處理好都插入tkinter控制元件中時,才執行下一步的程式(也就是self.dealline()之後的程式語句),這會造成什麼問題呢?如果處理的是result.txt檔案那種只有100條資料的檔案,使用者不會感受到什麼,但若處理result2.txt那樣的檔案,那麼就會感覺到卡頓,似乎要等一會才顯示應用程式。

解決思路

可否一開始只向控制元件中插入10條或者50條資料,當用戶瀏覽到第10條資料時就馬上載入接下來的10條資料?

實現一:繫結滑鼠的滾輪事件,一旦監聽到下滾事件,就觸發載入。
實現二:當用戶點選按鈕時,就載入資料。這種一般用於翻頁等等。
實現三:當用戶拖拽滑塊到底端時,若還有資料沒載入完,就觸發載入(為實現)。

改進後代碼實現

# -*- coding: utf-8 -*-
"""
Created on Tue Jan  8 13:45:21 2019

@author: HJY
"""

# -*- coding: utf-8 -*-
"""
Created on Fri Jan  4 13:44:40 2019

@author: HJY
"""

import tkinter as tk
from tkinter import ttk

import re
import time

#固定
pattern = '{"排名": "(.*?)", "片名": "(.*?)", "主演": "(.*?)", "上映時間": "(.*?)", "評分": "(.*?)"}\n'
patch = re.compile(pattern)


class info():
    def __init__(self,):
        self.root = tk.Tk()
        self._setpage()       
        
    def _setpage(self,):
        start= time.time()
        
        self.scrollbar = tk.Scrollbar(self.root,command=self.moveScroll)
        self.scrollbar.bind("<MouseWheel>",self.moveScroll)
        self.scrollbar.pack(side=tk.RIGHT,fill=tk.Y)
    
        
        title=['1','2','3','4','5',]
        self.box = ttk.Treeview(self.root,columns=title,
                                yscrollcommand=self.scrollbar.set,
                                show='headings')
        self.box.bind("<MouseWheel>",self.moveScroll)
        
        self.box.column('1',width=50,anchor='center')
        self.box.column('2',width=200,anchor='center')
        self.box.column('3',width=300,anchor='center')
        self.box.column('4',width=150,anchor='center')
        self.box.column('5',width=50,anchor='center')
        
        self.box.heading('1',text='Range')
        self.box.heading('2',text='Flim Name')
        self.box.heading('3',text='Actor')
        self.box.heading('4',text='Time')
        self.box.heading('5',text='Score')
        
        #物件處理
        self.op = self.readdata()
        self.dealline(self.op)
        
        self.scrollbar.config(command=self.box.yview)
        self.box.pack()

        end=time.time()
        tk.Label(self.root,text=end-start,fg='red').pack()
        tk.Button(self.root,text='Look',bg='green',command=self.turn).pack()
        
        
    #翻頁模式,每點選一次,載入多10條資料
    def turn(self):
#        self.scrollbar.set(0.89,0.99)    
#        print(self.scrollbar.get())

        self.dealline(self.op)
        
    #滑鼠滾動模式,下滑時載入資料
    def moveScroll(self,event):
        if event.delta < 0:
            self.dealline(self.op)
               
    def dragScroll(self):
        #未實現
        pass
    
    def readdata(self,):    
        """逐行讀取檔案"""    
        
        #讀取gbk編碼檔案,需要加encoding='utf-8'
        f = open('result2.txt','r',encoding='utf-8')
        line = f.readline()
        while line:
            yield line
            line = f.readline()
            
        f.close()
       
    def dealline(self,op):
        self.cal = 0
        while 1:
            try:
                line = next(op)
            except StopIteration:
                break
            else:
                result = patch.match(line)
                self.box.insert('','end',values=[result.group(i) for i in range(1,6)])
                
                self.cal +=1
                if self.cal == 10:
                    break



if __name__ == '__main__':
    op = info()
    op.root.mainloop()

評註

這種模式的問題:  
1、如果是資料是用來搜尋的,而使用者沒有觸發載入,且所要搜尋的資料並未在已經載入的資料中,那麼就會導致搜尋不到。當然,如果搜尋時不基於控制元件中的資料,而基於檔案本身或者資料庫等,就不存在這種考慮的必要。另外,如果要求展
示搜尋到的資料所在的行,那麼就需要一旦搜尋的資料行未載入,就要馬上載入到所搜尋的資料為止的所有未載入資料。 

2、滾輪下滾時,使用者還沒將資料翻到底端資料,就觸發了載入。

新的想法

可以利用scrollbar控制元件的get()方法,獲得滑塊的位置,一旦滑塊的底端位置為1,則滑塊已經到底端,此時觸發載入,又由於next()迭代器再沒有資料時會觸發stopiteration異常阻止載入,而若還有資料則載入。我們可以將這一過程實現為函式,與scrollbar控制元件的command屬性繫結,這樣只要滑動滑塊就會觸發函式呼叫。

但是我們已經將其與treeview控制元件的yview函式繫結,聯動實現滑塊滑動列表框,所以我們需要把我們自己的實現嵌入這個yview函式,或者yview函式嵌入我們實現的函式裡,只是中間一些環節只有理解了yview函式的處理模式,才好做了。