我对python很陌生,来自Java,我想更新一个初始化类中的变量
这是我的完整代码
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()
deftickByTickBidAsk
是一个回调函数,每2秒调用一次我想更新TkinterClass
类中的price1
变量,但是当我尝试执行我的代码时,tkinterApp.price1 = bidPrice
行给了我一个名称错误:TkinterClass is not defined
这可能是一个新手的错误,我知道:)
几年前我使用tk,这就是我构建代码的方式。我创建了一个tkinter窗口,并从tkinter类连接到TWS。
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()
如果您这样做可能会有所帮助:
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()
然而,仍然存在问题:
- 用数值覆盖
tkinterApp.price1
Label
要设置标签,使用:tkinterApp.price1.config(str(value))
或使用tkinter
StringVar
来存储price1
文本,并使用该StringVar
作为Label
值。
- 在两个线程中直接使用
tkinterApp.price1
变量
如果您在后台线程中处理Tk变量,Tk可能会不高兴。我建议在前台线程中运行某种定时器,并轮询在后台更新的变量,这样您只需要从前台更新Tk变量。
使用root.after(ms, callback)
在前台调度回调(在调用root.mainloop()
之前)。
我不认为在读取另一个线程中更新的Python值时需要threading.Lock
,但是在更新和访问逻辑周围添加lock()/unlock()
会更安全。