From caa76a5db41bff43f57142031691ccf950e092f7 Mon Sep 17 00:00:00 2001 From: Brett Dutro Date: Fri, 2 Oct 2020 18:59:28 -0500 Subject: [PATCH 1/5] Fixes for GTK3 --- helios/pipeViewer/pipe_view/core/setup.py | 19 ++- helios/pipeViewer/pipe_view/gui/font_utils.py | 2 +- .../pipeViewer/pipe_view/gui/hover_preview.py | 113 +++++++++--------- .../gui/widgets/frame_playback_bar.py | 88 ++++++++------ .../pipe_view/model/schedule_element.py | 42 +++++-- 5 files changed, 160 insertions(+), 104 deletions(-) diff --git a/helios/pipeViewer/pipe_view/core/setup.py b/helios/pipeViewer/pipe_view/core/setup.py index 34e7b4c373..dc2d987e6a 100755 --- a/helios/pipeViewer/pipe_view/core/setup.py +++ b/helios/pipeViewer/pipe_view/core/setup.py @@ -6,6 +6,7 @@ import os.path import glob import distutils +import shutil # Force use of a different Cython cython_dir = os.environ.get('CYTHON_DIR', None) @@ -41,7 +42,21 @@ py_src_dir = Path(__file__).parent.resolve() / 'src' -flags = subprocess.check_output(['wx-config', '--cppflags']).decode('utf-8') +# Support for systems with GTK3 and GTK2 installed side-by-side + +# Try GTK3-specific utility first +wx_config = shutil.which('wx-config-gtk3') + +# Try generic wx-config next +if wx_config is None: + wx_config = shutil.which('wx-config') + +# Couldn't find wx-config - cannot continue +if wx_config is None: + print('wx-config must be installed and present in your PATH in order to build Argos') + sys.exit(1) + +flags = subprocess.check_output([wx_config, '--cppflags']).decode('utf-8') flags = flags.split() wx_inc_dirs = [flg[2:] for flg in flags if flg.startswith('-I')] wx_defines = [tuple(flg[2:].split('=')) for flg in flags if flg[:2] == '-D'] @@ -55,7 +70,7 @@ def ensure_2_tuple(t): wx_defines = list(map(ensure_2_tuple, wx_defines)) -flags = subprocess.check_output(['wx-config', '--libs']).decode('utf-8') +flags = subprocess.check_output([wx_config, '--libs']).decode('utf-8') flags = flags.split() wx_link_args = flags diff --git a/helios/pipeViewer/pipe_view/gui/font_utils.py b/helios/pipeViewer/pipe_view/gui/font_utils.py index 1e0959719a..e5a62580e6 100644 --- a/helios/pipeViewer/pipe_view/gui/font_utils.py +++ b/helios/pipeViewer/pipe_view/gui/font_utils.py @@ -3,7 +3,7 @@ def ScaleFont(font_size): # Assume default DPI is 72 DEFAULT_DPI = 72 - dpi = wx.ScreenDC().GetPPI()[1] # Point size determines font height, so look at the vertical DPI + dpi = wx.GetDisplayPPI()[1] # Point size determines font height, so look at the vertical DPI return int(round((font_size * DEFAULT_DPI) / dpi)) # Rounding up seems to give better results def GetMonospaceFont(size): diff --git a/helios/pipeViewer/pipe_view/gui/hover_preview.py b/helios/pipeViewer/pipe_view/gui/hover_preview.py index 159af14ff1..0b6fb8b343 100644 --- a/helios/pipeViewer/pipe_view/gui/hover_preview.py +++ b/helios/pipeViewer/pipe_view/gui/hover_preview.py @@ -33,67 +33,59 @@ class HoverPreview: LINE_LENGTH = 50 # The hover will be rendered in a separate window so we don't have to deal with manually repainting the layout canvas - class HoverPreviewWindow(wx.Window): + class HoverPreviewWindow(wx.PopupWindow): def __init__(self, canvas, handler): - super(self.__class__, self).__init__(canvas.GetParent(), - style=wx.BORDER_NONE | wx.WS_EX_TRANSIENT | wx.STAY_ON_TOP) + super(self.__class__, self).__init__(canvas.GetParent()) self.Show(False) self.__canvas = canvas self.__handler = handler - self.__element = None - self.__annotation = None - self.SetFont(GetMonospaceFont(11)) - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - self.SetForegroundColour(wx.BLACK) - self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouse) + sizer = wx.BoxSizer(wx.HORIZONTAL) + panel_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.__panel = wx.Panel(self) + self.__panel.SetFont(GetMonospaceFont(11)) + self.__panel.SetForegroundColour(wx.BLACK) + self.__text_ctrl = wx.StaticText(self.__panel) + panel_sizer.Add(self.__text_ctrl) + self.__panel.SetSizer(panel_sizer) + sizer.Add(self.__panel, + flag=wx.TOP | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER, + border=1) + self.SetSizer(sizer) def UpdateInfo(self, element, annotation, text, position): - self.__element = element - self.__annotation = annotation - self.__text = text - self.__position = position - self.Refresh() - - def AcceptsFocus(self): - return False - - # Handle the corner case where the user manages to mouse over the window - def OnMouse(self, event): - self.Show(False) - mousePos = wx.GetMousePosition() - self.__handler.HandleMouseMove(mousePos, self.__canvas) - event.Skip() - - def OnPaint(self, event): - BORDER_LIGHTNESS = 0.7 - - dc = wx.AutoBufferedPaintDC(self) - _, brush = self.__canvas.UpdateTransactionColor(self.__element, self.__annotation) - dc.SetBrush(brush) - dc.SetPen(wx.Pen((int(brush.Colour[0] * BORDER_LIGHTNESS), - int(brush.Colour[1] * BORDER_LIGHTNESS), - int(brush.Colour[2] * BORDER_LIGHTNESS)), 1)) # Darker border around brush - br = dc.GetMultiLineTextExtent(self.__text) - tr = self.__position - rect_mop = [tr[0], tr[1], br[0] + 1, br[1] + 1] + BORDER_LIGHTNESS = 70 + + _, brush = self.__canvas.UpdateTransactionColor(element, annotation) + color = brush.GetColour() + self.__panel.SetBackgroundColour(color) + self.SetBackgroundColour(color.ChangeLightness(BORDER_LIGHTNESS)) + self.__text_ctrl.SetLabel(text) + width, height = self.GetBestSize() visible_area = self.__canvas.GetVisibleArea() - box_right = rect_mop[0] + rect_mop[2] - box_bottom = rect_mop[1] + rect_mop[3] + x, y = position + box_right = x + width + box_bottom = y + height if box_right > visible_area[2]: # off the right edge # shift left - rect_mop[0] -= (box_right - visible_area[2]) + x -= (box_right - visible_area[2]) if box_bottom > visible_area[3]: - rect_mop[1] -= (box_bottom - visible_area[3]) + y -= (box_bottom - visible_area[3]) - if rect_mop[0] < 0 or rect_mop[1] < 0: + if x < 0 or y < 0: return # screen too small - rect_mop = wx.Rect(rect_mop) - self.SetRect(rect_mop) - rect_mop.SetPosition((0, 0)) - dc.DrawRectangle(*rect_mop) - dc.DrawLabel(self.__text, rect = rect_mop) + x, y = self.__canvas.ClientToScreen((x, y)) + self.SetRect((x, y, width, height)) + + def AcceptsFocus(self): + return False + + # Handle the corner case where the user manages to mouse over the window + def OnMouse(self, event): + self.Disable() + self.Show(False) + self.__handler.DestroyWindow() def __init__(self, canvas, context): @@ -106,7 +98,7 @@ def __init__(self, canvas, context): self.show = False self.position = (0, 0) self.__last_move_tick = None - self.__window = HoverPreview.HoverPreviewWindow(self.__canvas, self) + self.__window = None self.__fields = ['annotation'] @@ -170,12 +162,10 @@ def GetText(self): ''' return self.__value - def Redraw(self): - ''' - Send a hover redraw event to the canvas to visually synch the data - ''' - # issue a limited redraw event - self.__window.Refresh() + def GetWindow(self): + if not self.__window: + self.__window = HoverPreview.HoverPreviewWindow(self.__canvas, self) + return self.__window def HandleMenuClick(self, position): ''' @@ -409,7 +399,6 @@ def HandleMouseMove(self, position, canvas, redraw = True): hover needs to be enabled and mode needs to not be edit ''' # On a different tick for the same element, force an upate - self.__window.Show(False) force_update = self.__context.GetHC() != self.__last_move_tick self.__last_move_tick = self.__context.GetHC() (x, y) = canvas.CalcUnscrolledPosition(position) @@ -428,8 +417,13 @@ def HandleMouseMove(self, position, canvas, redraw = True): is_dirty = old_show_state or self.show if is_dirty: - self.__window.UpdateInfo(self.element, self.annotation, self.__value, self.position) - self.__window.Show(bool(self.__value)) + self.GetWindow().UpdateInfo(self.element, self.annotation, self.__value, self.position) + if self.__value: + self.GetWindow().Show(True) + else: + self.DestroyWindow() + elif not self.show: + self.DestroyWindow() def __SetElement(self, pair): ''' @@ -486,6 +480,11 @@ def __SetElement(self, pair): self.annotation = repr(e) self.SetValue(self.annotation) + def DestroyWindow(self): + if self.__window: + self.__window.Destroy() + self.__window = None + class HoverPreviewOptionsDialog(wx.Dialog): ''' diff --git a/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py b/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py index 611e597d80..b2932f7557 100644 --- a/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py +++ b/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py @@ -150,11 +150,11 @@ def __init__(self, frame, layout): self.__txt_goto = wx.TextCtrl(self, wx.ID_ANY, size = (60, -1), value = '+10') self.__txt_goto.SetToolTip('Enter an absolute or relative cycle number (in the current clock domain) ' \ - 'to jump to. Decimal, octal (0NNN), hex (0xNNN), or binary (0bNNN) ' \ - 'literals are all acceptable inputs. Prefixing the number with a + or ' \ - '- sign will result in this value being interpreted as a relative value ' \ - 'from the current cycle. Press Enter or click "jump" to jump to the ' \ - 'specified cycle') + 'to jump to. Decimal, octal (0NNN), hex (0xNNN), or binary (0bNNN) ' \ + 'literals are all acceptable inputs. Prefixing the number with a + or ' \ + '- sign will result in this value being interpreted as a relative value ' \ + 'from the current cycle. Press Enter or click "jump" to jump to the ' \ + 'specified cycle') self.__btn_goto = ShyButton(self, wx.ID_ANY, 'jump', style = wx.BU_EXACTFIT) self.__btn_goto.SetToolTip('Jump to the absolute or relative cycle specified in the jump text control') @@ -216,39 +216,59 @@ def __init__(self, frame, layout): row1 = wx.BoxSizer(wx.HORIZONTAL) row1.Add(self.__hl_start, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2) - row1.Add(self.__time_slider, 1, wx.ALIGN_CENTER_VERTICAL) + row1.Add(self.__time_slider, 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) row1.Add(self.__hl_end, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 2) - row2 = wx.BoxSizer(wx.HORIZONTAL) - row2.Add(self.__drop_clock, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add((3, 1), 0, wx.EXPAND) - row2.Add(curticks, 0, wx.EXPAND) - row2.Add((1, 1), 1, wx.EXPAND) # Space sink - row2.Add(self.__btn_rw_hold, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_back30, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_back10, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_back3, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_back1, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_forward1, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_forward3, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_forward10, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_forward30, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_ff_hold, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add((1, 1), 1, wx.EXPAND) # Space sink - row2.Add(wx.StaticLine(self, wx.ID_ANY, style = wx.VERTICAL), 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) - row2.Add((1, 1), 1, wx.EXPAND) # Space sink - row2.Add(self.__txt_goto, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__btn_goto, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add((1, 1), 1, wx.EXPAND) # Space sink - row2.Add(wx.StaticLine(self, wx.ID_ANY, style = wx.VERTICAL), 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) - row2.Add((1, 1), 1, wx.EXPAND) # Space sink - row2.Add(self.__btn_playpause, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(self.__static_playback_speed_units, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2) - row2.Add(self.__spin_playback_speed, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 1) + row2 = wx.FlexGridSizer(cols=6) + for i in range(5): + row2.AddGrowableCol(i) + row2.AddGrowableRow(0) + + clock_sizer = wx.FlexGridSizer(2) + clock_sizer.AddGrowableRow(0) + clock_sizer.Add(self.__drop_clock, 0, wx.ALIGN_CENTER_VERTICAL | wx.SHAPED) + clock_sizer.Add(curticks, 0, wx.EXPAND | wx.LEFT, 3) + row2.Add(clock_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + + nav_sizer = wx.FlexGridSizer(10) + nav_sizer.AddGrowableRow(0) + nav_sizer.Add(self.__btn_rw_hold, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_back30, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_back10, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_back3, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_back1, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_forward1, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_forward3, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_forward10, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_forward30, 0, wx.ALIGN_CENTER_VERTICAL) + nav_sizer.Add(self.__btn_ff_hold, 0, wx.ALIGN_CENTER_VERTICAL) + row2.Add(nav_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + + line_sizer1 = wx.BoxSizer(wx.HORIZONTAL) + line_sizer1.Add(wx.StaticLine(self, wx.ID_ANY, style = wx.VERTICAL), 0, wx.SHAPED | wx.ALIGN_CENTER_VERTICAL) + row2.Add(line_sizer1, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL) + + goto_sizer = wx.FlexGridSizer(2) + goto_sizer.AddGrowableRow(0) + goto_sizer.Add(self.__txt_goto, 0, wx.ALIGN_CENTER_VERTICAL) + goto_sizer.Add(self.__btn_goto, 0, wx.ALIGN_CENTER_VERTICAL) + row2.Add(goto_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + + line_sizer2 = wx.BoxSizer(wx.HORIZONTAL) + line_sizer2.Add(wx.StaticLine(self, wx.ID_ANY, style = wx.VERTICAL), 0, wx.SHAPED | wx.ALIGN_CENTER_VERTICAL) + row2.Add(line_sizer2, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL) + + playback_sizer = wx.FlexGridSizer(2) + playback_sizer.AddGrowableRow(0) + playback_sizer.Add(self.__btn_playpause, 0, wx.ALIGN_CENTER_VERTICAL) + spinner_sizer = wx.BoxSizer(wx.HORIZONTAL) + spinner_sizer.Add(self.__static_playback_speed_units, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2) + spinner_sizer.Add(self.__spin_playback_speed, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 1) + playback_sizer.Add(spinner_sizer, 0, wx.ALIGN_CENTER_VERTICAL) + row2.Add(playback_sizer, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) rows = wx.BoxSizer(wx.VERTICAL) - rows.Add((1, 2), 0, wx.EXPAND) - rows.Add(row2, 0, wx.EXPAND) + rows.Add(row2, 0, wx.EXPAND | wx.TOP, 2) rows.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 4) rows.Add(row1, 0, wx.EXPAND) diff --git a/helios/pipeViewer/pipe_view/model/schedule_element.py b/helios/pipeViewer/pipe_view/model/schedule_element.py index f576786172..62a2d7d57e 100644 --- a/helios/pipeViewer/pipe_view/model/schedule_element.py +++ b/helios/pipeViewer/pipe_view/model/schedule_element.py @@ -291,11 +291,18 @@ def DrawRoutine(self, # val = val.decode('utf-8') # integer when phantom elements refer to other phantom elements NOT_MISSING_LOC = False short_format = self.GetProperty('short_format') - canvas.GetRenderer().drawInfoRectangle(interval[0], self, dc, canvas, rect, val, NOT_MISSING_LOC, - content_type, - auto_color, - clip, - schedule_settings = (local_period_width, renderer_flags), short_format = short_format) + canvas.GetRenderer().drawInfoRectangle(interval[0], + self, + dc, + canvas, + rect, + val, + NOT_MISSING_LOC, + content_type, + auto_color, + clip, + schedule_settings = (local_period_width, renderer_flags), + short_format = short_format) dc.DestroyClippingRegion() self.UnsetNeedsRedraw() @@ -443,10 +450,17 @@ def DrawRoutine(self, rect = (c_x + start, c_y, width + 1, c_h) val = 'C=1 %i' % (current_time / period) NOT_MISSING_LOC = False - canvas.GetRenderer().drawInfoRectangle(tick, self, dc, canvas, rect, val, NOT_MISSING_LOC, - 'caption', ('', ''), - clip, - schedule_settings = (full_interval, self.DRAW_RULER)) + canvas.GetRenderer().drawInfoRectangle(tick, + self, + dc, + canvas, + rect, + val, + NOT_MISSING_LOC, + 'caption', + ('', ''), + clip, + schedule_settings = (full_interval, self.DRAW_RULER)) current_time += full_interval dc.DestroyClippingRegion() @@ -470,6 +484,7 @@ def __init__(self, *args, **kwargs): MultiElement.__init__(self, *args, **kwargs) self.__buffer = None self.__dc = wx.MemoryDC() # store our own DC + self.__temp_dc = wx.MemoryDC() self.__graphics_dc = None self.__old_dimensions = None @@ -549,7 +564,13 @@ def SetProperty(self, key, val): def __ReinitializeBuffer(self, canvas, width, height): self.__buffer = wx.Bitmap(canvas.MAX_ZOOM * width, canvas.MAX_ZOOM * height) + self.__temp_buffer = wx.Bitmap(canvas.MAX_ZOOM * width, canvas.MAX_ZOOM * height) + self.__SwapBuffers(canvas) + + def __SwapBuffers(self, canvas): + self.__buffer, self.__temp_buffer = self.__temp_buffer, self.__buffer self.__dc.SelectObject(self.__buffer) + self.__temp_dc.SelectObject(self.__temp_buffer) self.__graphics_dc = wx.GCDC(self.__dc) self.__graphics_dc.SetFont(self.__dc.GetFont()) self.__graphics_dc.SetLogicalScale(canvas.MAX_ZOOM, canvas.MAX_ZOOM) @@ -689,7 +710,8 @@ def DrawRoutine(self, pair, dc, canvas, tick): sub_width = self.__buffer.GetWidth() - dest_x self.__graphics_dc.SetLogicalScale(1, 1) - self.__dc.Blit(dest_x, 0, sub_width, self.__buffer.GetHeight(), self.__dc, sub_x, 0) + self.__temp_dc.Blit(dest_x, 0, sub_width, self.__buffer.GetHeight(), self.__dc, sub_x, 0) + self.__SwapBuffers(canvas) self.__graphics_dc.SetLogicalScale(canvas.MAX_ZOOM, canvas.MAX_ZOOM) # --Render Loop-- From f6b79d8deed767b56f6e96e2705182ab4d3ec793 Mon Sep 17 00:00:00 2001 From: Brett Dutro Date: Mon, 5 Oct 2020 17:04:55 -0500 Subject: [PATCH 2/5] Fixes for wxPython 4.1.0/wxWidgets 3.1 --- helios/pipeViewer/pipe_view/core/setup.py | 8 +++- .../pipeViewer/pipe_view/core/src/helpers.h | 6 ++- helios/pipeViewer/pipe_view/gui/argos_menu.py | 6 +-- .../gui/widgets/frame_playback_bar.py | 39 ++++++++++++------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/helios/pipeViewer/pipe_view/core/setup.py b/helios/pipeViewer/pipe_view/core/setup.py index dc2d987e6a..d3c354c4c0 100755 --- a/helios/pipeViewer/pipe_view/core/setup.py +++ b/helios/pipeViewer/pipe_view/core/setup.py @@ -44,8 +44,12 @@ # Support for systems with GTK3 and GTK2 installed side-by-side -# Try GTK3-specific utility first -wx_config = shutil.which('wx-config-gtk3') +# Check for user-specified wx-config +wx_config = os.environ.get('WX_CONFIG') + +# Try GTK3-specific utility next +if wx_config is None: + wx_config = shutil.which('wx-config-gtk3') # Try generic wx-config next if wx_config is None: diff --git a/helios/pipeViewer/pipe_view/core/src/helpers.h b/helios/pipeViewer/pipe_view/core/src/helpers.h index dc7265f92d..dce1284780 100644 --- a/helios/pipeViewer/pipe_view/core/src/helpers.h +++ b/helios/pipeViewer/pipe_view/core/src/helpers.h @@ -60,7 +60,11 @@ inline wxBrush* getBrush_wrapped(PyObject* brush) { inline void getTextExtent(wxGCDC* dc, long* char_width, long* char_height) { static const wxString M("m"); - dc->GetTextExtent(M, char_width, char_height); + wxCoord width = 0; + wxCoord height = 0; + dc->GetTextExtent(M, &width, &height); + *char_width = width; + *char_height = height; } #endif // #ifndef __HELPERS_H__ diff --git a/helios/pipeViewer/pipe_view/gui/argos_menu.py b/helios/pipeViewer/pipe_view/gui/argos_menu.py index 15a2cc13c6..2938c42e64 100644 --- a/helios/pipeViewer/pipe_view/gui/argos_menu.py +++ b/helios/pipeViewer/pipe_view/gui/argos_menu.py @@ -1009,13 +1009,13 @@ def __UpdateUndoRedoItems(self): editmode = self.__parent.GetCanvas().GetInputDecoder().GetEditMode() undos = self.__selection.NumUndos() redos = self.__selection.NumRedos() - self.menuUndo.SetText(UNDO_FMT.format(self.__selection.GetNextUndoDesc(), undos)) + self.menuUndo.SetItemLabel(UNDO_FMT.format(self.__selection.GetNextUndoDesc(), undos)) undo_enabled = editmode and (undos != 0) self.menuUndo.Enable(undo_enabled) self.__edit_toolbar.EnableTool(self.toolbarUndo.GetId(), undo_enabled) - self.menuRedo.SetText(REDO_FMT.format(self.__selection.GetNextRedoDesc(), redos)) + self.menuRedo.SetItemLabel(REDO_FMT.format(self.__selection.GetNextRedoDesc(), redos)) redo_enabled = editmode and (redos != 0) self.menuRedo.Enable(redo_enabled) self.__edit_toolbar.EnableTool(self.toolbarRedo.GetId(), redo_enabled) - self.menuRedoAll.SetText(REDO_ALL_FMT.format(redos)) + self.menuRedoAll.SetItemLabel(REDO_ALL_FMT.format(redos)) self.menuRedoAll.Enable(editmode and (redos != 0)) diff --git a/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py b/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py index b2932f7557..6ccd752341 100644 --- a/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py +++ b/helios/pipeViewer/pipe_view/gui/widgets/frame_playback_bar.py @@ -125,26 +125,27 @@ def __init__(self, frame, layout): self.__static_curtick.SetForegroundColour(self.__med_blue) self.__static_curtick.SetFont(self.__fnt_tiny) - self.__btn_rw_hold = ShyButton(self, wx.ID_ANY, '<<', style = wx.BU_EXACTFIT) + PLAYBACK_BUTTON_WIDTH = 40 + self.__btn_rw_hold = ShyButton(self, wx.ID_ANY, '<<', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_rw_hold.SetToolTip('Rewinds while left-mouse is held. Using space or enter to activate ' \ 'this control also rewinds but rate is dictated by keyboard repeat rate') - self.__btn_back30 = ShyButton(self, wx.ID_ANY, '-30', style = wx.BU_EXACTFIT) + self.__btn_back30 = ShyButton(self, wx.ID_ANY, '-30', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_back30.SetToolTip('Steps back 30 cycle in the current clock domain') - self.__btn_back10 = ShyButton(self, wx.ID_ANY, '-10', style = wx.BU_EXACTFIT) + self.__btn_back10 = ShyButton(self, wx.ID_ANY, '-10', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_back10.SetToolTip('Steps back 10 cycle in the current clock domain') - self.__btn_back3 = ShyButton(self, wx.ID_ANY, '-3', style = wx.BU_EXACTFIT) + self.__btn_back3 = ShyButton(self, wx.ID_ANY, '-3', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_back3.SetToolTip('Steps back 3 cycle in the current clock domain') - self.__btn_back1 = ShyButton(self, wx.ID_ANY, '-1', style = wx.BU_EXACTFIT) + self.__btn_back1 = ShyButton(self, wx.ID_ANY, '-1', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_back1.SetToolTip('Steps back 1 cycle in the current clock domain') - self.__btn_forward1 = ShyButton(self, wx.ID_ANY, '+1', style = wx.BU_EXACTFIT) + self.__btn_forward1 = ShyButton(self, wx.ID_ANY, '+1', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_forward1.SetToolTip('Steps forward 1 cycle in the current clock domain') - self.__btn_forward3 = ShyButton(self, wx.ID_ANY, '+3', style = wx.BU_EXACTFIT) + self.__btn_forward3 = ShyButton(self, wx.ID_ANY, '+3', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_forward3.SetToolTip('Steps forward 3 cycle in the current clock domain') - self.__btn_forward10 = ShyButton(self, wx.ID_ANY, '+10', style = wx.BU_EXACTFIT) + self.__btn_forward10 = ShyButton(self, wx.ID_ANY, '+10', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_forward10.SetToolTip('Steps forward 10 cycle in the current clock domain') - self.__btn_forward30 = ShyButton(self, wx.ID_ANY, '+30', style = wx.BU_EXACTFIT) + self.__btn_forward30 = ShyButton(self, wx.ID_ANY, '+30', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_forward30.SetToolTip('Steps forward 30 cycle in the current clock domain') - self.__btn_ff_hold = ShyButton(self, wx.ID_ANY, '>>', style = wx.BU_EXACTFIT) + self.__btn_ff_hold = ShyButton(self, wx.ID_ANY, '>>', size = (PLAYBACK_BUTTON_WIDTH, -1), style = wx.BU_EXACTFIT) self.__btn_ff_hold.SetToolTip('Fast-Forwards while left-mouse is held. Using space or enter to activate ' \ 'this control also fast-forwards but rate is dictated by keyboard repeat rate') @@ -155,7 +156,7 @@ def __init__(self, frame, layout): '- sign will result in this value being interpreted as a relative value ' \ 'from the current cycle. Press Enter or click "jump" to jump to the ' \ 'specified cycle') - self.__btn_goto = ShyButton(self, wx.ID_ANY, 'jump', style = wx.BU_EXACTFIT) + self.__btn_goto = ShyButton(self, wx.ID_ANY, 'jump', size = (60, -1), style = wx.BU_EXACTFIT) self.__btn_goto.SetToolTip('Jump to the absolute or relative cycle specified in the jump text control') self.__static_playback_speed_units = wx.StaticText(self, wx.ID_ANY, 'cyc/\nsec') @@ -215,9 +216,11 @@ def __init__(self, frame, layout): curticks.Add(self.__static_curtick, 1, wx.EXPAND) row1 = wx.BoxSizer(wx.HORIZONTAL) - row1.Add(self.__hl_start, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2) - row1.Add(self.__time_slider, 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) - row1.Add(self.__hl_end, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 2) + row1.Add(self.__hl_start, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 2) + slider_sizer = wx.BoxSizer(wx.HORIZONTAL) + slider_sizer.Add(self.__time_slider, 1, wx.ALIGN_CENTER_VERTICAL) + row1.Add(slider_sizer, 1, wx.EXPAND) + row1.Add(self.__hl_end, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 2) row2 = wx.FlexGridSizer(cols=6) for i in range(5): @@ -265,7 +268,13 @@ def __init__(self, frame, layout): spinner_sizer.Add(self.__static_playback_speed_units, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2) spinner_sizer.Add(self.__spin_playback_speed, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 1) playback_sizer.Add(spinner_sizer, 0, wx.ALIGN_CENTER_VERTICAL) - row2.Add(playback_sizer, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + row2.Add(playback_sizer, 0, wx.ALIGN_RIGHT | wx.EXPAND) + + playback_controls = [c for c in self.GetChildren() if not isinstance(c, wx.StaticText) and row2.GetItem(c, recursive=True) is not None] + min_playback_height = max(c.GetBestSize().GetHeight() for c in playback_controls) + for c in playback_controls: + orig_width, _ = c.GetMinSize() + c.SetMinSize((orig_width, min_playback_height)) rows = wx.BoxSizer(wx.VERTICAL) rows.Add(row2, 0, wx.EXPAND | wx.TOP, 2) From 303b08fd8cd7495a32a7025306a586aff755f0a3 Mon Sep 17 00:00:00 2001 From: Brett Dutro Date: Mon, 5 Oct 2020 17:42:03 -0500 Subject: [PATCH 3/5] Fix crash on OSX when destroying hover preview --- helios/pipeViewer/pipe_view/gui/hover_preview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helios/pipeViewer/pipe_view/gui/hover_preview.py b/helios/pipeViewer/pipe_view/gui/hover_preview.py index 0b6fb8b343..5977ef7773 100644 --- a/helios/pipeViewer/pipe_view/gui/hover_preview.py +++ b/helios/pipeViewer/pipe_view/gui/hover_preview.py @@ -83,8 +83,6 @@ def AcceptsFocus(self): # Handle the corner case where the user manages to mouse over the window def OnMouse(self, event): - self.Disable() - self.Show(False) self.__handler.DestroyWindow() @@ -482,7 +480,9 @@ def __SetElement(self, pair): def DestroyWindow(self): if self.__window: - self.__window.Destroy() + self.__window.Disable() + self.__window.Show(False) + self.__window.DestroyLater() self.__window = None From 20f6625f60dd1c79802c7eef7d1c320ad1f93d2f Mon Sep 17 00:00:00 2001 From: Brett Dutro Date: Mon, 5 Oct 2020 17:52:39 -0500 Subject: [PATCH 4/5] Get full canonical path to WX_CONFIG value when building Argos --- helios/pipeViewer/pipe_view/core/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helios/pipeViewer/pipe_view/core/setup.py b/helios/pipeViewer/pipe_view/core/setup.py index d3c354c4c0..a0bf69153a 100755 --- a/helios/pipeViewer/pipe_view/core/setup.py +++ b/helios/pipeViewer/pipe_view/core/setup.py @@ -45,7 +45,7 @@ # Support for systems with GTK3 and GTK2 installed side-by-side # Check for user-specified wx-config -wx_config = os.environ.get('WX_CONFIG') +wx_config = os.path.realpath(os.environ.get('WX_CONFIG')) # Try GTK3-specific utility next if wx_config is None: From ce53bfebba13a5739d43a2091dd9af79811fe546 Mon Sep 17 00:00:00 2001 From: Brett Dutro Date: Mon, 5 Oct 2020 17:58:53 -0500 Subject: [PATCH 5/5] Fix build failure if WX_CONFIG environment variable is not set --- helios/pipeViewer/pipe_view/core/setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/helios/pipeViewer/pipe_view/core/setup.py b/helios/pipeViewer/pipe_view/core/setup.py index a0bf69153a..9f6aa6ba1a 100755 --- a/helios/pipeViewer/pipe_view/core/setup.py +++ b/helios/pipeViewer/pipe_view/core/setup.py @@ -45,7 +45,11 @@ # Support for systems with GTK3 and GTK2 installed side-by-side # Check for user-specified wx-config -wx_config = os.path.realpath(os.environ.get('WX_CONFIG')) +wx_config = os.environ.get('WX_CONFIG') + +# Get canonical path +if wx_config is not None: + wx_config = os.path.realpath(wx_config) # Try GTK3-specific utility next if wx_config is None: