I am trying to update progressbar in tkinter.
First of i have two documents.
document 1: conatins GUI
document 2: Contains functions, that are called when pressing the button widgets attached to the GUI.
When i perform a calculation within document 2 i want to update the progress bar which is in document 1.
An exaple of the code is as follows. (just to mention, this is my first script in python, so please correct me if im wrong in any of the coding)
#Document 1
from Document2 import *
import tkinter as tk
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
from tkinter import font as tkfont # python 3
from PIL import ImageTk, Image
from tkinter.ttk import Progressbar
class TEST(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.resizable(self, width=False, height=False)
tk.Tk.title(self, "APP NAME")
tk.Tk.iconbitmap(self, filepath_NAME_icon)
tk.Tk.geometry(self, "465x262")
#self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (ProjectingPage):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("ProjectingPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class ProjectingPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent, bg=bg_color)
self.controller = controller
Button_get_TK = Button(self, text="Indsæt terrænkote",
font=("Calibri", btn_font_size, "normal"),
width=std_button_width,
bg=bg_color_button,
fg=fg_color_button,
borderwidth=size_border_width,
activebackground=act_bg,
command=Get_TK)
Button_get_TK.place(x=12, y=166)
Button_get_TK.update()
self.progress = Progressbar(self, orient = HORIZONTAL,
length = 100, mode = 'determinate')
self.progress.pack()
def UpdateProgressBar(self, data):
self.progress.start()
self.progress.update_idletasks()
and
#Document 2
import Document1 as KTP
from Document1 import *
def Get_TK():
for i in range(0,100):
test_progressbar = KTP.ProjectingPage
test_progressbar.UpdateProgressBar(i)
I have trimmed down my code to just the relevant part. I need to know the principals of getting my progressbar to update by calling a function from a separate document.
I really hope someone can help me. thanks in advance :-)
I wouldn't uses elements from Document1 as hardcoded in Document2 but as argument in function.
#Document 2
# without imports from Document1
def Get_TK(item):
test_progressbar = item.ProjectingPage
for i in range(100):
test_progressbar.UpdateProgressBar(i)
And then I would use lambda to assing function with values
command=lambda:Get_TK(self.UpdateProgressBar):
I would said that sending as argument is consistent with the principle of The Zen of Python
Explicit is better than implicit.
But if I would have to use Get_TK in ProjectingPage to access element in the same class ProjectingPage then I wouldn't put it in separated file but directly in ProjectingPage
class ProjectingPage(tk.Frame):
def Get_TK(self):
for i in range(100):
self.UpdateProgressBar(i)
I have a dataframe with various values in and am trying to plot it onto a figure embedded into tkinter. Here is the code:
figure = plt.Figure(figsize=((0.8 / 3) * res_width / 100, 0.2 * res_height / 100), facecolor="#67676b")
figure.add_subplot(fc="#15151c").plot(df["Close time"][0:50], df["Close"][0:50], "-g")
Here is what is shown:
It shows a straight line, why is this, when the data clearly shows otherwise?
I ouputted Close axis as a list and here is the output:
['19183.62000000', '19184.58000000', '19185.57000000', '19183.94000000', '19202.16000000', '19213.30000000', '19199.25000000', '19176.14000000', '19186.30000000', '19179.88000000', '19179.34000000', '19178.32000000', '19187.08000000', '19181.12000000', '19200.12000000', '19202.25000000', '19209.05000000', '19208.59000000', '19200.92000000', '19205.37000000', '19205.23000000', '19205.19000000', '19200.26000000', '19199.92000000', '19198.12000000', '19200.99000000', '19204.54000000', '19210.29000000', '19210.00000000', '19209.86000000', '19219.52000000', '19222.24000000', '19253.65000000', '19241.24000000', '19255.85000000', '19257.37000000', '19250.00000000', '19239.38000000', '19236.97000000', '19246.57000000', '19230.70000000', '19229.76000000', '19228.76000000', '19229.75000000', '19231.22000000', '19227.77000000', '19229.69000000', '19209.75000000', '19211.64000000', '19232.56000000']
As you can see, the graph should go up and down and not just a straight line. Is it only plotting a trend line for some reason?
I recreated a "minimal reproducible example":
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import sys
platform = sys.platform
# root class
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
# cycle through windows
for F in (MainMenu, PageTwo):
frame = F(container)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainMenu)
# method to change frames
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class MainMenu(tk.Frame): # main menu
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.configure(bg="#15151c")
close_time = [1605549599999, 1605553199999, 1605556799999, 1605560399999, 1605563999999, 1605567599999, 1605571199999, 1605574799999, 1605578399999, 1605581999999, 1605585599999, 1605589199999, 1605592799999, 1605596399999, 1605599999999, 1605603599999, 1605607199999, 1605610799999, 1605614399999, 1605617999999, 1605621599999, 1605625199999, 1605628799999, 1605632399999, 1605635999999, 1605639599999, 1605643199999, 1605646799999, 1605650399999, 1605653999999, 1605657599999, 1605661199999, 1605664799999, 1605668399999, 1605671999999, 1605675599999, 1605679199999, 1605682799999, 1605686399999, 1605689999999, 1605693599999, 1605697199999, 1605700799999, 1605704399999, 1605707999999, 1605711599999, 1605715199999, 1605718799999, 1605722399999, 1605725999999]
close = ['16691.91000000', '16690.85000000', '16779.01000000', '16825.56000000', '16695.41000000', '16713.86000000', '16713.57000000', '16806.09000000', '16690.21000000', '16678.02000000', '16573.58000000', '16668.95000000', '16619.81000000', '16631.72000000', '16750.00000000', '16704.83000000', '16659.11000000', '16763.47000000', '16995.06000000', '17083.01000000', '16947.38000000', '17153.93000000', '17322.19000000', '17639.35000000', '17808.66000000', '17638.37000000', '17685.22000000', '17677.64000000', '17624.15000000', '17596.78000000', '17659.38000000', '17693.64000000', '17739.42000000', '17680.64000000', '18030.71000000', '18369.33000000', '17623.39000000', '17780.19000000', '18063.70000000', '18211.02000000', '18110.00000000', '18250.00000000', '18249.99000000', '18024.03000000', '17882.89000000', '17683.11000000', '17873.88000000', '17793.71000000', '17890.45000000', '17775.92000000']
# graphs
figure = plt.Figure(figsize=(100, 100), facecolor="#67676b")
figure.add_subplot(fc="#15151c").plot(close_time, close, "-g")
FigureCanvasTkAgg(figure, self).get_tk_widget().pack()
class PageTwo(tk.Frame): # second page
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.configure(bg="#15151c")
tk.Label(self, text="pg2", font=("Consolas", 40), fg="#67676b", bg="#15151c").pack()
tk.Button(self, text="Return", command=lambda: app.show_frame(MainMenu)).pack()
# launch application
app = Application()
app.title("Test App")
if platform == "linux":
app.wm_attributes("-zoomed", 1)
else:
app.state("zoomed")
app.configure(bg="black")
app.mainloop()
The issue is because your close list is a list of strings, not floats, so matplotlib is treating them as catagorical data.
close = ['16691.91000000', '16690.85000000', '16779.01000000', '16825.56000000', '16695.41000000', '16713.86000000', '16713.57000000', '16806.09000000', '16690.21000000', '16678.02000000', '16573.58000000', '16668.95000000', '16619.81000000', '16631.72000000', '16750.00000000', '16704.83000000', '16659.11000000', '16763.47000000', '16995.06000000', '17083.01000000', '16947.38000000', '17153.93000000', '17322.19000000', '17639.35000000', '17808.66000000', '17638.37000000', '17685.22000000', '17677.64000000', '17624.15000000', '17596.78000000', '17659.38000000', '17693.64000000', '17739.42000000', '17680.64000000', '18030.71000000', '18369.33000000', '17623.39000000', '17780.19000000', '18063.70000000', '18211.02000000', '18110.00000000', '18250.00000000', '18249.99000000', '18024.03000000', '17882.89000000', '17683.11000000', '17873.88000000', '17793.71000000', '17890.45000000', '17775.92000000']
Convert the items in close to floats, and it should work fine. In your minimal exmaple, something like:
close = [float(i) for i in close]
should do it.
In your original code you have a dataframe, rather than a list; you could use df["Close"][0:50].astype(float) as you plot the data in that case
figure.add_subplot(fc="#15151c").plot(df["Close time"][0:50], df["Close"][0:50].astype(float), "-g")
I am trying to create a 2 mins countdown app with multiple windows. I am putting all windows(frames) into the main container, and then use tkraise() to raise the frame whenever the navigation button to specific window is clicked(eg:'startPage' frame will be raised if 'back to startPage' button is clicked).
The code below works well only when the object of class PracticePage was first created.
However, the timer is still running at the back when I navigate from the countdown timer frame(class PracticePage) to another page. In other words, the timer will not count down from 2 mins whenever I navigate from another page back to the countdown timer page. I want it to countdown from 2 mins whenever the timer frame is raised.
I am a beginner in programming. I apologized if my question and code is confusing. Can someone help? Thank you in advance.
Below is my code:
import tkinter as tk
from tkinter import *
from tkinter import ttk
import time
class App(tk.Tk): #we want this class to inherit from tk.Tk
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self,"PreSys")
container = tk.Frame(self, height = 1000, width =1000)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (SignInPage, StartPage, PracticePage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(SignInPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise() #to raise one of the frames up to the front
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.startButton = ttk.Button(self, text="スタート", command = lambda: controller.show_frame(PracticePage))
self.startButton.pack()
class PracticePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.timeLeft = tk.Label(self,text= "")
self.backButton = ttk.Button(self, text="やり直す", command = lambda: controller.show_frame(StartPage))
self.homeButton = ttk.Button(self, text="サインアウト", command = lambda: controller.show_frame(SignInPage))
self.timeLeft.pack()
self.backButton.pack()
self.homeButton.pack()
#rc.start_record(SignInPage.entry_name.get())
self.remaining = 0
self.countdown(121)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.timeLeft.configure(text="お疲れ様です!")
else:
mins, secs = divmod(self.remaining,60)
mins = round(mins)
secs = round(secs)
self.timeLeft.configure(text=str(mins) +"分"+ str(secs) +"秒")
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
apps = App()
apps.mainloop()
Before we get to the solution, when you supply your code make sure it works. You removed the SignInPage because it wasn't necessary, but you didn't follow up and remove the other references to it (i.e.- self.show_frame(SignInPage) in App.__init__ and self.homebutton in PracticePage.
As for your issue with resetting the timer, there are many ways to go about it, but they all come down to the fact that all you are doing is raising frames: you don't include any code that influences countdown based on what frame you are currently switching to.
Since this is a relatively simple application, I'll suggest the simplest solution: when you raise PracticePage reset its remaining variable. To keep it as easy and consistent as possible, we'll just modify show_frame
## You should store variables that are meaningful (you can place this underneath the imports)
PRACTICETIME = 121
## App():
def show_frame(self, cont):
frame = self.frames[cont]
if cont == PracticePage:
frame.remaining = PRACTICETIME
frame.tkraise() #to raise one of the frames up to the front
In a larger application I would recommend either using widget.after_cancel() to stop the countdown completely or simply to destroy and recreate your Pages if reasonable to do so. With this method you may notice a 1-second lag where the PracticePage is still displaying the previous remaining time: if this really bothers you, then I would recommend extracting the code that formats PracticePage.timeLeft into its own function and then calling that function from App.show_frame right after you set remaining.
I have written an GUI with tkinter. In the background I have to perfom some intensive calculations. From time to time I want to write some information from the calculation thread to the GUI window.
First I thought it was a good idea to add the computation code into the "mainloop". But this doesn't work, because the mainloop is resposible for keeping the GUI reactive. It seams to be no good idea to mainpulate it.
Below I have created a dummy app that scatches my new idea. The Sample app has a container. Inside that container, there is a TitleBar. The class TitleBar is defined below. It contains only one label.
Next I define a simple thread. It simulates a timeconsuming computation that wants to write some information to the GUI.
import tkinter as tk
import threading
import time
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
# initialize the main window
tk.Tk.__init__(self, *args, **kwargs)
# add a container which will take all the widgets
container = tk.Frame(self, bg="green")
container.pack(side="top", fill="both", expand=True)
# Add a titlebar object (defined below)
titleBar = TitleBar(container, controller=self)
titleBar.grid(row=0, column=0, columnspan=2, sticky=tk.N+tk.W+tk.E)
class TitleBar(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
# the title bar contains only one label element
titleLabel = tk.Label(self, text="This is the initial text")
titleLabel.pack(side=tk.LEFT)
# Define a thread that runs in the background to perform intensive calculations
class MyTestThread(threading.Thread):
def run(self):
for i in range(10):
time.sleep(1)
a = i+100 # intensive calculation
# from time to time: inform use about status
print(a) # printing to console works fine
app.titleBar.titleLabel['text'] = "test 1" # --- FAILS ---
if __name__ == "__main__":
app = SampleApp()
app.titleBar.titleLabel['text'] = "test 2" # --- FAILS ---
t = MyTestThread()
t.start()
app.mainloop()
The problem is that I cannot access the label to write information to it. The writing fails, both, from within the thread and from the app itself. In both cases it fails with the following error:
AttributeError: '_tkinter.tkapp' object has no attribute 'titleBar'
How can I access and change the properties of the label-object?
Thank you
Bernd
With the help of eyllanesc and Mark_Bassem I was able to solve the problem. It seems that the problem was indeed very simple.
Just in case people will wisit this post in the future, I leave the correct code here:
import tkinter as tk
import threading
import time
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
# initialize the main window
tk.Tk.__init__(self, *args, **kwargs)
# add a container which will take all the widgets
container = tk.Frame(self, bg="green")
container.pack(side="top", fill="both", expand=True)
# Add a titlebar object (defined below)
self.titleBar = TitleBar(container, controller=self)
self.titleBar.grid(row=0, column=0, columnspan=2, sticky=tk.N+tk.W+tk.E)
class TitleBar(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
# the title bar contains only one label element
self.titleLabel = tk.Label(self, text="This is the initial text")
self.titleLabel.pack(side=tk.LEFT)
# Define a thread that runs in the background to perform intensive calculations
class MyTestThread(threading.Thread):
def run(self):
for i in range(10):
time.sleep(1)
a = i+100 # intensive calculation
# from time to time: inform use about status
print(a) # printing to console works fine
app.titleBar.titleLabel['text'] = "status: " + str(a)
if __name__ == "__main__":
app = SampleApp()
#app.titleBar.titleLabel['text'] = "test 2"
t = MyTestThread()
t.start()
app.mainloop()
You forgot to put self before the titleBar attribute
I want to script two widgets which would be stacked and I would switch from one to another with a key. Each widget would be a Frame containing several Labels. I have the following code so far (only one Label per Frame):
import Tkinter as tk
import ttk
import datetime
def main():
# initialize root
root = tk.Tk()
# initialize widgets
dash = Dashboard(root)
notepad = Notepad(root)
# set key actions
root.bind('<F11>', root.lift)
root.bind('<F1>', dash.raiseme)
root.bind('<F2>', notepad.raiseme)
root.mainloop()
class Dashboard(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent) # voodoo
self.dashframe = tk.Frame(parent)
self.labone = tk.Label(self.dashframe, text="lab1", fg='black', bg='blue')
self.labone.grid(row=0, column=0)
def raiseme(self, event):
print "raiseme dash"
self.labone.configure(text=datetime.datetime.now())
self.dashframe.lift()
class Notepad(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent) # also voodoo
self.noteframe = tk.Frame(parent)
self.laboneone = tk.Label(self.noteframe, text="lab11", fg='white', bg='red')
self.laboneone.grid(row=0, column=0)
def raiseme(self, event):
print "raiseme notepad"
self.laboneone.configure(text=datetime.datetime.now())
self.noteframe.lift()
if __name__ == '__main__':
main()
Pressing F1 and F2 reach the correct routines but the only thing I get is the main window, empty. There are no errors displayed so I guess that the code runs fine (just not the way O would like to :)).
Can I achieve the switch using the skeleton above?
There are at least two big problems with your code.
First, you're creating all these new frames, but not placing them anywhere, so they will never show up anywhere. If you have a main window with nothing placed on it, of course you will just "get the main window, empty". You need to call pack or some other layout method on any widget to get it to show up on its parent. In this case, it sounds like you want to put them both in the exact same place, so grid or place is probably what you want.
Second, your Dashboard and Notepad classes are themselves Frames, but they don't do any Frame-ish stuff; instead, they each create another, sibling Frame and attach a label to that sibling. So, even if you packed the Dashboard and Notepad, they're just empty frame widgets, so that wouldn't do any good.
If you fix both of those, I think your code does what you want:
class Dashboard(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent) # voodoo
self.labone = tk.Label(self, text="lab1", fg='black', bg='blue')
self.labone.grid(row=0, column=0)
self.grid(row=0, column=0)
def raiseme(self, event):
print "raiseme dash"
self.labone.configure(text=datetime.datetime.now())
self.lift()
class Notepad(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent) # also voodoo
self.laboneone = tk.Label(self, text="lab11", fg='white', bg='red')
self.laboneone.grid(row=0, column=0)
self.grid(row=0, column=0)
def raiseme(self, event):
print "raiseme notepad"
self.laboneone.configure(text=datetime.datetime.now())
self.lift()
However, you might also want to set a fixed size for everything; otherwise you could end up lifting the red widget and, e.g., only covering 96% of the blue one because the current time is a bit narrower than the previous one…
The code you linked to for inspiration attempted to do this:
newFrame = tkinter.Frame(root).grid()
newFrame_name = tkinter.Label(newFrame, text="This is another frame").grid()
That won't work, because grid returns None, not the widget. But at least it calls grid; yours doesn't even do that.