线程tkinter在函数执行期间在帧中添加标签

时间:2015-05-15 15:08:27

标签: python multithreading tkinter

我为实验室编写了一个管道,所以我知道在GUI中插入一个“控制台”是不可能的,所以我用一个Frame制作它并在上面贴上标签。

但问题是,我是线程的初学者,我不知道在循环函数执行后如何使用它将我的标签放入我的框架中。

所以这是我的代码(python 3.x):

########
# IMPORTS #
########

from tkinter import *
from tkinter import ttk
from tkinter.filedialog import *
from tkinter.messagebox import *
import os
import glob
from datetime import date, time, datetime

#########
# FUNCTION #
#########

def OneFile(path1,DB,path2,seq,seq2,typ,path3):
    """
    This function is very long, take all the sequences of the input file and BLAST it to the Library DATABASE
    path : path to the library databse
    DB : library database name
    path2 : path of the file with the query sequences
    seq : name of the file with the query sequences append with a job ID
    seq2 : name of the file with the query sequence 
    Typ : Nucleotide or Proteine
    """
    from datetime import date, time, datetime
    import subprocess
    import platform
    import time
    OS = platform.system()
    if OS == 'Linux' or OS == 'Darwin':
        pathLibrary = path1+'/'
        pathSequence = path2+'/'
        pathFolder = path3+'/'
    if OS == 'Windows':
        pathLibrary = path1+'\\'
        pathSequence = path2+'\\'
    if typ not in [(1,1),(2,2),(1,2),(2,1)]:
        showerror('Error : Missing Type !', "You do not choose your type\n(nucleotides or proteins)")
    else:
        library = DB
        if os.path.isfile(pathLibrary+library) != True:
            showerror('Error : Missing File !', "You must choose a Library Database file")
        else:
            if os.path.isfile(pathSequence+seq2) != True:
                showerror('Error : Missing File !', "You must choose your sequence file")
            else:
                if typ == (1,1):
                    typ = "blastn"
                if typ == (2,2):
                    typ = "blastp"
                if typ == (1,2):
                    typ = "blastx"
                if typ == (2,1):
                    typ = "tblastn"
                if OS == 'Linux' or OS == 'Darwin':
                    t0 = time.time()
                    query = str(seq2)
                    blast = str(seq)+'_Blast.txt'
                    seqs = str(seq)+'_seqs.txt'
                    subprocess.call(typ+" -query "+pathSequence+query+" -db "+pathLibrary+library+" -evalue 1e-10 -out "+pathFolder+blast, shell=True)
                    subprocess.call("grep '\(Sbjct\|>\)' "+pathFolder+blast+" > "+pathFolder+seqs, shell=True)
                    t1 = time.time()
                    print('Job finish in '+str(round(t1-t0,2))+' seconds')
                if OS == 'Windows':
                    t0 = time.time()
                    query = str(seq2)
                    blast = str(seq)+'_Blast.txt'
                    seqs = str(seq)+'_seqs.txt'
                    subprocess.call(typ+' -query '+pathSequence+query+' -db '+pathLibrary+library+' -evalue 1e-10 -out '+pathSequence+blast, shell=True)
                    print('Fichier n° '+str(1)+' '+str(seq2))
                    subprocess.Popen('findstr "Sbjct >" '+pathSequence+blast+' > '+pathSequence+seqs, shell=True)
                    t1 = time.time()
                    print('Job finish in '+str(round(t1-t0,2))+' seconds')

#######
# CLASS  #
#######

