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

Font dialog #1060

Merged
merged 16 commits into from
Jan 26, 2022
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
9 changes: 9 additions & 0 deletions docs/source/standard_dialogs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ Typical usage for a save dialog looks like this::
print(f"Save to {dialog.path}")


Font Dialog
===========

A dialog that takes a |Font| object and displays a standard OS font selection.
A convenience function |get_font| opens the dialog and returns a font value.


Message Dialog
==============

Expand Down Expand Up @@ -195,6 +202,7 @@ user's selection or ``None`` if nothing is selected.
.. |CANCEL| replace:: :py:obj:`~pyface.constants.CANCEL`
.. |Color| replace:: :py:class:`~pyface.color.Color`
.. |Executor| replace:: :py:class:`~concurrent.futures.Executor`
.. |Font| replace:: :py:class:`~pyface.font.Font`
.. |IDialog| replace:: :py:class:`~pyface.i_dialog.IDialog`
.. |NO| replace:: :py:obj:`~pyface.constants.NO`
.. |OK| replace:: :py:obj:`~pyface.constants.OK`
Expand All @@ -203,6 +211,7 @@ user's selection or ``None`` if nothing is selected.
.. |confirm| replace:: :py:func:`~pyface.confirmation_dialog.confirm`
.. |error| replace:: :py:func:`~pyface.message_dialog.error`
.. |get_color| replace:: :py:func:`~pyface.color_dialog.get_color`
.. |get_font| replace:: :py:func:`~pyface.font_dialog.get_font`
.. |information| replace:: :py:func:`~pyface.message_dialog.information`
.. |open| replace:: :py:meth:`~pyface.i_dialog.IDialog.open`
.. |warning| replace:: :py:func:`~pyface.message_dialog.warning`
Expand Down
16 changes: 11 additions & 5 deletions etstool.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@

supported_combinations = {
"3.6": {"pyqt5", "pyside2", "pyside6", "wx"},
"3.8": {"pyside6"},
}

