Question

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()
Was it helpful?

Solution

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()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top