午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

python實(shí)現(xiàn)文件搜索工具(簡(jiǎn)易版)

 wenxuefeng360 2022-07-03 發(fā)布于四川

在python學(xué)習(xí)過(guò)程中有一次需要進(jìn)行GUI 的繪制,

而在python中有自帶的庫(kù)tkinter可以用來(lái)簡(jiǎn)單的GUI編寫(xiě),于是轉(zhuǎn)而學(xué)習(xí)tkinter庫(kù)的使用。

學(xué)以致用,現(xiàn)在試著編寫(xiě)一個(gè)簡(jiǎn)單的磁文件搜索工具,

方法就是將指定的文件夾進(jìn)行掃描遍歷,把其中的每個(gè)文件路徑數(shù)據(jù)存入數(shù)據(jù)庫(kù),

然后使用數(shù)據(jù)庫(kù)搜索文件就很快捷。實(shí)現(xiàn)的效果大致如下:

 

 

整個(gè)程序分為大致幾個(gè)模塊:

主界面的繪制,

指定文件夾功能函數(shù),

搜索文件功能函數(shù),

ui線(xiàn)程與掃描線(xiàn)程同步函數(shù),

掃描線(xiàn)程工作函數(shù)

 

要實(shí)現(xiàn)掃描文件功能時(shí),

導(dǎo)入了一個(gè)這樣的模塊 disk.py 

這個(gè)模塊實(shí)現(xiàn)的功能就是將指定文件夾下的所有文件遍歷,并將路徑和所在盤(pán)符存到一個(gè)列表中返回

import os
import os.path as pt


def scan_file(path):
    result = []
    for root, dirs, files in os.walk(path):
        for f in files:
            file_path = pt.abspath(pt.join(root, f))

            result.append((file_path, file_path[0]))  # 保存路徑與盤(pán)符

    return result

然后我們需要將掃描到的文件存入到數(shù)據(jù)庫(kù)中,

因此需要編寫(xiě)數(shù)據(jù)庫(kù)模塊 datebase.py

import sqlite3


class DataMgr:

    def __init__(self):
        # 創(chuàng)建或打開(kāi)一個(gè)數(shù)據(jù)庫(kù)
        # check_same_thread 屬性用來(lái)規(guī)避多線(xiàn)程操作數(shù)據(jù)庫(kù)的問(wèn)題
        self.conn = sqlite3.connect("file.db", check_same_thread=False)
        # 建表
        self.conn.execute('create table if not exists disk_table(' 
                          'id integer primary key autoincrement,' 
                          'file_path text,' 
                          'drive_letter text)')
        # 創(chuàng)建索引 用來(lái)提高搜索速度
        self.conn.execute('create index if not exists index_path on disk_table(file_path)')

    # 批量插入數(shù)據(jù)
    def batch_insert(self, data):
        for line in data:
            self.conn.execute('insert into disk_table values (null,?,?)', line)
        self.conn.commit()

    # 模糊搜索
    def query(self, key):
        cursor = self.conn.cursor()
        cursor.execute("select file_path from disk_table where file_path like ?", ('%{0}%'.format(key),))
        r = [row[0] for row in cursor]
        cursor.close()
        return r

    def close(self):
        self.conn.close()

 

還需要一個(gè)額外的模塊為 progressbar.py

這個(gè)模塊的功能是在掃描時(shí)彈出一個(gè)進(jìn)度條窗口,

使得GUI功能看起來(lái)更完善

from tkinter import *
from tkinter import ttk


class GressBar:

    def start(self):
        top = Toplevel()  # 彈出式窗口,實(shí)現(xiàn)多窗口時(shí)經(jīng)常用到
        self.master = top
        top.overrideredirect(True)  # 去除窗體的邊框
        top.title("進(jìn)度條")
        Label(top, text="正在掃描選定路徑的文件,請(qǐng)稍等……", fg="blue").pack(pady=2)
        prog = ttk.Progressbar(top, mode='indeterminate', length=200)  # 創(chuàng)建進(jìn)度條
        prog.pack(pady=10, padx=35)
        prog.start()

        top.resizable(False, False)  # 參數(shù)為false表示不允許改變窗口尺寸
        top.update()
        # 計(jì)算窗口大小,使顯示在屏幕中央
        curWidth = top.winfo_width()
        curHeight = top.winfo_height()
        scnWidth, scnHeight = top.maxsize()
        tmpcnf = '+%d+%d' % ((scnWidth - curWidth) / 2, (scnHeight - curHeight) / 2)
        top.geometry(tmpcnf)
        top.mainloop()

    def quit(self):
        if self.master:
            self.master.destroy()

 

