I have a pyglet program that runs a GUI in one thread and receives a message from another thread. It's supposed to update the GUI based on that message. The problem though is whenever I go to update a GUI element, I get an error message like this one:
Traceback (most recent call last):
File --- line 40, in run
pub.sendMessage("update", msg="Banana")
File --- line 77, in updateDisplay
self.label.text = msg
File "---\pyglet\text\__init__.py", line 289, in text
self.document.text = text
File "---\pyglet\text\document.py", line 294, in text
self.insert_text(0, text)
File "---\pyglet\text\document.py", line 425, in insert_text
self.dispatch_event('on_insert_text', start, text)
File "---\pyglet\event.py", line 408, in dispatch_event
if handler(*args):
File "---\pyglet\text\layout.py", line 1045, in on_insert_text
self._init_document()
File "---\pyglet\text\layout.py", line 1034, in _init_document
self._update()
File "---\pyglet\text\layout.py", line 957, in _update
lines = self._get_lines()
File "---\pyglet\text\layout.py", line 933, in _get_lines
glyphs = self._get_glyphs()
...
File "---\pyglet\font\base.py", line 246, in fit
region.blit_into(image, 0, 0, 0)
File "---\pyglet\image\__init__.py", line 1730, in blit_into
self.owner.blit_into(source, x + self.x, y + self.y, z + self.z)
File "---\pyglet\image\__init__.py", line 1622, in blit_into
glBindTexture(self.target, self.id)
File "---\pyglet\gl\lib.py", line 107, in errcheck
raise GLException(msg)
pyglet.gl.lib.GLException: b'invalid operation'
The operative error being pyglet.gl.lib.GLException: b'invalid operation'
I know self.label.text = Y works because it updates the text if I put it in the init() function. However, trying to do any kind of update from the update_display function (see code below) throws this error. I know the function gets called correctly because it successfully prints the message to console, but it breaks on the update element line. I've tried other elements than label and it's the same thing.
I have no idea why it's throwing this error or how to resolve it. Any ideas?
My code is:
import pyglet
from threading import Thread
from random import choice
from time import sleep
from pubsub import pub
RUN = True
class MessageThread(Thread):
def __init__(self):
"""Init Thread Class."""
Thread.__init__(self)
self.start() # start the thread
def run(self):
"""Run Thread."""
array = [0, 1, 2]
while True:
if RUN == False:
print("End runner")
break
value = choice(array)
sleep(1)
if value == 0:
print("event", value, ": Apple", flush=True)
pub.sendMessage("update", msg="Apple")
elif value == 1:
print("event", value, ": Banana", flush=True)
pub.sendMessage("update", msg="Banana")
else:
print("event", value, ": Carrot", flush=True)
pub.sendMessage("update", msg="Carrot")
class MyGUI(pyglet.window.Window):
# Note: With this structure, there is no "self.app", you just use
# self. This structure allows onDraw() to be called.
def __init__(self):
super(MyGUI, self).__init__(width=600,height=650,resizable=True)
self.label = pyglet.text.Label('Waiting...',
font_name='Times New Roman',
font_size=36,
x=self.width/2, y=self.height - 36,
anchor_x='center', anchor_y='center')
# create a pubsub receiver
# update is the topic subscribed to, updateDisplay the callable
pub.subscribe(self.updateDisplay, "update")
MessageThread()
def on_draw(self):
self.clear()
self.label.draw()
def updateDisplay(self, msg):
""" Receives data from thread and updates the display """
print ("Updating Display")
self.label.text = msg
def on_close(self):
""" Override the normal onClose behavior so that we can
kill the MessageThread, too
"""
print("Closing Application")
global RUN
RUN = False
self.close() # Need to include since defining on_close overrides it
if __name__ == "__main__":
window = MyGUI()
pyglet.app.run()
Related
I am trying to stop the loop after 1 go through, and the next time through the loop i want it to hit the other button, but right now its stuck in the first if bid_balance%2 == 0
def market_search(self):
bid_balance = 0
player_found = False
while player_found is False:
if self.stopped:
break
print("BOT looking for players .....")
if (bid_balance%2 == 0):
# Edit bid price
pyautogui.moveTo(1300, 798)
sleep(0.500)
#pyautogui.click()
else:
pyautogui.moveTo(828, 798)
sleep(0.500)
#pyautogui.click()
# Serching knap
pyautogui.moveTo(1622, 1281)
sleep(0.500)
#pyautogui.click()
# time for Site to load
sleep(self.SITLOADINGTIME_SECONDS)
bid_balance += 1
player_found = True
return player_found
The multithreading and the logic controller for the bot
# threading methods
def update_target(self, rectangles):
self.lock.acquire()
self.rectangles = rectangles
self.lock.release()
def update_screenshot(self, screenshot):
self.lock.acquire()
self.screenshot = screenshot
self.lock.release()
def start(self):
self.stopped = False
t = Thread(target=self.run)
t.start()
def stop(self):
self.stopped = True
# main logic controller
def run(self):
while not self.stopped:
if self.state == BotState.INITIALIZING:
# do no bot actions until the startup waiting period is complete
if time() > self.timestamp + self.INITIALIZING_SECONDS:
# start searching when the waiting period is over
self.lock.acquire()
self.state = BotState.SEARCHING
self.lock.release()
elif self.state == BotState.SEARCHING:
# Call the market searching function
self.market_search()
# start buying when the waiting period is over
self.lock.acquire()
self.state = BotState.BUYING
self.lock.release()
elif self.state == BotState.BUYING:
# Call the buyer when serching is over
self.buyer()
# Start seaching again
self.lock.acquire()
self.state = BotState.SEARCHING
self.lock.release()
error i recive:
BOT looking for players .....
BOT looking for players .....
BOT looking for players .....
BOT looking for players .....
BOT looking for players .....
BOT looking for players .....
Exception in thread Thread-3 (run):
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\threading.py", line 1016, in _bootstrap_inner
self.run()
File "C:\Program Files\Python310\lib\threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "c:\Users\nickl\Desktop\Code\Projekt Fifa 23\bot.py", line 198, in run
self.market_search()
File "c:\Users\nickl\Desktop\Code\Projekt Fifa 23\bot.py", line 73, in market_search
pyautogui.moveTo(1622, 1281)
File "C:\Users\nickl\AppData\Roaming\Python\Python310\site-packages\pyautogui\__init__.py", line 597, in wrapper
failSafeCheck()
File "C:\Users\nickl\AppData\Roaming\Python\Python310\site-packages\pyautogui\__init__.py", line 1722, in failSafeCheck
raise FailSafeException(
pyautogui.FailSafeException: PyAutoGUI fail-safe triggered from mouse moving to a corner of the screen. To disable this fail-safe, set pyautogui.FAILSAFE to False. DISABLING FAIL-SAFE IS NOT RECOMMENDED.
Screen Capture FPS: 53
Done.
I cant stop it either, even thoug i made a stop fucntion under the main python file, so i have to use the Fail Safe Exception from pyautogui
I have this python code:
from pytchat import LiveChat
import time
import requests
import threading
video_id = None
liveChat = None
def runChat():
global liveChat
liveChat = LiveChat(video_id)
while liveChat.is_alive():
try:
chatdata = liveChat.get()
for c in chatdata.items:
print(f"{c.datetime} [{c.author.channelId}]- {c.message}")
chatdata.tick()
except KeyboardInterrupt:
liveChat.terminate()
break
def checkId():
threading.Timer(1.0, checkId).start()
global video_id
req = requests.get("http://localhost/getId")
if req.status_code == 200:
vidId = req.text
if vidId != "null" and vidId != video_id:
video_id = vidId
print(f"got id: {video_id}")
if liveChat is not None:
liveChat.terminate()
runChat()
def main():
checkId()
if __name__ == '__main__':
main()
That utilizes the pytchat library (https://github.com/taizan-hokuto/pytchat).
As you can see, I want to dynamically change the videoId that the chat is bundled to, but with the code above this is the result:
root#vps:~# python3 liveChat.py
got id: a10TAdVZmOq # First id, the library works, we can capture chat messages.
got id: NMre6IAPoLI # After second id appears, there is this exception:
Exception in thread Thread-52:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.6/threading.py", line 1182, in run
self.function(*self.args, **self.kwargs)
File "liveChat.py", line 36, in checkId
runChat()
File "liveChat.py", line 11, in runChat
liveChat = LiveChat(video_id)
File "/usr/local/lib/python3.6/site-packages/pytchat/core_multithread/livechat.py", line 110, in __init__
signal.signal(signal.SIGINT, lambda a, b: self.terminate())
File "/usr/local/lib/python3.6/signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread
How can this be solved since signals only work in main thread?
Adding the parameter interruptable=False to the LiveChat object solved the issue.
def runChat():
global liveChat
liveChat = LiveChat(video_id, interruptable=False) # <---THIS
while liveChat.is_alive():
# ....
https://github.com/taizan-hokuto/pytchat/issues/8
I am trying to make a makeblock mblock implementation with python and i found this api on github but it uses python 2.7 and i am using python 3.8 so i am trying to configure the code so it can run on python 3.8 but i ran into an error which is
init mBot
bot
<lib.mBot.mSerial object at 0x02CE9898>
start with serial
<lib.mBot.mHID object at 0x02E2B310>
self.device = mHID()
'mHID' object has no attribute 'dict'
Exception in thread Thread-2:
self.start()
Traceback (most recent call last):
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 154, in __onRead
start with HID
--------------------
n = self.device.inWaiting()
<lib.mBot.mBot object at 0x002FF640>
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 96, in inWaiting
--------------------
Error in sys.excepthook:
buf = self.dict.device.read(64)
Traceback (most recent call last):
AttributeError: 'mHID' object has no attribute 'dict'
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 134, in excepthook
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\abdsak11\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
self.close()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 142, in close
self.device.close()
self.run()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 106, in close
File "C:\Users\abdsak11\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 870, in run
self.dict.device.close()
AttributeError: 'mHID' object has no attribute 'dict'
Original exception was:
Traceback (most recent call last):
File "c:/Users/abdsak11/OneDrive - Lärande/Dokument/GitHub/python-for-mbot/test.py", line 15, in <module>
self._target(*self._args, **self._kwargs)
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 163, in __onRead
bot.doMove(50,50)
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 179, in doMove
self.close()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 142, in close
self.__writePackage(bytearray([0xff,0x55,0x7,0x0,0x2,0x5]+self.short2bytes(-leftSpeed)+self.short2bytes(rightSpeed)))
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 278, in short2bytes
self.device.close()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 106, in close
return [ord(val[0]),ord(val[1])]
TypeError: ord() expected string of length 1, but int found
self.dict.device.close()
AttributeError: 'mHID' object has no attribute 'dict'
'mHID' object has no attribute 'dict'
Exception in thread Thread-1:
Traceback (most recent call last):
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 154, in __onRead
n = self.device.inWaiting()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 96, in inWaiting
buf = self.dict.device.read(64)
AttributeError: 'mHID' object has no attribute 'dict'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\abdsak11\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
self.run()
File "C:\Users\abdsak11\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 163, in __onRead
self.close()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 142, in close
self.device.close()
File "c:\Users\abdsak11\OneDrive - Lärande\Dokument\GitHub\python-for-mbot\lib\mBot.py", line 106, in close
self.dict.device.close()
AttributeError: 'mHID' object has no attribute 'dict'
So what i am trying to do is to control and upload code to my mbot via usb COM3 serial and thats the error that i am getting i tried everthing but its not working and i cant seem to figure out the cause of the error.
mbot.py
this is the main api that i found
import serial
import sys,time
import signal
from time import ctime,sleep
import glob,struct
from multiprocessing import Process,Manager,Array
import threading
import hid
class mSerial():
ser = None
def __init__(self):
print (self)
def start(self, port):
self.ser = serial.Serial(port,115200)
def device(self):
return self.ser
def serialPorts(self):
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
s = serial.Serial()
s.port = port
s.close()
result.append(port)
return result
def writePackage(self,package):
self.ser.write(package)
sleep(0.01)
def read(self):
return self.ser.read()
def isOpen(self):
return self.ser.isOpen()
def inWaiting(self):
return self.ser.inWaiting()
def close(self):
self.ser.close()
class mHID():
def __init__(self):
print (self)
def start(self):
print ("starting start \n\n")
self.manager = Manager()
print("manager pass \n\n")
self.dict = self.manager.dict()
print("dict pass \n\n")
self.dict.device = hid.device()
print("dict device pass \n\n")
self.dict.device.open(0x0416, 0xffff)
print("dict device open pass \n\n")
self.dict.device.hid_set_nonblocking(self.device,1)
print ("start")
self.buffer = []
self.bufferIndex = 0
def enumerate(self):
print ("enumerate")
for dev in self.dict.device.enumerate():
print ('------------------------------------------------------------')
print (dev.description())
def writePackage(self,package):
buf = []
buf += [0, len(package)]
for i in range(len(package)):
buf += [package[i]]
n = self.dict.device.write(buf)
sleep(0.01)
def read(self):
c = self.buffer[0]
self.buffer = self.buffer[1:]
return chr(c)
def isOpen(self):
return True
def inWaiting(self):
buf = self.dict.device.read(64)
l = 0
if len(buf)>0:
l = buf[0]
if l>0:
for i in range(0,l):
self.buffer += [buf[i+1]]
return len(self.buffer)
def close(self):
self.dict.device.close()
class mBot():
def __init__(self):
print ("init mBot")
signal.signal(signal.SIGINT, self.exit)
self.manager = Manager()
self.__selectors = self.manager.dict()
self.buffer = []
self.bufferIndex = 0
self.isParseStart = False
self.exiting = False
self.isParseStartIndex = 0
def startWithSerial(self, port):
self.device = mSerial()
self.device.start(port)
self.start()
def startWithHID(self):
self.device = mHID()
print ("self.device = mHID()\n \n")
self.device.start()
print ("self.device.start()\n \n")
self.start()
print ("self.start()\n \n")
def excepthook(self, exctype, value, traceback):
self.close()
def start(self):
sys.excepthook = self.excepthook
th = threading.Thread(target=self.__onRead,args=(self.onParse,))
th.start()
def close(self):
self.device.close()
def exit(self, signal, frame):
self.exiting = True
sys.exit(0)
def __onRead(self,callback):
while 1:
if(self.exiting==True):
break
try:
if self.device.isOpen()==True:
n = self.device.inWaiting()
for i in range(n):
r = ord(self.device.read())
callback(r)
sleep(0.01)
else:
sleep(0.5)
except Exception as ex:
print (str(ex))
self.close()
sleep(1)
def __writePackage(self,pack):
self.device.writePackage(pack)
def doRGBLed(self,port,slot,index,red,green,blue):
self.__writePackage(bytearray([0xff,0x55,0x9,0x0,0x2,0x8,port,slot,index,red,green,blue]))
def doRGBLedOnBoard(self,index,red,green,blue):
self.doRGBLed(0x7,0x2,index,red,green,blue)
def doMotor(self,port,speed):
self.__writePackage(bytearray([0xff,0x55,0x6,0x0,0x2,0xa,port]+self.short2bytes(speed)))
def doMove(self,leftSpeed,rightSpeed):
self.__writePackage(bytearray([0xff,0x55,0x7,0x0,0x2,0x5]+self.short2bytes(-leftSpeed)+self.short2bytes(rightSpeed)))
def doServo(self,port,slot,angle):
self.__writePackage(bytearray([0xff,0x55,0x6,0x0,0x2,0xb,port,slot,angle]))
def doBuzzer(self,buzzer,time=0):
self.__writePackage(bytearray([0xff,0x55,0x7,0x0,0x2,0x22]+self.short2bytes(buzzer)+self.short2bytes(time)))
def doSevSegDisplay(self,port,display):
self.__writePackage(bytearray([0xff,0x55,0x8,0x0,0x2,0x9,port]+self.float2bytes(display)))
def doIROnBoard(self,message):
self.__writePackage(bytearray([0xff,0x55,len(message)+3,0x0,0x2,0xd,message]))
def requestLightOnBoard(self,extID,callback):
self.requestLight(extID,8,callback)
def requestLight(self,extID,port,callback):
self.__doCallback(extID,callback)
self.__writePackage(bytearray([0xff,0x55,0x4,extID,0x1,0x3,port]))
def requestButtonOnBoard(self,extID,callback):
self.__doCallback(extID,callback)
self.__writePackage(bytearray([0xff,0x55,0x4,extID,0x1,0x1f,0x7]))
def requestIROnBoard(self,extID,callback):
self.__doCallback(extID,callback)
self.__writePackage(bytearray([0xff,0x55,0x3,extID,0x1,0xd]))
def requestUltrasonicSensor(self,extID,port,callback):
self.__doCallback(extID,callback)
self.__writePackage(bytearray([0xff,0x55,0x4,extID,0x1,0x1,port]))
def requestLineFollower(self,extID,port,callback):
self.__doCallback(extID,callback)
self.__writePackage(bytearray([0xff,0x55,0x4,extID,0x1,0x11,port]))
def onParse(self, byte):
position = 0
value = 0
self.buffer+=[byte]
bufferLength = len(self.buffer)
if bufferLength >= 2:
if (self.buffer[bufferLength-1]==0x55 and self.buffer[bufferLength-2]==0xff):
self.isParseStart = True
self.isParseStartIndex = bufferLength-2
if (self.buffer[bufferLength-1]==0xa and self.buffer[bufferLength-2]==0xd and self.isParseStart==True):
self.isParseStart = False
position = self.isParseStartIndex+2
extID = self.buffer[position]
position+=1
type = self.buffer[position]
position+=1
# 1 byte 2 float 3 short 4 len+string 5 double
if type == 1:
value = self.buffer[position]
if type == 2:
value = self.readFloat(position)
if(value<-255 or value>1023):
value = 0
if type == 3:
value = self.readShort(position)
if type == 4:
value = self.readString(position)
if type == 5:
value = self.readDouble(position)
if(type<=5):
self.responseValue(extID,value)
self.buffer = []
def readFloat(self, position):
v = [self.buffer[position], self.buffer[position+1],self.buffer[position+2],self.buffer[position+3]]
return struct.unpack('<f', struct.pack('4B', *v))[0]
def readShort(self, position):
v = [self.buffer[position], self.buffer[position+1]]
return struct.unpack('<h', struct.pack('2B', *v))[0]
def readString(self, position):
l = self.buffer[position]
position+=1
s = ""
for i in Range(l):
s += self.buffer[position+i].charAt(0)
return s
def readDouble(self, position):
v = [self.buffer[position], self.buffer[position+1],self.buffer[position+2],self.buffer[position+3]]
return struct.unpack('<f', struct.pack('4B', *v))[0]
def responseValue(self, extID, value):
self.__selectors["callback_"+str(extID)](value)
def __doCallback(self, extID, callback):
self.__selectors["callback_"+str(extID)] = callback
def float2bytes(self,fval):
val = struct.pack("f",fval)
return [ord(val[0]),ord(val[1]),ord(val[2]),ord(val[3])]
def short2bytes(self,sval):
val = struct.pack("h",sval)
return [ord(val[0]),ord(val[1])]
test.py
this is the test file that i am working with
from lib.mBot import *
if __name__ == "__main__":
bot = mBot()
print ("bot \n\n")
bot.startWithSerial("COM3")
print ("start with serial\n\n")
bot.startWithHID()
print ("start with HID \n\n")
print(f'--------------------\n')
print (bot)
print(f'--------------------\n')
bot.doMove(50,50)
I believe your trouble is here:
class mHID():
def __init__(self):
print (self)
def start(self):
# ...
self.dict = self.manager.dict()
# ...
def close(self):
self.dict.device.close()
You should always define your instance attributes in __init__(), at least with an initial value of None, even if you plan to change them later.
Additionally, whenever you have a situation where "A must be called before B", you should write the code to properly handle when B is called without A. In this case, what should close() do if start() was never called? For simplicity, I'm just having it do nothing, but you may consider raising an exception.
class mHID():
def __init__(self):
print (self)
self.dct = None
def start(self):
# ...
self.dct = self.manager.dict()
# ...
def close(self):
if self.dct:
self.dct.device.close()
You'll notice I also renamed self.dict to self.dct. Although the former name may technically be allowable because of the self. namespace, dict is actually a built-in class. Under various circumstances, you could wind up dealing with shadowing. In the very least, it's confusing to always have to remind oneself that "this isn't the built-in dict, but an instance variable instead.
(Possible aside: is there a particular reason why you don't want to call start() from the initializer?)
WARNING: This is not a simple case of "copy and paste the answer". I've illustrated a couple of design principles here that you should incorporate into the rest of your code.
I am trying to create an UI interface where all the videos in ROS bag file will be played simultaneously. For this I have used threading concept like each thread for each video.
I am able to play all videos , but while playing the following error is thrown stopping the play.
class Main(QMainWindow):
...
...
class videoThread(QtCore.QThread):
def __init__(self,topic,bag,label):
super().__init__()
self.bag = bag
self.topic = topic
self.label = label
def run(self):
bridge = CvBridge()
for topic, msg, t in self.bag.read_messages(topics=[self.topic]):
img_dcam = bridge.compressed_imgmsg_to_cv2(msg, desired_encoding="passthrough")
rgbImage = cv2.cvtColor(img_dcam, cv2.COLOR_BGR2RGB)
convertToQtFormat = QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
QImage.Format_RGB888)
convertToQtFormat = QPixmap.fromImage(convertToQtFormat)
pixmap = QPixmap(convertToQtFormat)
resizeImage = pixmap.scaled(300, 300, Qt.KeepAspectRatio)
self.label.setPixmap(resizeImage)
time.sleep(0.033)
def playVideo(self,topic):
self.x = self.videoThread(topic,self.bag,self.label)
self.x.start()
Traceback (most recent call last):
File "rosmanager_main.py", line 1276, in run
for topic, msg, t in self.bag.read_messages(topics=[self.topic]):
File "/opt/ros/melodic/lib/python2.7/dist-packages/rosbag/bag.py", line 2679, in read_messages
yield self.seek_and_read_message_data_record((entry.chunk_pos, entry.offset), raw, return_connection_header)
File "/opt/ros/melodic/lib/python2.7/dist-packages/rosbag/bag.py", line 2826, in seek_and_read_message_data_record
header = _read_header(f)
File "/opt/ros/melodic/lib/python2.7/dist-packages/rosbag/bag.py", line 1999, in _read_header
raise ROSBagFormatException('Error reading header: %s' % str(ex))
rosbag.bag.ROSBagFormatException: Error reading header: expecting 606348583 bytes, read 871518
I wrote a simple Tkinter based Python application that reads text from a serial connection and adds it to the window, specifically a text widged.
After a lot of tweaks and some very strange exceptions, this works. Then I added autoscrolling by doing this:
self.text.insert(END, str(parsed_line))
self.text.yview(END)
These lines run in a thread. The thread blocks on reading fromt the serial connection, splits lines and then adds all lines to the widget.
This works, too. Then I wanted to allow the user to scroll which should disable auto-scroll until the user scrolls back to the bottom.
I found this
Stop Text widget from scrolling when content is changed
which seems to be related. Especially, I tried the code from DuckAssasin's comment:
if self.myWidgetScrollbar.get() == 1.0:
self.myWidget.yview(END)
I also tried .get()[1] which is actually the element I want (bottom position). However, this crashes with the following exception:
Traceback (most recent call last):
File "transformer-gui.py", line 119, in run
pos = self.scrollbar.get()[1]
File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
return self._getdoubles(self.tk.call(self._w, 'get'))
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1028, in _getdoubles
return tuple(map(getdouble, self.tk.splitlist(string)))
ValueError: invalid literal for float(): None
It seems as if tkinter somewhere returns None which then is being parsed as a float. I read somewhere, that e.g. the index method of the text widged sometimes returnes None if the requested location is not visible.
Hopefully, anybody can help me out with this problem!
[EDIT]
Ok, I have assembled a demo script that can reproduce this issue on my Win XP machine:
import re,sys,time
from Tkinter import *
import Tkinter
import threading
import traceback
class ReaderThread(threading.Thread):
def __init__(self, text, scrollbar):
print "Thread init"
threading.Thread.__init__(self)
self.text = text
self.scrollbar = scrollbar
self.running = True
def stop(self):
print "Stopping thread"
running = False
def run(self):
print "Thread started"
time.sleep(5)
i = 1
try:
while(self.running):
# emulating delay when reading from serial interface
time.sleep(0.05)
line = "the quick brown fox jumps over the lazy dog\n"
curIndex = "1.0"
lowerEdge = 1.0
pos = 1.0
# get cur position
pos = self.scrollbar.get()[1]
# Disable scrollbar
self.text.configure(yscrollcommand=None, state=NORMAL)
# Add to text window
self.text.insert(END, str(line))
startIndex = repr(i) + ".0"
curIndex = repr(i) + ".end"
# Perform colorization
if i % 6 == 0:
self.text.tag_add("warn", startIndex, curIndex)
elif i % 6 == 1:
self.text.tag_add("debug", startIndex, curIndex)
elif i % 6 == 2:
self.text.tag_add("info", startIndex, curIndex)
elif i % 6 == 3:
self.text.tag_add("error", startIndex, curIndex)
elif i % 6 == 4:
self.text.tag_add("fatal", startIndex, curIndex)
i = i + 1
# Enable scrollbar
self.text.configure(yscrollcommand=self.scrollbar.set, state=DISABLED)
# Auto scroll down to the end if scroll bar was at the bottom before
# Otherwise allow customer scrolling
if pos == 1.0:
self.text.yview(END)
#if(lowerEdge == 1.0):
# print "is lower edge!"
#self.text.see(curIndex)
#else:
# print "Customer scrolling", lowerEdge
# Get current scrollbar position before inserting
#(upperEdge, lowerEdge) = self.scrollbar.get()
#print upperEdge, lowerEdge
#self.text.update_idletasks()
except Exception as e:
traceback.print_exc(file=sys.stdout)
print "Exception in receiver thread, stopping..."
pass
print "Thread stopped"
class Transformer:
def __init__(self):
pass
def start(self):
"""starts to read linewise from self.in_stream and parses the read lines"""
count = 1
root = Tk()
root.title("Tkinter Auto-Scrolling Test")
topPane = PanedWindow(root, orient=HORIZONTAL)
topPane.pack(side=TOP, fill=X)
lowerPane = PanedWindow(root, orient=VERTICAL)
scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
text = Text(wrap=WORD, yscrollcommand=scrollbar.set)
scrollbar.config(command=text.yview)
# Color definition for log levels
text.tag_config("debug",foreground="gray50")
text.tag_config("info",foreground="green")
text.tag_config("warn",foreground="orange")
text.tag_config("error",foreground="red")
text.tag_config("fatal",foreground="#8B008B")
# set default color
text.config(background="black", foreground="gray");
text.pack(expand=YES, fill=BOTH)
lowerPane.add(text)
lowerPane.pack(expand=YES, fill=BOTH)
t = ReaderThread(text, scrollbar)
print "Starting thread"
t.start()
try:
root.mainloop()
except Exception as e:
print "Exception in window manager: ", e
t.stop()
t.join()
if __name__ == "__main__":
try:
trans = Transformer()
trans.start()
except Exception as e:
print "Error: ", e
sys.exit(1)
I let this scipt run and start to scroll up and down and after some time I get a lot of always different exceptions such as:
.\source\testtools\device-log-transformer>python tkinter-autoscroll.py
Thread init
Starting thread
Thread started
Traceback (most recent call last):
File "tkinter-autoscroll.py", line 59, in run
self.text.configure(yscrollcommand=self.scrollbar.set, state=DISABLED)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1202, in configure
Stopping thread
return self._configure('configure', cnf, kw)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1193, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
TclError: invalid command name ".14762592"
Exception in receiver thread, stopping...
Thread stopped
.\source\testtools\device-log-transformer>python tkinter-autoscroll.py
Thread init
Starting thread
Thread started
Stopping thread
Traceback (most recent call last):
File "tkinter-autoscroll.py", line 35, in run
pos = self.scrollbar.get()[1]
File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
return self._getdoubles(self.tk.call(self._w, 'get'))
TclError: invalid command name ".14762512"
Exception in receiver thread, stopping...
Thread stopped
.\source\testtools\device-log-transformer>python tkinter-autoscroll.py
Thread init
Starting thread
Thread started
Traceback (most recent call last):
File "tkinter-autoscroll.py", line 65, in run
self.text.yview(END)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 3156, in yview
self.tk.call((self._w, 'yview') + what)
Stopping threadTclError: invalid command name ".14762592"
Exception in receiver thread, stopping...
Thread stopped
.\source\testtools\device-log-transformer>python tkinter-autoscroll.py
Thread init
Starting thread
Thread started
Traceback (most recent call last):
File "tkinter-autoscroll.py", line 35, in run
pos = self.scrollbar.get()[1]
File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
return self._getdoubles(self.tk.call(self._w, 'get'))
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1028, in _getdoubles
return tuple(map(getdouble, self.tk.splitlist(string)))
ValueError: invalid literal for float(): None
Exception in receiver thread, stopping...
Thread stopped
Stopping thread
.\source\testtools\device-log-transformer>python tkinter-autoscroll.py
Thread init
Starting thread
Thread started
Traceback (most recent call last):
File "tkinter-autoscroll.py", line 53, in run
self.text.tag_add("error", startIndex, curIndex)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 3057, in tag_add
(self._w, 'tag', 'add', tagName, index1) + args)
TclError: bad option "261.0": must be bbox, cget, compare, configure, count, debug, delete, dlineinfo, dump, edit, get, image, index, insert, mark, pe
er, replace, scan, search, see, tag, window, xview, or yview
Exception in receiver thread, stopping...
Thread stopped
I hope this helps you to help me :)
Thanks,
/J
It's hard to tell what's really going on but have you considered using a Queue?
from Tkinter import *
import time, Queue, thread
def simulate_input(queue):
for i in range(100):
info = time.time()
queue.put(info)
time.sleep(0.5)
class Demo:
def __init__(self, root, dataQueue):
self.root = root
self.dataQueue = dataQueue
self.text = Text(self.root, height=10)
self.scroller = Scrollbar(self.root, command=self.text.yview)
self.text.config(yscrollcommand=self.scroller.set)
self.text.tag_config('newline', background='green')
self.scroller.pack(side='right', fill='y')
self.text.pack(fill='both', expand=1)
self.root.after_idle(self.poll)
def poll(self):
try:
data = self.dataQueue.get_nowait()
except Queue.Empty:
pass
else:
self.text.tag_remove('newline', '1.0', 'end')
position = self.scroller.get()
self.text.insert('end', '%s\n' %(data), 'newline')
if (position[1] == 1.0):
self.text.see('end')
self.root.after(1000, self.poll)
q = Queue.Queue()
root = Tk()
app = Demo(root, q)
worker = thread.start_new_thread(simulate_input, (q,))
root.mainloop()
Regarding your demo script.
You're doing GUI stuff from the non-GUI thread. That tends to cause problems.
see: http://www.effbot.org/zone/tkinter-threads.htm
OK,
based on the valuable suggestions by noob oddy I was able to rewrite the example script by using the Tkinter.generate_event() method to generate asynchronous event and a queue to pass the information.
Every time a line is read from the stream (which is simulated by a constant string and a delay), I append the line to a queue (because passing objects to the event method is not supported AFAIK) and then create a new event.
The event callback method retrieves the message from the queue and adds it to the Text widged. This works because this method is called from the Tkinter mainloop an thus it cannot interfere with the other jobs.
Here is the script:
import re,sys,time
from Tkinter import *
import Tkinter
import threading
import traceback
import Queue
class ReaderThread(threading.Thread):
def __init__(self, root, queue):
print "Thread init"
threading.Thread.__init__(self)
self.root = root
self.running = True
self.q = queue
def stop(self):
print "Stopping thread"
running = False
def run(self):
print "Thread started"
time.sleep(5)
try:
while(self.running):
# emulating delay when reading from serial interface
time.sleep(0.05)
curline = "the quick brown fox jumps over the lazy dog\n"
try:
self.q.put(curline)
self.root.event_generate('<<AppendLine>>', when='tail')
# If it failed, the window has been destoyed: over
except TclError as e:
print e
break
except Exception as e:
traceback.print_exc(file=sys.stdout)
print "Exception in receiver thread, stopping..."
pass
print "Thread stopped"
class Transformer:
def __init__(self):
self.q = Queue.Queue()
self.lineIndex = 1
pass
def appendLine(self, event):
line = self.q.get_nowait()
if line == None:
return
i = self.lineIndex
curIndex = "1.0"
lowerEdge = 1.0
pos = 1.0
# get cur position
pos = self.scrollbar.get()[1]
# Disable scrollbar
self.text.configure(yscrollcommand=None, state=NORMAL)
# Add to text window
self.text.insert(END, str(line))
startIndex = repr(i) + ".0"
curIndex = repr(i) + ".end"
# Perform colorization
if i % 6 == 0:
self.text.tag_add("warn", startIndex, curIndex)
elif i % 6 == 1:
self.text.tag_add("debug", startIndex, curIndex)
elif i % 6 == 2:
self.text.tag_add("info", startIndex, curIndex)
elif i % 6 == 3:
self.text.tag_add("error", startIndex, curIndex)
elif i % 6 == 4:
self.text.tag_add("fatal", startIndex, curIndex)
i = i + 1
# Enable scrollbar
self.text.configure(yscrollcommand=self.scrollbar.set, state=DISABLED)
# Auto scroll down to the end if scroll bar was at the bottom before
# Otherwise allow customer scrolling
if pos == 1.0:
self.text.yview(END)
self.lineIndex = i
def start(self):
"""starts to read linewise from self.in_stream and parses the read lines"""
count = 1
self.root = Tk()
self.root.title("Tkinter Auto-Scrolling Test")#
self.root.bind('<<AppendLine>>', self.appendLine)
self.topPane = PanedWindow(self.root, orient=HORIZONTAL)
self.topPane.pack(side=TOP, fill=X)
self.lowerPane = PanedWindow(self.root, orient=VERTICAL)
self.scrollbar = Scrollbar(self.root)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.text = Text(wrap=WORD, yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.text.yview)
# Color definition for log levels
self.text.tag_config("debug",foreground="gray50")
self.text.tag_config("info",foreground="green")
self.text.tag_config("warn",foreground="orange")
self.text.tag_config("error",foreground="red")
self.text.tag_config("fatal",foreground="#8B008B")
# set default color
self.text.config(background="black", foreground="gray");
self.text.pack(expand=YES, fill=BOTH)
self.lowerPane.add(self.text)
self.lowerPane.pack(expand=YES, fill=BOTH)
t = ReaderThread(self.root, self.q)
print "Starting thread"
t.start()
try:
self.root.mainloop()
except Exception as e:
print "Exception in window manager: ", e
t.stop()
t.join()
if __name__ == "__main__":
try:
trans = Transformer()
trans.start()
except Exception as e:
print "Error: ", e
sys.exit(1)
Thanks again to everybody who contributed for your help!