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:
- 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.
- 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()