tkinter使用线程崩溃发送请求

时间:2012-05-22 13:03:10

标签: python multithreading thread-safety tkinter python-2.7

我使用tkinter创建了一个GUI,用于将请求(从文件)发送到特定的URL

一切顺利,直到我想用线程......

因此我使用一个线程发送请求以保持对GUI的控制。

现在它不稳定,应用程序几乎每次都会崩溃!

我尝试使用'threading.Thread'代替'thread.start_new_thread',但它不能用作新线程(在发送请求时无法控制GUI)

我很确定这个问题与'updateStatus'函数有关,它将字符串写入状态栏以及结果或日志选项卡,因为当我删除此选项时它会起作用。

我很想知道如何将该线程与GUI和状态更新程序一起使用

同样,结果选项卡有时会崩溃应用程序 - 但只是随机(对我来说)......

这是代码:


from ftplib import FTP
from Tkinter import *
import threading
import thread
import Queue
import Tkinter, tkFileDialog, ttk
import os, base64, time, string, sys
import urllib2, telnetlib



class App:
    def __init__(self, master):

        self.selectedFileName   = StringVar()
        self.inProgress         = 1.0
        self.headers            = {}
        self.word_list          = []
        self.localPath          = os.path.join(os.getcwd())

        self.tabs =  ttk.Notebook(root)
        frame = Frame(self.tabs, width=750, height=300)
        tab_log     = ttk.Frame(self.tabs)
        tab_results = ttk.Frame(self.tabs)

        self.tabs.add(frame, text="  Main  ")
        self.tabs.add(tab_log, text="   Log   ")
        self.tabs.add(tab_results, text="  Results  ")
        self.tabs.pack()


        ######################
        #      Main TAB:     #
        ######################

        # Application URL: LABEL
        self.label_url = Label(frame, text="URL  (https://myApp:666/):", foreground="black")
        self.label_url.pack(side=LEFT)
        self.label_url.place(bordermode=OUTSIDE, x=5, y=40)

        # App URL: TEXT
        self.text_url = Entry(frame, width=66)
        self.text_url.place(bordermode=OUTSIDE, x=200, y=40)

        # Upload File: Button
        self.upload_button = Button(frame, text="Upload File", command=self.browseFile, state=NORMAL)
        self.upload_button.place(bordermode=OUTSIDE, x=5, y=100)

        # Upload File: Label
        self.upload_label = Label(frame, text="")
        self.upload_label.place(bordermode=OUTSIDE, x=100, y=100)

        ########## Main Buttons ###########

        # >> Run Button
        self.button_run = Button(frame, text="Run", width=25, height=2, command=self.runPressed)
        self.button_run.place(bordermode=OUTSIDE, x=300, y=325)

        # >> Quit Button
        self.quit_button = Button(frame, text="Quit", width=10, height=1, command=self.executeQuit)
        self.quit_button.place(bordermode=OUTSIDE, x=666, y=335)

        # Status bars
        self.text_status = Text(frame, width=106, height=2, background="#DDDDDD", foreground="#990000")
        self.text_status.insert(INSERT, "Status")
        self.text_status.configure(state=DISABLED)
        self.text_status.place(bordermode=OUTSIDE, x=0, y=260)

        # Progress bar
        self.progressBar = ttk.Progressbar(frame, orient="horizontal", length=748, mode="determinate")
        #self.progressBar.pack()
        self.progressBar.place(bordermode=OUTSIDE, x=1, y=300)

        ######################
        #      Log TAB:      #
        ######################
        self.log_text = Text(tab_log, width=104, state=DISABLED)
        self.log_text.pack(side=LEFT, fill=BOTH)
        self.button_toResults = Button(tab_log, text="Clear Log", command=self.clearLog)
        self.button_toResults.place(bordermode=OUTSIDE, x=678, y=0)

        # Scroll Bar
        self.logScrollBar = Scrollbar(tab_log)
        self.logScrollBar.pack(side=RIGHT, fill=Y)
        self.logScrollBar.configure(command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=self.logScrollBar.set)



        ######################
        #    Results TAB:    #
        ######################
        self.results_text = Text(tab_results, width=104, state=NORMAL)
        self.results_text.pack(side=LEFT, fill=BOTH)

        # Scroll Bar
        self.resScrollBar = Scrollbar(tab_results)
        self.resScrollBar.pack(side=RIGHT, fill=Y)
        self.resScrollBar.configure(command=self.log_text.yview)
        self.results_text.configure(yscrollcommand=self.resScrollBar.set)


    ## functions... ##


    # clears log in 'Log' tab
    def clearLog(self):
        self.log_text.configure(state=NORMAL)
        self.log_text.delete(1.0, END)
        self.log_text.configure(state=DISABLED)



    # Browse file system to get file to use for requests
    def browseFile(self):
        self.selectedFileName = tkFileDialog.askopenfilename(filetypes = ( ("Text files", "*.txt"), ("All files", "*.*") ))
        try:
            tmpFile = open(self.selectedFileName, 'r')
            self.editStatus('opened: %s' %self.selectedFileName, 1)
            tmpFile.close()
            selectedFileLength = len(self.selectedFileName)
            if selectedFileLength > 75:
                displayedFileName = '...' +self.selectedFileName[selectedFileLength -70:]
            else:
                displayedFileName = self.selectedFileName

            self.upload_label.configure(text='Selected file:        %s' %displayedFileName, foreground="darkblue")
            tmpFile.close()

        except:
            'Error! file does not exist...'



    ## run() ##

    def runPressed(self):
        try:
            self.executeRun()
            """self.lock = thread.allocate_lock()
            thread.start_new_thread(self.executeRun, ())"""
        except:
            self.editStatus('Could not start! please try again...', 1)



    def executeRun(self):
        self.progressBar["value"] = 0

        # is URL missing?
        self.URL_TEXT = self.text_url.get().strip()
        lenURL = len(self.URL_TEXT)
        if lenURL < 1:
            self.editStatus("Please fill in the URL and Press 'Run' again", 1)
            return 0

        if self.URL_TEXT[lenURL -1:] < '/':
            self.URL_TEXT = self.URL_TEXT +'/'

        try:
            tmpFile = open(self.selectedFileName, 'r')

        except:
            self.editStatus("Please select file Press 'Run' again", 1)
            return 0

        tmpFile.close()

        self.progressBar["value"] = 33

        word_file = self.selectedFileName
        myFile = open(word_file, 'r')
        self.word_list = myFile.read().split('\n')
        myFile.close()

        self.progressBar["value"] = 66

        # Create Results folder and files
        self.resultsFolder = self.localPath +'\\' +'Results'

        # Creates 'Results' folder
        try:
            os.makedirs(self.resultsFolder)
            self.editStatus('folder created...', 1)
        except:
            self.editStatus('Folder already exists', 1)

        self.result_file = open(self.resultsFolder +'\\results.txt', 'w')
        self.result_file.close()

        self.progressBar["value"] = 100

        self.lineCounter = 1
        self.totalLines = len(self.word_list)
        self.editStatus('Total lines in file: %d' %self.totalLines, 1)
        # number of reconnection times in case connection has been losts
        timestoTry = 12
        timesTried = 1

        self.progressBar["value"] = 0

        #self.myThread = threading.Thread(target=self.sendRequests())
        thread.start_new_thread(self.sendRequests, ())
        """while self.lineCounter < self.totalLines and timesTried < timestoTry:
            self.editStatus('Stopped @ line number: %d' %self.lineCounter, 1)
            self.editStatus('connection lost, will try again(%s)...' %timesTried, 1)
            time.sleep(5 +timesTried)
            timesTried = timesTried +1
            self.word_list = self.word_list[self.lineCounter -1:]
            thread.start_new_thread(self.sendRequests, ())"""



    # send requests from file
    def sendRequests(self):
        url = self.URL_TEXT
        progress = self.inProgress
        for word in self.word_list:
            self.editStatus(str('Sending (%d): %s%s' %(self.lineCounter, url, word)),1)
            self.lineCounter = self.lineCounter +1
            progress = (self.lineCounter / float(self.totalLines)) * 100
            self.progressBar["value"] = progress
            try:
                myRequest = urllib2.Request(url +word, data=None, headers=self.headers)
                myResponse = urllib2.urlopen(myRequest, timeout=3)
                res_html = myResponse.read()
                myResponse.close()

                if myResponse.getcode() == 200:
                    self.editStatus(url +word +'\n', 2)
                    resFile = open(self.resultsFolder +'\\results.txt', 'a')
                    resFile.write(url +word +'\n')
                    resFile.close()



            except urllib2.HTTPError as e:
                 print 'could not reach: %s%s' %(url, word)


            except urllib2.URLError as e:
                self.editStatus(str(e), 1)
                #thread.exit()
                return 0

        #thread.exit()
        if self.lineCounter == self.totalLines +1:
            self.finito()
            return 1
        else:
            self.editStatus('something went wrong... finished but not done?!', 1)

            return 0


    # grand finale!
    def finito(self):   
        self.editStatus('                 ', 1)
        self.editStatus('                 ', 1)
        self.editStatus('** !!! Mission accomplished !!! **', 1)


    # 'Quit' button clicked
    def executeQuit():
        global root
        root.destroy()


    # writes passed string into Status-Bar
    # if '1' is passed - will also write string to 'Log'
    # if '2' is passed - will write to 'Results'
    def editStatus(self, newStatus, toLog):
        if toLog == 1:
            self.text_status.configure(state=NORMAL)
            self.text_status.delete(1.0, END)
            self.text_status.insert(END, newStatus)
            self.text_status.configure(state=DISABLED)

            self.log_text.configure(state=NORMAL)
            self.log_text.insert(END, newStatus +'\n')
            self.log_text.configure(state=DISABLED)

        elif toLog == 2:
            self.results_text.configure(state=NORMAL)
            self.results_text.insert(END, newStatus +'\n')
            self.results_text.configure(state=DISABLED)

        else:
            print newStatus +'\n'

        #print newStatus +'\n'




root = Tk()

app = App(root)

root.mainloop()

2 个答案:

答案 0 :(得分:0)

您不能直接操纵另一个线程的GUI对象。您唯一的选择是将某些内容推送到线程安全的队列,然后让您的GUI线程轮询该队列。

答案 1 :(得分:0)

tkinter不支持线程。您无法在同一程序中使用tkinter和线程,它只会崩溃