Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context menu improvements #793

Merged
merged 17 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
81764b7
ADD: Delete option to the marker list context menu
henrikkauppi Jun 13, 2024
4b33a19
ADD: Context menu to masks list with options to edit, duplicate and d…
henrikkauppi Jun 20, 2024
eeff860
Merge branch 'invesalius:master' into context-menu-improvements
henrikkauppi Jun 25, 2024
3166197
ADD: Context menu with change color, duplicate, and delete options to…
henrikkauppi Jun 26, 2024
2243d30
ADD: option to change surface transparency in the surface list contex…
henrikkauppi Jun 26, 2024
f58a1e6
Merge branch 'invesalius:master' into context-menu-improvements
henrikkauppi Jun 26, 2024
7d0133c
ADD: Confirmation dialog window when deleting masks or surfaces
henrikkauppi Jun 26, 2024
858ce21
MOD: simplify context menu item names
henrikkauppi Jun 26, 2024
a57fe83
MOD: make function names more descriptive
henrikkauppi Jun 26, 2024
7a64b2c
FIX: Moving the transparency slider now updates surface transparency …
henrikkauppi Jun 27, 2024
81e9f15
FIX: Change label dialog in MacOS parameter error
vhosouza Jun 27, 2024
d3461f8
CLN: Remove unnecessary if statement
henrikkauppi Jun 27, 2024
0768e26
ENH: Improve surface transparency update
vhosouza Jun 27, 2024
a49f010
ENH: Improve surface transparency dialog control
vhosouza Jun 27, 2024
c1daba6
FIX: Update the color in the surface properties UI after deleting a s…
henrikkauppi Jun 27, 2024
600cc97
FIX: cancel color dialog bug and KeyError when trying to change color…
henrikkauppi Jul 3, 2024
35d6551
Merge branch 'invesalius:master' into context-menu-improvements
henrikkauppi Jul 3, 2024
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
3 changes: 3 additions & 0 deletions invesalius/data/slice_.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,9 @@ def SelectCurrentMask(self, index):
future_mask = proj.GetMask(index)
future_mask.is_shown = True
self.current_mask = future_mask
# Update the current mask index because is some edge cases
# it will be incorrect after self.current_mask = future_mask
self.current_mask.index = index
self.current_mask.on_show()

colour = future_mask.colour
Expand Down
166 changes: 159 additions & 7 deletions invesalius/gui/data_notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import invesalius.constants as const
import invesalius.data.slice_ as slice_
import invesalius.gui.dialogs as dlg
import wx.lib.colourselect as csel
# import invesalius.gui.widgets.listctrl as listmix
import wx.lib.mixins.listctrl as listmix

Expand Down Expand Up @@ -430,6 +431,8 @@ def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
self._click_check = False
self.mask_list_index = {}
self.current_index = 0
# Color of the currently selected surface when opening context menu, default is white
self.current_color = [255, 255, 255]
self.__init_columns()
self.__init_image_list()
self.__bind_events_wx()
Expand All @@ -438,6 +441,7 @@ def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
def __bind_events_wx(self):
self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel)
self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent)
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.on_mouse_right_click)

def __bind_events(self):
Publisher.subscribe(self.AddMask, 'Add mask')
Expand All @@ -450,6 +454,60 @@ def __bind_events(self):
Publisher.subscribe(self.__hide_current_mask, 'Hide current mask')
Publisher.subscribe(self.__show_current_mask, 'Show current mask')
Publisher.subscribe(self.OnCloseProject, 'Close project data')
Publisher.subscribe(self.update_current_colour, 'Set GUI items colour')

def on_mouse_right_click(self, event):
start_idx = 1
focused_item = self.GetFocusedItem()

# Create the context menu and add all the menu items
mask_context_menu = wx.Menu()

colour_id = mask_context_menu.Append(start_idx, _('Change color'))
mask_context_menu.Bind(wx.EVT_MENU, self.change_mask_color, colour_id)

