-2

I am creating a python program for a competition displaying the price of shares and the share prices are meant to be updated every minute. I have an excel sheet through which I need to sync prices every minute. To achieve this, I have used openpyxl external library.

I have created the core program successfully but I am badly stuck when it is coming to implementing it on GUI. I don't have any idea how to create a tkinter GUI which updates price at the end of every while loop iteration,

Here's the code till now (I know it can be better but I have to deliver it tomorrow and it's only one-time thing so it won't matter) -

UPDATE There is no need of user interacting with software. I only require formatting for sleek looks.

#GUI
app = Tk()
app.title("Trading Times 2016")
app.geometry('768x720')

#Execution
Raw = 3
timer = 1
int(Raw)
while Raw != 141:
os.system('CLS')
#timer
print("Time - %i" %timer)
timer += 1
#Currency
print("\n")
print("Dollar -", currency["C%d" %Raw].value)
print("Pound -", currency["E%d" %Raw].value)
print("Euro -", currency["G%d" %Raw].value)
print("Yuan -", currency["I%d" %Raw].value)
#Commodity
print("\n")
print("Gold per 100gm -", commodity["C%d" %Raw].value)
print("Wheat per Quintal -", commodity["E%d" %Raw].value)
print("Silver per Kg -", commodity["G%d" %Raw].value)
print("Crude per Barrel -", commodity["I%d" %Raw].value)
#Bonds
print("\n")
print("Bonds -", bonds["C%i" %Raw].value)
percent =  bonds["E%i" %Raw].value
int(percent)
print("RBI Bonds Yield(In Per Cent) - %i" %percent)
#ETF's
print("\n")
print("Small Cap Index -", etf["B%d" %Raw].value)  
print("Junior BEES -", etf["C%d" %Raw].value)
print("Bank BEES -", etf["D%d" %Raw].value)
print("PSUBNK BEES -", etf["E%d" %Raw].value)
print("CPSTTEF -", etf["F%d" %Raw].value)
print("Infra BEES -", etf["G%d" %Raw].value)           
print("Nifty BEES -", etf["H%d" %Raw].value)
print("SENSEX -", etf["I%d" %Raw].value)
#Mutual Funds
print("\n")
print("ABC -", mutual_funds["B%d" %Raw].value)
print("DEF -", mutual_funds["C%d" %Raw].value)
print("TNC -", mutual_funds["D%d" %Raw].value)     
print("KFJ -", mutual_funds["E%d" %Raw].value)
print("YWU -", mutual_funds["F%d" %Raw].value)
print("QNV -", mutual_funds["G%d" %Raw].value)
print("NBV -", mutual_funds["H%d" %Raw].value)
print("KAS -", mutual_funds["I%d" %Raw].value)
print("AYD -", mutual_funds["J%d" %Raw].value)
print("IT FUND -", mutual_funds["K%d" %Raw].value)
print("PHARMA -", mutual_funds["L%d" %Raw].value)
print("FMCG -", mutual_funds["M%d" %Raw].value)
#Shift Raw
Raw += 1
time.sleep(60)
app.mainloop()

Can anyone please help me in implement a GUI which can update itself every minute. Thanks

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Prince
  • 1
  • 2

3 Answers3

1

Use Tkinter's after command to run a function (see this or this) after X number of milliseconds. As for the GUI itself, you'll need to use either a series of labels that get updated or, preferably, the ttk.TreeView widget which would look better.

Community
  • 1
  • 1
Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • Actually, I need to update it inside a while loop iteration rather than a particular amount of time. I have a sleep timer embedded in the while loop so the variable can beupdated. Basically, I need to get it updated inside while loop. Can you please help me on this? – Prince Feb 02 '16 at 16:13
  • You stated that it needed to be `updated every minute` and you have a `time.sleep(60)` in your code. So it is indeed time-based. Note that the sleep command will block your main loop, so that's not going to work. – Mike Driscoll Feb 02 '16 at 16:30
  • True I need the information change every 60 seconds but on GUI it should happen through the end every while loop iteration not through the timer. Through timer I am updating the variables which are then implemented on the GUI – Prince Feb 02 '16 at 17:10
1

Tkinter widgets support a method after which invokes a function after a given interval im milliseconds. It is only approximate but might be good enough for you. This might show you what I mean. Forgive any formatting problems - this is my first post here.

import tkinter as tk

class App(tk.Tk):

def __init__(self):
    tk.Tk.__init__(self)

    self.counter  = 0
    self.str_counter = tk.StringVar()
    tk.Label(self,textvariable = self.str_counter).pack()

    self.after(1000,self.bump)
    self.mainloop()

def bump(self):
    self.counter += 1
    self.str_counter.set(str(self.counter))

    self.after(1000,self.bump)

App()

  • Actually, I don't need to update the information after some time. It need to be updated at the end of each while loop iteration. How can I achieve this? And can you explain me without OOP approach because its a single use software and I am a newbie. – Prince Feb 02 '16 at 16:28
0

You seem to have a fundamental misunderstanding of how GUI programs work. You shouldn't be creating your own while loop. Tkinter (and other GUI toolkits) have a custom while loop that needs to run.

The reason is that, in order for the GUI to work, it needs to be able to process events. Even something as simple as redrawing the window when another window moves across it is a response to an event.

In Tkinter, this event processor is mainloop. If mainloop isn't running (before you call it, if your program is sleeping, if you have your own long running code executing, ...), it can't handle events. If it can't handle events then it can't update the screen.

You can think of mainloop as being nothing more than an infinite loop that looks like this:

while the_app_is_running:
    event = wait_for_next_event()
    process_event(event)

Note: you don't actually write the above code, but metaphorically that's what calling mainloop does.

In your case, it appears you want the display to update once every minute. This is very easy to do by leveraging this event loop. The event loop is already running so all you need to do is tell it to wake up once a minute and update its data. You do this with the after method. Give this method the name of a function to call and a delay, and it will call that function after the delay. It does this by placing a time-based event on the event queue.

The first step is to set up the display with all of your widgets. For example:

app = Tk()
...
dollar_label = Label(app, text="")
pound_label = Label(app, text="")
...

The next step is to create a function that will fill in all the labels with the values you want them to have. It's important to do this in a separate step from when you create the labels, since you want to be able to do this every minute.

def refresh_display():
    # First, get the new data
    currency = ...
    commodity = ...
    ...
    # next, update the widgets
    dollar_label.configure(text="Dollar: %s" % currency["dollar"]
    pound_label.configure(text="Pound: %s" % currency["pound"]
    ...

Finally, you want to call this function every minute. You can do that by writing a function that calls the refresh_display function, then schedules itself to run again in 60 seconds:

def call_every_60_seconds():
    refresh_display()
    app.after(60000, call_every_60_seconds)

The final step is to start the updating by calling this function once, before you start the mainloop. This will update the display with the initial values, and then start the schedule running every minute after that.

call_every_60_seconds()
app.mainloop()

That's the basics of what you have to do. It's pretty straight forward, it just requires a bit of organization to your code.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685