Issue with crash in TKinter Application - python

I have a program running that is having issues when the timer runs. Every time "start" is hit, the application crashes. Any thoughts?
##-- Imports --##
import time
import openpyxl as xl
from Tkinter import *
##-- Classes --##
class App(Frame):
def startTimer(self):
self.check = True
self.now = time.strftime("%H:%M:%S")
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)
def initUI(self):
self.parent.title("Emma's Time Manager")
self.pack(fill = BOTH, expand = 1)
def initWidget(self):
##Create button definitions##
self.buttonwidth = 12
self.quit = Button(self, text = "Quit", comman = self.quit, width = self.buttonwidth)
self.newClient = Button(self, text = "Add Client", command = lambda:self.newClientFunc(), width = self.buttonwidth)
self.timeStart = Button(self, text = "Start", command = lambda:self.startTimer(), width = self.buttonwidth)
self.timeEnd = Button(self, text = "End", command = lambda:self.endTimer(), width = self.buttonwidth)
self.saveBut = Button(self, text = "Save", command = lambda:self.saveFunc(), width = self.buttonwidth)
self.viewClient = Button(self, text = "View Client", command = lambda:self.viewCliFunc(), width = self.buttonwidth)
##Create lable definitions##
self.timer = Label(self, text = "00:00:00") ##self.timer used as display for timer##
##Create the listbox for Client Selection##
self.wb = xl.load_workbook("clients.xlsx")
self.clientNames = self.wb.get_sheet_names()
self.clivar = StringVar(self)
self.clivar.set(self.clientNames[0])
self.clilist = apply(OptionMenu, (self, self.clivar) + tuple(self.clientNames))
##Create Entry Box to describe work information##
self.info = Entry(self, width = 50)
##Create GUI for widgets##
self.clilist.grid(row = 0, column = 0)
self.timer.grid(row = 0, column = 1)
self.timeStart.grid(row = 0, column = 2)
self.timeEnd.grid(row = 0, column = 3)
self.info.grid(row = 1, column = 0, columnspan = 4)
self.newClient.grid(row = 2, column = 0)
self.viewClient.grid(row = 2, column = 1)
self.saveBut.grid(row = 2, column = 2)
self.quit.grid(row = 2, column = 3)
def __init__(self, parent):
Frame.__init__(self, parent, background = "light blue")
self.parent = parent
self.initUI()
self.initWidget()
def main():
try:
xl.load_workbook("clients.xlsx")
except:
temp = xl.Workbook()
temp.save("clients.xlsx")
temp.remove_sheet(temp.get_sheet_by_name("Sheet"))
root = Tk()
bob = App(root)
root.mainloop()
main()
Please note that most of the program is not yet finished. I just cannot seem to get this timer to run properly.

Looks like you have no way out of your while loop. You'll either need to set self.check to False, or break out of the loop.
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)

Related

Python tkinter maintaining gui and reading from pipe

