problem
I have written a small program to implement a stopwatch. This stopwatch will begin when s is pressed and stop running when l is pressed. For this I have used the following code:
f = self.frame
w = self.window
info = Label(f,text="\nPress \'s\' to start running and \'l\' to stop running\n")
info.pack()
w.bind('<KeyPress-s>',self.startrunning)
w.bind('<KeyPress-l>',self.stoprunning)
The stoprunning and start running functions are as so:
def startrunning(self):
r = Frame(self.window)
r.pack()
self.start = time.time()
start = Label(r,text="\nStarted running")
start.pack()
def stoprunning(self):
r = Frame(self.window)
r.pack()
self.stop = time.time()
self.timeConsumed = self.stop - self.start
Label(r,text='\nstopped running').pack()
end = Label(r,text="\nTime consumed is: %0.2f seconds" %self.timeConsumed)
end.pack(side = "bottom")
Error
On pressing the s key I get the following error:
>>>
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python25\lib\lib-tk\Tkinter.py", line 1414, in __call__
return self.func(*args)
TypeError: startrunning() takes exactly 1 argument (2 given)
Specs
Python 2.7
I am new to tkinter programming and am unable to understand what or why this error is being shown. Please tell me if I am using the code correctly. Also please help me resolve this problem.
use
def startrunning(self,ev):
def stoprunning(self,ev):
bind send event to a subroutine (http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm).
alternate, you can describe bind as
w.bind('<KeyPress-s>',lambda ev:self.startrunning())
w.bind('<KeyPress-l>',lambda ev:self.stoprunning())
Related
I am trying to follow a ROS2 testing tutorial which tests a topic listener to understand how ROS2 testing works. Here is a screenshot of the related code at 21:15
I have a node target_control_node which subscribes the topic turtle1/pose and then move the turtle to a new random pose.
import math
import random
import rclpy
from geometry_msgs.msg import Twist
from rclpy.node import Node
from turtlesim.msg import Pose
class TargetControlNode(Node):
def __init__(self):
super().__init__("target_control_node")
self.get_logger().info("target_control_node")
self._target_pose = None
self._cmd_vel_publisher = self.create_publisher(Twist, "turtle1/cmd_vel", 10)
self.create_subscription(Pose, "turtle1/pose", self.subscribe_target_pose, 10)
self.create_timer(1.0, self.control_loop)
def subscribe_target_pose(self, msg):
self._target_pose = msg
def control_loop(self):
if self._target_pose is None:
return
target_x = random.uniform(0.0, 10.0)
target_y = random.uniform(0.0, 10.0)
dist_x = target_x - self._target_pose.x
dist_y = target_y - self._target_pose.y
distance = math.sqrt(dist_x**2 + dist_y**2)
msg = Twist()
# position
msg.linear.x = 1.0 * distance
# orientation
goal_theta = math.atan2(dist_y, dist_x)
diff = goal_theta - self._target_pose.theta
if diff > math.pi:
diff -= 2 * math.pi
elif diff < -math.pi:
diff += 2 * math.pi
msg.angular.z = 2 * diff
self._cmd_vel_publisher.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = TargetControlNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
I am trying to write a simple test for the subscription part based on the tutorial above to understand how it works.
Here is my initial test code. Note inside I am using expected_output=str(msg), however, it is wrong, and I am not sure what to put there.
import pathlib
import random
import sys
import time
import unittest
import uuid
import launch
import launch_ros
import launch_testing
import pytest
import rclpy
import std_msgs.msg
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
#pytest.mark.rostest
def generate_test_description():
src_path = pathlib.Path(__file__).parent.parent
target_control_node = launch_ros.actions.Node(
executable=sys.executable,
arguments=[src_path.joinpath("turtle_robot/target_control_node.py").as_posix()],
additional_env={"PYTHONUNBUFFERED": "1"},
)
return (
launch.LaunchDescription(
[
target_control_node,
launch_testing.actions.ReadyToTest(),
]
),
{
"target_control_node": target_control_node,
},
)
class TestTargetControlNodeLink(unittest.TestCase):
#classmethod
def setUpClass(cls):
rclpy.init()
#classmethod
def tearDownClass(cls):
rclpy.shutdown()
def setUp(self):
self.node = rclpy.create_node("target_control_test_node")
def tearDown(self):
self.node.destroy_node()
def test_target_control_node(self, target_control_node, proc_output):
pose_pub = self.node.create_publisher(Pose, "turtle1/pose", 10)
try:
msg = Pose()
msg.x = random.uniform(0.0, 10.0)
msg.y = random.uniform(0.0, 10.0)
msg.theta = 0.0
msg.linear_velocity = 0.0
msg.angular_velocity = 0.0
pose_pub.publish(msg)
success = proc_output.waitFor(
# `str(msg)` is wrong, however, I am not sure what to put here.
expected_output=str(msg), process=target_control_node, timeout=1.0
)
assert success
finally:
self.node.destroy_publisher(pose_pub)
When I run launch_test src/turtle_robot/test/test_target_control_node.py, it only prints this without telling me what is actual output:
[INFO] [launch]: All log files can be found below /home/parallels/.ros/log/2023-01-02-16-37-27-631032-ubuntu-linux-22-04-desktop-1439830
[INFO] [launch]: Default logging verbosity is set to INFO
test_target_control_node (test_target_control_node.TestTargetControlNodeLink) ... [INFO] [python3-1]: process started with pid [1439833]
[python3-1] [INFO] [1672706247.877402445] [target_control_node]: target_control_node
FAIL
======================================================================
FAIL: test_target_control_node (test_target_control_node.TestTargetControlNodeLink)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/my-ros/src/turtle_robot/test/test_target_control_node.py", line 91, in test_target_control_node
assert success
AssertionError
----------------------------------------------------------------------
Ran 1 test in 1.061s
FAILED (failures=1)
[INFO] [python3-1]: sending signal 'SIGINT' to process[python3-1]
[python3-1] Traceback (most recent call last):
[python3-1] File "/my-ros/src/turtle_robot/turtle_robot/target_control_node.py", line 59, in <module>
[python3-1] main()
[python3-1] File "/my-ros/src/turtle_robot/turtle_robot/target_control_node.py", line 53, in main
[python3-1] rclpy.spin(node)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/__init__.py", line 222, in spin
[python3-1] executor.spin_once()
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 705, in spin_once
[python3-1] handler, entity, node = self.wait_for_ready_callbacks(timeout_sec=timeout_sec)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 691, in wait_for_ready_callbacks
[python3-1] return next(self._cb_iter)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 588, in _wait_for_ready_callbacks
[python3-1] wait_set.wait(timeout_nsec)
[python3-1] KeyboardInterrupt
[ERROR] [python3-1]: process has died [pid 1439833, exit code -2, cmd '/usr/bin/python3 /my-ros/src/turtle_robot/turtle_robot/target_control_node.py --ros-args'].
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
I checked the source code of waitFor, but still no clue.
Is there a way to print the actual output so that I can give correct expected_output? Thanks!
To answer your question(and give some more general tips): You can always print out the msg inside the node. That said, the reason you're getting an error is because msg is a ros message type, meaning it's an object. So by doing str(msg) your expected output will be something like <object of type Pose at (some_address)>; and not the actual message values. Also since it appears you're just testing a subscriber, it doesn't usually make sense to have a unit test that's expecting stdout. I would also note that you're publishing a message before actually waiting for the result, this should always fail. The reason is because by the time your subscriber is started, the message has already been published and is gone.
Finally, you shouldn't have a publisher included in any unit tests. It adds an extra dependency and since it's a prepackaged transport layer testing if it works is irrelevant. Instead to fix your problem you should simply be calling the individual methods of your node directly. Basically import the script, instantiate the object, and don't try to deal with the whole node lifecycle.
Edit based on comments
Looking at your subscriber code, you'll need to actually print something out. Unfortunately because it's not a std_msg(i.e. it has more fields than just .data) you'll need to decide how you want to go about confirming the data is right. You could simply look at one field or all of them in order. For example you might have in your test:
success = proc_output.waitFor(
expected_output=str(msg.x),
process=target_control_node, timeout=1.0)
And in your control node:
def subscribe_target_pose(self, msg):
self._target_pose = msg
print(msg.x)
That said, this IO handling method doesn't seem like the best to me. Mainly because it relies on stdout which isn't something you always want.
def Main_Menu():
for widget in myframe_1.winfo_children():
widget.destroy()
PostIt_Count = len([name for name in os.listdir('C:/Users/Aatu/Documents/python/pythonleikit/tkinterstuff/PostItApp/PostIts')])
if PostIt_Count > 0:
for i in range(PostIt_Count):
PostIt_NamesList = [name for name in os.listdir('C:/Users/Aatu/Documents/python/pythonleikit/tkinterstuff/PostItApp/PostIts')]
PostIt_LabelName = 'PostItLabel' + str(i)
global selected_postit
selected_postit = tk.StringVar
PostIt_LabelName = ttk.Radiobutton(myframe_1, text=PostIt_NamesList[i], variable=selected_postit)
y = ([x for x in range(1, PostIt_Count +1)][i])- 0.4
y = str(y)[:1] + str(y)[2:]
yname = '.' + y
PostIt_LabelName.place(relx=.1, rely=yname)
def Read_PostIt():
for widget in myframe_3.winfo_children():
widget.destroy()
filepath = 'C:/Users/Aatu/Documents/python/pythonleikit/tkinterstuff/PostItApp/PostIts/'
postit = selected_postit.get()
f = filepath + postit
with open('{}'.format(f), 'r') as fi:
global text
text = fi.readlines()
fi.close()
text_label = Label(myframe_3, text='{}'.format(text))
text_label.place(relx=.01, rely=.01)
So when I run this I get TypeError: get() missing 1 required positional argument: 'self' , why does this happen and how can I fix this. I'm trying to do a Post-It app and I'd like to show the text of the post-it file. I'm calling the Main_Menu function when I start this app and the Read_PostIt function is called when I select a radiobutton of a post-it and click Read -button.
I don't know these kind of stuff very well and I would be veery glad if somebody helped.
Full error traceback:
PS C:\Users\Aatu\Documents\python\pythonleikit> & C:/Python39ni/python.exe c:/Users/Aatu/Documents/python/pythonleikit/tkinterstuff/PostItApp/post-its.py
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python39ni\lib\tkinter\__init__.py", line 1892, in __call__
return self.func(*args)
File "c:\Users\Aatu\Documents\python\pythonleikit\tkinterstuff\PostItApp\post-its.py", line 73, in Read_PostIt
postit = selected_postit.get()
TypeError: get() missing 1 required positional argument: 'self'
Im currently in an intro coding class and for my final project, i am trying to learn the pyglet module to create a game with a picture in the background, and have a character on the left that a user can make jump, and then have jumps come from the right at a set speed that the user will jump over. i need to use classes for the assignment, and im really having a hard time using creating a sprite class. heres my current code:
import pyglet
window = pyglet.window.Window(700,700)
image = pyglet.image.load('IMG_3315.jpg')#use 10x10 in. image
#image_2 = pyglet.image.load('IMG_3559.jpg')
main_batch = pyglet.graphics.Batch()
score_label = pyglet.text.Label(text="Score: 0", x=570, y=650, batch=main_batch)
the_jump = pyglet.image.load("jumpsi.png")
#horse = pyglet.sprite.Sprite(image_2, x = 50, y = 50)
# background_sound = pyglet.media.load(
# 'Documents/Leave The Night On.mp3',
# streaming=False)
class Jump(pyglet.sprite.Sprite):
def __init__(self, img, x=0, y=0, blend_src=770, blend_dest=771, batch=None, group=None, usage='dynamic', subpixel=False):
self.img = the_jump
self.x = 50
self.y = 50
def draw(self):
self.draw()
# verticle = Jump('verticle')
#window.event
def on_draw():
window.clear()
image.blit(0, 0)
main_batch.draw()
window = Jump()
#horse.draw()
#background_sound.play()
if __name__ == "__main__":
sprite = Jump()
pyglet.app.run()
i know its probably wrong but everything else i have tried (using preexisting games as examples) hasn't worked either.
my current error message is:
Traceback (most recent call last):
File "jumper.py", line 39, in <module>
sprite = Jump()
TypeError: __init__() takes at least 2 arguments (1 given)
im just really stuck and have been trying to figure this out for hours and not made any leeway. any help you can offer would be greatly appreciated. Thanks so much!
UPDATE: i recently changed the code, noticing the problem that Gustav pointed out, and change the end call to
if __name__ == "__main__":
sprite = Jump(the_jump)
pyglet.app.run()
but now i get the error
Traceback (most recent call last):
File "jumper.py", line 39, in <module>
sprite = Jump(the_jump)
File "jumper.py", line 21, in __init__
self.x = 50
File "/Library/Python/2.7/site-packages/pyglet/sprite.py", line 459, in _set_x
self._update_position()
File "/Library/Python/2.7/site-packages/pyglet/sprite.py", line 393, in _update_position
img = self._texture
AttributeError: 'Jump' object has no attribute '_texture'
The error message is telling you exactly which line the error is in and exactly what is wrong. You are initializing the Jump class without passing in an argument for the required parameter img.
You can fix this by either changing the initialization method or your call to Jump().
Here's an example for how to read Python's traceback.
This is a very easy code to understand things :
Main :
import pdb
#pdb.set_trace()
import sys
import csv
sys.version_info
if sys.version_info[0] < 3:
from Tkinter import *
else:
from tkinter import *
from Untitled import *
main_window =Tk()
main_window.title("Welcome")
label = Label(main_window, text="Enter your current weight")
label.pack()
Current_Weight=StringVar()
Current_Weight.set("0.0")
entree1 = Entry(main_window,textvariable=Current_Weight,width=30)
entree1.pack()
bouton1 = Button(main_window, text="Enter", command= lambda evt,Current_Weight,entree1: get(evt,Current_Weight,entree1))
bouton1.pack()
and in another file Untitled i have the "get" function :
def get (event,loot, entree):
loot=float(entree.get())
print(loot)
When i run the main i receive the following error :
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/idlelib/run.py", line 121, in main
seq, request = rpc.request_queue.get(block=True, timeout=0.05)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/queue.py", line 175, in get
raise Empty
queue.Empty
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/tkinter/init.py", line 1533, in call
return self.func(*args)
TypeError: () missing 3 required positional arguments: 'evt', 'Current_Weight', and 'entree1'
How can i solve that ?
I thought the lambda function allows us to uses some args in a event-dependant function.
The command lambda does not take any arguments at all; furthermore there is no evt that you can catch. A lambda can refer to variables outside it; this is called a closure. Thus your button code should be:
bouton1 = Button(main_window, text="Enter",
command = lambda: get(Current_Weight, entree1))
And your get should say:
def get(loot, entree):
loot = float(entree.get())
print(loot)
Actually, you just need the Entry object entree1 as the lamda pass-in argument. Either statement below would work.
bouton1 = Button(main_window, text="Enter", command=lambda x = entree1: get(x))
bouton1 = Button(main_window, text="Enter", command=lambda : get(entree1))
with the function get defined as
def get(entree):
print(float(entree.get()))
This is a GUI I’ve been writing for a script I already have working. What I’m struggling with here is retrieving the information in the textboxes.
Under the definition generate I am able to pop a name off of listx but I am unable to grab the local variable entry from any of the instances of the new_title_box class.
from Tkinter import *
import ttk
boxvar=""
folder=""
listx=[]
count = 1
myrow = 1
class new_title_box:
def __init__(self,name):
global myrow, count, listx
self.entry = StringVar()
self.name = name
self.name = ttk.Entry(mainframe,width=45,textvariable=self.entry)
self.name.grid(column=1,row=myrow+1,sticky=(N,W))
listx.append(name)
print(listx) ## For debugging to insure that it is working correctly, if it gives output it, this part works
myrow = myrow + 1
count=count+1
def make_new(*args):
new_title_box('box'+str(count))
def generate(*args):
global listx, boxvar
while len(listx) > 0:
boxvar=listx.pop(0)
print(boxvar) ## For debugging to insure that it is working correctly, if it gives output it, this part works
folder = boxvar.entry.get() ## Not working here
print(folder) ## For debugging to insure that it is working correctly, if it gives output it, this part works
root = Tk()
root.title("File Maker")
mainframe = ttk.Frame(root, padding = "50 50 50 50")
mainframe.grid(column = 0,row = 0,sticky = (N, W, E, S))
mainframe.columnconfigure(0,weight=1)
mainframe.columnconfigure(0,weight=1)
add_entry = ttk.Button(mainframe,width=20, text = "add entry", command=make_new)
add_entry.grid(column=2,row=2,sticky=(N,W))
add_entry = ttk.Button(mainframe,width=20, text = "make files", command=generate)
add_entry.grid(column=2,row=3,sticky=(N,W))
root.mainloop()
Here's the traceback I'm getting:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter_init_.py", line 1442, in call
return self.func(*args)
File "C:\python\SampAqTkinter.py", line 28, in generate
folder = boxvar.entry ## Not working here
AttributeError: 'str' object has no attribute 'entry'
There are two things that need to be changed to fix the problem you describe:
In the new_title_box.__init__() method change: listx.append(name) to listx.append(self.name)
In the generate() function, change: folder = boxvar.entry.get() to folder = boxvar.get().
You are appending a string to listx, use self.name instead of the local string name