我正在用Python中的TKinter构建一个简单的GUI。我有一个浏览按钮,可以浏览计算机上的文件并打开选定的文件。当选择文件时,会调用一个函数来打开文件并开始读取。然而,在我单击按钮后,窗口不再响应。当我搜索时,我发现这是因为TKinter是单线程的,主gui线程可能会被一些任务卡住,但即使在我使用了一些简单的线程之后,窗口仍然会冻结。
这是我正在做的一个示例代码,
import threading
from Tkinter import *
from PyQt4 import *
from tkFileDialog import askopenfilename
import numpy as np
import xlrd
global columnList
def open_file (file_name):
#fileName=input ("Enter file name (example.dat)")
#colCount= input ("Enter number of columns in the file")
try:
workbook = xlrd.open_workbook(file_name)
sheet=workbook.sheet_by_index(0)
columns = []
for i in range (0,sheet.ncols-1):
columns.append(np.array (sheet.col_values(i,1))) # make a list, each index has a numpy array that represnts a column.
if (i!=0):
columns[i]= columns[i].astype(np.float)
#Preprocessing depth:
m= columns [0]
for i in range (m.shape[0]):
m[i]= m[i]*2 +1
m=m.astype(np.int)
columns[0]=m
print( sheet.row_values(0,sheet.ncols) )
# removing nans:
index=input("enter the column index to interpolate: ")
m= columns [index]
for i in range (m.shape[0]-1, -1, -1):
if (np.isnan(m[i])):
m=np.delete(m,i)
columns[0]=np.delete(columns[0],i)
columns [index]= np.delete(columns[index],i)
except IOError:
print ("The specified file was not found")
return columns [0], columns [index]
class Interface:
def __init__(self, master):
#frame= Frame (master)
#frame.grid (sticky=Y)
self.title= Label(master,text="Kriging Missing data Imputation", fg="blue", font=("Helvetica", 18))
self.select_file= Label (master, text="Select the file that contains the data (must be an excel file): ", font=("Helvetica", 12))
self.title.grid (row=1, column=5, columnspan= 4, pady= (20,0))
self.select_file.grid (row=3, column=1, sticky=W, pady=(20,0), padx=(5,2))
self.browse_button= Button (master, text="Browse", command=self.browser, font=("Helvetica", 12), width=12)
self.browse_button.grid (row=3, column=3, pady=(20,0))
self.varColumn= StringVar(master)
self.varColumn.set("depth")
self.columnLabel= Label(master,text="Select a column to process", font=("Helvetica", 12))
self.columnList= OptionMenu (master, self.varColumn,"nan", "?", "*")
self.columnLabel.grid (row=5, column=1, pady=(20,0), sticky=W, padx=(5,0))
self.columnList.grid(row=5, column= 3, pady= (20,0))
self.missing_label= Label(master, text="Select missing data indicator: ", font=("Helvetica", 12))
self.var = StringVar (master)
self.var.set("nan")
self.menu= OptionMenu (master, self.var,"nan", "?", "*")
self.missing_label.grid (row=7, column=1, padx=(5,2), pady= (20,0), sticky=W)
self.menu.grid(row=7, column=3, pady= (20,0))
self.extrapolate= Label (master, text="Select a range for extrapolation (max=800): ", font=("Helvetica", 12))
self.max_extra= Entry (master)
self.extrapolate.grid (row=9, column=1, padx=(5,2), pady= (20,0), sticky=W)
self.max_extra.grid (row=9, column=3, pady=(20,0))
self.a_label= Label (master, text="enter the value of a (range): ", font=("Helvetica", 12))
self.a_value= Entry (master)
self.a_label.grid (row=11, column=1, padx=(5,2), pady=(20,0), sticky=W)
self.a_value.grid (row=11, column=3, pady=(20,0))
self.start_button= Button (master, text="Start", font=("Helvetica", 12), width=12)
self.pause_button= Button (master, text= "Pause", font=("Helvetica", 12),width=12)
self.stop_button= Button (master, text="stop", font=("Helvetica", 12),width=12)
self.start_button.grid (row=13, column=1, pady=(30,0) )
self.pause_button.grid (row=13, column=2, pady=(30,0))
self.stop_button.grid (row=13, column=3, pady=(30,0))
def browser (self):
filename = askopenfilename()
x,v= threading.Thread (target=open_file(filename))
#open_file(filename)
# we must then send the file name to the function that reads it somehow.
window= Tk () #main window.
starter= Interface (window)
window.mainloop() #keep the window open until the user decides to close it.
我读到Tkinter不是线程安全的,我可能在其他一些函数中使用了一些线程,那么我如何解决使用线程冻结的问题呢?或者,我应该使用另一个像PyQt4这样的GUI构建器吗?
感谢您的帮助。
谢谢。
您不使用线程。您正在调用open_file
,并尝试从它的返回值开始一个新线程。您必须将函数用作参数,而不是调用它:
th = threading.Thread(target=open_file, args=(filename,))
th.start()
下一点是,线程没有返回值。当线程完成时,browser
-函数早就完成了。因此无法获得返回值x
和v
。如果要在线程和GUI之间进行通信,则必须使用队列和单独的事件循环,该循环检查队列并使用after
再次调用自身。
即使Qt在某种程度上也不是线程安全的,某些线程可以更改GUI。但它有信号槽的概念,它以一种更方便的方式取代了队列进行通信。