# Traits version requirement (empty string to mean no specific requirement).
Expand Down Expand Up @@ -211,11 +212,16 @@ def install(edm, runtime, toolkit, environment, editable, source):
]
)
elif toolkit == "pyside6":
commands.extend(
[
"{edm} run -e {environment} -- pip install pyside6",
"{edm} run -e {environment} -- pip install pillow",
]
if sys.platform == 'darwin':
commands.append(
"{edm} run -e {environment} -- pip install pyside6<6.2.2'"
)
else:
commands.append(
"{edm} run -e {environment} -- pip install pyside6"
)
commands.append(
"{edm} run -e {environment} -- pip install pillow"
)
elif toolkit == "wx":
if sys.platform == "darwin":
Expand Down
16 changes: 16 additions & 0 deletions examples/dialogs/font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

from pyface.font_dialog import get_font

# display a font dialog and get the result
font = get_font(None, font='12 pt Helvetica sans-serif')
if font is not None:
print(font)
41 changes: 41 additions & 0 deletions pyface/font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The implementation of a dialog that allows the user to select a font.
"""

from .constant import OK
from .toolkit import toolkit_object


FontDialog = toolkit_object("font_dialog:FontDialog")


def get_font(parent, font):
flongford marked this conversation as resolved.
Show resolved Hide resolved
""" Convenience function that displays a font dialog.

Parameters
----------
parent : toolkit control
The parent toolkit control for the modal dialog.
font : Font or font description
The initial Font object or string describing the font.

Returns
-------
font : Font or None
The selected font, or None if the user made no selection.
"""
dialog = FontDialog(parent=parent, font=font)
result = dialog.open()
if result == OK:
return dialog.font
else:
return None
24 changes: 24 additions & 0 deletions pyface/i_font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The interface for a dialog that allows the user to select a color. """

from pyface.ui_traits import PyfaceFont
from pyface.i_dialog import IDialog


class IFontDialog(IDialog):
""" The interface for a dialog that allows the user to choose a font.
"""

# 'IFontDialog' interface ---------------------------------------------#

#: The font in the dialog.
font = PyfaceFont()
87 changes: 87 additions & 0 deletions pyface/tests/test_font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!


import unittest

from ..font import Font
from ..font_dialog import FontDialog, get_font
from ..toolkit import toolkit_object
from ..util.font_parser import simple_parser

GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant")
no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented"

ModalDialogTester = toolkit_object(
"util.modal_dialog_tester:ModalDialogTester"
)
no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented"


@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant")
class TestFontDialog(unittest.TestCase, GuiTestAssistant):

def setUp(self):
GuiTestAssistant.setUp(self)
self.dialog = FontDialog(font="12 Helvetica sans-serif")

def tearDown(self):
if self.dialog.control is not None:
with self.delete_widget(self.dialog.control):
self.dialog.destroy()
del self.dialog
GuiTestAssistant.tearDown(self)

def test_font(self):
# test that fonts are translated as expected
self.dialog.font = "10 bold condensed Helvetica sans-serif"

self.assertFontEqual(
self.dialog.font,
Font(**simple_parser("10 bold condensed Helvetica sans-serif")),
)

def test_create(self):
# test that creation and destruction works as expected
with self.event_loop():
self.dialog._create()
with self.event_loop():
self.dialog.destroy()

def test_destroy(self):
# test that destroy works even when no control
with self.event_loop():
self.dialog.destroy()

def test_close(self):
# test that close works
with self.event_loop():
self.dialog._create()
with self.event_loop():
self.dialog.close()

def assertFontEqual(self, font1, font2):
state1 = font1.trait_get(transient=lambda x: not x)
state2 = font2.trait_get(transient=lambda x: not x)
self.assertEqual(state1, state2)


@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant")
class TestGetFont(unittest.TestCase, GuiTestAssistant):

@unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable")
def test_close(self):
# test that cancel works as expected
tester = ModalDialogTester(
lambda: get_font(None, "12 Helvetica sans-serif")
)
tester.open_and_run(when_opened=lambda x: x.close(accept=False))

self.assertEqual(tester.result, None)
58 changes: 58 additions & 0 deletions pyface/ui/qt4/font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" A dialog that allows the user to select a font. """

from pyface.qt import QtGui

from traits.api import provides

from pyface.font import Font
from pyface.ui_traits import PyfaceFont
from pyface.i_font_dialog import IFontDialog
from .dialog import Dialog


@provides(IFontDialog)
class FontDialog(Dialog):
""" A dialog that allows the user to choose a font.
"""

# 'IFontDialog' interface ----------------------------------------------

#: The font in the dialog.
font = PyfaceFont()

# ------------------------------------------------------------------------
# 'IDialog' interface.
# ------------------------------------------------------------------------

def _create_contents(self, parent):
# In PyQt this is a canned dialog so there are no contents.
pass

# ------------------------------------------------------------------------
# 'IWindow' interface.
# ------------------------------------------------------------------------

def close(self):
if self.control.result() == QtGui.QDialog.Accepted:
qfont = self.control.selectedFont()
self.font = Font.from_toolkit(qfont)
return super(FontDialog, self).close()

# ------------------------------------------------------------------------
# 'IWindow' interface.
# ------------------------------------------------------------------------

def _create_control(self, parent):
qfont = self.font.to_toolkit()
dialog = QtGui.QFontDialog(qfont, parent)
return dialog
63 changes: 63 additions & 0 deletions pyface/ui/wx/font_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" The interface for a dialog that allows the user to select a font. """

import wx

from traits.api import provides

from pyface.font import Font
from pyface.ui_traits import PyfaceFont
from pyface.i_font_dialog import IFontDialog
from .dialog import Dialog

# The WxPython version in a convenient to compare form.
wx_version = tuple(int(x) for x in wx.__version__.split('.')[:3])


@provides(IFontDialog)
class FontDialog(Dialog):
""" A dialog for selecting fonts.
"""

# 'IFontDialog' interface ----------------------------------------------

#: The font in the dialog.
font = PyfaceFont()

# ------------------------------------------------------------------------
# 'IDialog' interface.
# ------------------------------------------------------------------------

def _create_contents(self, parent):
# In wx this is a canned dialog.
pass

# ------------------------------------------------------------------------
# 'IWindow' interface.
# ------------------------------------------------------------------------

def close(self):
font_data = self.control.GetFontData()
wx_font = font_data.GetChosenFont()
self.font = Font.from_toolkit(wx_font)
super(FontDialog, self).close()

# ------------------------------------------------------------------------
# 'IWidget' interface.
# ------------------------------------------------------------------------

def _create_control(self, parent):
wx_font = self.font.to_toolkit()
data = wx.FontData()
data.SetInitialFont(wx_font)
dialog = wx.FontDialog(parent, data)
return dialog