Skip to content

Commit

Permalink
Add Python to Shuffleboard docs (#2465)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasondaming committed Jan 2, 2024
1 parent d50ab48 commit 960fd2b
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ To see the status of a subsystem while the robot is operating in either autonomo

SmartDashboard::PutData(subsystem-pointer);

.. code-block:: python
from wpilib import SmartDashboard
SmartDashboard.putData(subsystem-reference)
Shuffleboard will display the subsystem name, the default command associated with this subsystem, and the currently running command. In this example the default command for the Elevator subsystem is called ``AutonomousCommand`` and it is also the current command that is using the Elevator subsystem.

.. image:: images/commands-subsystems-1.png
Expand Down Expand Up @@ -52,6 +58,12 @@ Using commands and subsystems makes very modular robot programs that can easily

SmartDashboard::PutData("ElevatorMove: up", new ElevatorMove(2.7));

.. code-block:: python
from wpilib import SmartDashboard
SmartDashboard.putData("ElevatorMove: up", ElevatorMove(2.7))
Shuffleboard will display the command name and a button to execute the command. In this way individual commands and command groups can easily be tested without needing special test code in a robot program. In the image below there are a number of commands contained in a Shuffleboard list. Pressing the button once runs the command and pressing it again stops the command. To use this feature the robot must be enabled in teleop mode.

.. image:: images/commands-subsystems-3.png
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,19 @@ The following example demonstrates how to create a button on your dashboard that
// and sends it to a motor
motor.Set(pid.Calculate(encoder.GetDistance(), setpoint));
}

.. code-block:: python
from wpilib.shuffleboard import Shuffleboard
tab = Shuffleboard.getTab("Shooter")
shooterEnable = tab.add("Shooter Enable", false).getEntry()
# Command Example assumed to be in a PIDSubsystem
NetworkButton(shooterEnable).onTrue(InstantCommand(m_shooter.enable()))
# Timed Robot Example
if (shooterEnable.getBoolean()):
# Calculates the output of the PID algorithm based on the sensor reading
# and sends it to a motor
motor.set(pid.calculate(encoder.getDistance(), setpoint))
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,42 @@ Displaying values in normal operating mode (autonomous or teleop)

.. tab-set-code::

.. code-block:: java
protected void execute() {
SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge());
SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition());
SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle());
SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder());
SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder());
SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle());
SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage());
SmartDashboard.putNumber("RPM", shooter.getRPM());
}
.. code-block:: java
protected void execute() {
SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge());
SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition());
SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle());
SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder());
SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder());
SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle());
SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage());
SmartDashboard.putNumber("RPM", shooter.getRPM());
}
.. code-block:: c++

frc::SmartDashboard::PutBoolean("Bridge Limit", bridgeTipper.AtBridge());
frc::SmartDashboard::PutNumber("Bridge Angle", bridgeTipper.GetPosition());
frc::SmartDashboard::PutNumber("Swerve Angle", drivetrain.GetSwerveAngle());
frc::SmartDashboard::PutNumber("Left Drive Encoder", drivetrain.GetLeftEncoder());
frc::SmartDashboard::PutNumber("Right Drive Encoder", drivetrain.GetRightEncoder());
frc::SmartDashboard::PutNumber("Turret Pot", turret.GetCurrentAngle());
frc::SmartDashboard::PutNumber("Turret Pot Voltage", turret.GetAverageVoltage());
frc::SmartDashboard::PutNumber("RPM", shooter.GetRPM());

.. code-block:: python
from wpilib import SmartDashboard
SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge())
SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition())
SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle())
SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder())
SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder())
SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle())
SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage())
SmartDashboard.putNumber("RPM", shooter.getRPM())
.. figure:: images/display-code-result.png
:alt:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Frequently Asked Questions
How do I report issues, bugs or feature requests with Shuffleboard?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Bugs, issues, and feature requests can be added on the Shuffleboard GitHub page by creating an issue. We will try to address them as they are entered into the system. Please try to look at existing issues before creating new ones to make sure you aren't duplicating something that has already been reported or work that is planned. You can find the issues on the `Shuffleboard GitHub page <https://github.com/wpilibsuite/shuffleboard>`__.
There is no active maintainer of Shuffleboard, but we are accepting pull requests. Bugs, issues, and feature requests can be added on the Shuffleboard GitHub page by creating an issue. Please try to look at existing issues before creating new ones to make sure you aren't duplicating something that has already been reported or work that is planned. You can find the issues on the `Shuffleboard GitHub page <https://github.com/wpilibsuite/shuffleboard>`__.

