Isaac Sim Version
√5.1.0
√ 4.5.0
Operating System
√Windows 11
GPU Information
- ---------------------------------------------------------------------------------------------|
| Driver Version: 581.57 | Graphics API: Vulkan
| NVIDIA GeForce RTX 3070 Ti Lap..
Detailed Description
I am encountering an issue with the UI design while attempting to extend a TreeView structure to create an interface similar to an Excel spreadsheet. The same code works correctly in Isaac Sim 4.5, both in the Script Editor and in custom extensions. However, in Isaac Sim 5.1, double-clicking often fails to activate table editing, and after some time, the double-click interaction stops working entirely. The code is provided below and can be run directly in the Script Editor.
__all__ = ["ReflectanceDatabaseWindow"]
import omni.ui as ui
import asyncio
import omni.kit.app
LABEL_HEIGHT = 34
SPACING = 8
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
global my_db_window
try:
if my_db_window:
my_db_window.destroy()
except NameError:
my_db_window = None
# ---------------------------------------------------------------------------
# Model
# ---------------------------------------------------------------------------
class FloatModel(ui.AbstractValueModel):
def __init__(self, value: float):
super().__init__()
self._value = value
def get_value_as_float(self): return self._value or 0.0
def get_value_as_string(self): return "{0:g}".format(self._value) if self._value is not None else ""
def set_value(self, value):
try:
val = float(value)
if val != self._value:
self._value = val
self._value_changed()
except ValueError: pass
class TableItem(ui.AbstractItem):
def __init__(self, param_name, value, note):
super().__init__()
self.name_model = ui.SimpleStringModel(str(param_name))
self.value_model = FloatModel(float(value))
self.note_model = ui.SimpleStringModel(str(note))
class TableModel(ui.AbstractItemModel):
def __init__(self, headers, data):
super().__init__()
self.headers = headers
self._items = [TableItem(*row) for row in data]
def get_item_children(self, item): return self._items if item is None else []
def get_item_value_model_count(self, item): return len(self.headers)
def get_item_value_model(self, item, column_id):
if item is None: return None
return [item.name_model, item.value_model, item.note_model][column_id]
# ---------------------------------------------------------------------------
# (Delegate)
# ---------------------------------------------------------------------------
class TableDelegate(ui.AbstractItemDelegate):
def __init__(self, model):
super().__init__()
self._model = model
self._subscriptions = {}
def build_branch(self, model, item, column_id, level, expanded):
pass
def build_header(self, column_id):
title = self._model.headers[column_id] if self._model and column_id < len(self._model.headers) else ""
with ui.ZStack(height=LABEL_HEIGHT):
ui.Rectangle(style={"background_color": 0xFF3A3A3A, "border_color": 0xFF555555, "border_width": 1})
ui.Label(title, alignment=ui.Alignment.CENTER, style={"color": 0xFFAAAAAA, "font_size": 14})
def build_widget(self, model, item, column_id, level, expanded):
if item is None: return
value_model = model.get_item_value_model(item, column_id)
is_float_col = (column_id == 1)
stack = ui.ZStack(height=24)
with stack:
ui.Rectangle(style={"background_color": 0xFF222222, "border_color": 0xFF444444, "border_width": 1})
with ui.Placer(offset_x=5):
label = ui.Label(value_model.as_string, alignment=ui.Alignment.LEFT_CENTER)
hit_cover = ui.Rectangle(style={"background_color": 0x01000000}, visible=True)
edit_style = {"background_color": 0xFF000000}
if is_float_col:
field = ui.FloatField(value_model, visible=False, style=edit_style)
else:
field = ui.StringField(value_model, visible=False, style=edit_style)
hit_cover.set_mouse_double_clicked_fn(
lambda x, y, b, m, f=field, l=label: self._start_edit(b, f, l)
)
def _start_edit(self, button, field, label):
if button != 0: return
field.visible = True
async def focus_with_delay():
await omni.kit.app.get_app().next_update_async()
field.focus_keyboard()
asyncio.ensure_future(focus_with_delay())
sub_id = id(field)
self._subscriptions[sub_id] = field.model.subscribe_end_edit_fn(
lambda m, f=field, l=label, sid=sub_id: self._end_edit(m, f, l, sid)
)
def _end_edit(self, model, field, label, sub_id):
field.visible = False
label.text = model.as_string
if sub_id in self._subscriptions:
del self._subscriptions[sub_id]
class ReflectanceDatabaseWindow(ui.Window):
def __init__(self, title: str, **kwargs):
super().__init__(title, **kwargs)
# 测试数据
headers = ["Name", "Reflectance", "Transmittance"]
data = [
["leaf", "100", "Max velocity"],
["branch", "0.5", "Surface friction"],
["ground", "0.1", "Restitution"],
["water", "-9.8", "Standard Earth"],
]
self._model = TableModel(headers, data)
self._delegate = TableDelegate(self._model)
self.frame.set_build_fn(self._build_fn_observation)
def destroy(self):
super().destroy()
self._model = None
self._delegate = None
def _build_fn_observation(self):
with ui.VStack(spacing=5):
ui.Label("ReflectanceDatabase (Fixed)", height=20, style={"color": 0xFF88FF88})
with ui.ScrollingFrame(
horizontal_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
vertical_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_ON
):
self._tree_view = ui.TreeView(
self._model,
delegate=self._delegate,
root_visible=False,
header_visible=True,
columns_resizable=True,
column_widths=[ui.Fraction(1), ui.Fraction(1), ui.Fraction(2)]
)
# ---------------------------------------------------------------------------
# run
# ---------------------------------------------------------------------------
my_db_window = ReflectanceDatabaseWindow("Reflectance DB", width=500, height=400)