Skip to content

Commit

Permalink
Merge pull request #79 from nucleic/feature-std-dialog
Browse files Browse the repository at this point in the history
Feature std dialog
  • Loading branch information
sccolbert committed Nov 7, 2013
2 parents 0aadf41 + b744473 commit 5583808
Show file tree
Hide file tree
Showing 12 changed files with 905 additions and 8 deletions.
7 changes: 6 additions & 1 deletion enaml/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ class Image(Atom):
'pgm', # Portable Graymap
'ppm', # Portable Pixmap
'tiff', # Tagged Image File Format
# 'array', # A numpy array with an appropriate image dtype.
'argb32', # Raw data in the 0xAARRGGBB format.
# The `raw_size` of the image must be provided.
)

#: The (width, height) raw size of the image. This must be provided
#: for images where the size is not encoded in the data stream.
raw_size = Coerced(Size, (0, 0))

#: The (width, height) size of the image. An invalid size indicates
#: that the size of the image should be automatically inferred. A
#: valid size indicates that the toolkit image should be scaled to
Expand Down
10 changes: 7 additions & 3 deletions enaml/qt/q_resource_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ def QImage_from_Image(image):
"""
format = image.format
if format == 'auto':
format = ''
qimage = QImage.fromData(image.data, format)
if format == 'argb32':
w, h = image.raw_size
qimage = QImage(image.data, w, h, QImage.Format_ARGB32)
else:
if format == 'auto':
format = ''
qimage = QImage.fromData(image.data, format)
if -1 not in image.size and not qimage.isNull():
qsize = QSize(*image.size)
if qsize != qimage.size():
Expand Down
8 changes: 6 additions & 2 deletions enaml/qt/qt_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,12 @@ def _update_sizes(self):
"""
widget = self.widget
widget.setSizeHint(self.compute_best_size())
widget.setMinimumSize(self.compute_min_size())
widget.setMaximumSize(self.compute_max_size())
if not isinstance(widget.parent(), QContainer):
# Only set min and max size if the parent is not a container.
# The layout manager needs to be the ultimate authority when
# dealing with nested containers.
widget.setMinimumSize(self.compute_min_size())
widget.setMaximumSize(self.compute_max_size())

def _build_refresher(self, manager):
""" Build the refresh function for the container.
Expand Down
18 changes: 18 additions & 0 deletions enaml/qt/qt_push_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ def create_widget(self):
"""
self.widget = QPushButton(self.parent_widget())

def init_widget(self):
""" Initialize the state of the widget.
"""
super(QtPushButton, self).init_widget()
self.set_default(self.declaration.default)

