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

Argos compatibility fixes for GTK3 and wxPython 4.1.0 #211

Merged
5 commits merged into from
Oct 7, 2020
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
27 changes: 25 additions & 2 deletions helios/pipeViewer/pipe_view/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -41,7 +42,29 @@

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

# Check for user-specified 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:
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']
Expand All @@ -55,7 +78,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

Expand Down
6 changes: 5 additions & 1 deletion helios/pipeViewer/pipe_view/core/src/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
6 changes: 3 additions & 3 deletions helios/pipeViewer/pipe_view/gui/argos_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
2 changes: 1 addition & 1 deletion helios/pipeViewer/pipe_view/gui/font_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
113 changes: 56 additions & 57 deletions helios/pipeViewer/pipe_view/gui/hover_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,67 +33,57 @@ 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.__handler.DestroyWindow()


def __init__(self, canvas, context):
Expand All @@ -106,7 +96,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']

Expand Down Expand Up @@ -170,12 +160,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):
'''
Expand Down Expand Up @@ -409,7 +397,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)
Expand All @@ -428,8 +415,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):
'''
Expand Down Expand Up @@ -486,6 +478,13 @@ def __SetElement(self, pair):
self.annotation = repr(e)
self.SetValue(self.annotation)

def DestroyWindow(self):
if self.__window:
self.__window.Disable()
self.__window.Show(False)
self.__window.DestroyLater()
self.__window = None


class HoverPreviewOptionsDialog(wx.Dialog):
'''
Expand Down
Loading