Skip to content

Commit b8fb744

Browse files
author
Gin
committed
add button to load custom label set, add signal_all_text_changes to string option
1 parent 742119b commit b8fb744

File tree

8 files changed

+117
-16
lines changed

8 files changed

+117
-16
lines changed

Common/Cpp/Options/StringOption.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct StringCell::Data{
1616
const bool m_is_password;
1717
const std::string m_default;
1818
const std::string m_placeholder_text;
19+
const bool m_report_all_text_changes;
1920

2021
std::atomic<bool> m_locked;
2122

@@ -25,11 +26,13 @@ struct StringCell::Data{
2526
Data(
2627
bool is_password,
2728
std::string default_value,
28-
std::string placeholder_text
29+
std::string placeholder_text,
30+
bool report_all_text_changes
2931
)
3032
: m_is_password(is_password)
3133
, m_default(std::move(default_value))
3234
, m_placeholder_text(std::move(placeholder_text))
35+
, m_report_all_text_changes(report_all_text_changes)
3336
, m_current(m_default)
3437
{}
3538
};
@@ -40,10 +43,11 @@ StringCell::StringCell(
4043
bool is_password,
4144
LockMode lock_while_program_is_running,
4245
std::string default_value,
43-
std::string placeholder_text
46+
std::string placeholder_text,
47+
bool signal_all_text_changes
4448
)
4549
: ConfigOption(lock_while_program_is_running)
46-
, m_data(CONSTRUCT_TOKEN, is_password, std::move(default_value), std::move(placeholder_text))
50+
, m_data(CONSTRUCT_TOKEN, is_password, std::move(default_value), std::move(placeholder_text), signal_all_text_changes)
4751
{}
4852

4953
bool StringCell::is_password() const{
@@ -55,6 +59,9 @@ const std::string& StringCell::placeholder_text() const{
5559
const std::string StringCell::default_value() const{
5660
return m_data->m_default;
5761
}
62+
bool StringCell::signal_all_text_changes() const{
63+
return m_data->m_report_all_text_changes;
64+
}
5865

5966

6067
bool StringCell::is_locked() const{
@@ -108,9 +115,10 @@ StringOption::StringOption(
108115
std::string label,
109116
LockMode lock_while_program_is_running,
110117
std::string default_value,
111-
std::string placeholder_text
118+
std::string placeholder_text,
119+
bool signal_all_text_changes
112120
)
113-
: StringCell(is_password, lock_while_program_is_running, default_value, placeholder_text)
121+
: StringCell(is_password, lock_while_program_is_running, default_value, placeholder_text, signal_all_text_changes)
114122
, m_label(std::move(label))
115123
{}
116124

Common/Cpp/Options/StringOption.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ class StringCell : public ConfigOption{
2020
bool is_password,
2121
LockMode lock_while_program_is_running,
2222
std::string default_value,
23-
std::string placeholder_text
23+
std::string placeholder_text,
24+
bool signal_all_text_changes = false
2425
);
2526

2627
bool is_password() const;
2728
const std::string& placeholder_text() const;
2829
const std::string default_value() const;
30+
bool signal_all_text_changes() const;
2931

3032
bool is_locked() const;
3133
void set_locked(bool locked);
@@ -56,7 +58,8 @@ class StringOption : public StringCell{
5658
std::string label,
5759
LockMode lock_while_program_is_running,
5860
std::string default_value,
59-
std::string placeholder_text
61+
std::string placeholder_text,
62+
bool signal_all_text_changes = false
6063
);
6164

6265
const std::string& label() const{ return m_label; }

Common/Qt/Options/StringWidget.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ StringCellWidget::StringCellWidget(QWidget& parent, StringCell& value)
4747
m_value.set(this->text().toStdString());
4848
}
4949
);
50+
if (m_value.signal_all_text_changes()){
51+
connect(
52+
this, &QLineEdit::textChanged,
53+
[this]{
54+
std::string old_value = (std::string)m_value;
55+
std::string text = this->text().toStdString();
56+
if (old_value == text){
57+
return;
58+
}
59+
m_value.set(std::move(text));
60+
}
61+
);
62+
}
5063

5164
m_value.add_listener(*this);
5265
}
@@ -110,6 +123,19 @@ StringOptionWidget::StringOptionWidget(QWidget& parent, StringOption& value)
110123
m_value.set(m_box->text().toStdString());
111124
}
112125
);
126+
if (m_value.signal_all_text_changes()){
127+
connect(
128+
m_box, &QLineEdit::textChanged,
129+
[this]{
130+
const std::string old_value = (std::string)m_value;
131+
std::string text = m_box->text().toStdString();
132+
if (old_value == text){
133+
return;
134+
}
135+
m_value.set(std::move(text));
136+
}
137+
);
138+
}
113139

114140
m_value.add_listener(*this);
115141
}

Common/Qt/Options/TextEditWidget.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ class TextEditWidget::Box : public QTextEdit{
4545
connect(
4646
this, &QTextEdit::textChanged,
4747
[this]{
48-
std::string new_value = (std::string)m_parent.m_value;
48+
const std::string old_value = (std::string)m_parent.m_value;
4949
std::string text = this->toPlainText().toStdString();
50-
if (new_value == text){
50+
if (old_value == text){
5151
return;
5252
}
5353
// cout << new_value << " : " << text << endl;

SerialPrograms/Source/CommonTools/Options/StringSelectOption.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct StringSelectDatabase::Data{
7575

7676
m_longest_text_length = std::max(m_longest_text_length, item.display_name.size());
7777
}
78+
size_t size() const { return m_list.size(); }
7879
};
7980

8081

@@ -106,6 +107,9 @@ size_t StringSelectDatabase::search_index_by_name(const std::string& display_nam
106107
void StringSelectDatabase::add_entry(StringSelectEntry entry){
107108
m_data->add_entry(std::move(entry));
108109
}
110+
size_t StringSelectDatabase::size() const{
111+
return m_data->size();
112+
}
109113

110114
StringSelectDatabase create_string_select_database(const std::vector<std::string>& slugs){
111115
StringSelectDatabase database;

SerialPrograms/Source/CommonTools/Options/StringSelectOption.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class StringSelectDatabase{
5151
public:
5252
StringSelectDatabase();
5353
void add_entry(StringSelectEntry entry);
54+
size_t size() const;
5455

5556
public:
5657
const std::vector<StringSelectEntry>& case_list() const;

SerialPrograms/Source/ML/Programs/ML_LabelImages.cpp

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,9 @@ LabelImages::LabelImages(const LabelImages_Descriptor& descriptor)
148148
, LABEL_TYPE_DATABASE(create_label_type_database())
149149
, LABEL_TYPE("<b>Select Label:</b>", LABEL_TYPE_DATABASE, LockMode::UNLOCK_WHILE_RUNNING, 0)
150150
, FORM_LABEL("bulbasaur")
151-
, CUSTOM_LABEL_DATABASE(create_string_select_database({
152-
"sun",
153-
"mc"
154-
}))
151+
, CUSTOM_LABEL_DATABASE(create_string_select_database({"mc"})) // mc for "main character"
155152
, CUSTOM_SET_LABEL(CUSTOM_LABEL_DATABASE, LockMode::UNLOCK_WHILE_RUNNING, 0)
156-
, MANUAL_LABEL(false, "Input: ", LockMode::UNLOCK_WHILE_RUNNING, "", "Custom Label")
153+
, MANUAL_LABEL(false, LockMode::UNLOCK_WHILE_RUNNING, "", "Custom Label", true)
157154
{
158155
ADD_OPTION(X);
159156
ADD_OPTION(Y);
@@ -203,10 +200,15 @@ void LabelImages::from_json(const JsonValue& json){
203200
m_display_option.load_json(*value);
204201
}
205202
m_options.load_json(json);
203+
const std::string* file_path = obj->get_string("CUSTOM_LABEL_SET_FILE_PATH");
204+
if (file_path){
205+
load_custom_label_set(*file_path);
206+
}
206207
}
207208
JsonValue LabelImages::to_json() const{
208209
JsonObject obj = std::move(*m_options.to_json().to_object());
209210
obj["ImageSetup"] = m_display_option.to_json();
211+
obj["CUSTOM_LABEL_SET_FILE_PATH"] = m_custom_label_set_file_path;
210212

211213
save_annotation_to_file();
212214
return obj;
@@ -561,6 +563,40 @@ void LabelImages::set_selected_label(const std::string& slug){
561563
MANUAL_LABEL.set(slug);
562564
}
563565

566+
void LabelImages::load_custom_label_set(const std::string& json_path){
567+
StringSelectDatabase new_database;
568+
try{
569+
JsonValue value = load_json_file(json_path);
570+
const JsonArray& json_array = value.to_array_throw();
571+
for(size_t i = 0; i < json_array.size(); i++){
572+
const std::string& label_slug = json_array[i].to_string_throw();
573+
new_database.add_entry(StringSelectEntry(label_slug, label_slug));
574+
}
575+
} catch(FileException& e){
576+
std::cerr << "Error: File exception " << e.message() << std::endl;
577+
QMessageBox box;
578+
box.warning(nullptr, "Unable to Load Custom Label Set",
579+
QString::fromStdString("Cannot open JSON file " + json_path + " for the custom label set. Probably wrong permission?"));
580+
return;
581+
} catch(JsonParseException& e){
582+
std::cerr << "Error: JSON parse exception " << e.message() << std::endl;
583+
QMessageBox box;
584+
box.warning(nullptr, "Unable to Load Custom Label Set",
585+
QString::fromStdString("Cannot parse JSON file " + json_path + " for the custom label set. Probably wrong file content?"));
586+
return;
587+
}
588+
589+
cout << "Loaded " << new_database.size() << " custom labels from " << json_path << endl;
590+
CUSTOM_LABEL_DATABASE = new_database;
591+
if (&json_path != &m_custom_label_set_file_path){
592+
m_custom_label_set_file_path = json_path;
593+
}
594+
595+
// if the current label is set by MANUAL_LABEL but its value appears in the newly loaded custom set,
596+
// the label UI should switch the label to be shown as part of the custom set.
597+
// so call the following line to achieve that
598+
set_selected_label(selected_label());
599+
}
564600

565601
LabelImages_Widget::~LabelImages_Widget(){
566602
m_display_session.overlay().remove_listener(*this);
@@ -637,14 +673,18 @@ LabelImages_Widget::LabelImages_Widget(
637673
ConfigWidget* custom_label_widget = program.CUSTOM_SET_LABEL.make_QtWidget(*scroll_inner);
638674
annotation_row->addWidget(&custom_label_widget->widget(), 2);
639675
ConfigWidget* manual_input_label_widget = program.MANUAL_LABEL.make_QtWidget(*scroll_inner);
640-
annotation_row->addWidget(&manual_input_label_widget->widget(), 4);
676+
annotation_row->addWidget(&manual_input_label_widget->widget(), 2);
677+
QPushButton* load_custom_set_button = new QPushButton("Load Custom Set", scroll_inner);
678+
annotation_row->addWidget(load_custom_set_button, 2);
641679
annotation_row->addWidget(new QLabel(scroll_inner), 10); // an empty label to push other UIs to the left
642680

643681
// add compute embedding button
644682

645683
QPushButton* compute_embedding_button = new QPushButton("Compute Image Embeddings (SLOW!)", scroll_inner);
646684
scroll_layout->addWidget(compute_embedding_button);
647685

686+
// connect button signals to define button actions
687+
648688
connect(delete_anno_button, &QPushButton::clicked, this, [this](bool){
649689
auto& program = this->m_program;
650690
program.delete_selected_annotation();
@@ -659,6 +699,21 @@ LabelImages_Widget::LabelImages_Widget(
659699
program.select_next_annotation();
660700
});
661701

702+
connect(load_custom_set_button, &QPushButton::clicked, this, [this](bool){
703+
const std::string& last_loaded_file_path = m_program.m_custom_label_set_file_path;
704+
std::string starting_dir = ".";
705+
if (last_loaded_file_path.size() > 0){
706+
starting_dir = std::filesystem::path(last_loaded_file_path).parent_path().string();
707+
}
708+
const std::string path = QFileDialog::getOpenFileName(
709+
nullptr, "Open JSON file", QString::fromStdString(starting_dir), "*.json"
710+
).toStdString();
711+
if (path.size() > 0){
712+
cout << "File dialog returns JSON path " << path << endl;
713+
m_program.load_custom_label_set(path);
714+
}
715+
});
716+
662717
connect(compute_embedding_button, &QPushButton::clicked, this, [this](bool){
663718
std::string folder_path = QFileDialog::getExistingDirectory(
664719
nullptr, "Open image folder", ".").toStdString();

SerialPrograms/Source/ML/Programs/ML_LabelImages.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ class LabelImages : public PanelInstance, public ConfigOption::Listener {
112112
std::string selected_label() const;
113113
void set_selected_label(const std::string& label);
114114

115+
void load_custom_label_set(const std::string& json_path);
116+
115117
private:
116118
void on_config_value_changed(void* object) override;
117119

@@ -141,7 +143,7 @@ class LabelImages : public PanelInstance, public ConfigOption::Listener {
141143
// source 2: a dropdown menu for custom labels
142144
StringSelectCell CUSTOM_SET_LABEL;
143145
// source 3: editable text input
144-
StringOption MANUAL_LABEL;
146+
StringCell MANUAL_LABEL;
145147

146148
size_t source_image_height = 0;
147149
size_t source_image_width = 0;
@@ -162,6 +164,8 @@ class LabelImages : public PanelInstance, public ConfigOption::Listener {
162164
// we fail to load it, then we shouldn't overwrite this file to possibly erase the previous work.
163165
// so this flag is used to denote if we fail to load an annotation file
164166
bool m_fail_to_load_annotation_file = false;
167+
168+
std::string m_custom_label_set_file_path;
165169
};
166170

167171

0 commit comments

Comments
 (0)