Skip to content

Commit

Permalink
add a color selection dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
sccolbert committed Jun 18, 2013
1 parent d0fefde commit d722a87
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 0 deletions.
152 changes: 152 additions & 0 deletions enaml/qt/qt_color_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#------------------------------------------------------------------------------
from PyQt4.QtCore import pyqtSignal
from PyQt4.QtGui import QColor, QColorDialog

from atom.api import Int, Typed

from enaml.colors import Color
from enaml.widgets.color_dialog import ProxyColorDialog

from .qt_toolkit_dialog import QtToolkitDialog


def color_from_qcolor(q):
""" Convert a QColor into an Enaml Color.
Parameters
----------
q : QColor
The Qt color to convert to Enaml Color.
Returns
-------
result : Color or None
An Enaml Color or None if the QColor is not valid.
"""
if not q.isValid():
return None
return Color(q.red(), q.green(), q.blue(), q.alpha())


# Guard flags
CURRENT_GUARD = 0x1


class QColorDialogEx(QColorDialog):
""" A custom QColorDialog which emits a custom finished signal.
"""
#: A signal emitted at the end of the 'done' method. This works
#: around the standard QColorDialog behavior which emits the
#: 'colorSelected' signal *after* the 'finished' signal.
reallyFinished = pyqtSignal(int)

def done(self, result):
""" A reimplemented done method.
This method emits the 'reallyFinished' signal on completion.
"""
super(QColorDialogEx, self).done(result)
self.reallyFinished.emit(result)


class QtColorDialog(QtToolkitDialog, ProxyColorDialog):
""" A Qt implementation of an Enaml ProxyColorDialog.
"""
#: A reference to the widget created by the proxy.
widget = Typed(QColorDialogEx)

#: Cyclic notification guard. This a bitfield of multiple guards.
_guard = Int(0)

def create_widget(self):
""" Create the underlying QColorDialog.
"""
self.widget = QColorDialogEx(self.parent_widget())

def init_widget(self):
""" Initialize the underlying widget.
"""
# Do not call super(...) as it connects the standard 'finished'
# signal. This widget uses the custom 'reallyFinished' signal.
d = self.declaration
self.set_title(d.title)
self.set_current_color(d.current_color)
self.set_show_alpha(d.show_alpha)
self.set_show_buttons(d.show_buttons)
widget = self.widget
widget.currentColorChanged.connect(self.on_current_color_changed)
widget.colorSelected.connect(self.on_color_selected)
widget.reallyFinished.connect(self.on_finished)

#--------------------------------------------------------------------------
# Signal Handlers
#--------------------------------------------------------------------------
def on_current_color_changed(self, qcolor):
""" Handle the 'currentColorChanged' signal from the widget.
"""
d = self.declaration
if d is not None:
self._guard |= CURRENT_GUARD
try:
d.current_color = color_from_qcolor(qcolor)
finally:
self._guard &= ~CURRENT_GUARD

def on_color_selected(self, qcolor):
""" Handle the 'colorSelected' signal from the widget.
"""
d = self.declaration
if d is not None:
d.selected_color = color_from_qcolor(qcolor)

#--------------------------------------------------------------------------
# ProxyColorDialog API
#--------------------------------------------------------------------------
def set_current_color(self, color):
""" Set the current color for the underlying widget.
"""
if not self._guard & CURRENT_GUARD:
if color is not None:
qcolor = QColor.fromRgba(color.argb)
else:
qcolor = QColor()
self.widget.setCurrentColor(qcolor)

def set_show_alpha(self, show):
""" Set the show alpha option on the underlying widget.
"""
widget = self.widget
opt = widget.options()
if show:
opt |= QColorDialog.ShowAlphaChannel
else:
opt &= ~QColorDialog.ShowAlphaChannel
widget.setOptions(opt)

def set_show_buttons(self, show):
""" Set the show buttons option on the underlying widget.
"""
widget = self.widget
opt = widget.options()
if show:
opt &= ~QColorDialog.NoButtons
else:
opt |= QColorDialog.NoButtons
widget.setOptions(opt)
6 changes: 6 additions & 0 deletions enaml/qt/qt_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def check_box_factory():
return QtCheckBox


def color_dialog_factory():
from .qt_color_dialog import QtColorDialog
return QtColorDialog


def combo_box_factory():
from .qt_combo_box import QtComboBox
return QtComboBox
Expand Down Expand Up @@ -270,6 +275,7 @@ def window_factory():
'ActionGroup': action_group_factory,
'Calendar': calendar_factory,
'CheckBox': check_box_factory,
'ColorDialog': color_dialog_factory,
'ComboBox': combo_box_factory,
'Container': container_factory,
'DateSelector': date_selector_factory,
Expand Down
97 changes: 97 additions & 0 deletions enaml/widgets/color_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#------------------------------------------------------------------------------
from atom.api import Bool, Typed, ForwardTyped, observe

from enaml.colors import ColorMember
from enaml.core.declarative import d_

from .toolkit_dialog import ToolkitDialog, ProxyToolkitDialog


class ProxyColorDialog(ProxyToolkitDialog):
""" The abstract defintion of a proxy ColorDialog object.
"""
#: A reference to the ColorDialog declaration.
declaration = ForwardTyped(lambda: ColorDialog)

def set_current_color(self, color):
raise NotImplementedError

def set_show_alpha(self, show):
raise NotImplementedError

def set_show_buttons(self, show):
raise NotImplementedError


class ColorDialog(ToolkitDialog):
""" A toolkit dialog that allows the user to select a color.
"""
#: The currently selected color of the dialog.
current_color = d_(ColorMember('white'))

#: Whether or not to show the alpha value control.
show_alpha = d_(Bool(True))

#: Whether or not to show the dialog ok/cancel buttons.
show_buttons = d_(Bool(True))

#: The color selected when the user clicks accepts the dialog.
#: This value is output only.
selected_color = ColorMember()

#: A reference to the ProxyColorDialog object.
proxy = Typed(ProxyColorDialog)

@staticmethod
def get_color(parent=None, **kwargs):
""" A static method which launches a color dialog.
Parameters
----------
parent : ToolkitObject or None
The parent toolkit object for this dialog.
**kwargs
Additional data to pass to the dialog constructor.
Returns
-------
result : Color or None
The selected color or None if no color was selected.
"""
dialog = ColorDialog(parent, **kwargs)
dialog.exec_()
if dialog.result:
return dialog.selected_color

#--------------------------------------------------------------------------
# Observers
#--------------------------------------------------------------------------
@observe(('current_color', 'show_alpha', 'show_buttons'))
def _update_proxy(self, change):
""" An observer which updates the proxy when the data changes.
"""
# The superclass implementation is sufficient.
super(ColorDialog, self)._update_proxy(change)

#--------------------------------------------------------------------------
# Utility Methods
#--------------------------------------------------------------------------
def _prepare(self):
""" A reimplemented preparation method.
This method resets the selected color to None.
"""
super(ColorDialog, self)._prepare()
self.selected_color = None

0 comments on commit d722a87

Please sign in to comment.