Skip to content

Instantly share code, notes, and snippets.

@jones2126
Created August 21, 2021 23:09
Show Gist options
  • Save jones2126/3460e9a51e1bb943eef714edf1c3eb3b to your computer and use it in GitHub Desktop.
Save jones2126/3460e9a51e1bb943eef714edf1c3eb3b to your computer and use it in GitHub Desktop.
Python Tkinter script to help start launch files from my laptop and to start windows that show the values of ros topics.
#!/usr/bin/env python
#
import tkinter as tk
from tkinter import ttk
import subprocess
import time
'''
issues:
- speed up the login process by looking for the $ which shows the login has completed
- add steps for cmd_vel
- add display for heading
'''
class Steps_to_Process(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.name = tk.StringVar()
self.title_string = tk.StringVar()
self.title_string.set("Lawn Tractor Startup")
title_label = ttk.Label(self, textvariable=self.title_string, font=("TkDefaultFont", 12), wraplength=200)
step_1_button = ttk.Button(self, text="Start Sensors", width=50, command=self.step_1_actions)
step_2_button = ttk.Button(self, text="Launch Teleop", width=50, command=self.step_2_actions)
step_3_button = ttk.Button(self, text="NMEA Driver", width=50, command=self.step_3_actions)
step_4_button = ttk.Button(self, text="GPS ODOM", width=50, command=self.step_4_actions)
step_5_button = ttk.Button(self, text="Move Base Launch", width=50, command=self.step_5_actions)
step_6_button = ttk.Button(self, text="Save Rosbag", width=50, command=self.step_6_actions)
# Layout form
self.columnconfigure(0, weight=1)
title_label.grid(row=0, column=0, columnspan=2)
step_1_button.grid(row=1, column=0, sticky=tk.W)
step_2_button.grid(row=2, column=0, sticky=tk.W)
step_3_button.grid(row=3, column=0, sticky=tk.W)
step_4_button.grid(row=4, column=0, sticky=tk.W)
step_5_button.grid(row=5, column=0, sticky=tk.W)
step_6_button.grid(row=6, column=0, sticky=tk.W)
def RPi_login_steps(self):
time.sleep(2)
cmd_RPi_login = "plink RPI_local -pw ubuntu" # command to login to RPi
time.sleep(.1)
subprocess.check_output(["xdotool", "type", cmd_RPi_login + "\n"])
time.sleep(5) # delay for the login process to the RPi
subprocess.check_output(["xdotool", "type", "\n"])
time.sleep(.5)
def step_1_actions(self): # main launch of sensors
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+150+800", "--name='title 1'", working_directory1])
self.RPi_login_steps()
cmd = "bash ros_start.sh" # bash file on RPi which has the main start launch file
subprocess.check_output(["xdotool", "type", cmd + "\n"])
time.sleep(10) # delay for sensors to fire up
cmd = 'python3 /home/al/python/ros_gui/steer_angle_low_res.py' # starts a window that displays front angle sensor
subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
def step_2_actions(self): # joystick and teleop
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+350+800", working_directory1])
self.RPi_login_steps()
# this starts the joystick/gamepad process for the gamepad connected to the RPi on input js0
# the 2.4 GHz gamepad should have been connected
subprocess.check_output(["xdotool", "type", "roslaunch tractor_teleop drive.launch" + "\n"])
def step_3_actions(self): # nmea driver
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+550+800", working_directory1])
self.RPi_login_steps()
# this starts the node to access the GPS signal connected to the udev port "<arg name="port" default="/dev/gps" />"
# the package will also parse the sentence and publish the data
subprocess.check_output(["xdotool", "type", "cd /home/ubuntu/catkin_ws/src/nmea_navsat_driver/launch" + "\n"])
time.sleep(.5)
subprocess.check_output(["xdotool", "type", "roslaunch nmea_serial_driver.launch" + "\n"])
time.sleep(10)
cmd = 'python3 /home/al/python/ros_gui/NavSatFix.py' # starts a window that displays gps quality status
subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
def step_4_actions(self): # gps odom
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+750+800", working_directory1])
self.RPi_login_steps()
subprocess.check_output(["xdotool", "type", "cd ~/catkin_ws" + "\n"])
time.sleep(.5)
subprocess.check_output(["xdotool", "type", "rosrun beginner_tutorials gps_odom.py" + "\n"])
def step_5_actions(self): # move base
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+950+700", working_directory1])
self.RPi_login_steps()
subprocess.check_output(["xdotool", "type", "cd ~/catkin_ws" + "\n"])
time.sleep(.5)
subprocess.check_output(["xdotool", "type", "roslaunch lawn_tractor_sim lawn_tractor.launch" + "\n"])
def step_6_actions(self): # rosbag
working_directory1 = "--working-directory=/home/al"
subprocess.call(["xdotool", "exec", "gnome-terminal", "--geometry=120x10+1150+700", working_directory1])
self.RPi_login_steps()
subprocess.check_output(["xdotool", "type", "bash ros_bagfile.sh" + "\n"])
class ROS_GUI(tk.Tk):
"""ROS GUI Main Application"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# set the window properties
self.title("ROS GUI")
self.geometry("200x300")
self.resizable(width=False, height=False)
# Define the UI
Steps_to_Process(self).grid(sticky=(tk.E + tk.W + tk.N + tk.S))
self.columnconfigure(0, weight=1)
if __name__ == '__main__':
app = ROS_GUI()
app.mainloop()
@jones2126
Copy link
Author

jones2126 commented Aug 21, 2021

ROS Lawn Tractor Startup GUI Overview - to be clear, I'm less than a novice, but this works for me.

menu

One of the actions will then call a script that displays a ros topic(cmd = 'python3 /home/al/python/ros_gui/steer_angle_low_res.py'). The script is here:

`import tkinter as tk
from tkinter import ttk
import time
from random import randint
import rospy
from std_msgs.msg import Int16

"""
subscribes to a ROS topic (/front_angle_low_res) which in my case is outputing an integer result and displays the result in a tkinter window

credit: https://www.pythontutorial.net/tkinter/tkinter-after/
credit: https://www.youtube.com/watch?v=EAAd5vXA8lE&t=260s

before running make sure to $ rostopic echo /front_angle_low_res to make sure data is available
This script can be started from the folder it resides in with $ python3 sensor_display.py

"""

class Display_Sensor_1(tk.Tk):

    def __init__(self):
        super().__init__()
        self.sub = rospy.Subscriber("/front_angle_low_res", Int16, self.callback_sensor_1)
        self.sensor_1_data = tk.IntVar()

        # configure the root window
        self.title('front_angle_low_res')
        self.resizable(0, 0)
        # width x height = 1920 x 1080 (in pixels) of the Ubuntu laptop
        #self.geometry('250x70+220+80')
        self.geometry('250x70+1600+100')
        self['bg'] = 'black'
        
        # change the background color to black
        self.style = ttk.Style(self)
        self.style.configure('TLabel', background='black', foreground='red')
        self.label = ttk.Label(self, text=self.get_sensor_data(), font=('Digital-7', 20))
        self.label.pack(expand=True)
        self.label.after(1000, self.update)     # schedule an update every 1 second

    def callback_sensor_1(self, data):   
        self.sensor_1_data = data.data
        #print(self.sensor_1_data)
        #rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)

    def get_sensor_data(self):
        return self.sensor_1_data

    def update(self):
        """ update the label every 1 second """
        self.label.configure(text=self.get_sensor_data())
        self.label.after(1000, self.update)     # schedule another timer
 
if __name__ == "__main__":
    rospy.init_node('listener', anonymous=True)
    sensor = Display_Sensor_1()
    sensor.mainloop()`

The image of that display value is below:
value

@jones2126
Copy link
Author

jones2126 commented Aug 23, 2021

Output from a newer version
Screenshot from 2021-08-29 12-41-38

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment