Skip to content
Draft
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
Binary file modified examples/application/python_editor/images/python_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/application/python_shell/images/python_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 14 additions & 2 deletions pyface/gui_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class GUIApplication(Application):
#: The about dialog for the application.
about_dialog = Instance(IDialog)

#: Icon for the application (used in window titlebars)
#: Icon for the application (used in window titlebars, docks, etc.)
#: This should be large (eg. 1024x1024 is standard on MacOS).
icon = Image

#: Logo of the application (used in splash screens and about dialogs)
Expand Down Expand Up @@ -166,7 +167,7 @@ def start(self):
if ok:
# create the GUI so that the splash screen comes up first thing
if self.gui is Undefined:
self.gui = GUI(splash_screen=self.splash_screen)
self.gui = GUI(self.splash_screen, self.name, self.icon)

# create the initial windows to show
self._create_windows()
Expand Down Expand Up @@ -301,3 +302,14 @@ def _on_window_closed(self, event):
window = event.object
if window in self.windows:
self.windows.remove(window)

@observe('icon', post_init=True)
def _icon_updated(self, event):
print('--------------------------> here', event, self.gui)
if self.gui and event.new is not None:
self.gui.set_application_icon(event.new)

@observe('name', post_init=True)
def _name_updated(self, event):
if self.gui and event.new:
self.gui.set_application_name(event.new)
19 changes: 18 additions & 1 deletion pyface/i_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


from traits.etsconfig.api import ETSConfig
from traits.api import Bool, HasTraits, Interface, Str
from traits.api import Any, Bool, HasTraits, Interface, Str


# Logging.
Expand All @@ -28,6 +28,9 @@ class IGUI(Interface):

# 'GUI' interface -----------------------------------------------------#

#: The toolkit application object. This is typically read-only.
application = Any()

#: Is the GUI busy (i.e. should the busy cursor, often an hourglass, be
#: displayed)?
busy = Bool(False)
Expand Down Expand Up @@ -158,6 +161,20 @@ def start_event_loop(self):
def stop_event_loop(self):
""" Stop the GUI event loop. """

def set_application_icon(self, image):
""" Set the application icon in the OS.

This controls the icon displayed in system docks and similar locations
within the operating system.
"""

def set_application_name(self, image):
""" Set the application name in the OS.

This controls the name displayed in system docks and similar locations
within the operating system.
"""


class MGUI(HasTraits):
""" The mixin class that contains common code for toolkit specific
Expand Down
52 changes: 52 additions & 0 deletions pyface/tests/test_gui_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from shutil import rmtree
from tempfile import mkdtemp
import unittest
from unittest import mock

from traits.api import Bool, observe

Expand Down Expand Up @@ -148,6 +149,8 @@ def test_defaults(self):
self.assertEqual(app.home, ETSConfig.application_home)
self.assertEqual(app.user_data, ETSConfig.user_data)
self.assertEqual(app.company, ETSConfig.company)
self.assertIsNone(app.icon)
self.assertIsNone(app.logo)

def test_initialize_application_home(self):
dirpath = mkdtemp()
Expand Down Expand Up @@ -293,3 +296,52 @@ def test_bad_stop(self):
event_order = [event.event_type for event in self.application_events]
self.assertEqual(event_order, EVENTS[:-1])
self.assertEqual(app.windows, [])

def test_application_icon(self):
app = GUIApplication(
icon='core',
)
app.gui

with mock.patch('pyface.gui.GUI.set_application_icon') as mock_method:
self.gui.invoke_after(1000, app.exit)
app.run()

mock_method.assert_called_once_with(app.icon)

def test_application_icon_changed(self):
app = GUIApplication()

with mock.patch('pyface.gui.GUI.set_application_icon') as mock_method:
self.gui.invoke_after(1000, app.exit)
app.run()

mock_method.assert_not_called()

with mock.patch('pyface.gui.GUI.set_application_icon') as mock_method:
app.icon = 'core'

mock_method.assert_called_once_with(app.icon)

def test_application_name(self):
app = GUIApplication()

with mock.patch('pyface.gui.GUI.set_application_name') as mock_method:
self.gui.invoke_after(1000, app.exit)
app.run()

mock_method.assert_called_once_with(app.name)

def test_application_name_changed(self):
app = GUIApplication(name="")

with mock.patch('pyface.gui.GUI.set_application_name') as mock_method:
self.gui.invoke_after(1000, app.exit)
app.run()

mock_method.assert_not_called()

with mock.patch('pyface.gui.GUI.set_application_name') as mock_method:
app.name = 'changed name'

mock_method.assert_called_once_with(app.name)
49 changes: 41 additions & 8 deletions pyface/ui/qt4/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@


import logging
import sys

from traits.api import Bool, HasTraits, Property, observe, provides, Str

from pyface.qt import QtCore, QtGui


from traits.api import Bool, HasTraits, observe, provides, Str
from pyface.util.guisupport import start_event_loop_qt4


from pyface.i_gui import IGUI, MGUI
from pyface.util.guisupport import get_app_qt4, start_event_loop_qt4


# Logging.
Expand All @@ -37,6 +34,8 @@ class GUI(MGUI, HasTraits):

# 'GUI' interface -----------------------------------------------------#

application = Property()

busy = Bool(False)

started = Bool(False)
Expand All @@ -47,7 +46,15 @@ class GUI(MGUI, HasTraits):
# 'object' interface.
# ------------------------------------------------------------------------

def __init__(self, splash_screen=None):
def __init__(self, splash_screen=None, name="", icon=None):
# Change the application icon, if any
if icon is not None:
self.set_application_icon(icon)

# Change the application name, if any
if name:
self.set_application_name(name)

# Display the (optional) splash screen.
self._splash_screen = splash_screen

Expand Down Expand Up @@ -109,7 +116,30 @@ def start_event_loop(self):

def stop_event_loop(self):
logger.debug("---------- stopping GUI event loop ----------")
QtGui.QApplication.quit()
self.application.quit()

def set_application_icon(self, image):
""" Set the application icon in the OS.

