Home > Back-end >  How do I change the variable of this class
How do I change the variable of this class

Time:10-20

I am pretty new to python, coming from Java and I want to update a variable in an initialized class
This is my full code

import datetime import time import threading

from tkinter import * from ibapi.client import EClient, TickAttribBidAsk from ibapi.wrapper import EWrapper, TickTypeEnum from ibapi.contract import Contract


class TestApp(EWrapper, EClient):

    def __init__(self):
        EClient.__init__(self, self)

    def tickPrice(self, reqId, tickType, price, attrib):
        print("Tick price. Ticker Id:", reqId, "tickType:", TickTypeEnum.to_str(tickType), "Price:", price, end=' ')

    def tickByTickBidAsk(self, reqId: int, time: int, bidPrice: float, askPrice: float, bidSize: int, askSize: int, tickAttribBidAsk: TickAttribBidAsk):
        print(bidPrice)
        tkinterApp.price1 = bidPrice


class Application:

    def runTest(self):
        app = TestApp()
        app.connect("127.0.0.1", 7497, 0)
        contract = Contract()
        contract.symbol = "PROG"
        contract.secType = "STK"
        contract.currency = "USD"
        contract.exchange = "SMART"
        contract.primaryExchange = "NASDAQ"

        time.sleep(1)

        app.reqMarketDataType(1)

        app.reqTickByTickData(19003, contract, "BidAsk", 0, True)

        app.run()

    def __init__(self):
        t = threading.Thread(target=self.runTest)
        t.start()
        self.runTest()


class TkinterClass:
    ibkrConnection = Application()
    root = Tk()
    root.title("test")
    root.grid_columnconfigure((0, 1), weight=1)
    titleTicker = Label(root, text="TICKER", bg='black', fg='white', width=100)
    titleRating = Label(root, text="PRICE", bg='black', fg='white', width=100)
    ticker1 = Label(root, text="PROG", bg='black', fg='white', width=100)
    
    price1 = Label(root, text=0, bg='black', fg='white', width=100) # To be changed with every tick
    
    titleTicker.grid(row=1, column=1)
    titleRating.grid(row=1, column=2)
    ticker1.grid(row=2, column=1)
    price1.grid(row=2, column=2)
    root.mainloop()


tkinterApp = TkinterClass()

The def tickByTickBidAsk is a callback and is called every ~2 sec I want to update the price1 variable in the class TkinterClass, but when I try to execute my code, the line tkinterApp.price1 = bidPrice gives me a name error: TkinterClass is not defined

This is probably a noob mistake I know :)

CodePudding user response:

It would probably help if you did something like this:

class TkinterClass:
    def __init__(self):
        self.ibkrConnection = Application()
        self.root = Tk()
        self.root.title("test")
        self.root.grid_columnconfigure((0, 1), weight=1)
        self.titleTicker = Label(root, text="TICKER", bg='black', fg='white', width=100)
        self.titleRating = Label(root, text="PRICE", bg='black', fg='white', width=100)
        self.ticker1 = Label(root, text="PROG", bg='black', fg='white', width=100)
        self.price1 = Label(root, text=0, bg='black', fg='white', width=100) # To be changed with every tick
        self.titleTicker.grid(row=1, column=1)
        self.titleRating.grid(row=1, column=2)
        self.ticker1.grid(row=2, column=1)
        self.price1.grid(row=2, column=2)
    def run(self):
        self.root.mainloop()

tkinterApp = TkinterClass()
tkinterApp.run()

However, there are still issues:

  1. Overwriting the tkinterApp.price1 Label with a number value

To set the label, use: tkinterApp.price1.config(str(value)) or use a tkinter StringVar to store the price1 text, and use that StringVar as the Label value.

  1. Using the tkinterApp.price1 variable directly in two threads

Tk is likely to be unhappy if you muck about with Tk variables from a background thread. I'd suggest getting some kind of timer running in the foreground thread and poll a variable updated in the background, so you're only updating the Tk variable from the foreground.

Use root.after(ms, callback) to schedule a callback in the foreground (before invoking root.mainloop()).

I don't believe a threading.Lock is required when reading a Python value updated in another thread, but it would be safer to add a lock()/unlock() around both update and access logic to be sure.

CodePudding user response:

I played with tk a few years ago, this is how I structured my code. I make a tkinter window and connect to TWS from the tkinter class.

from tkinter import *
import threading

from ibapi import wrapper
from ibapi.client import EClient
from ibapi.utils import iswrapper #just for decorator
from ibapi.common import *
from ibapi.ticktype import *
from ibapi.contract import Contract

class TkWdow():
    def __init__(self):
        root = Tk()
        frame = Frame(root)
        frame.pack()
        button = Button(frame, text="START", fg="green", command=self.start)
        button.pack(side=LEFT)
        button = Button(frame, text="ReqData", command=self.reqData)
        button.pack(side=LEFT)
        button = Button(frame, text="QUIT", fg="red", command=self.quit)
        button.pack(side=LEFT)
        self.output = Text(root, height=50, width=100)
        self.output.pack(side=BOTTOM)
        self.log("This is where output goes")
        root.mainloop()
        #root.destroy()

    def start(self):
        self.client = TestApp(self)
        self.log("starting")
        self.client.connect("127.0.0.1", 7497, clientId=123)
        thread = threading.Thread(target = self.client.run)
        thread.start()
        
    def log(self, *args):
        for s in args:
            self.output.insert(END, str(s)   " ")
            
        self.output.insert(END, "\n")    
                
    def quit(self):
        self.log("quitting")
        self.client.disconnect()
        
    def reqData(self):
        self.log("reqData")
        cont = Contract()
        cont.symbol = "cl"
        cont.secType = "FUT"
        cont.currency = "USD"
        cont.exchange = "nymex"
        cont.lastTradeDateOrContractMonth = "202112"
        self.client.reqMktData(1, cont, "233", False, False, None)
        
    def cancelMktData(self, reqId:TickerId):
        super().cancelMktData(reqId)
        self.log('sub cancel')
        
    
class TestApp(wrapper.EWrapper, EClient):        
    
    def __init__(self, wdow):
        self.wdow = wdow
        wrapper.EWrapper.__init__(self)
        EClient.__init__(self, wrapper=self)
        
    @iswrapper
    def nextValidId(self, orderId:int):
        self.wdow.log("setting nextValidOrderId: " , orderId)
        self.nextValidOrderId = orderId

    @iswrapper
    def error(self, reqId:TickerId, errorCode:int, errorString:str):
        self.wdow.log("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

    @iswrapper
    def tickString(self, reqId:TickerId, tickType:TickType, value:str):
        if tickType == TickTypeEnum.RT_VOLUME:
            self.wdow.log(value)#price,size,time


#if __name__ == "__main__":            
TkWdow()
  • Related