Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions labeler/views/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,9 @@ def __init__(self, *args, **kwargs):
# Listener for label type
self.type_variable.trace_add("write", lambda *args: self.save_type())
self.label_type = ttk.Combobox(
self.top_frame, textvariable=self.type_variable, values=self.type_options, state="readonly"
self.top_frame, textvariable=self.type_variable, values=self.type_options, state="normal"
)
self.label_type.bind("<KeyRelease>", self._filter_label_type_values)
self.progress_bar = ttk.Progressbar(self.top_frame, orient="horizontal", length=100, mode="determinate")

# Canvas
Expand Down Expand Up @@ -373,7 +374,15 @@ def show_buttons(self):
self.draw_poly_button.configure(state="normal")
self.make_tight_button.configure(state="normal")
self.label_text.configure(state="normal")
self.label_type.configure(state="readonly")
self.label_type.configure(state="normal")

def _filter_label_type_values(self, event: tk.Event | None = None):
current_text = self.type_variable.get().strip().lower()
if not current_text:
self.label_type["values"] = self.type_options
return
filtered = [t for t in self.type_options if current_text in t.lower()]
self.label_type["values"] = filtered or self.type_options

def select_all(self, event: tk.Event | None = None):
"""
Expand Down Expand Up @@ -413,10 +422,13 @@ def save_type(self, *args):
if not self.img_cnv or not hasattr(self, "last_selected_polygon"):
return

new_type = self.type_variable.get().strip()
if new_type not in self.type_options:
return

selected_polys = [poly for poly in self.img_cnv.polygons if poly.select_poly]
if selected_polys:
with self.img_cnv.polygons_mutex:
new_type = self.type_variable.get().strip()
for poly in selected_polys:
poly.poly_type = new_type
self.show_case_type_variable.set(new_type)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from setuptools import setup

PKG_NAME = "doctr-labeler"
VERSION = os.getenv("BUILD_VERSION", "0.2.4")
VERSION = os.getenv("BUILD_VERSION", "0.3.0")


if __name__ == "__main__":
Expand Down
54 changes: 50 additions & 4 deletions tests/common/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_show_buttons(gui_app):
assert str(gui_app.draw_poly_button["state"]) == "normal"
assert str(gui_app.make_tight_button["state"]) == "normal"
assert str(gui_app.label_text["state"]) == "normal"
assert str(gui_app.label_type["state"]) == "readonly"
assert str(gui_app.label_type["state"]) == "normal"


def test_toggle_keep_drawing(gui_app):
Expand Down Expand Up @@ -154,7 +154,7 @@ def test_show_buttons_enables_buttons(gui_app):
]
for button in buttons:
assert str(button["state"]) == "normal"
assert str(gui_app.label_type["state"]) == "readonly"
assert str(gui_app.label_type["state"]) == "normal"


def test_select_all(gui_app):
Expand Down Expand Up @@ -215,19 +215,36 @@ def test_save_type(gui_app):
mock_polygon.select_poly = True
gui_app.img_cnv.polygons = [mock_polygon]
gui_app.type_variable = Mock()
gui_app.type_variable.get.return_value = " polygon_type "
gui_app.type_variable.get.return_value = " words "

gui_app.save_image_button = Mock() # Mock the save_image_button
gui_app.save_image_button.configure = Mock() # Mock the configure method

gui_app.save_type()

gui_app.img_cnv.polygons_mutex.__enter__.assert_called_once()
assert mock_polygon.poly_type == "polygon_type"
assert mock_polygon.poly_type == "words"
assert not gui_app.img_cnv.current_saved
gui_app.save_image_button.configure.assert_called_once_with(state="normal")


def test_save_type_new_type_check_coverage(gui_app):
gui_app.img_cnv = Mock()
mutex_mock = Mock()
mutex_mock.__enter__ = Mock()
mutex_mock.__exit__ = Mock()
gui_app.img_cnv.polygons_mutex = mutex_mock

gui_app.last_selected_polygon = "123"
gui_app.type_options = ["valid"]
gui_app.type_variable = Mock()
gui_app.type_variable.get.return_value = "invalid_xyz"

gui_app.save_type()

gui_app.img_cnv.polygons_mutex.__enter__.assert_not_called()


def test_load_new_img(gui_app):
gui_app.canvas = Mock()
gui_app.save_image_button = Mock()
Expand Down Expand Up @@ -436,3 +453,32 @@ def test_update_color_palette_with_mapping_and_new_types(gui_app):
assert gui_app.color_palette[1] == "#123456"
assert len(gui_app.color_palette) == 4
assert gui_app.color_palette[3].startswith("#")


def test_filter_label_type_values(gui_app):
gui_app.type_options = ["words", "lines", "header", "paragraph", "footer"]

gui_app.type_variable.set("")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == tuple(gui_app.type_options)

gui_app.type_variable.set("he")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == ("header",)

gui_app.type_variable.set("par")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == ("paragraph",)

gui_app.type_variable.set("xyz")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == tuple(gui_app.type_options)

gui_app.type_variable.set("HEAD")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == ("header",)

gui_app.type_options = ["words"]
gui_app.type_variable.set("w")
gui_app._filter_label_type_values(None)
assert tuple(gui_app.label_type["values"]) == ("words",)