def init_layout(self):
""" Handle layout initialization for the push button.
Expand Down Expand Up @@ -72,3 +79,14 @@ def child_removed(self, child):
super(QtPushButton, self).child_removed(child)
if isinstance(child, QtMenu):
self.widget.setMenu(self.menu())

#--------------------------------------------------------------------------
# ProxyPushButton API
#--------------------------------------------------------------------------
def set_default(self, default):
""" Set the default button behavior for the widget.
"""
self.widget.setDefault(default)
if default:
self.widget.setFocus()
192 changes: 192 additions & 0 deletions enaml/src/winutil.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*-----------------------------------------------------------------------------
| 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.
|----------------------------------------------------------------------------*/
// Get access to the icon defines in Winuser.h
#ifndef OEMRESOURCE
#define OEMRESOURCE
#endif
#include <windows.h>
#include "pythonhelpersex.h"


using namespace PythonHelpers;


// Builtin Icons
static PyObject* Py_OIC_SAMPLE;
static PyObject* Py_OIC_HAND;
static PyObject* Py_OIC_QUES;
static PyObject* Py_OIC_BANG;
static PyObject* Py_OIC_NOTE;
static PyObject* Py_OIC_WINLOGO;
static PyObject* Py_OIC_WARNING;
static PyObject* Py_OIC_ERROR;
static PyObject* Py_OIC_INFORMATION;
#if(WINVER >= 0x0600)
static PyObject* Py_OIC_SHIELD;
#endif


typedef struct {
PyObject_HEAD;
UINT value;
} WinEnum;


static PyTypeObject
WinEnum_Type = {
PyObject_HEAD_INIT( 0 )
0, /* ob_size */
"winutil.WinEnum", /* tp_name */
sizeof( WinEnum ), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)PyObject_Del, /* tp_dealloc */
(printfunc)0, /* tp_print */
(getattrfunc)0, /* tp_getattr */
(setattrfunc)0, /* tp_setattr */
(cmpfunc)0, /* tp_compare */
(reprfunc)0, /* tp_repr */
(PyNumberMethods*)0, /* tp_as_number */
(PySequenceMethods*)0, /* tp_as_sequence */
(PyMappingMethods*)0, /* tp_as_mapping */
(hashfunc)0, /* tp_hash */
(ternaryfunc)0, /* tp_call */
(reprfunc)0, /* tp_str */
(getattrofunc)0, /* tp_getattro */
(setattrofunc)0, /* tp_setattro */
(PyBufferProcs*)0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* Documentation string */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)0, /* tp_iter */
(iternextfunc)0, /* tp_iternext */
(struct PyMethodDef*)0, /* tp_methods */
(struct PyMemberDef*)0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)0, /* tp_init */
(allocfunc)PyType_GenericAlloc, /* tp_alloc */
(newfunc)0, /* tp_new */
(freefunc)0, /* tp_free */
(inquiry)0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
(destructor)0 /* tp_del */
};


static PyObject*
PyString_FromHICON( HICON icon, int& width_out, int& height_out )
{
HDC screen_device = GetDC( 0 );
HDC hdc = CreateCompatibleDC( screen_device );
ReleaseDC( 0, screen_device );

ICONINFO icon_info;
GetIconInfo( icon, &icon_info );
int w = icon_info.xHotspot * 2;
int h = icon_info.yHotspot * 2;

BITMAPINFO bmi;
memset( &bmi, 0, sizeof( bmi ) );
bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h; // flip the origin to top-left
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
VOID* bits;

HBITMAP win_bitmap = CreateDIBSection( hdc, &bmi, DIB_RGB_COLORS, &bits, 0, 0 );
HGDIOBJ old_hdc = ( HBITMAP )SelectObject( hdc, win_bitmap );
DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_NORMAL );

PyObject* result = PyString_FromStringAndSize( ( const char* )bits, w * h * 4 );

// dispose resources created by GetIconInfo
DeleteObject( icon_info.hbmMask );
DeleteObject( icon_info.hbmColor );

SelectObject( hdc, old_hdc ); // restore state
DeleteObject( win_bitmap );
DeleteDC( hdc );

width_out = w;
height_out = h;
return result;
}


static PyObject*
load_icon( PyObject* mod, PyObject* args )
{
WinEnum* win_enum;
if( !PyArg_ParseTuple( args, "O!", &WinEnum_Type, &win_enum ) )
return 0;
HANDLE hicon = LoadImage(
0, MAKEINTRESOURCE( win_enum->value ), IMAGE_ICON, 0, 0, LR_SHARED
);
if( !hicon )
return Py_BuildValue( "(s, (i, i))", "", -1, -1 );
int width, height;
PyObjectPtr result( PyString_FromHICON( ( HICON )hicon, width, height ) );
if( !result )
return 0;
return Py_BuildValue( "(O, (i, i))", result.get(), width, height );
}


static PyMethodDef
winutil_methods[] = {
{ "load_icon", ( PyCFunction )load_icon, METH_VARARGS,
"Load a builtin Windows icon" },
{ 0 } // Sentinel
};


#define MAKE_ENUM( TOKEN, VALUE ) \
do { \
TOKEN = PyType_GenericNew( &WinEnum_Type, 0, 0 ); \
if( !TOKEN ) \
return; \
reinterpret_cast<WinEnum*>( TOKEN )->value = VALUE; \
if( PyModule_AddObject( mod, #VALUE, newref( TOKEN ) ) < 0 ) \
return; \
} while( 0 )


PyMODINIT_FUNC
initwinutil( void )
{
PyObject* mod = Py_InitModule( "winutil", winutil_methods );
if( !mod )
return;
if( PyType_Ready( &WinEnum_Type ) )
return;
MAKE_ENUM( Py_OIC_SAMPLE, OIC_SAMPLE );
MAKE_ENUM( Py_OIC_HAND, OIC_HAND );
MAKE_ENUM( Py_OIC_QUES, OIC_QUES );
MAKE_ENUM( Py_OIC_BANG, OIC_BANG );
MAKE_ENUM( Py_OIC_NOTE, OIC_NOTE );
MAKE_ENUM( Py_OIC_WINLOGO, OIC_WINLOGO );
MAKE_ENUM( Py_OIC_WARNING, OIC_WARNING );
MAKE_ENUM( Py_OIC_ERROR, OIC_ERROR );
MAKE_ENUM( Py_OIC_INFORMATION, OIC_INFORMATION );
#if(WINVER >= 0x0600)
MAKE_ENUM( Py_OIC_SHIELD, OIC_SHIELD );
#endif
}
95 changes: 95 additions & 0 deletions enaml/stdlib/dialog_buttons.enaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#------------------------------------------------------------------------------
# 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 Atom, Bool, Enum, Unicode
from enaml.core.looper import Looper
from enaml.layout.layout_helpers import hbox
from enaml.widgets.container import Container
from enaml.widgets.push_button import PushButton


class DialogButton(Atom):
""" A class for specifying a button in a button box.