主體的search.py 代碼:

  1 from tkinter import *
  2 from tkinter import ttk
  3 import tkinter.filedialog as dir
  4 import queue
  5 import threading
  6 import progressbar
  7 import disk
  8 from database import DataMgr
  9 
 10 
 11 class SearchUI:
 12 
 13     def __init__(self):
 14         # 創(chuàng)建一個(gè)消息隊(duì)列
 15         self.notify_queue = queue.Queue()
 16         root = Tk()
 17         self.master = root
 18         self.create_menu(root)
 19         self.create_content(root)
 20         self.path = 'D:'
 21         root.title('the search tool')
 22         root.update()
 23         # 在屏幕中心顯示窗體
 24         curWidth = root.winfo_width()
 25         curHeight = root.winfo_height()
 26         scnWidth, scnHeight = root.maxsize()  # 得到屏幕的寬度和高度
 27         tmpcnf = '+%d+%d' % ((scnWidth - curWidth)/2, (scnHeight-curHeight)/2)
 28         root.geometry(tmpcnf)
 29 
 30         # 創(chuàng)建一個(gè)進(jìn)度條對(duì)話(huà)框?qū)嵗?/span>
 31         self.gress_bar = progressbar.GressBar()
 32 
 33         # 創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)的實(shí)例
 34         self.data_mgr = DataMgr()
 35 
 36         # 在UI線(xiàn)程啟動(dòng)消息隊(duì)列循環(huán)
 37         self.process_msg()
 38         root.mainloop()
 39 
 40     # ui線(xiàn)程與掃描線(xiàn)程同步
 41     def process_msg(self):
 42         # after方法,相當(dāng)于一個(gè)定時(shí)器,
 43         # 第一個(gè)參數(shù)是時(shí)間的毫秒值,
 44         # 第二個(gè)參數(shù)指定執(zhí)行一個(gè)函數(shù)
 45         self.master.after(400, self.process_msg)
 46         # 這樣我們就在主線(xiàn)程建立了一個(gè)消息隊(duì)列,
 47         # 每隔一段時(shí)間去消息隊(duì)列里看看,
 48         # 有沒(méi)有什么消息是需要主線(xiàn)程去做的,
 49         # 有一點(diǎn)需要特別注意,
 50         # 主線(xiàn)程消息隊(duì)列里也不要干耗時(shí)操作,
 51         # 該隊(duì)列僅僅用來(lái)更新UI。
 52         while not self.notify_queue.empty():
 53             try:
 54                 msg = self.notify_queue.get()
 55                 if msg[0] == 1:
 56                     self.gress_bar.quit()
 57 
 58             except queue.Empty:
 59                 pass
 60 
 61     # 掃描線(xiàn)程工作
 62     def execute_asyn(self):
 63         # 定義一個(gè)scan函數(shù),放入線(xiàn)程中去執(zhí)行耗時(shí)掃描
 64         def scan(_queue):
 65             if self.path:
 66                 paths = disk.scan_file(self.path)  # 位于disk.py
 67                 self.data_mgr.batch_insert(paths)  # 位于database.py
 68 
 69             _queue.put((1,))
 70         th = threading.Thread(target=scan, args=(self.notify_queue,))
 71         th.setDaemon(True)  # 設(shè)置為守護(hù)進(jìn)程
 72         th.start()
 73 
 74         self.gress_bar.start()
 75 
 76     # 菜單繪制
 77     def create_menu(self, root):
 78         menu = Menu(root)  # 創(chuàng)建菜單
 79 
 80         # 二級(jí)菜單
 81         file_menu = Menu(menu, tearoff=0)
 82         file_menu.add_command(label='設(shè)置路徑', command=self.open_dir)
 83         file_menu.add_separator()
 84         file_menu.add_command(label='掃描', command=self.execute_asyn)
 85 
 86         about_menu = Menu(menu, tearoff=0)
 87         about_menu.add_command(label='version1.0')
 88 
 89         # 在菜單欄中添加菜單
 90         menu.add_cascade(label='文件', menu=file_menu)
 91         menu.add_cascade(label='關(guān)于', menu=about_menu)
 92         root['menu'] = menu
 93 
 94     # 主界面繪制
 95     def create_content(self, root):
 96         lf = ttk.LabelFrame(root, text='文件搜索')
 97         lf.pack(fill=X, padx=15, pady=8)
 98 
 99         top_frame = Frame(lf)
