日本語 · English
Goal = make Fullseye Studio (
studio.py) an image-processing IDE faithful to MVTec HDevelop. Principle = “the IDE screen is simple yet multifunctional,” “high information density” (user, 2026-08-15, [[feedback_ide_design_simple_multifunctional]]). This document is an implementation spec spanning multiple sessions. Each session reads this and continues implementing.
content/ide/index.htmlcontent/ide/debugging/index.htmlcontent/halcon_script/index.htmlcontent/reference/index.htmldev_display/dev_set_color/dev_set_draw/dev_set_colored/dev_set_lut act on the current active window.dev_display). Double-clicking a control opens Variable Inspection. Variable state updates in sync with step execution.| Operator Window: operator-selection combo box (autocomplete) + category tree + search. 1 parameter = 1 line with **name / type / direction (iconic | control × in | out) / default / candidate-value dropdown / min-max / inline doc. **You can even enter arguments. Both inserting into code and single-shot execution are possible, and the result can be checked in the Graphics Window. |
read_image→Image, threshold→Region, connection→ConnectedRegions, edges_sub_pix→XLD.dev_display. A region is color-overlaid on the image (margin/fill of dev_set_draw, multiple colors via dev_set_colored).if / elseif / else / endif, for / endfor, while / endwhile, repeat / until, break / continue / return / stop / exit, assignment :=.< > <= >= = #, logic and / or / xor / not. Tuples [1,2,3].operator (Params) should be confirmed in the official Script Reference before implementation.Evolve in stages from the linear a/b pipeline (a sequence of op,a,b) toward the HDevelop model of variables + control statements + typed arguments.
op_arg_roles/op_impl_source/op_signature_detail show the knob a/b roles + implementation expression at high density (resolving “can’t tell what the argument is”).op (params), :=, comments, if/else/for/while). Execution model (step/breakpoint/execution cursor) with Variable/Graphics linkage.QT_QPA_PLATFORM=offscreen) via the build_window handle.QMenu(title, parent)) to prevent shiboken garbage-collecting them (discovered in v18.7).open_window/dev_open_window). The dev_* operators (dev_display/dev_set_color/dev_set_draw/dev_clear_window, etc.) draw to the current window. dev_set_window(Handle) switches the current one. → Studio implementation: a handle number per graphics window + a current-window pointer + variable double-click = draw to the current window + a current-switching UI (click a window or specify a handle).★Implemented the autonomous UI-operation harness (tools/studio_ui_harness.py). It launches Studio in an offscreen subprocess and injects real mouse events (QMouseEvent sent directly = drag keeps buttons=LeftButton) into every QPushButton / every QAction / every dock drag / the variable list / right-click context menus (183 steps across P0–P9). Design:
faulthandler. A hard C++ segfault is attributed by the parent via exit code + the last phase_start in the step-log + the native traceback.QDialog.exec) get a CONFIRM/ERROR stub + QFileDialog monkeypatch + a watchdog QTimer (closes both activeModalWidget and activePopupWidget = QMenu.exec too).sys.excepthook records asynchronous exceptions inside queued signals, tagged with the phase.graphics_primary + whether it is in-MDI.★The harness auto-detected → I fixed 3 crashes that only appear under real event injection (all in studio.py, with regression tests in tests/test_studio.py):
Q3DSurface() in show_3d_surface (formerly line 737) native-segfaults when no OpenGL context exists (offscreen / Remote Desktop / software GL); the try/except only caught import failures. → Gate it with _opengl_available() (a QOffscreenSurface+QOpenGLContext probe; offscreen is immediately False), and open_3d flashes “3-D surface needs OpenGL” when it is None. It works if GL is present / degrades harmlessly if not / never crashes.b_save (Save result) lives inside image_panel (= the primary Graphics window) and is deleted when the window is destroyed, but a queued currentRowChanged→update_actions hits setEnabled with RuntimeError: Internal C++ object already deleted. → An _enable(cond, *widgets) helper skips deleted ones (state sync continues for the survivors).view/b_save along with image_panel. → A close-rejecting event filter on the primary subwindow (_ResidentCloseGuard) + detach_graphics refusing to detach the primary (objectName == "graphics_primary"). The first step toward the resident current-window model.Verification = re-running the harness: all 183 steps OK, 0 slot exceptions, 0 crashes, and the primary window stayed alive+in-MDI across all 10 phases (before the fix, the MDI “Close” in P2 made in-MDI=False → deleted in P9 → crash). Studio tests 69 → 72 (updated the detach test to the resident spec + added 3 regressions for resident/update_actions/3D). Dock drag (suspected GroupedDragging) did not crash across all 25 patterns (the automated proxy passes; since it differs from the real windowing-system native drag, on-device confirmation is separate).
Use case = design work to “interactively compose an image-processing algorithm, tune parameters, and validate on holdout” (an HDevelop-experienced user expects an equivalent feel). I had Codex read studio.py + this doc and obtained 12 code-backed critiques limited to operation flow (I verified each against the primary code. Discipline = no blind acceptance [[feedback_external_ai_verify]]). Adopted points (★ = next implementation target):
new_graphics_window 1550– only adds a window; _render always goes to a fixed view) = matches this doc’s north star, “current-window model.” The operation target and the result-display target diverge. → P4-current: current-window pointer + handle number + a dev_set_window-equivalent switch UI + route _render/display_variable/run_op_once to the current window. Fix 3 above (resident) is its foundation.display_variable(True) → always a new window). HDevelop displays to the active window. → double-click = to the current window, Shift/button for a new window.run_op_once 1861–, input fixed to model.image). You can’t use an intermediate result or a selected variable as input. → current-window display + let the input choose the result of a selected stage/variable._var_icon handles only 2D/3D ndarrays).step_to doesn’t call refresh_variables; because all stages are precomputed, even not-yet-executed later ones appear to exist).confirm_discard covers only the Pipeline; code_edit changes aren’t tracked; sync_program overwrites).remove refreshes without specifying a selection).load_image is single-image only; load_frame_b is for 2-frame perception) = the final step of the use case is unfinished. Needs user judgment (large scope).Deliberated implementation order: (P4-current) the current-window model ← foundation already laid by the resident work, threading the largest cluster (Codex #1–5) at once → (P2b’) per-op argument UI → (step linkage) → (Program dirty tracking). Undo / holdout path are large-scope, so after user confirmation.
_fs_handle, a never-reused running counter _gfx_handle_seq) to each graphics window. primary=1, new/reattach increment.win._current_gfx (default = the resident primary) + mdi.subWindowActivated→follow (clicking a window switches the current one = dev_set_window) + a persistent “current: Graphics N” display at the right of the status bar.display_variable(target) becomes 3-way "current"/"new"/"main" (keeps the old bool compatibility). v_disp = new window / v_here “Display → current” / right-click also current/new._current_view() (the current window’s ImageView; self-heals to the primary when closed) / _current_handle(). Regression test_current_graphics_window_model. Harness measurement = window proliferation from variable operations dropped from 8 → 5. Remaining (Codex #1) = the idea of sending the pipeline result _render itself to the current window is a large change to the meaning of result display, so it stays fixed to the primary for now (needs consideration). Next = P2b’ per-op argument UI._ARG_ROLES (the existing curated table) + a _knob_label helper. Public win._op_arg_labels. Regression test_operator_arg_labels_reflect_selected_op.User guidance = “the image is largest / then the script code / op-selection and variable windows are vertically small / mostly undocked, shown only when needed.”
setCorner confines the bottom Program to below the image only. layout_version 2 → 3 (invalidates existing saves to reflect the new default). Regression test_default_layout_is_image_dominant. The “Balanced (default)” preset still shows everything as before (reset shows all).run_holdout(stages, image_paths, gt_paths=None) (Qt-free; api.run_pipeline batch-runs a validation-image folder → per-image ran/failed + timing; if a GT folder exists, holdout_metric = IoU(binary)/PSNR(intensity) per-image + mean; honest = no metric emitted without GT) + Studio “Validate on holdout…” (Ctrl+H, Run menu) = folder selection → result table + aggregation + honest note. Connects the imgevolve north star (holdout gate) to the UI. Remaining (Codex #1) = only the idea of sending the pipeline result _render itself to the current window is unimplemented (large semantic change, needs consideration). Regressions test_undo_redo_pipeline_edits / test_program_editor_tracks_unapplied_edits / test_step_through_marks_variable_frontier / test_remove_keeps_a_neighbour_selected / test_holdout_*. Studio tests 69 → 82, full suite 4300 → (next measurement), harness 192 steps, fail0, slot0.