Instances of this class are created by users to specify the buttons
which will be shown in a DialogButtonBox.

"""
#: The text for the button.
text = Unicode()

#: The dialog action to perform when the button is clicked.
action = Enum('accept', 'reject')

#: Whether or not the button is the default button for the dialog.
default = Bool(False)

#: Whether or not the button is enabled. The button will subscribe
#: to this attribute, and reflect the changes at runtime.
enabled = Bool(True)

#: Whether or not the button was clicked by the user.
was_clicked = Bool(False)

def __init__(self, text, action, **kwargs):
""" Initialize a DialogButton.

Parameters
----------
text : unicode
The unicode label for the button.

action : 'accept' or 'reject'
The dialog action to perform when the button is clicked.

**kwargs
Additional optional state to apply to the button.

"""
super(DialogButton, self).__init__(text=text, action=action, **kwargs)


enamldef DialogButtonBox(Container): box:
""" A component for defining a button box for a dialog.

The dialog button box must be used as a decendant of a Dialog, and
relies on dynamic scoping to invoke the dialog action when a button
is clicked. The button widgets created by the dialog can be styled
using the style class 'dialog-box-button'.

Attributes
----------
buttons : list
A list of DialogButton objects which represent the buttons to
create for the dialog box. This value should be set before the
widget is shown. Dynamic changes will not update the UI.

Events
------
clicked
This event will be emitted when a button is clicked, but before
the dialog action is taken. The payload will be the DialogButton
instance for the button which was clicked.

"""
attr buttons: list = []
event clicked: DialogButton
padding = 0
hug_width = 'strong'
hug_height = 'strong'
constraints = [hbox(*sum(looper.items, []), spacing=6)]
Looper: looper:
iterable = buttons
PushButton:
style_class = 'dialog-box-button'
text = loop_item.text
default = loop_item.default
enabled << loop_item.enabled
clicked ::
loop_item.was_clicked = True
box.clicked(loop_item)
nonlocals[loop_item.action]()
Loading

0 comments on commit 5583808

Please sign in to comment.