100         top_frame.pack(fill=X, expand=YES, side=TOP, padx=15, pady=8)
101 
102         self.search_key = StringVar()
103         ttk.Entry(top_frame, textvariable=self.search_key, width=50).pack(fill=X, expand=YES, side=LEFT)
104         ttk.Button(top_frame, text="搜索", command=self.search_file).pack(padx=15, fill=X, expand=YES)
105 
106         bottom_frame = Frame(lf)
107         bottom_frame.pack(fill=BOTH, expand=YES, side=TOP, padx=15, pady=8)
108 
109         band = Frame(bottom_frame)
110         band.pack(fill=BOTH, expand=YES, side=TOP)
111 
112         self.list_val = StringVar()
113         listbox = Listbox(band, listvariable=self.list_val, height=18)
114         listbox.pack(side=LEFT, fill=X, expand=YES)
115 
116         vertical_bar = ttk.Scrollbar(band, orient=VERTICAL, command=listbox.yview)
117         vertical_bar.pack(side=RIGHT, fill=Y)
118         listbox['yscrollcommand'] = vertical_bar.set
119 
120         horizontal_bar = ttk.Scrollbar(bottom_frame, orient=HORIZONTAL, command=listbox.xview)
121         horizontal_bar.pack(side=BOTTOM, fill=X)
122         listbox['xscrollcommand'] = horizontal_bar.set
123 
124         # 給list動(dòng)態(tài)設(shè)置數(shù)據(jù),set方法傳入一個(gè)元組
125         self.list_val.set(('等待搜索',))
126 
127     # 搜索文件
128     def search_file(self):
129         if self.search_key.get():
130             result_data = self.data_mgr.query(self.search_key.get())
131             if result_data:
132                  self.list_val.set(tuple(result_data))
133 
134     # 指定文件夾
135     def open_dir(self):
136         d = dir.Directory()
137         self.path = d.show(initialdir=self.path)
138 
139 
140 if __name__ == '__main__':
141     SearchUI()

 

問(wèn)題總結(jié):

1.UI線(xiàn)程負(fù)責(zé)界面的繪制與更新,如果在UI線(xiàn)程中進(jìn)行耗時(shí)操作,會(huì)影響界面的流暢性,所以需要異步線(xiàn)程。 

 此時(shí)的問(wèn)題在于UI的主線(xiàn)程與異步線(xiàn)程的通信問(wèn)題,為什么一定要兩個(gè)線(xiàn)程通信?

因?yàn)樵诖蠖鄶?shù)GUI界面編程中,異步線(xiàn)程都是不能對(duì)當(dāng)前界面進(jìn)行操作更新的,否則會(huì)引起界面混亂。

可以簡(jiǎn)單的理解成  如果異步線(xiàn)程也操作主界面,則兩個(gè)線(xiàn)程對(duì)相同資源進(jìn)行操作,就會(huì)導(dǎo)致混亂。

接下來(lái)的問(wèn)題是tkinter中沒(méi)有提供接口進(jìn)行線(xiàn)程通信,因此我們通過(guò)消息隊(duì)列的方式來(lái)同步線(xiàn)程,用到的類(lèi)為Queue。

項(xiàng)目中當(dāng)在消息隊(duì)列中檢索到消息為元組(1, )時(shí),說(shuō)明子線(xiàn)程(掃描)已經(jīng)結(jié)束了,告知主線(xiàn)程可以結(jié)束子線(xiàn)程了。

 

2.掃描文件夾時(shí)需要將所選文件夾中的所有文件遍歷一遍,發(fā)現(xiàn)python中提供了方法os.walk(path), 可以直接達(dá)到這一效果,所以說(shuō)python在寫(xiě)代碼時(shí)確實(shí)提供了方便。

 

3.該磁盤(pán)搜索工具用到的原理是將文件路徑存到數(shù)據(jù)庫(kù)中,再進(jìn)行檢索。 選用的數(shù)據(jù)庫(kù)為sqlite,已經(jīng)可以滿(mǎn)足該項(xiàng)目的要求。在主線(xiàn)程創(chuàng)建數(shù)據(jù)庫(kù),子線(xiàn)程操作數(shù)據(jù)庫(kù),有可能出現(xiàn)問(wèn)題,因此設(shè)置check_same_thread = false 來(lái)拒絕多線(xiàn)程的訪問(wèn)。

 

4.在進(jìn)行GUI編程時(shí),打算在掃描等待時(shí)添加一個(gè)進(jìn)度條顯示窗口,也就需要多窗口,用到了toplevel,表現(xiàn)為一個(gè)彈出式窗口,在使用toplevel時(shí),要注意首先需要一個(gè)根窗口。

 





    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多