Commit daf828ec authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'rel/0.8.3' into 'v0.8'

Rel/0.8.3

See merge request fsl/fsleyes/widgets!49
parents eb1c1467 d0ae4435
......@@ -2,6 +2,25 @@ This document contains the ``fsleyes-widgets`` release history in reverse
chronological order.
0.8.3 (Friday October 4th 2019)
-------------------------------
Added
^^^^^
* New ``vgap`` option for the :class:`.EditableListBox`.
* New ``minHeight`` option for the :class:`.WidgetList`.
Changed
^^^^^^^
* Minor GTK3 compatibility fixes.
0.8.2 (Wednesday September 18th 2019)
-------------------------------------
......
......@@ -18,7 +18,7 @@ This file is used to store the current ``fsleyes-widgets`` version.
"""
__version__ = '0.8.2'
__version__ = '0.8.3'
from fsleyes_widgets.utils import (WX_PYTHON, # noqa
......
......@@ -29,20 +29,22 @@ class AutoTextCtrl(wx.Panel):
"""
def __init__(self, parent, style=0, ownloop=True):
def __init__(self, parent, style=0, modal=True):
"""Create an ``AutoTextCtrl``.
:arg parent: The ``wx`` parent object.
:arg style: Can be :data:`ATC_CASE_SENSITIVE` to restrict the
auto-completion options to case sensitive matches.
arg ownloop: See :meth:`AutoCompletePopup.__init__`.
:arg modal: If ``True`` (the default), the :class:`AutoCompletePopup`
is shoown modally. This option is primarily for testing
purposes.
"""
self.__ownloop = ownloop
self.__caseSensitive = style & ATC_CASE_SENSITIVE
wx.Panel.__init__(self, parent)
self.__modal = modal
self.__popup = None
self.__textCtrl = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
......@@ -223,8 +225,7 @@ class AutoTextCtrl(wx.Panel):
self,
text,
self.__options,
style,
self.__ownloop)
style)
if popup.GetCount() == 0:
popup.Destroy()
......@@ -261,7 +262,9 @@ class AutoTextCtrl(wx.Panel):
popup.SetSize((-1, -1))
popup.SetPosition((posx, posy))
popup.Show()
if self.__modal: popup.ShowModal()
else: popup.Show()
ATC_CASE_SENSITIVE = 1
......@@ -286,12 +289,12 @@ Contains a single attribute, ``text``, which contains the text in the
"""
class AutoCompletePopup(wx.Frame):
class AutoCompletePopup(wx.Dialog):
"""The ``AutoCompletePopup`` class is used by the :class:`AutoTextCtrl`
to display a list of completion options to the user.
"""
def __init__(self, parent, atc, text, options, style=0, ownloop=True):
def __init__(self, parent, atc, text, options, style=0):
"""Create an ``AutoCompletePopup``.
:arg parent: The ``wx`` parent object.
......@@ -300,20 +303,12 @@ class AutoCompletePopup(wx.Frame):
:arg options: A list of all possible auto-completion options.
:arg style: Set to :data:`ATC_CASE_SENSITIVE` to make the
pattern matching case sensitive.
:arg ownloop: If ``True`` (the default), a separate event loop
is created and run while the popup is displayed.
This gives the popup modal behaviour.
"""
wx.Frame.__init__(self,
wx.Dialog.__init__(self,
parent,
style=(wx.NO_BORDER |
wx.STAY_ON_TOP |
wx.FRAME_NO_TASKBAR |
wx.FRAME_FLOAT_ON_PARENT))
style=(wx.NO_BORDER | wx.STAY_ON_TOP))
self.__ownloop = None
self.__evloop = None
self.__caseSensitive = style & ATC_CASE_SENSITIVE
self.__atc = atc
self.__options = options
......@@ -367,25 +362,6 @@ class AutoCompletePopup(wx.Frame):
self.__listBox .Bind(wx.EVT_SET_FOCUS, self.__onSetFocus)
def Show(self):
"""Shows this ``AutoCompletePopup``. If the ``ownloop`` parameter
passed to :meth:`__init__` was ``True``, a A new ``wx.GUIEventLoop``
is created and started, until this ``AutoCompletePopup`` loses focus.
"""
parent = self.GetParent().GetTopLevelParent()
parent.Disable()
wx.Frame.Show(self)
if self.__ownloop:
self.__evloop = wx.GUIEventLoop()
self.__evloop.Run()
self.__evloop = None
parent.Enable()
def GetCount(self):
"""Returns the number of auto-completion options currently available.
"""
......@@ -443,6 +419,7 @@ class AutoCompletePopup(wx.Frame):
this ``AutoCompletePopup`` to the owning :class:`AutoTextCtrl`,
and then (asynchronously) destroys this ``AutoCompletePopup``.
"""
value = self.__textCtrl.GetValue()
idx = self.__textCtrl.GetInsertionPoint()
atc = self.__atc
......@@ -471,8 +448,11 @@ class AutoCompletePopup(wx.Frame):
def destroy():
try:
self.Close()
self.Destroy()
if self.IsModal():
self.EndModal(wx.ID_OK)
else:
self.Close()
self.Destroy()
except wx.PyDeadObjectError:
pass
......@@ -481,12 +461,6 @@ class AutoCompletePopup(wx.Frame):
ev.SetEventObject(self)
wx.PostEvent(self, ev)
# Kill the event loop used by
# the popup (see the Show method)
if self.__evloop is not None:
self.__evloop.Exit()
self.__evloop = None
wx.CallAfter(destroy)
......
......@@ -56,6 +56,7 @@ class ColourButton(wx.Button):
self.Bind(wx.EVT_BUTTON, self.__onClick)
self.SetValue(colour)
self.SetMinSize(self.GetBestSize())
def GetValue(self):
......@@ -98,8 +99,7 @@ class ColourButton(wx.Button):
else:
self.__bmp = wx.BitmapFromBufferRGBA( w, h, data)
self.SetBitmapLabel(self.__bmp)
self.SetBitmap(self.__bmp)
def __onClick(self, ev):
......
......@@ -104,7 +104,8 @@ class EditableListBox(wx.Panel):
labels=None,
clientData=None,
tooltips=None,
style=0):
style=0,
vgap=0):
"""Create an ``EditableListBox``.
:arg parent: :mod:`wx` parent object
......@@ -122,6 +123,9 @@ class EditableListBox(wx.Panel):
:data:`ELB_NO_LABEL`, :data:`ELB_WIDGET_RIGHT`,
:data:`ELB_TOOLTIP_DOWN`, and
:data:`ELB_SCROLL_BUTTONS`.
:arg vgap: Vertical gap in pixels between each item. Ignored if
:data:`ELB_NO_LABEL` is set.
"""
wx.Panel.__init__(self, parent, style=wx.WANTS_CHARS)
......@@ -150,6 +154,7 @@ class EditableListBox(wx.Panel):
self.__widgetOnRight = widgetOnRight
self.__tooltipDown = tooltipDown
self.__scrollButtons = scrollButtons
self.__vgap = vgap
if labels is None: labels = []
if clientData is None: clientData = [None] * len(labels)
......@@ -632,9 +637,12 @@ class EditableListBox(wx.Panel):
container.SetSizer(sizer)
sizerItems = [labelWidget]
if self.__noLabels: sizerFlags = [{}]
else: sizerFlags = [{'flag' : wx.ALIGN_CENTRE,
'proportion' : 1}]
if self.__noLabels:
sizerFlags = [{}]
else:
sizerFlags = [{'flag' : wx.ALIGN_CENTRE | wx.BOTTOM,
'border' : self.__vgap,
'proportion' : 1}]
if extraWidget is not None:
extraWidget.Reparent(container)
......
......@@ -10,7 +10,7 @@
import six
import collections
from collections import abc
class TypeDict(object):
......@@ -189,15 +189,15 @@ class TypeDict(object):
if '.' in key: return tuple(key.split('.'))
else: return key
if isinstance(key, collections.Sequence):
if isinstance(key, abc.Sequence):
tKeys = map(self.tokenifyKey, key)
key = []
for tk in tKeys:
if isinstance(tk, six.string_types): key.append(tk)
elif isinstance(tk, collections.Sequence): key += list(tk)
else: key.append(tk)
if isinstance(tk, six.string_types): key.append(tk)
elif isinstance(tk, abc.Sequence): key += list(tk)
else: key.append(tk)
return tuple(key)
......
......@@ -1066,10 +1066,22 @@ class WidgetGrid(wx.ScrolledWindow):
log.debug('Keyboard event ({})'.format(key))
# ignore modified keypresses, and all
# keypresses that are not arrows
if ev.HasModifiers() or (key not in (up, down, left, right)):
ev.Skip()
return
# if up/down, but we can't select rows
if key in (up, down) and self.__selectable == 'columns':
ev.Skip()
return
# if left/right, but we can't select columns
if key in (left, right) and self.__selectable == 'rows':
ev.Skip()
return
row, col = self.__selected
if key == up: row -= 1
......
......@@ -62,11 +62,14 @@ class WidgetList(scrolledpanel.ScrolledPanel):
"""Border and title background colour for widget groups. """
def __init__(self, parent, style=0):
def __init__(self, parent, style=0, minHeight=-1):
"""Create a ``WidgetList``.
:arg parent: The :mod:`wx` parent object.
:arg parent: The :mod:`wx` parent object.
:arg style: Passed through to ``wx.ScrolledPanel.__init__``
:arg minHeight: Minimum height of each row
"""
self.__minHeight = minHeight
self.__widgSizer = wx.BoxSizer(wx.VERTICAL)
self.__sizer = wx.BoxSizer(wx.VERTICAL)
......@@ -353,6 +356,10 @@ class WidgetList(scrolledpanel.ScrolledPanel):
for child in widget.GetChildren():
child.GetWindow().Reparent(widgPanel)
else:
w, h = widget.GetBestSize().Get()
if self.__minHeight > h:
h = self.__minHeight
widget.SetMinSize( (w, h))
widget.Reparent(widgPanel)
label = wx.StaticText(widgPanel,
......
sphinx==1.6.*
sphinx_rtd_theme==0.*
mock==2.*
coverage==4.*
pytest==3.*
pytest-cov==2.*
sphinx
sphinx_rtd_theme
mock
coverage
pytest
pytest-cov
......@@ -60,7 +60,7 @@ class doc(Command):
print('Building documentation [{}]'.format(destdir))
import sphinx
import sphinx.cmd.build as sphinx_build
try:
import unittest.mock as mock
......@@ -81,7 +81,7 @@ class doc(Command):
[mock.patch(c, object) for c in mockedClasses]
[p.start() for p in patches]
sphinx.main(['sphinx-build', docdir, destdir])
sphinx_build.main([docdir, destdir])
[p.stop() for p in patches]
......@@ -91,6 +91,7 @@ setup(
version=version,
description='A collection of wxPython widgets used by FSLeyes',
long_description=readme,
long_description_content_type='text/x-rst',
url='https://git.fmrib.ox.ac.uk/fsl/fsleyes/widgets',
author='Paul McCarthy',
author_email='pauldmccarthy@gmail.com',
......@@ -100,9 +101,9 @@ setup(
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Software Development :: Libraries :: Python Modules'],
packages=packages,
......
......@@ -27,7 +27,7 @@ def test_getSet():
def _test_getSet():
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
atc.ChangeValue('a')
assert atc.GetValue() == 'a'
......@@ -47,7 +47,7 @@ def _test_event():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
atc.Bind(autott.EVT_ATC_TEXT_ENTER, handler)
......@@ -66,7 +66,7 @@ def test_onFocus():
def _test_onFocus():
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
atc.AutoComplete(['aaa', 'aab', 'aba', 'bcc'])
......@@ -89,7 +89,7 @@ def _test_popup_select1():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
addall(parent, [atc])
......@@ -110,7 +110,7 @@ def _test_popup_select2():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
addall(parent, [atc])
......@@ -128,7 +128,7 @@ def _test_popup_select3():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
addall(parent, [atc])
......@@ -149,7 +149,7 @@ def _test_popup_cancel():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
addall(parent, [atc])
......@@ -170,7 +170,7 @@ def _test_popup_focusback():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
addall(parent, [atc])
......@@ -195,7 +195,7 @@ def _test_popup_dblclick():
sim = wx.UIActionSimulator()
parent = wx.GetApp().GetTopWindow()
atc = autott.AutoTextCtrl(parent, ownloop=False)
atc = autott.AutoTextCtrl(parent, modal=False)
atc.AutoComplete(['aaa', 'aab', 'aba', 'bcc'])
......
......@@ -60,7 +60,7 @@ def _test_Bounce():
else:
dlg.Show()
eval()
if endfunc is not 'Destroy':
if endfunc != 'Destroy':
dlg.Destroy()
dlg = None
assert passed[0]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment