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 :)
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:
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.
tkinterApp.price1
variable directly in two threadsTk 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.
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()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.