How can I add my own widgets or other extensions to Shuffleboard?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Tour of Shuffleboard
====================

Shuffleboard is a dashboard for FRC\ |reg| based on newer technologies such as JavaFX that are available to Java programs. It is designed to be used for creating dashboards for C++ and Java programs. If you've used SmartDashboard in the past then you are already familiar with many of the features of Shuffleboard since they fundamentally work the same way. But Shuffleboard has many features that aren't in SmartDashboard. Here are some of the highlights:
Shuffleboard is a dashboard for FRC\ |reg| based on newer technologies such as JavaFX that are available to Java programs. It is designed to be used for creating dashboards for C++, Java, and Python programs. If you've used SmartDashboard in the past then you are already familiar with many of the features of Shuffleboard since they fundamentally work the same way. But Shuffleboard has many features that aren't in SmartDashboard. Here are some of the highlights:

- Graphics is based on **JavaFX**, the Java graphics standard. Each of the components has an associated style sheet so it becomes possible to have different "skins" or "themes" for Shuffleboard. We supply default light and dark themes.
- Shuffleboard supports **multiple sheets for the display of your data**. In fact you can create a new sheet (shown as a tab in the Shuffleboard window) and indicate if and which data should be autopopulated on it. By default there is a Test tab and a SmartDashboard tab that are autopopulated as data arrives. Other tabs might be for robot debugging vs. driving.
Expand Down Expand Up @@ -39,13 +39,23 @@ You can start Shuffleboard in one of four ways:
Getting robot data onto the dashboard
-------------------------------------

The easiest way to get data displayed on the dashboard is simply to use methods in the SmartDashboard class. For example to write a number to Shuffleboard write:
The easiest way to get data displayed on the dashboard is simply to use methods in the ``SmartDashboard`` class. For example to write a number to Shuffleboard write:

.. tab-set-code::

.. code-block:: java
SmartDashboard.putNumber("Joystick X value", joystick1.getX());
SmartDashboard.putNumber("Joystick X value", joystick1.getX());
.. code-block:: c++

frc::SmartDashboard::PutNumber("Joystick X value", joystick1.getX());

.. code-block:: python
from wpilib import SmartDashboard
SmartDashboard.putNumber("Joystick X value", joystick1.getX())
to see a field displayed with the label "Joystick X value" and a value of the X value of the joystick. Each time this line of code is executed, a new joystick value will be sent to Shuffleboard. Remember: you must write the joystick value whenever you want to see an updated value. Executing this line once at the start of the program will only display the value once at the time the line of code was executed.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ Call ``withWidget`` after ``add`` in the call chain:

.. code-block:: java
Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider) // specify the widget here
.getEntry();
Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider) // specify the widget here
.getEntry();
.. code-block:: c++

frc::Shuffleboard::GetTab("Drive")
.Add("Max Speed", 1)
.WithWidget(frc::BuiltInWidgets::kNumberSlider) // specify the widget here
.GetEntry();
frc::Shuffleboard::GetTab("Drive")
.Add("Max Speed", 1)
.WithWidget(frc::BuiltInWidgets::kNumberSlider) // specify the widget here
.GetEntry();

.. code-block:: python
from wpilib.shuffleboard import Shuffleboard
from wpilib.shuffleboard import BuiltInWidgets
(Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider) # specify the widget here
.getEntry())
In this example, we configure the "Max Speed" widget to use a slider to modify the values instead of a basic text field.

Expand All @@ -37,22 +47,33 @@ Since the maximum speed only makes sense to be a value from 0 to 1 (full stop to

.. code-block:: java
Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider)
.withProperties(Map.of("min", 0, "max", 1)) // specify widget properties here
.getEntry();
Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider)
.withProperties(Map.of("min", 0, "max", 1)) // specify widget properties here
.getEntry();
.. code-block:: c++

