YODAU 1.0
YEAR OF THE DEPEND ADULT UNDERGARMENT
Loading...
Searching...
No Matches
controller.cpp
Go to the documentation of this file.
1#include "helpers/controller.hpp"
2#include "geometry.hpp"
3#include "helpers/str_label.hpp"
4#include "stream.hpp"
5#include "widgets/settings_panel.hpp"
6
7#include <QCameraDevice>
8#include <QColor>
9#include <QDateTime>
10#include <QDebug>
11#include <QImage>
12#include <QMediaDevices>
13#include <QMetaObject>
14#include <QThread>
15#include <QtGlobal>
16#include <chrono>
17
18#include "event.hpp"
19#include "frame.hpp"
21#include "widgets/board.hpp"
22#include "widgets/grid_view.hpp"
23#include "widgets/stream_cell.hpp"
24
25controller::controller(
26 yodau::backend::stream_manager* mgr, settings_panel* panel, board* zone,
27 QObject* parent
28)
29 : QObject(parent)
30 , stream_mgr(mgr)
31 , settings(panel)
32 , main_zone(zone)
33 , grid(zone ? zone->grid_mode() : nullptr) {
34
36
37 if (stream_mgr) {
38 stream_mgr->set_analysis_interval_ms(66);
39#ifdef YODAU_OPENCV
40 stream_mgr->set_frame_processor(
41 yodau::backend::opencv_motion_processor
42 );
43#else
44 stream_mgr->set_frame_processor([](const yodau::backend::stream& s,
45 const yodau::backend::frame& f) {
46 Q_UNUSED(s);
47 Q_UNUSED(f);
48 return std::vector<yodau::backend::event> {};
49 });
50#endif
51 stream_mgr->set_event_batch_sink(
52 [this](const std::vector<yodau::backend::event>& evs) {
53 on_backend_events(evs);
54 }
55 );
56 }
57
58 if (settings && grid) {
59 settings->set_active_candidates(grid->stream_names());
60 settings->set_active_current(QString());
61 }
62
65}
66
68 if (!stream_mgr || !grid) {
69 return;
70 }
71
72 const int n = static_cast<int>(grid->stream_names().size());
73 const int interval = repaint_interval_for_count(n);
74 stream_mgr->set_analysis_interval_ms(interval);
75}
76
78 if (!stream_mgr || !settings) {
79 return;
80 }
81
82 QSet<QString> names;
83
84 const auto backend_names = stream_mgr->stream_names();
85 for (auto& n : backend_names) {
86 const auto qname = QString::fromStdString(n);
87 names.insert(qname);
88
89 QString desc = str_label("<unknown>");
90 const auto s = stream_mgr->find_stream(n);
91 if (s) {
92 // const auto t = s->get_type();
93 const auto path = QString::fromStdString(s->get_path());
94 const auto type = QString::fromStdString(
95 yodau::backend::stream::type_name(s->get_type())
96 );
97 desc = QString("%1:%2").arg(type, path);
98 }
99
100 settings->add_stream_entry(qname, desc);
101 }
102
103 settings->set_existing_names(names);
104}
105
106void controller::handle_add_file(
107 const QString& path, const QString& name, const bool loop
108) {
109 handle_add_stream_common(path, name, "file", loop);
110}
111
112void controller::handle_add_local(const QString& source, const QString& name) {
113 handle_add_stream_common(source, name, "local", true);
114}
115
116void controller::handle_add_url(const QString& url, const QString& name) {
117 handle_add_stream_common(url, name, "url", true);
118}
119
121 if (!stream_mgr || !settings) {
122 return;
123 }
124
125 const auto ts = now_ts();
126 stream_mgr->refresh_local_streams();
127
128 QStringList locals;
129 const auto backend_names = stream_mgr->stream_names();
130 for (const auto& n : backend_names) {
131 const auto qn = QString::fromStdString(n);
132 if (qn.startsWith("video")) {
133 locals << qn;
134 }
135 }
136
137 settings->set_local_sources(locals);
138 settings->append_add_log(
139 QString("[%1] ok: detected %2 local sources").arg(ts).arg(locals.size())
140 );
141
142 const auto cams = QMediaDevices::videoInputs();
143 // for (int i = 0; i < cams.size(); ++i) {
144 // const auto& c = cams[i];
145 // }
146}
147
148void controller::handle_show_stream_changed(
149 const QString& name, const bool show
150) {
151 if (!grid) {
152 return;
153 }
154
155 if (show) {
157 if (auto* tile = grid->peek_stream_cell(name)) {
158 connect(
159 tile, &stream_cell::frame_ready, this,
160 &controller::on_gui_frame, Qt::UniqueConnection
161 );
162 tile->set_persistent_lines(per_stream_lines.value(name));
163
164 const auto s = stream_mgr->find_stream(name.toStdString());
165 if (s) {
166 tile->set_loop(s->is_looping());
167 const auto path = QString::fromStdString(s->get_path());
168 const auto type = s->get_type();
169
170 if (type == yodau::backend::stream_type::local) {
171 tile->set_camera_id(path.toUtf8());
172 } else if (type == yodau::backend::stream_type::file) {
173 tile->set_source(QUrl::fromLocalFile(path));
174 } else {
175 tile->set_source(QUrl(path));
176 }
177 }
178 }
179 } else {
181 if (!active_name.isEmpty() && active_name == name && main_zone) {
182 if (auto* cell = main_zone->take_active_cell()) {
183 cell->deleteLater();
184 }
185 active_name.clear();
186 if (settings) {
187 settings->set_active_current(QString());
188 }
189 }
190 }
191
192 if (settings) {
193 settings->set_active_candidates(grid->stream_names());
194 }
195
198}
199
200void controller::handle_backend_event(const QString& text) {
201 if (settings) {
202 settings->append_active_log(text);
203 }
204}
205
206void controller::on_active_stream_selected(const QString& name) {
207 if (!main_zone) {
208 return;
209 }
210
211 active_name = name;
212
213 if (name.isEmpty()) {
215 } else {
217 }
218
219 if (auto* cell = main_zone->active_cell()) {
221
222 cell->clear_draft();
224
225 if (drawing_new_mode) {
226 cell->set_draft_params(
227 draft_line_name, draft_line_color, draft_line_closed
228 );
229 } else if (settings) {
230 apply_template_preview(settings->active_template_current());
231 }
232 }
233
237}
238
240 drawing_new_mode = drawing_new;
241
242 if (!main_zone) {
243 return;
244 }
245
246 if (auto* cell = main_zone->active_cell()) {
247 cell->clear_draft();
248 cell->set_drawing_enabled(drawing_new);
249
250 if (drawing_new) {
251 cell->set_draft_params(
252 draft_line_name, draft_line_color, draft_line_closed
253 );
254 } else if (settings) {
255 apply_template_preview(settings->active_template_current());
256 }
257 }
258
259 if (settings) {
260 settings->append_active_log(
261 QString("edit mode: %1")
262 .arg(drawing_new ? "draw new" : "use template")
263 );
264 }
265}
266
267void controller::on_active_line_params_changed(
268 const QString& name, const QColor& color, bool closed
269) {
270 draft_line_name = name;
271 draft_line_color = color;
272 draft_line_closed = closed;
273
274 if (main_zone) {
275 if (auto* cell = main_zone->active_cell()) {
276 cell->set_draft_params(
277 draft_line_name, draft_line_color, draft_line_closed
278 );
279 }
280 }
281
282 if (settings) {
283 settings->append_active_log(
284 QString("active line params: name='%1' color=%2 closed=%3")
285 .arg(draft_line_name)
286 .arg(draft_line_color.name())
287 .arg(draft_line_closed ? "true" : "false")
288 );
289 }
290}
291
292void controller::on_active_line_save_requested(
293 const QString& name, const bool closed
294) {
295 log_active(QString("save click: name='%1' closed=%2 active='%3'")
296 .arg(name)
297 .arg(closed ? "true" : "false")
298 .arg(active_name));
299
300 auto* cell = active_cell_checked("add line");
301 if (!cell) {
302 return;
303 }
304
305 const auto pts = cell->draft_points_pct();
306 if (pts.size() < 2) {
307 log_active("add line failed: need at least 2 points");
308 return;
309 }
310
311 const auto points_str = points_str_from_pct(pts);
312 log_active(QString("points_str = %1").arg(points_str));
313
314 try {
315 const auto lp = stream_mgr->add_line(
316 points_str.toStdString(), closed, name.toStdString()
317 );
318
319 const auto final_name = QString::fromStdString(lp->name);
320 apply_added_line(cell, final_name, pts, closed);
321 } catch (const std::exception& e) {
322 log_active(QString("add line failed: %1").arg(e.what()));
323 }
324}
325
326void controller::on_active_template_selected(const QString& template_name) {
327 if (drawing_new_mode) {
328 return;
329 }
330 apply_template_preview(template_name);
331}
332
334 Q_UNUSED(color);
335
336 if (drawing_new_mode) {
337 return;
338 }
339 if (!settings) {
340 return;
341 }
342
343 const auto t = settings->active_template_current();
344 if (t.isEmpty()) {
345 return;
346 }
347
348 apply_template_preview(t);
349}
350
351void controller::on_active_template_add_requested(
352 const QString& template_name, const QColor& color
353) {
354 auto* cell = active_cell_checked("add template");
355 if (!cell) {
356 return;
357 }
358
359 if (!templates.contains(template_name)) {
360 if (settings) {
361 settings->append_active_log(
362 QString("add template failed: unknown template '%1'")
363 .arg(template_name)
364 );
365 }
366 return;
367 }
368
369 const auto tpl = templates.value(template_name);
370
371 try {
372 stream_mgr->set_line(
373 active_name.toStdString(), template_name.toStdString()
374 );
375 } catch (const std::exception& e) {
376 if (settings) {
377 settings->append_active_log(
378 QString("add template failed: %1").arg(e.what())
379 );
380 }
381 return;
382 }
383
384 stream_cell::line_instance inst;
385 inst.template_name = template_name;
386 inst.color = color;
387 inst.closed = tpl.closed;
388 inst.pts_pct = tpl.pts_pct;
389
390 per_stream_lines[active_name].push_back(inst);
391 cell->add_persistent_line(inst);
392
393 if (settings) {
394 settings->append_active_log(
395 QString("template added to active: %1").arg(template_name)
396 );
397 }
398
399 cell->clear_draft();
400
401 if (settings) {
403 }
404
406}
407
409 if (!main_zone) {
410 return;
411 }
412
413 auto* cell = main_zone->active_cell();
414 if (!cell) {
415 return;
416 }
417
418 auto pts = cell->draft_points_pct();
419 if (pts.empty()) {
420 return;
421 }
422
423 pts.pop_back();
424 cell->set_draft_points_pct(pts);
425}
426
429
430 if (!main_zone) {
431 return;
432 }
433
434 if (auto* cell = main_zone->active_cell()) {
436 }
437}
438
440 if (!settings || !main_zone) {
441 return;
442 }
443
444 connect(
445 settings, &settings_panel::active_stream_selected, this,
446 &controller::on_active_stream_selected
447 );
448
449 connect(
452 );
453
454 connect(
455 settings, &settings_panel::active_line_params_changed, this,
456 &controller::on_active_line_params_changed
457 );
458
459 connect(
460 settings, &settings_panel::active_line_save_requested, this,
461 &controller::on_active_line_save_requested
462 );
463
464 connect(
465 settings, &settings_panel::active_template_add_requested, this,
466 &controller::on_active_template_add_requested
467 );
468
469 connect(
470 settings, &settings_panel::active_template_selected, this,
471 &controller::on_active_template_selected
472 );
473
474 connect(
475 settings, &settings_panel::active_template_color_changed, this,
476 &controller::on_active_template_color_changed
477 );
478
479 connect(
482 );
483
484 connect(
487 );
488}
489
491 if (!grid) {
492 return;
493 }
494
495 connect(grid, &grid_view::stream_closed, this, [this](const QString& name) {
496 if (settings) {
497 settings->set_stream_checked(name, false);
498 }
499 });
500
501 connect(
503 &controller::handle_enlarge_requested
504 );
505}
506
507void controller::handle_add_stream_common(
508 const QString& source, const QString& name, const QString& type, bool loop
509) {
510 if (!stream_mgr || !settings) {
511 return;
512 }
513
514 const auto ts = now_ts();
515
516 if (type == "url") {
517 const QUrl url(source);
518 const auto scheme = url.scheme().toLower();
519
520 if (!url.isValid() || scheme.isEmpty()) {
521 settings->append_add_log(
522 QString("[%1] error: invalid url '%2'").arg(ts, source)
523 );
524 return;
525 }
526
527 if (scheme != "rtsp" && scheme != "http" && scheme != "https") {
528 settings->append_add_log(
529 QString("[%1] error: unsupported url scheme '%2'")
530 .arg(ts, scheme)
531 );
532 return;
533 }
534 }
535
536 try {
537 const auto& s = stream_mgr->add_stream(
538 source.toStdString(), name.toStdString(), type.toStdString(), loop
539 );
540
541 const auto final_name = QString::fromStdString(s.get_name());
542 const auto source_desc = QString("%1:%2").arg(type, source);
543
544 QUrl url;
545 if (type == "file" || type == "local") {
546 url = QUrl::fromLocalFile(source);
547 } else {
548 url = QUrl(source);
549 }
550 stream_sources[final_name] = url;
551 stream_loops[final_name] = loop;
552
553 settings->append_add_log(
554 QString("[%1] ok: added %2 as %3").arg(ts, source_desc, final_name)
555 );
556
557 register_stream_in_ui(final_name, source_desc);
558 } catch (const std::exception& e) {
559 settings->append_add_log(
560 QString("[%1] error: add %2 failed: %3").arg(ts, type, e.what())
561 );
562 }
563}
564
565void controller::register_stream_in_ui(
566 const QString& final_name, const QString& source_desc
567) {
568 if (!settings) {
569 return;
570 }
571
572 settings->add_existing_name(final_name);
573 settings->add_stream_entry(final_name, source_desc);
575
578}
579
580QString controller::now_ts() {
581 return QDateTime::currentDateTime().toString("HH:mm:ss");
582}
583
584void controller::handle_enlarge_requested(const QString& name) {
585 if (name.isEmpty()) {
586 return;
587 }
588
589 if (!active_name.isEmpty() && active_name == name) {
591 return;
592 }
593
594 on_active_stream_selected(name);
595
596 if (settings) {
597 settings->set_active_current(name);
598 }
599}
600
602 on_active_stream_selected(QString());
603
604 if (settings) {
605 settings->set_active_current(QString());
606 }
607}
608
609void controller::handle_thumb_activate(const QString& name) {
610 handle_enlarge_requested(name);
611}
612
613stream_cell* controller::active_cell_checked(const QString& fail_prefix) {
614 if (!stream_mgr || !main_zone || active_name.isEmpty()) {
615 if (settings) {
616 settings->append_active_log(
617 QString("%1 failed: no active stream").arg(fail_prefix)
618 );
619 }
620 return nullptr;
621 }
622
623 auto* cell = main_zone->active_cell();
624 if (!cell) {
625 if (settings) {
626 settings->append_active_log(
627 QString("%1 failed: active cell not found").arg(fail_prefix)
628 );
629 }
630 return nullptr;
631 }
632
633 return cell;
634}
635
637 if (!main_zone || active_name.isEmpty()) {
638 if (settings) {
639 settings->set_template_candidates({});
640 }
641 return;
642 }
643
645
646 if (!settings) {
647 return;
648 }
649
650 const auto used = used_template_names_for_stream(active_name);
651 settings->set_template_candidates(template_candidates_excluding(used));
652}
653
654void controller::apply_template_preview(const QString& template_name) {
655 if (!main_zone) {
656 return;
657 }
658 auto* cell = main_zone->active_cell();
659 if (!cell) {
660 return;
661 }
662
663 cell->clear_draft();
664
665 if (template_name.isEmpty() || !templates.contains(template_name)) {
666 return;
667 }
668
669 const auto tpl = templates.value(template_name);
670
671 QColor c = Qt::red;
672 if (settings) {
673 c = settings->active_template_preview_color();
674 }
675
676 cell->set_draft_params(template_name, c, tpl.closed);
677 cell->set_draft_points_pct(tpl.pts_pct);
678}
679
680void controller::log_active(const QString& msg) const {
681 if (settings) {
682 settings->append_active_log(msg);
683 }
684}
685
686QString controller::points_str_from_pct(const std::vector<QPointF>& pts) {
687 QStringList parts;
688 parts.reserve(static_cast<int>(pts.size()));
689 for (const auto& p : pts) {
690 parts << QString("(%1,%2)").arg(p.x(), 0, 'f', 3).arg(p.y(), 0, 'f', 3);
691 }
692 return parts.join("; ");
693}
694
695void controller::apply_added_line(
696 stream_cell* cell, const QString& final_name,
697 const std::vector<QPointF>& pts, const bool closed
698) {
699 stream_cell::line_instance inst;
700 inst.template_name = final_name;
701 inst.color = draft_line_color;
702 inst.closed = closed;
703 inst.pts_pct = pts;
704
705 per_stream_lines[active_name].push_back(inst);
706 cell->add_persistent_line(inst);
707
708 templates[final_name] = tpl_line { pts, closed };
709
710 stream_mgr->set_line(active_name.toStdString(), final_name.toStdString());
711
712 cell->clear_draft();
713 cell->set_draft_params(QString(), QColor(Qt::red), false);
714
715 draft_line_name.clear();
716 draft_line_color = Qt::red;
717 draft_line_closed = false;
718
719 if (settings) {
721 settings->add_template_candidate(final_name);
723 }
724
725 log_active(
726 QString("line added: %1 (%2 points)").arg(final_name).arg(pts.size())
727 );
728
730}
731
733 if (!main_zone) {
734 return;
735 }
736
737 if (auto* cell = main_zone->active_cell()) {
738 cell->set_persistent_lines(per_stream_lines.value(active_name));
739 }
740}
741
742QSet<QString>
743controller::used_template_names_for_stream(const QString& stream) const {
744 QSet<QString> used;
745
746 const auto current_lines = per_stream_lines.value(stream);
747 for (const auto& inst : current_lines) {
748 const auto tn = inst.template_name.trimmed();
749 if (!tn.isEmpty()) {
750 used.insert(tn);
751 }
752 }
753
754 return used;
755}
756
758controller::template_candidates_excluding(const QSet<QString>& used) const {
759 QStringList candidates;
760 candidates.reserve(templates.size());
761
762 for (auto it = templates.begin(); it != templates.end(); ++it) {
763 const QString name = it.key();
764 if (!used.contains(name)) {
765 candidates << name;
766 }
767 }
768
769 return candidates;
770}
771
773 if (!grid) {
774 return;
775 }
776
777 const auto names = grid->stream_names();
778 const int n = static_cast<int>(names.size());
779 const int interval = repaint_interval_for_count(n);
780
781 for (const auto& name : names) {
782 if (auto* tile = grid->peek_stream_cell(name)) {
783 tile->set_repaint_interval_ms(interval);
784 }
785 }
786
787 if (!active_name.isEmpty()) {
788 if (auto* cell = grid->peek_stream_cell(active_name)) {
789 cell->set_repaint_interval_ms(active_interval_ms);
790 }
791 }
792}
793
795 const std::vector<yodau::backend::event>& evs
796) {
797 for (const auto& e : evs) {
798 on_backend_event(e);
799 }
800}
801
803 if (n <= 2) {
804 return 33;
805 }
806
807 if (n <= 4) {
808 return 66;
809 }
810
811 if (n <= 9) {
812 return 100;
813 }
814
815 return 166;
816}
817
818stream_cell* controller::tile_for_stream_name(const QString& name) const {
819 if (main_zone && !active_name.isEmpty() && active_name == name) {
820 if (auto* cell = main_zone->active_cell()) {
821 return cell;
822 }
823 }
824
825 if (grid) {
826 if (auto* tile = grid->peek_stream_cell(name)) {
827 return tile;
828 }
829 }
830
831 return nullptr;
832}
833
834yodau::backend::frame controller::frame_from_image(const QImage& image) const {
835 QImage img = image;
836
837 if (img.format() != QImage::Format_RGB888) {
838 img = img.convertToFormat(QImage::Format_RGB888);
839 }
840
841 yodau::backend::frame f;
842 f.width = img.width();
843 f.height = img.height();
844 f.stride = static_cast<int>(img.bytesPerLine());
846 f.ts = std::chrono::steady_clock::now();
847
848 const auto* ptr = img.constBits();
849 const int bytes = static_cast<int>(img.sizeInBytes());
850 if (ptr && bytes > 0) {
851 f.data.assign(ptr, ptr + bytes);
852 }
853
854 return f;
855}
856
857void controller::on_gui_frame(const QString& stream_name, const QImage& image) {
858 if (!stream_mgr) {
859 return;
860 }
861
862 auto f = frame_from_image(image);
863 stream_mgr->push_frame(stream_name.toStdString(), std::move(f));
864}
865
866void controller::on_backend_event(const yodau::backend::event& e) {
867 if (QThread::currentThread() != thread()) {
868 const auto copy = e;
869 QMetaObject::invokeMethod(
870 this, [this, copy]() { on_backend_event(copy); },
871 Qt::QueuedConnection
872 );
873 return;
874 }
875
876 const auto name = QString::fromStdString(e.stream_name);
877 auto* tile = tile_for_stream_name(name);
878 if (!tile) {
879 return;
880 }
881
882 if (!e.pos_pct.has_value()) {
883 return;
884 }
885
887 if (!e.line_name.empty()) {
888 const auto ln = QString::fromStdString(e.line_name);
889 const auto& p = *e.pos_pct;
890 tile->highlight_line_at(ln, QPointF(p.x, p.y));
891 }
892 }
893
894 const auto& p = *e.pos_pct;
895 tile->add_event(QPointF(p.x, p.y), Qt::gray);
896}
void clear_active()
Clear active mode and return the active cell to the grid.
Definition board.cpp:66
board(QWidget *parent=nullptr)
Construct the board widget.
Definition board.cpp:8
void set_active_stream(const QString &name)
Make a stream active by name.
Definition board.cpp:34
stream_cell * active_cell() const
Get the currently active (focused) stream cell, if any.
Definition board.cpp:32
grid_view * grid_mode() const
Access the grid view (thumbnail mode).
Definition board.cpp:30
stream_cell * take_active_cell()
Detach and return the active cell without putting it back to grid.
Definition board.cpp:80
bool drawing_new_mode
True if active edit mode is "draw new line".
void on_active_line_undo_requested()
Handler for undoing last draft point.
void update_analysis_caps()
Update backend analysis interval based on visible tile count.
void update_repaint_caps()
Update repaint interval caps for all visible tiles.
void on_backend_event(const yodau::backend::event &e)
Handle a single backend event (GUI-thread safe).
void on_active_template_color_changed(const QColor &color)
Handler for changing template preview color.
QMap< QString, std::vector< stream_cell::line_instance > > per_stream_lines
Per-stream persistent line instances keyed by stream name.
void on_active_edit_mode_changed(bool drawing_new)
Handler for toggling active edit mode.
void on_backend_events(const std::vector< yodau::backend::event > &evs)
Handle a batch of backend events.
yodau::backend::stream_manager * stream_mgr
Backend stream manager (non-owning).
void setup_grid_connections()
Connect grid_view signals to controller handlers.
void handle_detect_local_sources()
Handler for detecting available local sources.
QStringList template_candidates_excluding(const QSet< QString > &used) const
List templates not present in a given used set.
void handle_back_to_grid()
Return to grid mode (clear active stream).
void on_active_labels_enabled_changed(bool on)
Handler for toggling persistent label visibility in active view.
void sync_active_cell_lines() const
Push per-stream persistent lines into the active UI cell.
void sync_active_persistent()
Sync persistent line overlays and template candidates for active stream.
int active_interval_ms
Repaint interval for active (focused) stream in ms.
grid_view * grid
Grid view extracted from board (non-owning).
yodau::backend::frame frame_from_image(const QImage &image) const
Convert a QImage into backend frame.
void setup_settings_connections()
Connect settings_panel signals to controller slots.
settings_panel * settings
Settings panel (non-owning).
bool active_labels_enabled
Whether labels are enabled in active cell.
static int repaint_interval_for_count(int n)
Choose repaint interval given number of visible streams.
board * main_zone
Main board/zone widget (non-owning).
void init_from_backend()
Populate settings UI from backend at startup.
bool draft_line_closed
Draft line closed flag.
grid_view(QWidget *parent=nullptr)
Construct an empty grid view.
Definition grid_view.cpp:13
void remove_stream(const QString &name)
Remove a stream cell from the grid.
Definition grid_view.cpp:60
void add_stream(const QString &name)
Add a new stream cell to the grid.
Definition grid_view.cpp:40
stream_cell * peek_stream_cell(const QString &name) const
Get a pointer to a cell without removing it.
void stream_enlarge(const QString &name)
Emitted when a stream cell requests focus/enlargement.
void active_line_undo_requested()
Emitted when user requests undo of the last draft point.
settings_panel(QWidget *parent=nullptr)
Construct the settings panel.
void clear_add_inputs() const
Clear all add-tab input fields and reset validation.
void reset_active_template_form()
Reset the templates form to "none" selection.
void reset_active_line_form()
Reset the "new line" form in the active tab.
void active_edit_mode_changed(bool drawing_new)
Emitted when edit mode changes.
void active_labels_enabled_changed(bool on)
Emitted when label visibility toggle changes.
void set_loop(bool on)
Enable or disable looping for file-based playback.
void set_labels_enabled(bool on)
Enable or disable rendering of persistent line labels.
void clear_draft()
Clear all draft data (points, hover point, preview flag).
void set_drawing_enabled(bool on)
Enable or disable interactive drawing on this cell.
Central coordinator for streams, geometry, frame processing and events.
Represents a single video stream and its analytic connections.
Definition stream.hpp:64
event_kind
High-level classification of backend events.
Definition event.hpp:19
pixel_format
Pixel format of a frame buffer.
Definition frame.hpp:16
stream_type
Source/transport type of a video stream.
Definition stream.hpp:23
#define str_label(text)
Create a user-visible localized label.
Definition str_label.hpp:29
Instance of a persistent (saved) line to be rendered on the stream.
Generic event produced by the backend.
Definition event.hpp:44
event_kind kind
Type of the event.
Definition event.hpp:50
std::string line_name
Name of the line / ROI / rule responsible for this event.
Definition event.hpp:86
Video frame container.
Definition frame.hpp:44
int width
Frame width in pixels.
Definition frame.hpp:48
int stride
Number of bytes per row.
Definition frame.hpp:60
int height
Frame height in pixels.
Definition frame.hpp:53
pixel_format format
Pixel format of the buffer.
Definition frame.hpp:67