I am trying to display some data from /proc/stat file. Since this data constantly changes, I would like to update the values after every Interval (3 sec) on my screen. How do i achieve the same using poll() function?
Edit: I tried to achieve the same using threads and after. But I am not happy with the way it updates on my GUI. Below is my code for the same..
def displayTab1(self):
self.slabel1=ttk.Label(self.page1,text=" User Mode : ")
self.slabel1.grid(row=1,column=1,sticky=E,pady=15,padx=5)
self.slabel2=ttk.Label(self.page1,text=" System Mode : ")
self.slabel2.grid(row=2,column=1,sticky=E,pady=15,padx=5)
self.slabel3=ttk.Label(self.page1,text=" Total CPU utilization : ")
self.slabel3.grid(row=3,column=1,sticky=E,pady=15,padx=5)
self.slabel31=ttk.Label(self.page1,text="%")
self.slabel31.grid(row=3,column=3,sticky=N+W,pady=15,padx=5)
self.slabel4=ttk.Label(self.page1,text=" No: of Interrupts : ")
self.slabel4.grid(row=4,column=1,sticky=E,pady=15,padx=5)
self.slabel5=ttk.Label(self.page1,text=" Context Switches : ")
self.slabel5.grid(row=5,column=1,sticky=E,pady=15,padx=5)
self.slabel6=ttk.Label(self.page1,text=" Total Memory : ")
self.slabel6.grid(row=6,column=1,sticky=E,pady=15,padx=5)
self.slabel7=ttk.Label(self.page1,text=" Available Memory : ")
self.slabel7.grid(row=7,column=1,sticky=E,pady=15,padx=5)
self.slabel8=ttk.Label(self.page1,text=" Memory Utilization: ")
self.slabel8.grid(row=8,column=1,sticky=E,pady=15,padx=5)
self.book.grid()
t1 = threading.Thread(target=self.tab1_Layout, args = ())
t1.daemon = True
t1.start()
def tab1_Layout(self):
self.MemFile = open('/proc/meminfo','r').readlines()
for line in self.MemFile:
if line.startswith('MemTotal:'):
self.MemTotal = int(line.split()[1])
if line.startswith('MemAvailable:'):
self.MemAvail = int(line.split()[1])
break
MemTotal=self.MemTotal
MemAvail=self.MemAvail
VarS=System.SystemInfo(self.usr_prev,self.sys_prev,self.idle_prev)
self.usr_cur,self.sys_cur,self.idle_cur,usermode,sysmode,totalcpu,intr,ctxt,MemUtil= VarS.deltaTime()
self.usrmodeVal = ttk.Label(self.page1,text = usermode)
self.sysmodeVal = ttk.Label(self.page1,text = sysmode)
self.totcpuVal = ttk.Label(self.page1,text = totalcpu)
self.intrVal = ttk.Label(self.page1,text = intr)
self.ctxtVal = ttk.Label(self.page1,text = ctxt)
self.MemTotalVal = ttk.Label(self.page1,text = MemTotal)
self.MemAvailVal = ttk.Label(self.page1,text = MemAvail)
self.MemUtilVal = ttk.Label(self.page1,text = MemUtil)
self.usrmodeVal.grid(row=1,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.sysmodeVal.grid(row=2,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.totcpuVal.grid(row=3,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.intrVal.grid(row=4,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.ctxtVal.grid(row=5,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.MemTotalVal.grid(row=6,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.MemAvailVal.grid(row=7,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.MemUtilVal.grid(row=8,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.page1.after(I,self.tab1_Layout)
The main problem with your code is that you keep creating more and more widgets. Don't do that. Create the widgets once, and then update them.
You also have the problem that you're using threads, and trying to use widget commands in more than one thread. Tkinter isn't thread safe, and interacting with widgets in two separate threads will yield unpredictable results.
Here's a working example, though I've removed a bunch of widgets to make the code easier to grasp:
from Tkinter import *
import ttk
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.page1 = Frame(self)
self.page1.pack(fill="both", expand=True)
self.tab1_Layout()
self.tab1_Update()
def tab1_Update(self):
self.MemFile = open('/proc/meminfo','r').readlines()
self.MemTotal = self.MemAvail = 0
for line in self.MemFile:
if line.startswith('MemTotal:'):
self.MemTotal = int(line.split()[1])
if line.startswith('MemAvailable:'):
self.MemAvail = int(line.split()[1])
self.MemTotalVal.configure(text=self.MemTotal)
self.MemAvailVal.configure(text=self.MemAvail)
self.after(1000, self.tab1_Update)
def tab1_Layout(self):
self.MemTotalVal = ttk.Label(self.page1, width=10)
self.MemAvailVal = ttk.Label(self.page1, width=10)
self.MemTotalVal.grid(row=6,column=2,sticky=W+E+N+S,padx=5,pady=5)
self.MemAvailVal.grid(row=7,column=2,sticky=W+E+N+S,padx=5,pady=5)
if __name__ == "__main__":
root = Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Related
I have designed an marine engine simulator - it's a program that sends some serial DATA using a certain protocol called "BlueVision". The data is encoded based on a header, a block number, a block type, actual data (indexed), a checksum and a footer. Due to the fact that if you want to change a value in the actual data - then the checksum changes - I designed an recalculation of the block with the correct checksum when the data is changed. I made a GUI that allows me to change the value of 2 data points in order to be able to test live. The problem is that I don't know how to use threads or subprocesses correctly and the window keeps on freezing. The application works - but works badly and I kept on searching for a similar issue - and did find some suggestions but I haven't been able to implement it. Can someone help? PS: the code is awkward at best - please don't judge - I know I could of made it way better - but this is the best I could do with the time I had. The issue might not be clear to you if you don't have a COM port where the program writes to.
# Blue Vision is a MTU serial communication protocol - based on HEX values grouped in BLOCKS
import tkinter as tk
from tkinter import ttk
import time
import serial
def split_by_n(seq, n):
while seq:
yield seq[:n]
seq= seq[n:]
def after(self, ms, func=None, *args):
"""Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""
win = tk.Tk()
win.title("GUI - TEST VERSION")
win.geometry("750x250")
label = tk.Label(win, text = "Main Engine Overspeed")
label.pack()
v = tk.StringVar()
v.set('00')
c = tk.StringVar()
c.set("00")
def setText(word):
v.set(word)
a = ttk.Button(win, text ="01", command =lambda:setText("01"))
a.pack()
b = ttk.Button(win, text="00", command = lambda:setText("00"))
b.pack()
label1 = tk.Label(win, text ="Main Engine Speed")
label1.pack()
name_entry = tk.Entry(win, textvariable=c)
name_entry.pack()
def task():
MyVar = v.get()
priEngSp =c.get()
if len(priEngSp) == 0:
priEngSp = '00'
block_3 = 'FAF500030002000000290100000001000100000001000001000000000000000000000000000222AF5F'
block_4 = 'FAF500040003000001A000004650000047E00000000000000000000000000007EF4000083D6000000000000000000000000000000000000000000000012C000006D600000000000000000000278D00000000000000007FFFFFFF000000000001991500000000000000000016E36000000000000923D8000971F8000001F40000059F000026AC00002774000005800000251C00000580000027740000283C0000056200001D4C00001F400000061800000000000060FB00004650000036B000007D0000008CA0000006180000251C0000000000000000000000000000284800192D500017A6B00000051B0000251CFFFFFFA8000002580000044C000000FA0000000000000000000006770000CB200000D6D8000006770000CB200000D6D80000060600005DC000000000000027100000000000000000000000000000000000000000000003C2000061A8000000000000000000000000000000000000000000000000000000000000000000000000000000000000363300000EA6000249F0FFFFFB1E000F42400000000000000000000000000000000000000000000032D9AF5F'
block_5 = 'FAF5000500020000005600000000000000000000000000000000000000000000007F7F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034AAF5F'
block_6 = 'FAF5000600020000003D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000234AF5F'
block_7 = 'FAF5000700030000008C000006280000064A0000064A0000068D0000066B0000068D0000068D000006AE0000000000000000000006AE000006070000060700000607000005E5000005A2000006070000064A00000000000000000000062A000006AE000005A20000350EFFFFF07C00003CDEFFFFE8AC00000000000000000000000000000000000012DEAF5F'
block_8 = 'FAF50008000300000070000000000000112900000000000000000000059C000027740000283C000047E000000000000000000000000000000000000000000000000000000000000000007FFFFFFF7FFFFFFF0000055100002CEC0000000000000000000000000000000000000DD1AF5F'
block_9 = 'FAF50009000200000020000000000000000000000000000000000000021AAF5F'
block_10 = 'FAF5000A0002000000260000000000000000000000000000000000000000000000000221AF5F'
block_11 = 'FAF5000B0003000000EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E9AF5F'
block_12 = 'FAF5000C000200000045000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000242AF5F'
block_2 = 'FAF50002000200000074010001000000000000000000000000000000000000000000007F0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E9AF5F'
checksum = hex(0x00)
block_2split = list(split_by_n(block_2,2))
block_2split[10] = MyVar.upper() #Overspeed Alarm Position
for i in range(len(block_2split)-6):
checksum = hex(int(checksum, 16) + int(block_2split[i], 16))
checksum_string = str(checksum)
checksum_actvalue = checksum_string[2:]
checksum_long = checksum_actvalue.rjust(8,'0').upper()
checksum_split = list(split_by_n(checksum_long,2))
block_2split[len(block_2split)-3] = checksum_split[3]
block_2split[len(block_2split)-4] = checksum_split[2]
block_2split[len(block_2split)-5] = checksum_split[1]
block_2split[len(block_2split)-6] = checksum_split[0]
Block_2Output = ''.join(str(item) for item in block_2split)
iEngineSpeed = int(priEngSp,10)
hEngineSpeed = hex(iEngineSpeed * 10)
sEngineSpeed = str(hEngineSpeed)[2:].upper()
while (len(sEngineSpeed)<8):
sEngineSpeed = '0' + sEngineSpeed
block_4split = list(split_by_n(block_4,4))
sEngineSpeed_split = list(split_by_n(sEngineSpeed,4))
block_4split[5] = sEngineSpeed_split[0]
block_4split[6] = sEngineSpeed_split[1]
Block_4joint = ''.join(str(item) for item in block_4split)
Block_4joint_sp = list(split_by_n(Block_4joint,2))
checksumb4 = hex(0x00)
for i in range(len(Block_4joint_sp)-6):
checksumb4 = hex(int(checksumb4, 16) + int(Block_4joint_sp[i], 16))
checksumb4_string = str(checksumb4)
checksumb4_actvalue = checksumb4_string[2:]
checksumb4_long = checksumb4_actvalue.rjust(8,'0').upper()
checksumb4_split = list(split_by_n(checksumb4_long,2))
Block_4joint_sp[len(Block_4joint_sp)-3] = checksumb4_split[3]
Block_4joint_sp[len(Block_4joint_sp)-4] = checksumb4_split[2]
Block_4joint_sp[len(Block_4joint_sp)-5] = checksumb4_split[1]
Block_4joint_sp[len(Block_4joint_sp)-6] = checksumb4_split[0]
Block_4Output = ''.join(str(item) for item in Block_4joint_sp)
blocks = [Block_2Output, block_3, Block_4Output, block_5, block_6, block_7, block_8, block_9, block_10, block_11, block_12]
with serial.Serial('COM5', '9600') as ser: #you might wanna comment this part out
for block in blocks:
print(block)
ser.write(bytes.fromhex(block.strip())) #you might wanna comment this part out
time.sleep(1)
win.after(200, task)
win.after(200, task)
win.mainloop()
To avoid freezing, one of the way is to use thread:
from threading import Thread
...
def task():
while True:
MyVar = v.get()
...
blocks = [Block_2Output, block_3, Block_4Output, block_5, block_6, block_7, block_8, block_9, block_10, block_11, block_12]
with serial.Serial('COM5', '9600') as ser: #you might wanna comment this part out
for block in blocks:
print(block)
ser.write(bytes.fromhex(block.strip())) #you might wanna comment this part out
time.sleep(1)
time.sleep(0.2)
# run task() in a thread
Thread(target=task, daemon=1).start()
win.mainloop()
In this app I'm trying to execute a popup in which an user can write a date. This popup has to occur after the user clicks a submit button I have already created. The date the user input into this popup has to be saved into a variable which will be used later on on the code. In order to do all this I tried the following:
def CreateOrderPop(self):
def popup():
#contenido = input("Contenido de Orden ")
#diaDeEntregar = input("Dia de Entrega")
self.userentryA = Entry("Dia de Entrega: ")
self.userentryA.pack()
self.userentryAbu = Button(text= "Guardar", command = self.guardarFechaOrden)
self.userentryAbu.pack()
def guardarFechaOrden(self):
global userDate
userDate = self.userentryA.get()
self.destroy()
def submit(self):
result = next(self.counter)
global orderResult
orderResult = str(result)
global contents1
contents1 = ("Nombre: {}".format(self.entry_name.get()))
global contents2
contents2 = ("Email: {}".format(self.entry_email.get()))
global contents3
contents3 = ("Num Cel/Tel: {}".format(self.entry_numtc.get()))
global contents4
contents4 = ("InformaciĆ³n Adicional: {}".format(self.entry_addinf.get(1.0, "end")))
def CreateOrder():
fecha = datetime.now()
fechaDeCreacion = fecha.strftime(" %A, %B %d, %Y" )
#diaDeEntregar = userDate
#global fechaDeEntrega
#fechaDeEntrega = fechaDeCreacion + str(diaDeEntregar)
numOrden = orderResult
return fechaDeCreacion, orderResult
completeOrden = [contents1, contents2, contents3, contents4, CreateOrder()]
completeOrdenEnum = "Orden Num:" + orderResult, completeOrden
Database.mainDatabase.append(completeOrdenEnum)
command = self.CreateOrderPop()
After running the code and clicking the submit button, everything runs normal except I don't get the popup I want.
CHANGES
I added this class to help me create what I was looking for:
class PopOrden:
def __init__(self,master):
self.master = master
top=self.top=Toplevel(master)
self.l=Label(top,text="Fecha de Entrega")
self.l.pack()
self.e=Entry(top)
self.e.pack()
self.b=Button(top,text='Ok',command=self.cleanup)
self.b.pack()
def cleanup(self):
self.value=self.e.get()
self.top.destroy()
def entryValue(self):
return self.w.value
print(self.w.value)
The previous code along with this edited code:
def submit(self):
result = next(self.counter)
print (result)
def controLoo():
if result == 1:
self.CreateOrderPop()
command = controLoo()
global orderResult
orderResult = str(result)
global contents1
contents1 = ("Nombre: {}".format(self.entry_name.get()))
global contents2
contents2 = ("Email: {}".format(self.entry_email.get()))
global contents3
contents3 = ("Num Cel/Tel: {}".format(self.entry_numtc.get()))
global contents4
contents4 = ("InformaciĆ³n Adicional: {}".format(self.entry_addinf.get(1.0, "end")))
def CreateOrder():
fecha = datetime.now()
fechaDeCreacion = fecha.strftime(" %A, %B %d, %Y" )
#diaDeEntregar = PopOrden
#global fechaDeEntrega
#fechaDeEntrega = fechaDeCreacion + str(diaDeEntregar)
numOrden = orderResult
return fechaDeCreacion, orderResult
completeOrden = [contents1, contents2, contents3, contents4, CreateOrder()]
completeOrdenEnum = "Orden Num:" + orderResult, completeOrden
Database.mainDatabase.append(completeOrdenEnum)
command = self.database_window()
self.clear()
messagebox.showinfo(title = "Orden #"+ orderResult, message = "Orden Guardada")
However, I'm NOW having issues with a blank tk popu that's also generated with the popup I want.
I am not sure what you mean by everything runs normal, because your code seems to have some major formatting issues (indentation to say the least). However, 'pop-ups' are usually achieved with Toplevel() widgets. See this useful resource. It is a great resource for all things tkinter in my opinion.
Also, you might find the answer to this question helpful.
Why dont you use a message box directly
from tkinter import *
import tkMessageBox
root = Tk()
def popUp():
result = tkinter.messageBox.popUp("Quiz","Are you ready? ")
# result wil be yes or no
if result == 'yes':
#do something
else:
# do something
submitButton = Button(root,text= "Submit")
submitButton.bind("<Button-1",popup)
# onleft click on submit popup method gets called
submitButton.pack()
I'm writing a program for my class that allows you to make a recipe, save it and edit it even after closing the program. You obviously need a text file to do this.
I am using an OptionMenu (Tkinter, Python 3.3.3), but I cannot figure out how to keep updating it to have the first option in the list I have made in my text file. So how do I do that?
My code is thus:
###########################################
###########################################
### RECIPE BOOK TASK ##### By 18166 #######
###########################################
###########################################
from tkinter import *
def script ():
#### MAIN ####
fake_window = Tk()
new_recipe_window = fake_window
start_window = fake_window
start_window.title("Recipe Book Task")
#### MAIN ####
## DATA FILE ##
global datafile
datafile = open("StoredRecipes.txt", "a+")
## DATA FILE ##
### Functions ###
def close (x): ## Close Original Window ##
global start_window
global new_recipe_window
(x).withdraw()
def new_recipe ():
new_recipe_window = Tk() ## Making new window ##
new_recipe_window.title("New Recipe")
close(start_window)
recipe_name_label = Label(new_recipe_window, text="Recipe Name: ") ## Making new recipe label ##
recipe_name_label.grid(row=0, column=0)
recipe_name_box = Entry(new_recipe_window) ## Making new recipe entry ##
recipe_name_box.grid(row=0, column=1)
num_people_label = Label(new_recipe_window, text="Number of people: ") ## Making number of people label ##
num_people_label.grid(row=1, column=0)
num_people_box = Entry(new_recipe_window) ## Making number of people entry ##
num_people_box.grid(row=1, column=1)
item_label = Label(new_recipe_window, text="Items: ") ## Making item label ##
item_label.grid(row=2, column=0)
item_box = Entry(new_recipe_window) ## Making item entry ##
item_box.grid(row=2, column=1)
quantity_label = Label(new_recipe_window, text="Quantity: ") ## Making quantity label ##
quantity_label.grid(row=3, column=0)
quantity_box = Entry(new_recipe_window) ## Making quantity entry ##
quantity_box.grid(row=3, column=1)
unit_label = Label(new_recipe_window, text="Unit: ") ## Making unit label ##
unit_label.grid(row=4, column=0)
unit_box = Entry(new_recipe_window) ## Making unit entry ##
unit_box.grid(row=4, column=1)
def write ():
a = recipe_name_box.get()
b = num_people_box.get()
c = item_box.get()
d = quantity_box.get()
e = unit_box.get()
line = (a, b, c, d, e)
datafile.write(str(line) + "\n")
datafile.close()
saved_recipes.config(a)
close(new_recipe_window)
script()
finish_button = Button(new_recipe_window, text="Save and Finish", command=write) ## Making finish button ##
finish_button.grid(row=5, column=0, sticky=S)
# Dropdown Box #
default = StringVar(start_window, 'Recipe 1')
default.set("Select Your Recipe")
saved_recipes = OptionMenu(start_window, default, "Hi")
saved_recipes.grid(row=0, column=1)
# Dropdown Box #
# New Recipe Button #
new_recipe = Button(start_window, text="New Recipe", command=new_recipe)
new_recipe.grid(row=0, column=0)
# New Recipe Button #
script()
(Sorry for the block, I think all is useful to answering possibly?)
I believe you have two different options.
One option you could do is set up a timer to check the text file every couple of seconds, see if it's changed at all, and update your OptionMenu accordingly. You can find more info on how to do this here, but in a nutshell, you'd want your code to look something like:
def recheck(root, option_menu, file_name):
with open(file_name) as my_file:
lines = my_file.readlines():
# `lines` is a list where each item is a single line
# do any checks and updates you need here.
root.after(1000, recheck, root, option_menu, file_name)
# schedule the function to run again after 1000 milliseconds.
def script():
# set up your gui
start_window.after(1000, recheck, start_window, option_menu, "StoredRecipies.txt")
Note: you can find more info on the with statement here: http://effbot.org/zone/python-with-statement.htm
The downside of this is that the update will be a little laggy -- you'll end up rechecking the file only once a second, so the update won't be instantaneous.
Alternatively, you could use something like Watchdog. It's a 3rd party library that you can set up to "watch" a particular file and run a function whenever the file changes. It's much more responsive in that you'll call the function only if the file actually changes, but it might end up being more complicated since you need to figure out how to make it work with Tkinter. I'm going to guess that your code will look roughly like this:
import os.path
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
def setup_observer(option_menu, filename):
normalized_filename = os.path.normpath(input_filename)
class MyEvent(FileSystemEventHandler):
def on_modified(self, event):
if os.path.normpath(event.src_path) == normalized_filename:
# update your option menu
observer = Observer()
observer.schedule(MyEvent(), '.', recursive=False)
return observer
def script():
# setup gui
observer = setup_observer(option_menu, "myfile.txt")
start_window.mainloop()
To add elements to an OptionList, you can use the following method (from http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html)
datafile = open("StoredRecipes.txt", "r")
for line in datafile.readlines():
saved_recipes['menu'].add_command(label=line,
command=lambda temp = line: saved_recipes.setvar(saved_recipes.cget("textvariable"), value = temp))
Which uses (has to use) a closure and an anonymous function -- definitely nothing you should deal with on your level of experience (guessing from the structure of your code).
This snippet adds a command for each line in your file. Because an OptionMenu is something that executes things when elements are selected, you have to provide a command for each line. Right now this is just setting the displayed text to the selected line.
To accomplish this, it uses an anonymous function (lambda) that sets the textvariable of the OptionMenu to the current line.
I want to know, how to pop-up messages while processing/executing a program/function. I mean,
def Select():
path=tkFileDialog.askopenfilename(filetypes=[("Image File",'.jpg')])
im = skimage.io.imread(path, as_grey=True)
im = skimage.img_as_ubyte(im)
im /= 32
g = skimage.feature.greycomatrix(im, [1], [0], levels=8, symmetric=False, normed=True)
cont = skimage.feature.greycoprops(g, 'contrast')[0][0]
cont_list1.append(cont)
ene = skimage.feature.greycoprops(g, 'energy')[0][0]
ene_list1.append(ene)
homo = skimage.feature.greycoprops(g, 'homogeneity')[0][0]
homo_list1.append(homo)
cor = skimage.feature.greycoprops(g, 'correlation')[0][0]
cor_list1.append(cor)
dis = skimage.feature.greycoprops(g, 'dissimilarity')[0][0]
dis_list1.append(dis)
I want to display a message stating Features are being calculated, and once it calculates, the message should disappear.
But I don't need the ok button.I don't know how to achieve this.The result of these calculations will be displayed in separate Entry box. Any suggestions are welcome.
Have a look at this. It opens a window with the text and when the computation is done the text is changed to the result.
>>> import time
>>> def processingPleaseWait(text, function):
import Tkinter, time, threading
window = Tkinter.Toplevel() # or tkinter.Tk()
# code before computation starts
label = Tkinter.Label(window, text = text)
label.pack()
done = []
def call():
result = function()
done.append(result)
thread = threading.Thread(target = call)
thread.start() # start parallel computation
while thread.is_alive():
# code while computing
window.update()
time.sleep(0.001)
# code when computation is done
label['text'] = str(done)
>>> processingPleaseWait('waiting 2 seconds...', lambda: time.sleep(2))
I keep on getting an attribute error when trying to changing the text attribute of a tk label.
I declare it and it uses a temp image so it does exist but when I attempt to change it I get the error. If someone knows a better way to change the image or display it in a better method I would greatly like to here.
Here is the relevent code
self.threadLabelImage = tk.Label(self.threadFrame,image=self.threadImage,wraplength=400,padx=20,pady=5).grid(row=7,column=10,sticky = tk.EW)
self.threadFrame.grid(row=0,column=10,sticky=tk.EW,rowspan=8)
self.threadFrame.grid_propagate(0)
self.QUIT = tk.Button(self, text="QUIT", fg="red", command=main.destroy,padx=5, pady=5).grid(row=7)
def updateSelected(self, event):
# getting selected listbox item
i=self.thread_lb.curselection()
# Returns tuple that must be split
x,self.y,z = re.split("\D+",str(i))
self.y=int(self.y)
print(self.threadTitleList[self.y])
print(self.threadPubDateList[self.y])
print(self.threadLinkList[self.y])
print(self.threadDescList[self.y])
self.threadTitle.set(self.threadTitleList[self.y])
self.threadAuth.set(self.threadAuthList[self.y])
self.threadPub.set(self.threadPubDateList[self.y])
self.threadArtLink.set(self.threadLinkList[self.y])
self.threadLink.set(self.threadDescList[self.y])
self.threadImg.set('Will put image here')
if self.threadLinkList[self.y].find('imgur') != -1:
url = self.threadLinkList[self.y]+'.GIF'
imageName=self.threadLinkList[self.y][-11:-4]
urllib.request.urlretrieve(self.threadLinkList[self.y],imageName+'.jpg')
imgfile = Image.open(imageName+'.jpg')
imgfile = imgfile.resize((150,150),Image.ANTIALIAS)
# img = Image.open(file)
self.threadImage = ImageTk.PhotoImage(imgfile)
self.threadLabelImage.config(text = self.threadImage)
self.threadImage.image = imgfile
And here is the entire program so you can run it if need be.import xml.etree.ElementTree as ET
import webbrowser,time,urllib.request,re
import tkinter as tk
import urllib
from PIL import Image,ImageTk
main = tk.Tk()
class Application(tk.Frame):
def __init__(self, master=None):
self.threadTitle = tk.StringVar()
self.threadAuth = tk.StringVar()
self.threadPub = tk.StringVar()
self.threadArtLink = tk.StringVar()
self.threadLink = tk.StringVar()
self.threadImg = tk.StringVar()
self.threadArtLink.set('Click something to display thread info')
photo = Image.open("temp.png")
photo = photo.resize((150,150), Image.ANTIALIAS)
self.threadImage = ImageTk.PhotoImage(photo)
# Intializes tkinter gui framework
tk.Frame.__init__(self, master)
# Packs widgets needed
self.grid()
# Creates the widgets functions
self.createWidgets()
# Intializes the man rss.xml
self.initial()
# self.threadLabelArtLink = None
# self.threadLabelTitle = None
# self.threadLabelThreadLink = None
# self.threadLabelArtLink = None
# self.threadImgLink = None
def createWidgets(self):
# Create entrybox and align to grid
self.send_entry = tk.Entry(self)
self.send_entry.grid(row=0,column=0)
# Create button,allign to grid, get xml
self.change_sub = tk.Button(self,text='Change Subreddit',padx=5, pady=5, command=lambda :self.getXML(self.send_entry.get())).grid(row=0 , column=3)
# Create scrollbar on Y-Axis
self.lb_scrollY = tk.Scrollbar(self,orient=tk.VERTICAL)
# On grid next to Listbox(sticky means fill whole row
self.lb_scrollY.grid(row=1,column=4,sticky=tk.NS,rowspan=6)
# Create Listbox and get Y from scrollbar
self.thread_lb = tk.Listbox(self,yscrollcommand=self.lb_scrollY.set,height=20)
# Calls function whenever a new item is selected
self.thread_lb.bind('<<ListboxSelect>>',self.updateSelected)
self.thread_lb.bind('<Double-Button-1>',self.openPage)
# scrolly will change the view of listbox
self.lb_scrollY['command']=self.thread_lb.yview
self.thread_lb.grid(row=1,column=0,sticky=tk.NS+tk.EW,columnspan=4)
self.threadFrame = tk.LabelFrame(main,text='Reddit',width=450,height=350,labelanchor='n')
self.threadLabelTitle = tk.Label(self.threadFrame,textvariable=self.threadTitle,wraplength=400,padx=20, pady=5).grid(row=1,column=10,sticky= tk.EW)
self.threadLabelAuth = tk.Label(self.threadFrame, textvariable=self.threadAuth,wraplength=400,padx=20, pady=5).grid(row=2,column=10,sticky = tk.EW)
self.threadLabelPub = tk.Label(self.threadFrame, textvariable=self.threadPub,wraplength=400,padx=20, pady=5).grid(row=3,column=10,sticky = tk.EW)
self.threadLabelArtLink = tk.Label(self.threadFrame, textvariable=self.threadArtLink,wraplength=400,padx=20, pady=5).grid(row=4,column=10,sticky = tk.EW)
self.threadLabelThreadLink = tk.Label(self.threadFrame, textvariable=self.threadLink,wraplength=400,padx=20, pady=5).grid(row=5,column=10,sticky = tk.EW)
self.threadImgLink = tk.Label(self.threadFrame, textvariable=self.threadImg,wraplength=400,padx=20, pady=5).grid(row=6,column=10,sticky = tk.EW)
self.threadLabelImage = tk.Label(self.threadFrame,image=self.threadImage,wraplength=400,padx=20,pady=5).grid(row=7,column=10,sticky = tk.EW)
self.threadFrame.grid(row=0,column=10,sticky=tk.EW,rowspan=8)
self.threadFrame.grid_propagate(0)
self.QUIT = tk.Button(self, text="QUIT", fg="red", command=main.destroy,padx=5, pady=5).grid(row=7)
def updateSelected(self, event):
# getting selected listbox item
i=self.thread_lb.curselection()
# Returns tuple that must be split
x,self.y,z = re.split("\D+",str(i))
self.y=int(self.y)
print(self.threadTitleList[self.y])
print(self.threadPubDateList[self.y])
print(self.threadLinkList[self.y])
print(self.threadDescList[self.y])
self.threadTitle.set(self.threadTitleList[self.y])
self.threadAuth.set(self.threadAuthList[self.y])
self.threadPub.set(self.threadPubDateList[self.y])
self.threadArtLink.set(self.threadLinkList[self.y])
self.threadLink.set(self.threadDescList[self.y])
self.threadImg.set('Will put image here')
if self.threadLinkList[self.y].find('imgur') != -1:
url = self.threadLinkList[self.y]+'.GIF'
imageName=self.threadLinkList[self.y][-11:-4]
urllib.request.urlretrieve(self.threadLinkList[self.y],imageName+'.jpg')
imgfile = Image.open(imageName+'.jpg')
imgfile = imgfile.resize((150,150),Image.ANTIALIAS)
# img = Image.open(file)
self.threadImage = ImageTk.PhotoImage(imgfile)
self.threadLabelImage.config(text = self.threadImage)
self.threadImage.image = imgfile
# # threadTitle = self.threadTitleList[y]
# print(self.threadLabelTitle["text"])
# # self.threadLabelTitle['text']=threadTitle
# self.threadLabelAutPub['text']=self.threadPubDateList[y]
# self.threadImgLink['text']=self.threadLinkList[y]
# self.threadLabelThreadLink['text']=self.threadDescList[y]
# main.update()
def openPage(self,event):
webbrowser.get('windows-default').open_new(self.threadLinkList[self.y])
def descStripper(self,desc):
# Intialize values
l1,l2,l2Start = 0,0,0
t1,t2,t2start = 0,0,0
link = ""
thread = ""
# Where to start looking for each in description element
l1=int(desc.find('<br/> <a href="'))
t1=int(desc.find('</a> <a href="'))
a1=int(desc.find('"> '))
# If both of the tags are found then continue
if l1 != -1 and t1 != -1 and a1 != 1:
# Start looking for end of quotes 16 characters from beginning of tag
l2Start = l1+16
l2=int(desc.find('"',l2Start))
# Link is created from what is in the quotes
link = desc[l1+15:l2]
# Same as above but to find thread link
t2start = t1+15
t2=int(desc.find('"',t2start))
thread = desc[t1+14:t2]
a2start = a1+4
a2 = int(desc.find(' <',a2start))
author = desc[a1+3:a2]
return link,thread,author
else:
# If it can't find one it will return an error
link = "Couldn't find the stuff :("
thread = "Couldn't find the thread link :("
return link, thread
def lbPopulator(self,title,pub,link):
# Delete old entries from listbox
self.thread_lb.delete(0,tk.END)
# Iterate through all the items and append them to the listbox
for item in title:
self.thread_lb.insert(tk.END,item)
def getXmlData(self):
# Intialize lists
self.threadPubDateList = []
self.threadTitleList = []
self.threadLinkList = []
self.threadDescList = []
self.threadThumbNailList = []
self.threadAuthList = []
# Use the downloaded rss.xml for XML parsing
tree=ET.parse('rss.xml')
# define root as the base of the XML parsing tree
root=tree.getroot()
for channel in root:
# Iterate through all the channels
for SubChannel in channel:
# Iterate through all the items in the channel
if SubChannel.tag == 'item':
# If the SubChannel is called item then search for the items below
for threadInfo in SubChannel:
# iterate through all the items in the 'item'
if threadInfo.tag == 'title':
# append the tag from the title to the list
self.threadTitleList.append(threadInfo.text)
if threadInfo.tag == 'pubDate':
# Append the pubdate info to the list but remove excess characters
self.threadPubDateList.append(threadInfo.text[:-6])
if threadInfo.tag == 'description':
# Pass all the information from the description to the stripper to get the useful
# information and links
link,thread,author = self.descStripper(threadInfo.text)
self.threadLinkList.append(link)
self.threadDescList.append(thread)
self.threadAuthList.append(author)
# if threadInfo.tag == ''
# Populate the listbox with the newly generated lists
self.lbPopulator(self.threadTitleList,self.threadPubDateList,self.threadLinkList)
def getXML(self,subreddit):
try:
# Try to download the xml file using the user input subreddit
url = 'http://www.reddit.com'+subreddit+'.rss'
source = urllib.request.urlretrieve(url,'rss.xml')
self.getXmlData()
except urllib.error.HTTPError as err:
# Error caused by reddit API limiting connections
print('Too many requests-Try again')
def initial(self):
try:
# Same as above but downloads the front page
source = urllib.request.urlretrieve('http://www.reddit.com/.rss','rss.xml')
self.getXmlData()
except urllib.error.HTTPError as err:
print('Too many requests-Trying again 3')
# If error occurs program waits 3 seconds and then restarts
time.sleep(3)
self.__init__()
# main.geometry("350x400")
app = Application(master=main)
# Begins the applications GUI loop
app.mainloop()
The grid method of a Tkinter widget always returns None. So, any calls to it must be placed on their own line.
Meaning, all of the lines that are written like this:
self.threadLabelTitle = tk.Label(self.threadFrame,textvariable=self.threadTitle,wraplength=400,padx=20, pady=5).grid(row=1,column=10,sticky= tk.EW)
need to be rewritten like this:
self.threadLabelTitle = tk.Label(self.threadFrame,textvariable=self.threadTitle,wraplength=400,padx=20, pady=5)
self.threadLabelTitle.grid(row=1,column=10,sticky= tk.EW)