I am trying to achieve reading from subprocess pipe stdout and stderr and updating GUI simultaneously. However my code is waiting all pipe reading process then updates all.
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
import subprocess
import threading
import time
from queue import *
class Application:
# load main window of application
def __init__(self, master):
self.current_script_file_name = None
self.q = Queue()
self.window = master
self.window.title("TK")
self.window.rowconfigure(1, minsize = 600, weight = 2)
self.window.columnconfigure(1, minsize = 600, weight= 2)
# create frame for buttons and create buttons
self.frame_buttons = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.button_save = tk.Button(master = self.frame_buttons, text = "Save", command = self.save_script_to_file)
self.button_run = tk.Button(master = self.frame_buttons, text = "Run", command = self.run_script_from_file)
# create frame for tk.Text editor and output
self.frame_text = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.text_editor = tk.Text(self.frame_text)
self.text_output = tk.Text(self.frame_text, background = "Black", foreground = "White")
#adjust buttons
self.button_save.grid(row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
self.button_run.grid(row = 1, column = 0, sticky = "ew", padx = 5)
self.frame_buttons.grid(row = 0, column = 0, sticky = "ns")
#adjust text editor and text output
self.text_editor.grid(row = 0, column = 0, sticky = "ew", padx = 10, pady = 10)
self.text_output.grid(row = 1, column = 0, sticky = "ew", padx = 5, pady = 5)
self.frame_text.grid(row = 0, column = 1, sticky = "ns")
self.text_output.insert(tk.END, 'Script Result:\n')
def run(self):
self.window.mainloop()
def save_script_to_file(self):
file_path = asksaveasfilename(
filetypes=[("Python Scripts", "*.py"), ("Kotlin Scripts", "*.kts*")]
)
if not file_path:
return
with open(file_path, "w") as output_file:
text = self.text_editor.get(1.0, tk.END)
output_file.write("#!/usr/bin/env python3\n")
output_file.write(text)
self.window.title(f"Text Editor Application - {file_path}")
def run_script_from_file(self):
# start thread so main window not going to freeze
threading.Thread(target=self.run_script).start()
self.update()
def run_script(self):
sub_proc = subprocess.Popen(['python','script.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
threading.Thread(target=self.pipe_reader, args=[sub_proc.stdout]).start()
threading.Thread(target=self.pipe_reader, args=[sub_proc.stderr]).start()
def update(self):
while not self.q.empty():
for source, line in iter(self.q.get, None):
self.text_output.insert(tk.END,line)
self.window.after(1000,self.update)
def pipe_reader(self, pipe):
try:
with pipe:
for line in iter(pipe.readline, b''):
self.q.put((pipe, line))
finally:
self.q.put(None)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.run()
run_script_from_file is a button command. And self.q is a Queue belongs to same class of this functions.
Why my text_output tk.Text field is not updating asap
Script.py :
#!/usr/bin/env python3
import time
counter = 1
while counter < 6:
print ("The current counter value: %d" % counter)
counter = counter + 1
time.sleep(1)
print(asd)
Edit: I put all my code
Edit: Added script.py
Ok, so this is actually not too far off. One common gotcha, due to buffering you have to run python in -u mode. Only other thing really is to have the tkinter update loop running all the time. Try this:
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
import subprocess
import threading
import time
from queue import *
class Application:
# load main window of application
def __init__(self, master):
self.current_script_file_name = None
self.q = Queue()
self.window = master
self.window.title("TK")
self.window.rowconfigure(1, minsize = 600, weight = 2)
self.window.columnconfigure(1, minsize = 600, weight= 2)
# create frame for buttons and create buttons
self.frame_buttons = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.button_save = tk.Button(master = self.frame_buttons, text = "Save", command = self.save_script_to_file)
self.button_run = tk.Button(master = self.frame_buttons, text = "Run", command = self.run_script_from_file)
# create frame for tk.Text editor and output
self.frame_text = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.text_editor = tk.Text(self.frame_text)
self.text_output = tk.Text(self.frame_text, background = "Black", foreground = "White")
#adjust buttons
self.button_save.grid(row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
self.button_run.grid(row = 1, column = 0, sticky = "ew", padx = 5)
self.frame_buttons.grid(row = 0, column = 0, sticky = "ns")
#adjust text editor and text output
self.text_editor.grid(row = 0, column = 0, sticky = "ew", padx = 10, pady = 10)
self.text_output.grid(row = 1, column = 0, sticky = "ew", padx = 5, pady = 5)
self.frame_text.grid(row = 0, column = 1, sticky = "ns")
self.text_output.insert(tk.END, 'Script Result:\n')
self.update()
def save_script_to_file(self):
file_path = asksaveasfilename(
filetypes=[("Python Scripts", "*.py"), ("Kotlin Scripts", "*.kts*")]
)
if not file_path:
return
with open(file_path, "w") as output_file:
text = self.text_editor.get(1.0, tk.END)
output_file.write("#!/usr/bin/env python3\n")
output_file.write(text)
self.window.title(f"Text Editor Application - {file_path}")
def run_script_from_file(self):
sub_proc = subprocess.Popen(['python3', '-u','script.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
threading.Thread(target=self.pipe_reader, args=[sub_proc.stdout]).start()
threading.Thread(target=self.pipe_reader, args=[sub_proc.stderr]).start()
def update(self):
while not self.q.empty():
source, line = self.q.get()
if line is None:
line = "DONE"
self.text_output.insert(tk.END,line)
self.window.after(100, self.update)
def pipe_reader(self, pipe):
for line in iter(pipe.readline, b''):
self.q.put((pipe, line))
self.q.put((pipe, None))
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
root.mainloop()

How to make Tkinter and Selenium work together

Hello , my question is how can i make command a and b work at the same time , i tried threading but the thread executes the command immediatelly , thanks in advace for the help !
I am a beginner programmer so any help with this issue would help a lot , this project is intended to be a refresh bot but i can't figure it out !
import tkinter as tk
from time import sleep
from selenium import webdriver
def a():
driver1 = webdriver.Chrome(executable_path="/Users/user/Downloads/chromedriver")#your file
driver1.get(entry.get())
while True:
sleep(5)
driver1.refresh()
def b():
app.destroy()
def c():
entry.delete(0, tk.END)
app = tk.Tk()
app.title("Youtube Bot")
app.geometry("425x350")
app.resizable(False, False)
app.configure(bg="#333436")
textOne = tk.Label(
text = "Youtube Bot",
font = ("Helvetica", 28),
fg = "white",
bg = "#333436",
height = "1"
)
textOne.grid(pady = 10)
entry = tk.Entry(
app,
width = 25,
font = ("Arial", 15),
fg = "white",
bg = "#6b6d75"
)
entry.focus_set()
entry.grid(padx=10, pady=18 )
entry.get()
entry.insert(0, "Paste Video Link")
buttonRefresh = tk.Button(
text = "Delete",
font = ("Arial", 15),
width = 8,
command = c
)
buttonRefresh.grid(padx = 6)
buttonOne = tk.Button(
text = "Start process",
font =("Helvetica", 20),
height = "3",
width = "20",
command = a
)
buttonOne.grid()
buttonTwo = tk.Button(
text="Stop process",
font=("Helvetica", 20),
height="3",
width="20",
command = b
)
buttonTwo.grid(pady=20)
textOne.grid(row = 0, column = 1)
entry.grid(row = 1, column = 1)
buttonRefresh.grid(row = 1, column = 0)
buttonOne.grid(row = 2, column = 1)
buttonTwo.grid(row = 3, column = 1)
app.mainloop()
This is where the thread runs, to call this thread, you would do a().start()
class a(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global driver1
driver1 = webdriver.Chrome(executable_path="/Users/user/Downloads/chromedriver")
driver1.get(entry.get())
while True:
sleep(5)
driver1.refresh()
Note I have defined driver1 so I can access it and close it when you call the function b
I changed this line:
buttonOne = tk.Button(text = "Start process",font =("Helvetica", 20),height = "3",width = "20",command = a)
To:
buttonOne = tk.Button(text = "Start process",font =("Helvetica", 20),height = "3",width = "20",command = lambda: a().start())
Becuase:
Since its a().start()) not just a, you need to include lambda, so the class a is only called when buttonOne is pressed.
I also included driver1.close() in the function b to also close the browser when you quit the app.
Full code:
from time import sleep
from selenium import webdriver
import threading
class a(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global driver1
driver1 = webdriver.Chrome(executable_path="/Users/user/Downloads/chromedriver")
driver1.get(entry.get())
while True:
sleep(5)
driver1.refresh()
def b():
app.destroy()
driver1.close()
def c():
entry.delete(0, tk.END)
app = tk.Tk()
app.title("Youtube Bot")
app.geometry("425x350")
app.resizable(False, False)
app.configure(bg="#333436")
textOne = tk.Label(text = "Youtube Bot",font = ("Helvetica", 28),fg = "white",bg = "#333436",height = "1")
textOne.grid(pady = 10)
entry = tk.Entry(app,width = 25,font = ("Arial", 15),fg = "white",bg = "#6b6d75")
entry.focus_set()
entry.grid(padx=10, pady=18 )
entry.get()
entry.insert(0, "Paste Video Link")
buttonRefresh = tk.Button(text = "Delete",font = ("Arial", 15),width = 8,command = c)
buttonRefresh.grid(padx = 6)
buttonOne = tk.Button(text = "Start process",font =("Helvetica", 20),height = "3",width = "20",command = lambda: a().start())
buttonOne.grid()
buttonTwo = tk.Button(text="Stop process",font=("Helvetica", 20),height="3",width="20",command = b)
buttonTwo.grid(pady=20)
textOne.grid(row = 0, column = 1)
entry.grid(row = 1, column = 1)
buttonRefresh.grid(row = 1, column = 0)
buttonOne.grid(row = 2, column = 1)
buttonTwo.grid(row = 3, column = 1)
app.mainloop()
The line driver1 = webdriver.Chrome(executable_path="/Users/user/Downloads/chromedriver") was originally defined outside class a
This meant it looked something like this
driver1 = webdriver.Chrome(executable_path="/Users/user/Downloads/chromedriver")
class a(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
driver1.get(entry.get())
while True:
sleep(5)
driver1.refresh()
This would mean that every time you start the program driver1would be automatically defined, resulting in the browser opening.
So I just moved driver1 in class a so that when buttonOne is pressed, that is when driver1 would be defined, and then that's when the browser would open.
I also made driver1 a global variable so you can access it when calling the function:
def b():
app.destroy()
driver1.close()

Calling a class method in another class - Tkinter

I am using Tkinter in Python 3 to create a converter between hex, binary and denary - while creating my own methods for conversions instead of using the built in methods.
I've completed the denary to binary conversion through the use of my 'highestPowerOf2' method in the DenToBin class. I want to use this method in my HexToBin class, but whenever I try to, an additional blank window pops up and I receive - AttributeError: 'NoneType' object has no attribute 'title' - error.
How would I run 'highestPowerOf2' in the HexToBin class?
(also sorry for any poor programming practise)
Code:
from tkinter import *
from functools import partial
class Menu(Frame):
def __init__(self, master= None): # initialise Menu
Frame.__init__(self, master) # initialise Frame
self.master = master # what does this do
self.createWindow()
def createWindow(self):
self.pack(fill = BOTH, expand = 1)
denToBin = Button(self, text = 'Denary to Binary', command = lambda: self.changeWindow(DenToBin))
denToBin.pack(fill = X, padx = 10, pady = 10)
hexToBin = Button(self, text = 'Hex to Binary', command = lambda: self.changeWindow(HexToBin))
hexToBin.pack(fill = X, padx = 10, pady = 10)
def changeWindow(self, object):
root.withdraw()
currentFrame = object(root)
class DenToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
# print(self.denary) -- Why is this not recognised?????????
self.master.title('Unit Converter: Denary to Binary')
instructionDen = Label(self, text = 'Denary Value: ')
instructionDen.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.denaryEntry = Entry(self)
self.denaryEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = lambda: self.highestPowerOf2(10)) # as an example
convertButton.grid(row = 3, column = 1)
# finds highest power of 2 that is less than denary number - helps self.convert()
def highestPowerOf2(self,number):
print('I find the highest power of 2 that fits into the number given.')
class HexToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
self.master.title('Unit Converter: Hexadecimal to Binary')
instructionHex = Label(self, text = 'Hexadecimal Value: ')
instructionHex.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.hexadecimalEntry = Entry(self)
self.hexadecimalEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = self.convert)
convertButton.grid(row = 3, column = 1)
def convert(self):
# I need to call the 'highestPowerOf2' method here.
pass
root = Tk()
unitConverter = Menu(root) # root is master
root.mainloop()

creating button with tkinter

I was really curious why I cannot get my add_button to work,
as the window fails to come up when creating it.
from tkinter import *
class Calculator:
#-------------------------------------------------
def __init__(self, master):
self.master = master
master.title("Calculator")
self.close_button = Button(master, text = "Close", command = master.destroy)
Label(master, text = "First Digit").grid(row = 0)
Label(master, text = "Second Digit").grid(row = 1)
self.input1 = 0
self.input2 = 0
input1 = Entry(master)
input2 = Entry(master)
input1.grid(row = 0, column = 1)
input2.grid(row = 1, column = 1)
self.close_button.grid(row = 2, column = 0)
self.add_buton = Button(master, text = "Add", command = self.add())
self.add_button.grid(row = 2, column = 1)
master.configure(background = 'grey')
return
#-------------------------------------------------
def add(self):
return self.input1.get() + self.input2.get()
#-------------------------------------------------
#-------------------------------------------------
root = Tk()
calc = Calculator(root)
root.mainloop()
#-------------------------------------------------
Welcome to Stack!
I've looked through you code I've been able to do what you are asking. There were a few errors within your code:
a) you had self.add_buton and self.add_button which caused an error.
b) self.input1 = 0 and self.input2 = 0 are not required.
c) You were calling self.add() as the command and you should be calling self.add. When calling it as a command you do not need ()
d)input1 = Entry(master) should be self.input1 = tk.Entry(master)
e) You should convert your input values into int or float as otherwise it will just one value onto the end of the other. (Eg, 1 + 5 = 15 whereas int(1) + int(5) = 6
Here is your code with the entry boxes working as they should. I have import tkinter as tk hence why it is tk.Entry
from tkinter import *
import tkinter as tk
class Calculator:
#-------------------------------------------------
def __init__(self, master):
self.master = master
master.title("Calculator")
self.close_button = Button(master, text = "Close", command = master.destroy)
Label(master, text = "First Digit").grid(row = 0)
Label(master, text = "Second Digit").grid(row = 1)
self.input1 = tk.Entry(bd=5, width=35, background='gray35', foreground='snow')
self.input2 = tk.Entry(bd=5, width=35, background='gray35', foreground='snow')
self.input1.grid(row = 0, column = 1)
self.input2.grid(row = 1, column = 1)
self.close_button.grid(row = 2, column = 0)
self.add_button = tk.Button(master, text = "Add", command = self.add)
self.add_button.grid(row = 2, column = 1)
master.configure(background = 'grey')
return
#-------------------------------------------------
def add(self):
val = self.input1.get()
print(val)
#-------------------------------------------------
#-------------------------------------------------
root = Tk()
calc = Calculator(root)
root.mainloop()
This should now work how you wanted it too. The variables within the entry can be changed to suit. You were correct in calling the value of the entry with self.input1.get().
Hope this has helped.

Refering to Labels Python, Tkinter

I'd like to refer to labels in def check(self) . It should check if feedback from external device equals to something, but I don't know how to refer to label and change colour of it if it's equal or not. I'd like to change for example lab3 background to green or red, depending on equality. Here is my code:
# -*- coding: utf-8 -*-
import Tkinter as T, tkFileDialog
import os
from time import *
import serial
from ttk import Button, Label, Frame, Entry, Style
class Program(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.root = root
self.initUI()
def initUI(self):
self.root.title('OptoMaQ')
Style().configure('TFrame', background = '#6666ff')
Style().configure('TButton',background = '#6666ff')
lab1 = Label(self, text = 'Press Save to save a file', background = '#6666ff').grid(row = 0, columnspan = 5)
but1 = Button(self, text='Save', command=self.save).grid(row = 2,column = 1)
lab2 = Label(self, text = 'Press Exit to quite', background = '#6666ff').grid(row = 1, columnspan = 5)
but2 = Button(self, text = 'Exit',command = self.exit).grid(row = 2, column = 2)
lab3 = Label(self, text = 'Spectra-Hub', background = '#6666ff').grid(row = 3, columnspan = 5)
lab4 = Label(self, text = 'SpectraPro VM-504',background = '#6666ff').grid(row = 4,columnspan = 5)
lab5 = Label(self, text = 'SpectraPro SP-2-300i',background = '#6666ff').grid(row = 5, columnspan = 5)
but3 = Button(self, text = 'Check',command = self.check).grid(row = 6, columnspan = 5)
lab6 = Label(self, text = 'Type sth here', background = '#6666ff').grid(row = 7,columnspan = 5)
self.entry = Entry(self, justify = 'center',text = '1')
self.entry.grid(row = 8, columnspan =3)
self.fileop = options = {}
options['filetypes'] = [('all files', '.*'),('dat files','.dat'),('text files', '.txt')]
options['initialfile'] = 'file.txt'
options['parent'] = root
def check(self):
port = serial.Serial(15)
command = 'WHO'
port.write(command + '\r')
out = port.read(50)
if out == 'Acton Research Corp. \nSpectra-Hub':
lab3 = Label(self, text = 'Spectra-Hub', background = '#6666ff').grid(row = 3, columnspan = 5)
lab3.config(background = 'green')
port.close()
else:
lab3 = Label(self, text = 'Spectra-Hub', background = '#6666ff').grid(row = 3, columnspan = 5)
lab3.config(background = 'red')
port.close()
port2 = serial.Serial(16)
port2.write(command +'\r')
out2 = port2.read(50)
if out2 == 'Acton Research Corp. \nSpectraPro VM-504':
port2.close()
else:
port2.close()
port3 = serial.Serial(17)
port3.write(command + '\r')
out3 = port3.read(46)
if out3 == 'Acton Research Corp. \n SpectraPro SP-2-300i':
port3.close()
else:
port3.close()
def save(self):
filename = tkFileDialog.asksaveasfilename(**self.fileop)
if filename:
file = open(filename, 'a+')
time = strftime("%A, %d %b %Y, %H:%M:%S ", gmtime())
print time
file.write(time)
file.write('\n')
input = str(self.entry.get())
file.write(input)
file.close()
def exit(self):
root.destroy()
if __name__=='__main__':
root = T.Tk()
Program(root).pack()
root.mainloop()
I've tried something like this in lines 46-53 but it doesn't work. It shows that 'NoneType' object has no attribute 'config' in line 52. Any ideas? It's really important to me please help :)
That is because your labels are only defined in the scope of def initUI(self): and therefore inaccessible to def check(self)
Try defining your buttons as self.lab1 instead of lab1 and also refer to them in the same way.

Categories