This controls the icon displayed in system docks and similar locations
within the operating system.
"""
# ensure application exists before doing anything else
app = self.application
app.setWindowIcon(image.create_icon())

def set_application_name(self, name):
""" Set the application name at the toolkit level.

This sets the name displayed for the application in various places
in the OS.

Note
----
This does not change the name of the application in the MacOS menu or
dock.
"""
self.application.setApplicationDisplayName(name)

# ------------------------------------------------------------------------
# Trait handlers.
Expand All @@ -129,6 +159,9 @@ def _update_busy_state(self, event):
else:
QtGui.QApplication.restoreOverrideCursor()

def _get_application(self):
return get_app_qt4(*sys.argv)


class _FutureCall(QtCore.QObject):
""" This is a helper class that is similar to the wx FutureCall class. """
Expand Down
55 changes: 48 additions & 7 deletions pyface/ui/wx/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@
import logging
import sys


import wx

from traits.api import Bool, HasTraits, Property, provides, Str

from traits.api import Bool, HasTraits, provides, Str
from pyface.util.guisupport import start_event_loop_wx


from pyface.util.guisupport import get_app_wx, start_event_loop_wx
from pyface.i_gui import IGUI, MGUI


Expand All @@ -36,6 +33,8 @@ class GUI(MGUI, HasTraits):

# 'GUI' interface -----------------------------------------------------#

application = Property()

busy = Bool(False)

started = Bool(False)
Expand All @@ -46,7 +45,15 @@ class GUI(MGUI, HasTraits):
# 'object' interface.
# ------------------------------------------------------------------------

def __init__(self, splash_screen=None):
def __init__(self, splash_screen=None, name="", icon=None):
# Change the application icon, if any
if icon is not None:
self.set_application_icon(icon)

# Change the application name, if any
if name:
self.set_application_name(name)

# Display the (optional) splash screen.
self._splash_screen = splash_screen

Expand Down Expand Up @@ -120,7 +127,38 @@ def stop_event_loop(self):
""" Stop the GUI event loop. """

logger.debug("---------- stopping GUI event loop ----------")
wx.GetApp().ExitMainLoop()
self.application.ExitMainLoop()

def set_application_icon(self, image):
""" Set the application icon in the OS.

This controls the icon displayed in system docks and similar locations
within the operating system.

Note
----
This does not change the dock icon in MacOS.
"""
# ensure app exists before doing anything else
self.application
icon = image.create_icon()
dock_icon = wx.adv.TaskBarIcon(wx.adv.TBI_DOCK)
dock_icon.SetIcon(icon)

def set_application_name(self, name):
""" Set the application name at the toolkit level.

This sets the name displayed for the application in various places
in the OS.

Note
----
This does not change the name of the application in the MacOS menu or
dock.
"""
# ensure app exists before doing anything else
app = self.application
app.SetAppDisplayName(name)

# ------------------------------------------------------------------------
# Trait handlers.
Expand All @@ -140,3 +178,6 @@ def _busy_changed(self, new):
del self._wx_cursor

return

def _get_application(self):
return get_app_wx()