@@ -154,12 +154,12 @@ void CameraDisplay::resizeEvent(QResizeEvent* event) {
154154 }
155155}
156156
157- StatusLed::StatusLed (QString name) : led_(nullptr ) {
157+ StatusLed::StatusLed (QString name, bool is_recording_led ) : led_(nullptr ) {
158158 QFont font (" Arial" );
159159 font.setPointSize (15 );
160160 QLabel* label = new QLabel (name);
161161 label->setFont (font);
162- led_ = new Led ();
162+ led_ = new Led (is_recording_led );
163163 led_->setFixedSize (30 , 30 );
164164 QHBoxLayout* layout = new QHBoxLayout ();
165165 layout->addWidget (label);
@@ -316,7 +316,8 @@ MainWindow::MainWindow(QWidget* parent) :
316316 display_ (nullptr ),
317317 status_label_ (nullptr ),
318318 disk_space_bar_ (nullptr ),
319- stop_config_button_ (nullptr ) {
319+ stop_config_button_ (nullptr ),
320+ recording_led_ (nullptr ) {
320321 this ->setWindowTitle (" UT AUTOmataGUI" );
321322
322323 // Ensure window takes full screen space
@@ -336,8 +337,15 @@ MainWindow::MainWindow(QWidget* parent) :
336337 status_label_ = new QLabel (" Mode: Autonomous\n Battery: 0V" );
337338 status_label_->setFont (font);
338339 status_label_->setAlignment (Qt::AlignHCenter);
340+
341+ // Add recording indicator
342+ recording_led_ = new StatusLed (" REC" , true ); // true = use grey when off
343+ recording_led_->setFixedHeight (60 );
344+
339345 top_bar->addWidget (ipaddr_label_);
340346 top_bar->addStretch ();
347+ top_bar->addWidget (recording_led_);
348+ top_bar->addStretch ();
341349 top_bar->addWidget (status_label_);
342350 top_bar->addStretch ();
343351 top_bar->addWidget (close_button);
@@ -437,6 +445,67 @@ MainWindow::MainWindow(QWidget* parent) :
437445
438446 tab_widget_->addTab (main_widget, " Main" );
439447 tab_widget_->addTab (ros_group, tr (" Configurations" ));
448+
449+ // Create Recorder Tab
450+ QWidget* recorder_widget = new QWidget ();
451+ QVBoxLayout* recorder_layout = new QVBoxLayout ();
452+
453+ QLabel* recorder_title = new QLabel (" Recording Topics" );
454+ recorder_title->setFont (font);
455+ recorder_layout->addWidget (recorder_title);
456+
457+ // Create scroll area for topics
458+ QWidget* topics_container = new QWidget ();
459+ QVBoxLayout* topics_layout = new QVBoxLayout ();
460+ topics_container->setLayout (topics_layout);
461+
462+ // Initialize default topics
463+ selected_topics_ = {
464+ " /scan" ,
465+ " /camera_0/image_raw" ,
466+ " /ackermann_curvature_drive" ,
467+ " /car_status" ,
468+ " /joystick" ,
469+ " /imu" ,
470+ " /odom"
471+ };
472+
473+ available_topics_ = {
474+ " /scan" ,
475+ " /camera_0/image_raw" ,
476+ " /ackermann_curvature_drive" ,
477+ " /car_status" ,
478+ " /joystick" ,
479+ " /imu" ,
480+ " /odom" ,
481+ " /tf" ,
482+ " /tf_static"
483+ };
484+
485+ topic_buttons_.clear ();
486+
487+ for (const auto & topic : available_topics_) {
488+ QPushButton* topic_btn = new QPushButton (QString::fromStdString (topic));
489+ topic_btn->setFont (font);
490+ topic_btn->setCheckable (true );
491+
492+ // Check if topic is in selected list
493+ bool is_selected = std::find (selected_topics_.begin (),
494+ selected_topics_.end (),
495+ topic) != selected_topics_.end ();
496+ topic_btn->setChecked (is_selected);
497+
498+ connect (topic_btn, SIGNAL (clicked ()), this , SLOT (ToggleTopicRecording ()));
499+ topic_buttons_.push_back (topic_btn);
500+ topics_layout->addWidget (topic_btn);
501+ }
502+
503+ topics_layout->addStretch ();
504+
505+ recorder_layout->addWidget (topics_container);
506+ recorder_widget->setLayout (recorder_layout);
507+
508+ tab_widget_->addTab (recorder_widget, tr (" Recorder" ));
440509 }
441510
442511 main_layout_ = new QVBoxLayout (this );
@@ -464,6 +533,10 @@ MainWindow::MainWindow(QWidget* parent) :
464533 connect (this ,
465534 SIGNAL (UpdateCameraSignal (const QPixmap&)),
466535 SLOT (UpdateCameraSlot (const QPixmap&)));
536+
537+ connect (this ,
538+ SIGNAL (UpdateRecordingStatusSignal (bool )),
539+ SLOT (UpdateRecordingStatusSlot (bool )));
467540}
468541
469542MainWindow::~MainWindow () {
@@ -737,4 +810,54 @@ void MainWindow::StopTmuxConfiguration() {
737810 }
738811}
739812
813+ void MainWindow::UpdateRecordingStatus (bool recording) {
814+ UpdateRecordingStatusSignal (recording);
815+ }
816+
817+ void MainWindow::UpdateRecordingStatusSlot (bool recording) {
818+ if (recording_led_) {
819+ recording_led_->SetStatus (recording);
820+ }
821+ }
822+
823+ void MainWindow::ToggleTopicRecording () {
824+ QPushButton* button = qobject_cast<QPushButton*>(sender ());
825+ if (!button) return ;
826+
827+ // Find which topic was toggled
828+ for (size_t i = 0 ; i < topic_buttons_.size (); ++i) {
829+ if (topic_buttons_[i] == button) {
830+ std::string topic = available_topics_[i];
831+
832+ // Update selected topics list
833+ auto it = std::find (selected_topics_.begin (), selected_topics_.end (), topic);
834+ if (button->isChecked ()) {
835+ if (it == selected_topics_.end ()) {
836+ selected_topics_.push_back (topic);
837+ }
838+ } else {
839+ if (it != selected_topics_.end ()) {
840+ selected_topics_.erase (it);
841+ }
842+ }
843+
844+ // TODO: Publish updated topic list to recorder node
845+ UpdateTopicsToRecord ();
846+ break ;
847+ }
848+ }
849+ }
850+
851+ void MainWindow::UpdateTopicsToRecord () {
852+ // This will be called when topics are toggled
853+ // For now, we'll log the change
854+ std::string topics_str = " Selected topics: " ;
855+ for (const auto & topic : selected_topics_) {
856+ topics_str += topic + " , " ;
857+ }
858+ printf (" %s\n " , topics_str.c_str ());
859+
860+ // TODO: Publish to /recorder/set_topics topic
861+ }
862+
740863} // namespace ut_automata_gui
0 commit comments