frc::Shuffleboard::GetTab("Drive")
.Add("Max Speed", 1)
.WithWidget(frc::BuiltInWidgets::kNumberSlider)
.WithProperties({ // specify widget properties here
{"min", nt::Value::MakeDouble(0)},
{"max", nt::Value::MakeDouble(1)}
})
.GetEntry();
frc::Shuffleboard::GetTab("Drive")
.Add("Max Speed", 1)
.WithWidget(frc::BuiltInWidgets::kNumberSlider)
.WithProperties({ // specify widget properties here
{"min", nt::Value::MakeDouble(0)},
{"max", nt::Value::MakeDouble(1)}
})
.GetEntry();

.. code-block:: python
from wpilib.shuffleboard import Shuffleboard
from wpilib.shuffleboard import BuiltInWidgets
(Shuffleboard.getTab("Drive")
.add("Max Speed", 1)
.withWidget(BuiltInWidgets.kNumberSlider)
.withProperties(map("min", 0, "max", 1)) # specify widget properties here
.getEntry())
.. image:: images/configuring-widgets/maxspeed.png
:alt: The max speed widget limited from 0 to 1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ Call ``withSize`` and ``withPosition`` to set the size and position of the widge
.WithSize(2, 1)
.WithPosition(0,0);

.. code-block:: python
from wpilib.shuffleboard import Shuffleboard
(Shuffleboard.getTab("Pre-round")
.add("Auto Mode", autoModeChooser)
.withSize(2, 1) # make the widget 2x1
.withPosition(0, 0)) # place it in the top-left corner
Adding Widgets to Layouts
-------------------------

Expand All @@ -42,7 +51,6 @@ If there are many widgets in a tab with related data, it can be useful to place
elevatorCommands.add(new ElevatorDownCommand());
elevatorCommands.add(new ElevatorUpCommand());
// Similarly for the claw commands
.. code-block:: c++

Expand All @@ -61,5 +69,18 @@ If there are many widgets in a tab with related data, it can be useful to place
elevatorCommands.Add("Elevator Down", elevatorDown);
elevatorCommands.Add("Elevator Up", elevatorUp);

.. code-block:: python
from wpilib.shuffleboard import Shuffleboard
from wpilib.shuffleboard import BuiltInLayouts
(elevatorCommands = Shuffleboard.getTab("Commands")
.getLayout("Elevator", BuiltInLayouts.kList)
.withSize(2, 2)
.withProperties(map("Label position", "HIDDEN"))) # hide labels for commands
elevatorCommands.add(ElevatorDownCommand())
elevatorCommands.add(ElevatorUpCommand())
.. image:: images/organizing-widgets/organized.png
:alt: Commands buttons organized by the order they are added for the Elevator and Claw subsystems.
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,39 @@ Unlike ``SmartDashboard.getNumber`` and friends, retrieving data from Shuffleboa

.. code-block:: java
class DriveBase extends Subsystem {
private ShuffleboardTab tab = Shuffleboard.getTab("Drive");
private NetworkTableEntry maxSpeed =
tab.add("Max Speed", 1)
.getEntry();
private DifferentialDrive robotDrive = ...;
public void drive(double left, double right) {
// Retrieve the maximum speed from the dashboard
double max = maxSpeed.getDouble(1.0);
robotDrive.tankDrive(left * max, right * max);
}
}
class DriveBase extends Subsystem {
private ShuffleboardTab tab = Shuffleboard.getTab("Drive");
private NetworkTableEntry maxSpeed =
tab.add("Max Speed", 1)
.getEntry();
private DifferentialDrive robotDrive = ...;
public void drive(double left, double right) {
// Retrieve the maximum speed from the dashboard
double max = maxSpeed.getDouble(1.0);
robotDrive.tankDrive(left * max, right * max);
}
}
.. code-block:: python
import commands2
import wpilib.drive
from wpilib.shuffleboard import Shuffleboard
class DriveSubsystem(commands2.SubsystemBase):
def __init__(self) -> None:
super().__init__()
tab = Shuffleboard.getTab("Drive")
self.maxSpeed = tab.add("Max Speed", 1).getEntry()
this.robotDrive = ...
def drive(self, left: float, right: float):
# Retrieve the maximum speed from the dashboard
max = self.maxSpeed.getDouble(1.0)
self.robotDrive.tankDrive(left * max, right * max)
This basic example has a glaring flaw: the maximum speed can be set on the dashboard to a value outside [0, 1] - which could cause the inputs to be saturated (always at maximum speed), or even reversed! Fortunately, there is a way to avoid this problem - covered in the next article.

0 comments on commit 960fd2b

Please sign in to comment.