class GraphicalUserInterface():
    #principal application
    def __init__(self):
        #constructor
        self.fen = Tk()
        self.fen.title("Starch Enzyme Pipeline")

        #first label
        self.label1 = Label(self.fen, text="Folder with your set(s) : ")
        self.label1.grid(row=0, columnspan=2, sticky="W")
        #first button
        self.browse1 = Button(self.fen)
        self.browse1.config(text="Browse",command=self.folderPath)
        self.browse1.grid(row=1,column=0, sticky="W")

        #label to show the path
        self.varLabel1 = StringVar()
        self.pathLabel1 = Label(self.fen, textvariable=self.varLabel1, relief=SUNKEN)
        self.pathLabel1.grid(row=1,column=1, sticky="EW")

        #second title
        self.label2 = Label(self.fen, text="Folder with your library database(s) ")
        self.label2.grid(row=2,column = 0, columnspan=2 , sticky="W")

        #second button
        self.browse2 = Button(self.fen)
        self.browse2.config(text="Browse",command=self.folderPath2)
        self.browse2.grid(row=3,column=0, sticky="W")

        #label to show the path for database
        self.varLabel2 = StringVar()
        self.pathLabel2 = Label(self.fen, textvariable=self.varLabel2, relief=SUNKEN)
        self.pathLabel2.grid(row=3,column=1, sticky = "EW")

        #Frame wrappe listBox and other
        self.frameListBoxAll = Frame(self.fen)
        self.frameListBoxAll.grid(row=6,columnspan=2)

        #list box label
        self.labListBox1 = Label(self.frameListBoxAll, text="Your sets :",padx=10)
        self.labListBox1.grid(row=0,column=0)
        self.labListBox2 = Label(self.frameListBoxAll, text="Your library database :",padx=10)
        self.labListBox2.grid(row=0,column=1)

        #frame with listbox1
        self.frame1 = Frame(self.frameListBoxAll, bd=2, relief=SUNKEN)
        self.frame1.grid(row=1,column=0)

        #frame with listbox1
        self.frame2 = Frame(self.frameListBoxAll, bd=2, relief=SUNKEN)
        self.frame2.grid(row=1,column=1)

        #scrollbar listbox1
        self.scrollbar1 = Scrollbar(self.frame1)
        self.scrollbar1.grid(row=0,column=1, sticky="NS")
        self.scrollbar2 = Scrollbar(self.frame2)
        self.scrollbar2.grid(row=0,column=3, sticky="NS")
        self.scrollbar3 = Scrollbar(self.frame1, orient=HORIZONTAL)
        self.scrollbar3.grid(row=1,column=0, sticky="WE")
        self.scrollbar4 = Scrollbar(self.frame2, orient=HORIZONTAL)
        self.scrollbar4.grid(row=1,column=2, sticky="WE")

        #liste box
        self.listeBox1 = Listbox(self.frame1, selectmode=EXTENDED, exportselection=0, yscrollcommand=self.scrollbar1.set, xscrollcommand=self.scrollbar3.set)
        self.listeBox1.grid(row=0,column = 0)
        self.scrollbar1.config(command=self.listeBox1.yview)
        self.scrollbar3.config(command=self.listeBox1.xview)

        #liste box2
        self.listeBox2 = Listbox(self.frame2, selectmode=EXTENDED, exportselection=0, yscrollcommand=self.scrollbar2.set, xscrollcommand=self.scrollbar4.set)
        self.listeBox2.grid(row=0,column = 2)
        self.scrollbar2.config(command=self.listeBox2.yview)
        self.scrollbar4.config(command=self.listeBox2.xview)

        #radioboutton list box 1
        self.var = IntVar()
        for item in [1,2]:
            if item == 1:
                self.rb = Radiobutton(self.frameListBoxAll, text='Nucleotides',value=item,variable=self.var)
                self.rb.grid(row=2, column=0)
            if item == 2:
                self.rb = Radiobutton(self.frameListBoxAll, text='Proteins',value=item,variable=self.var)
                self.rb.grid(row=3, column=0)

        #radioboutton list box 2
        self.var2 = IntVar()
        for item in [1,2]:
            if item == 1:
                self.rb2 = Radiobutton(self.frameListBoxAll, text='Nucleotides',value=item,variable=self.var2)
                self.rb2.grid(row=2, column=1)
            if item == 2:
                self.rb2 = Radiobutton(self.frameListBoxAll, text='Proteins',value=item,variable=self.var2)
                self.rb2.grid(row=3, column=1)
        #variables
        self.path1 = str()
        self.path2 = str()
        self.path3 = str()
        #RUN Buttun
        self.runbutton = Button(self.fen, text="RUN",command=self.start_foo_thread).grid(row=7,column=0,columnspan=2)

        #FRAME CONSOLE
        self.console = Frame(self.fen)
        self.console.config(relief=SUNKEN, bg="black", height=200, width=400)
        self.console.grid(row=8, columnspan=10)
        self.console.grid_propagate(False) #to block the size of the frame

        #QUIT BUTTON
        self.quitButton = Button(self.fen)
        self.quitButton.config(text="QUIT", command=self.fen.destroy)
        self.quitButton.grid(row=100,column=0)

    def folderPath(self):
         path = askdirectory(title='Choose your set folder')
         self.varLabel1.set(path)
         self.listeBox1.delete(0, END)
         for filename in sorted(glob.glob(path+'/*')):
             if os.path.isfile(filename):
                 #stockage of path
                 self.path1 = os.path.split(filename)[0]
                 name = os.path.split(filename)[1]
                 self.listeBox1.insert(END, name)

    def folderPath2(self):
        path = askdirectory(title="Choose your library database folder")
        self.varLabel2.set(path)
        self.listeBox2.delete(0, END)
        for filename in sorted(glob.glob(path+'/*')):
            if os.path.isfile(filename):
                #stockage of path
                self.path2 = os.path.split(filename)[0]
                name = os.path.split(filename)[1]
                self.listeBox2.insert(END, name)

    def run(self):
        self.fen.mainloop()

    def createJobName():
        job = str(datetime.now())
        job = job.replace(" ","-")
        return job

    def typeNP(self):
        liste = []
        #selection of query files
        valListBox1 = [self.listeBox1.get(idx) for idx in self.listeBox1.curselection()]
        #selection of database file
        valListBox2 = [self.listeBox2.get(idx) for idx in self.listeBox2.curselection()]
        #selection of sequence type
        typ = (self.var.get(),self.var2.get())
        # loop
        for i in range(len(valListBox2)):
            job = GraphicalUserInterface.createJobName()
            path1 = self.path2
            path2 = self.path1
            DB = valListBox2[i]
            path3 = os.getcwd()+"/"+DB+job
            if os.path.isdir(DB+job) == True:
                showwarning('Warning', "The folder already exist \n or they are no folder name !\nChange or get the folder name")
            else:
                os.mkdir(DB+job)
            for filename in valListBox1:
                seq = filename+job
                seq2 = filename
                #stock data for OneFile function
                liste.append([path1,DB,path2,seq,seq2,typ,path3])
        return liste

    def start_foo_thread(self):
        liste = self.typeNP()
        for i in range(len(liste)):
            global foo_thread
            import threading
            print('Fichier n°'+str(i+1)+' '+str(liste[i][4]))
            stringLabel = Label(self.console,text='Fichier n°'+str(i+1)+' '+str(liste[i][4]),bg='black', fg='white')
            stringLabel.grid(row=i,sticky="W")
            foo_thread = threading.Thread(target=OneFile(liste[i][0],liste[i][1],liste[i][2],liste[i][3],liste[i][4],liste[i][5],liste[i][6]))
            foo_thread.daemon = True
            foo_thread.start()



#########
# AUTORUN #
#########

if __name__ == '__main__':
    app = GraphicalUserInterface()
    app.run()

问题是当循环开始时:

    def start_foo_thread(self):
        liste = self.typeNP()
        for i in range(len(liste)):

使用print函数,我看到函数运行,但迭代完成后标签不会进入框架。循环完成后,我在框架中看到了我的标签。 在我的循环中我的函数运行的同时,在我的框架中使用我的标签的正确代码是什么?

1 个答案:

答案 0 :(得分:0)

您有时必须手动更新小部件。你发布了太多的代码,所以这个简单的例子应该显示问题。按原样运行,标签不会显示,直到函数返回。使用update_idletasks()运行取消注释执行我认为你想要的。另请注意,程序会停止,直到调用子进程返回。

import sys
if sys.version_info[0] < 3:
    import Tkinter as tk     ## Python 2.x
else:
    import tkinter as tk     ## Python 3.x

class GraphicalUserInterface():
    def __init__(self):
        #constructor
        self.fen = tk.Tk()
        self.fen.title("Starch Enzyme Pipeline")
        self.console=tk.Frame(self.fen)
        self.console.grid()
        self.liste=["one", "two", "three"]
        tk.Button(self.fen, text="Exit", command=self.fen.quit).grid(row=1)
        self.start_foo_thread()
        self.fen.mainloop()

    def start_foo_thread(self):
        for ctr in range(len(self.liste)):
            lit='Fichier n %s' % (ctr)
            print(lit)
            stringLabel = tk.Label(self.console, text=lit,
                                   bg='black', fg='white')
            stringLabel.grid(row=ctr,sticky="W")
            ##self.console.update_idletasks()
        print("Waiting 2 seconds")
        self.fen.after(2000)  ## wait 2 seconds to show effect
        print("Return from function")

if __name__ == '__main__':
    app = GraphicalUserInterface()