duplicate_id = mask_context_menu.Append(start_idx + 1, _('Duplicate'))
mask_context_menu.Bind(wx.EVT_MENU, self.duplicate_masks, duplicate_id)

mask_context_menu.AppendSeparator()

delete_id = mask_context_menu.Append(start_idx + 2, _('Delete'))
mask_context_menu.Bind(wx.EVT_MENU, self.delete_mask, delete_id)

# Select the focused mask and show it in the slice viewer
Publisher.sendMessage('Change mask selected', index=focused_item)
Publisher.sendMessage('Show mask', index=focused_item, value=True)

self.PopupMenu(mask_context_menu)
mask_context_menu.Destroy()

def update_current_colour(self, colour):
self.current_colour = colour

def change_mask_color(self, event):
current_color = self.current_color

new_color = dlg.ShowColorDialog(color_current=current_color)

if not new_color:
return

Publisher.sendMessage('Change mask colour', colour=new_color)

def duplicate_masks(self, event):
selected_items = self.GetSelected()
if selected_items:
Publisher.sendMessage('Duplicate masks', mask_indexes=selected_items)
else:
dlg.MaskSelectionRequiredForDuplication()


def delete_mask(self, event):
result = dlg.ShowConfirmationDialog(msg=_("Delete mask?"))
if result != wx.ID_OK:
return
self.RemoveMasks()


def OnKeyEvent(self, event):
keycode = event.GetKeyCode()
Expand Down Expand Up @@ -745,26 +803,113 @@ def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
self.__init_image_list()
self.__init_evt()
self.__bind_events_wx()

# Color of the currently selected surface when opening context menu, default is white
self.current_color = [255, 255, 255]
self.current_transparency = 0
self.surface_list_index = {}
self.surface_bmp_idx_to_name = {}

def __init_evt(self):
Publisher.subscribe(self.AddSurface,
'Update surface info in GUI')
Publisher.subscribe(self.EditSurfaceTransparency,
'Set surface transparency')
Publisher.subscribe(self.EditSurfaceColour,
'Set surface colour')
Publisher.subscribe(self.AddSurface,'Update surface info in GUI')
Publisher.subscribe(self.update_current_surface_data, 'Update surface info in GUI')
Publisher.subscribe(self.EditSurfaceTransparency,'Set surface transparency')
Publisher.subscribe(self.EditSurfaceColour,'Set surface colour')
Publisher.subscribe(self.OnCloseProject, 'Close project data')
Publisher.subscribe(self.OnHideSurface, 'Hide surface items')

Publisher.subscribe(self.OnShowSingle, 'Show single surface')
Publisher.subscribe(self.OnShowMultiple, 'Show multiple surfaces')

def __bind_events_wx(self):
self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel)
#self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected_)
self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent)
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.on_mouse_right_click)

def on_mouse_right_click(self, event):
start_idx = 1
focused_idx = self.GetFocusedItem()

# Select the surface that was clicked
Publisher.sendMessage('Change surface selected', surface_index=focused_idx)

# Create the context menu and add all the menu items
surface_context_menu = wx.Menu()

colour_id = surface_context_menu.Append(start_idx, _('Change color'))
surface_context_menu.Bind(wx.EVT_MENU, self.change_surface_color, colour_id)

transparency_id = surface_context_menu.Append(start_idx + 1, _('Change transparency'))
surface_context_menu.Bind(wx.EVT_MENU, self.change_transparency, transparency_id)

duplicate_id = surface_context_menu.Append(start_idx + 2, _('Duplicate'))
surface_context_menu.Bind(wx.EVT_MENU, self.duplicate_surface, duplicate_id)

surface_context_menu.AppendSeparator()

delete_id = surface_context_menu.Append(start_idx + 3, _('Delete'))
surface_context_menu.Bind(wx.EVT_MENU, self.delete_surface, delete_id)

self.PopupMenu(surface_context_menu)
surface_context_menu.Destroy()

def update_current_surface_data(self, surface):
self.current_color = [int(255 * c) for c in surface.colour][:3]
self.current_transparency = int(100 * surface.transparency)

def change_surface_color(self, event):
focused_idx = self.GetFocusedItem()
current_color = self.current_color

new_color = dlg.ShowColorDialog(color_current=current_color)

if not new_color:
return

new_vtk_color = [c / 255.0 for c in new_color]

Publisher.sendMessage('Set surface colour',
surface_index=focused_idx,
colour=new_vtk_color)

# Select the edited surface again to update the color in the surface properties GUI
Publisher.sendMessage('Change surface selected', surface_index=focused_idx)

def change_transparency(self, event):
focused_idx = self.GetFocusedItem()
initial_value = self.current_transparency

transparency_dialog = dlg.SurfaceTransparencyDialog(self, surface_index=focused_idx,
transparency=initial_value,)

# Clicking OK keeps the current slider value, otherwise the inital transparency value is used
if transparency_dialog.ShowModal() == wx.ID_OK:
new_value = transparency_dialog.get_value()
else:
new_value = initial_value

transparency_dialog.Destroy()

Publisher.sendMessage('Set surface transparency',
surface_index=focused_idx,
transparency=new_value/100.)

# Select the edited surface again to update the slider in the surface properties GUI
Publisher.sendMessage('Change surface selected', surface_index=focused_idx)


def duplicate_surface(self, event):
selected_items = self.GetSelected()
if selected_items:
Publisher.sendMessage('Duplicate surfaces', surface_indexes=selected_items)
else:
dlg.SurfaceSelectionRequiredForDuplication()

def delete_surface(self, event):
result = dlg.ShowConfirmationDialog(msg=_("Delete surface?"))
if result != wx.ID_OK:
return
self.RemoveSurfaces()

def OnKeyEvent(self, event):
keycode = event.GetKeyCode()
Expand Down Expand Up @@ -800,6 +945,13 @@ def RemoveSurfaces(self):
self.surface_list_index = new_dict

Publisher.sendMessage('Remove surfaces', surface_indexes=selected_items)

# If there are still surfaces in the list, select the surface before the one that was removed
# to update the color in the surface properties UI
current_index = max(selected_items[0] - 1, 0)

if len(self.surface_list_index) > 0:
Publisher.sendMessage('Change surface selected', surface_index=current_index)
else:
dlg.SurfaceSelectionRequiredForRemoval()

Expand Down
66 changes: 60 additions & 6 deletions invesalius/gui/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1074,15 +1074,14 @@ def ReportICPDistributionError():
dlg.ShowModal()
dlg.Destroy()


def ShowEnterMarkerID(default):
msg = _("Change label")
if sys.platform == 'darwin':
dlg = wx.TextEntryDialog(None, "", msg, defaultValue=default)
else:
dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", value=default)
dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", value=default)
dlg.ShowModal()
result = dlg.GetValue()
dlg.Destroy()

return result


Expand Down Expand Up @@ -1786,6 +1785,61 @@ def GetValue(self):
"keep_largest": keep_largest,
"overwrite": False}

class SurfaceTransparencyDialog(wx.Dialog):
def __init__(self, parent, surface_index=0, transparency=0):
super(SurfaceTransparencyDialog, self).__init__(parent)

self.surface_index = surface_index

self.SetTitle("InVesalius 3")
self.SetSize((300, 180))

self.slider = wx.Slider(self,
value=transparency,
minValue=0,
maxValue=100,
style=wx.SL_HORIZONTAL)

self.slider.Bind(wx.EVT_SLIDER, self.on_slider)

# Current value
self.value_text = wx.StaticText(self, label=f"Surface transparency: {self.slider.GetValue()}%")

# Buttons
ok_button = wx.Button(self, wx.ID_OK, label="OK")
ok_button.Bind(wx.EVT_BUTTON, self.on_ok)

cancel_button = wx.Button(self, wx.ID_CANCEL, label="Cancel")

# Layout
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.value_text, 0, wx.ALL | wx.CENTER, 10)
sizer.Add(self.slider, 0, wx.ALL | wx.EXPAND, 10)

