I am trying to create a status/progress bar that shows how many web pages are left to scrape. I am getting lost on how to pass the # of pages between the modules. I think I am making a mistake by calling the tkinter window class. Any help on how to call this module and pass the pg/pages variables would be greatly appreciated.
Module getting page count:
from ProgressBar import StatusWindow
pages = 25
class crawler:
def get_pagecount():
for eachPage in range(1,pages):
pg = eachPage
Complete = '{:.0%}'.format(pg/pages)
print(Complete)
StatusWindow(pages,pg) """<== How do I call the progress bar and pass these variables?"""
if __name__=='__main__':
crawler.get_pagecount()
ProgressBar:
from tkinter import *
from tkinter import ttk
backRGB = '#e5e5e5'
root = Tk()
root.configure(bg=backRGB)
root.configure()
class StatusWindow(Frame,ToDo,Done): """<==Probably not where I should be calling and passing variables?"""
def __init__(self,master = None):
Frame.__init__(self,master)
self.master = master
self.init_window()
self.grid()
self.tk_setPalette(background='#e5e5e5', foreground = '#001030',activeBackground='#001030',activeForeground='white')
ToDo = 10
Done = 5
self.Progress_Bar(ToDo,Done)
def Progress_Bar(self,ToDo,Done):
progressbar = ttk.Progressbar(root, orient = HORIZONTAL, length = 300)
progressbar.grid(row=9, column = 0)
progressbar.config(mode = 'determinate', maximum = ToDo, value = Done)
def init_window(self):
self.master.title("Program Status")
self.pack(fill=BOTH, expand=1)
menu = Menu(self.master)
self.master.config(menu=menu)
text = Text(self.master)
app = StatusWindow(root)
root.mainloop()
Related
I'm trying to set up a list of checkbuttons from top to bottom in the GUI and add up the associated "onvalues" for each of the checkbuttons that are on.
My problem now is that for some reason my 'command' attribute in my 'calcbutton' is giving me a "Name 'calc_cost' is not defined" error.
I've added a bunch of imports that you see at the top of the code hoping that would solve the problem, to not much avail.
import tkinter as tk
from tkinter import *
from tkinter import Button
servicelist = ("Oil change","Lube job","Radiator flush","Transmission flush","Inspection","Muffler replacement","Tire rotation")
servicecost = (30,20,40,100,35,200,20)
a = 0
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def calc_cost(self):
print(a)
def init_window(self):
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
for i in range(len(servicelist)):
serviceButton = Checkbutton(self, text=servicelist[i], onvalue = servicecost[i], var = a)
serviceButton.place(x=0, rely = i*.1)
calcButton = tk.Button(self, text = "Calculate Cost", fg = "black", bg = "green", command = calc_cost)
calcButton.pack(side = "bottom")
root = Tk()
#size of the window
root.geometry("400x300")
app = Window(root)
root.mainloop()
The checkbuttons pop up and the GUI works for the most part besides the displaying of the 'calcbutton' as well as getting the "NameError: name 'calc_cost' is not defined"
Change command = calc_cost to command = self.calc_cost
self represents the instance of the class. By using the self keyword we can access the attributes and methods of the class in python.
It will give you this output
This question already has an answer here:
Passing argument to a function via a Button in Tkinter, starnge behaviour in loop [duplicate]
(1 answer)
Closed 4 years ago.
I'm trying to make a simple interface with 3 buttons, and each button should trigger an action based on its label. However, even though (I think) I'm passing the correct argument, it always passes the label of the last button. Here's a stripped-down version to show what's happening:
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda: self.verify(label)
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
Why?
You are encountering a (late biding) closure problem.
When you create a function using lambda, a closure is created. This means the variables in function's body are looked up at the time you call the lambda, not when you create it (and the scope in which lambda was created will contain variable with the final value that was assigned).
In order to prevent this, you need to create an argument and set it to a default value, which stores the current value of the variable at creation time.
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda label=label: self.verify(label) # Here
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
You can also use functools.partial, which looks cleaner in my opinion:
import tkinter as tk
import random
from functools import partial
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = partial(self.verify, label)
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
You need to assign a parameter to the lambda function, and pass it as argument to the function.
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda lbl=label: self.verify(lbl) # <-- here
def verify(self, label):
print(f'You pressed the button with label {label}', flush=True) # <-- added flush=True to ensure the printing is done as the moment you click
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
I have seen many explanations of how to turn an enabled button disabled but not when classes are involved. The error here is in the line 'button_1.config...' and the error message is that button_1 is not defined. I think this is because it is in a different method but im not sure how to disable a button from a different method. any help is appreciated.
from tkinter import *
class menu:
def __init__(self, master):
self.master = master
button_1 = Button(self.master, text = 'test', command = self.correct).pack()
def correct(self):
button_1.config(state = DISABLED)
def window():
root = Tk()
menu(root)
root.mainloop()
if __name__ == '__main__':
window()
The button needs to be an instance variable, if you're accessing it between methods in the class. Just add self. in front of it. It's also going to need to be packed on a separate line, otherwise the instance variable self.button_1 will return None:
class menu:
def __init__(self, master):
self.master = master
self.button_1 = Button(self.master, text = 'test', command = self.correct)
self.button_1.pack()
def correct(self):
self.button_1.config(state = DISABLED)
I've been building an app to track stock prices. The user should see a window with an entry widget and a button that creates a new frame with a label and a button. The label is the stock price and symbol, the button is a delete button, and should hide that frame if clicked.
I've re-written this program 4 times now, and it's been a great learning experience, but what I've learned is that I can't have the "mini-frames" being called from methods part of the main GUI class - this funks up the delete buttons, and updates the value behind frame.pack_forget() so it only deletes the last item ever.
I've moved my mini-frame widgets down into the class for the actual stock values. I've packed them (what I assume to be correct) but they don't show up. They also don't error out, which isn't very helpful. Here's my code, although I've omitted a lot of the functional parts to show what is happening with my frames. Keep in mind I need to keep it so that I can call my updater (self.update_stock_value) with a .after method against myapp.myContainer.
Is there a better way to do this?? Thanks in advance, my head hurts.
import re
import time
import urllib
from Tkinter import *
import threading
from thread import *
runningThreads = 0
# each object will be added to the gui parent frame
class MyApp(object):
def __init__(self, parent):
self.myParent = parent
self.myContainer = Canvas(parent)
self.myContainer.pack()
self.create_widgets()
# METHOD initiates basic GUI widgets
def create_widgets(self):
root.title("Stocker")
self.widgetFrame = Frame(self.myContainer)
self.widgetFrame.pack()
self.input = Entry(self.widgetFrame)
self.input.focus_set()
self.input.pack()
self.submitButton = Button(self.widgetFrame, command = self.onButtonClick)
self.submitButton.configure(text = "Add new stock")
self.submitButton.pack(fill = "x")
# METHOD called by each stock object
# returns the "symbol" in the entry widget
# clears the entry widget
def get_input_value(self):
var = self.input.get()
self.input.delete(0, END)
return var
# METHOD called when button is clicked
# starts new thread with instance of "Stock" class
def onButtonClick(self):
global runningThreads # shhhhhh im sorry just let it happen
runningThreads += 1 # count the threads open
threading.Thread(target = self.init_stock,).start() # force a tuple
if runningThreads == 1:
print runningThreads, "thread alive"
else:
print runningThreads, "threads alive"
def init_stock(self):
new = Stock()
class Stock(object):
def __init__(self):
# variable for the stock symbol
symb = self.stock_symbol()
# lets make a GUI
self.frame = Frame(myapp.myContainer)
self.frame.pack
# give the frame a label to update
self.testLabel = Label(self.frame)
self.testLabel.configure(text = self.update_stock_label(symb))
self.testLabel.pack(side = LEFT)
# create delete button to kill entire thread
self.killButton = Button(self.frame, command = self.kill_thread)
self.killButton.configure(text = "Delete")
self.killButton.pack(side = RIGHT)
# create stock label
# call updater
def kill_thread(self):
global runningThreads
runningThreads -= 1
self.stockFrame.pack_forget() # hide the frame
self.thread.exit() # kill the thread
def update_stock_label(self, symb):
self.testLabel.configure(text = str(symb) + str(get_quote(symb)))
myapp.myContainer.after(10000, self.update_stock_label(symb))
def stock_symbol(self):
symb = myapp.get_input_value()
print symb
# The most important part!
def get_quote(symbol):
try:
# go to google
base_url = "http://finance.google.com/finance?q="
# read the source code
content = urllib.urlopen(base_url + str(symbol)).read()
# set regex target
target = re.search('id="ref_\d*_l".*?>(.*?)<', content)
# if found, return.
if target:
print "found target"
quote = target.group(1)
print quote
else:
quote = "Not Found: "
return quote
# handling if no network connection
except IOError:
print "no network detected"
root = Tk()
root.geometry("280x200")
myapp = MyApp(root)
root.mainloop()
Your code won't run because of numerous errors, but this line is definitely not doing what you think it is doing:
self.frame.pack
For you to call the pack function you must include (), eg:
self.frame.pack()
You ask if your code is the best way to do this. I think you're on the right track, but I would change a few things. Here's how I would structure the code. This just creates the "miniframes", it doesn't do anything else:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.entry.pack(side="top", fill="x")
self.submit.pack(side="top")
def on_submit(self):
symbol = self.entry.get()
stock = Stock(self, symbol)
stock.pack(side="top", fill="x")
class Stock(tk.Frame):
def __init__(self, parent, symbol):
tk.Frame.__init__(self, parent)
self.symbol = tk.Label(self, text=symbol + ":")
self.value = tk.Label(self, text="123.45")
self.symbol.pack(side="left", fill="both")
self.value.pack(side="left", fill="both")
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
so when i run this code and click the button:
from Tkinter import *
import thread
class App:
def __init__(self, master):
print master
def creatnew():
admin=Tk()
lab=Label(admin,text='Workes')
lab.pack()
admin.minsize(width=250, height=250)
admin.maxsize(width=250, height=250)
admin.configure(bg='light green')
admin.mainloop()
def other():
la=Label(master,text='other')
la.pack()
bu=Button(master,text='clicks',command=lambda: thread.start_new_thread(creatnew,()))
bu.pack()
other()
Admin = Tk()
Admin.minsize(width=650, height=500)
Admin.maxsize(width=650, height=500)
app = App(Admin)
Admin.mainloop()
i get a second tkinter window but its a white blank screen that makes both programs not respond.
any ideas
Don't use threads. It's confusing the Tkinter mainloop. For a second window create a Toplevel window.
Your code with minimal modifications:
from Tkinter import *
# import thread # not needed
class App:
def __init__(self, master):
print master
def creatnew(): # recommend making this an instance method
admin=Toplevel() # changed Tk to Toplevel
lab=Label(admin,text='Workes')
lab.pack()
admin.minsize(width=250, height=250)
admin.maxsize(width=250, height=250)
admin.configure(bg='light green')
# admin.mainloop() # only call mainloop once for the entire app!
def other(): # you don't need define this as a function
la=Label(master,text='other')
la.pack()
bu=Button(master,text='clicks',command=creatnew) # removed lambda+thread
bu.pack()
other() # won't need this if code is not placed in function
Admin = Tk()
Admin.minsize(width=650, height=500)
Admin.maxsize(width=650, height=500)
app = App(Admin)
Admin.mainloop()