button_sizer = wx.BoxSizer(wx.HORIZONTAL)
button_sizer.Add(ok_button, 0, wx.ALL, 5)
button_sizer.Add(cancel_button, 0, wx.ALL, 5)

sizer.Add(button_sizer, 0, wx.ALL | wx.CENTER, 10)

self.SetSizer(sizer)
sizer.Fit(self)
self.Layout()
self.CenterOnScreen()

def on_slider(self, event):
value = self.slider.GetValue()
self.value_text.SetLabel(f"Surface transparency: {value}%")

Publisher.sendMessage('Set surface transparency',
surface_index=self.surface_index,
transparency=value/100.)

def on_ok(self, event):
self.EndModal(wx.ID_OK)

def get_value(self):
return self.slider.GetValue()

class CAOptions(wx.Panel):
'''
Expand Down Expand Up @@ -5616,8 +5670,8 @@ def _init_gui(self, volumes):
main_sizer.Add((5, 5))

self.SetSizer(main_sizer)
main_sizer.Fit(self)
self.Layout()
# main_sizer.Fit(self)
# self.Layout()
self.CenterOnParent()

def GetVolumeChoice(self):
Expand Down
11 changes: 7 additions & 4 deletions invesalius/gui/task_navigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2077,23 +2077,26 @@ def OnMouseRightDown(self, evt):
color_id = menu_id.Append(unique_menu_id + 1, _('Change color')) # Increment the unique_menu_id
menu_id.Bind(wx.EVT_MENU, self.ChangeColor, color_id)

delete_id = menu_id.Append(unique_menu_id + 2, _('Delete'))
menu_id.Bind(wx.EVT_MENU, self.OnDeleteSelectedMarkers, delete_id)

# Allow duplicate only for markers that are not fiducials.
if not is_fiducial:
duplicate_menu_item = menu_id.Append(unique_menu_id + 2, _('Duplicate'))
duplicate_menu_item = menu_id.Append(unique_menu_id + 3, _('Duplicate'))
menu_id.Bind(wx.EVT_MENU, self.OnMenuDuplicateMarker, duplicate_menu_item)

menu_id.AppendSeparator()

# Show 'Set as target'/'Unset target' menu item only if the marker is a coil target.
if is_coil_target:
if is_active_target:
target_menu_item = menu_id.Append(unique_menu_id + 3, _('Unset target'))
target_menu_item = menu_id.Append(unique_menu_id + 4, _('Unset target'))
menu_id.Bind(wx.EVT_MENU, self.OnMenuUnsetTarget, target_menu_item)
if has_mTMS:
brain_target_menu_item= menu_id.Append(unique_menu_id + 3, _('Set brain target'))
brain_target_menu_item= menu_id.Append(unique_menu_id + 4, _('Set brain target'))
menu_id.Bind(wx.EVT_MENU, self.OnSetBrainTarget, brain_target_menu_item)
else:
target_menu_item = menu_id.Append(unique_menu_id + 3, _('Set as target'))
target_menu_item = menu_id.Append(unique_menu_id + 4, _('Set as target'))
menu_id.Bind(wx.EVT_MENU, self.OnMenuSetTarget, target_menu_item)

# Show 'Create coil target' menu item if the marker is a coil pose.
Expand Down
5 changes: 0 additions & 5 deletions invesalius/gui/task_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,11 +605,6 @@ def OnSelectColour(self, evt):

def OnTransparency(self, evt):
transparency = evt.GetInt()/float(MAX_TRANSPARENCY)
# FIXME: In Mac OS/X, wx.Slider (wx.Python 2.8.10) has problem on the
# right-limit as reported on http://trac.wxwidgets.org/ticket/4555.
# This problem is in wx.Widgets and therefore we'll simply overcome it:
if (wx.Platform == "__WXMAC__"):
transparency = evt.GetInt()/(0.96*float(MAX_TRANSPARENCY))
Publisher.sendMessage('Set surface transparency',
surface_index=self.combo_surface_name.GetSelection(),
transparency=transparency)
Expand Down