From 193c31b3122023ccd74a2f2f63bff3dc99697531 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Fri, 24 May 2013 18:25:53 -0400 Subject: [PATCH 1/9] start work on user-stylable buttons --- button_test.py | 84 +++++++++++++++++++++++++++++ enaml/qt/docking/button_bitmaps.py | 85 ++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 button_test.py create mode 100644 enaml/qt/docking/button_bitmaps.py diff --git a/button_test.py b/button_test.py new file mode 100644 index 000000000..3bdc5ac60 --- /dev/null +++ b/button_test.py @@ -0,0 +1,84 @@ + +from enaml.qt.docking.button_bitmaps import restore_bitmap + +from PyQt4.QtCore import QSize, QAbstractFileEngineHandler, Qt +from PyQt4.QtGui import ( + QApplication, QAbstractButton, QFrame, QVBoxLayout, QPainter, + QStyleOption, QStyle, QColor +) + +class MyButton(QAbstractButton): + + def sizeHint(self): + return QSize(20, 20) + + def paintEvent(self, event): + p = QPainter(self) + opt = QStyleOption() + opt.initFrom(self) + # opt.state |= QStyle.State_AutoRaise + # if (self.isEnabled() and self.underMouse() and not + # self.isChecked() and not self.isDown()): + # opt.state |= QStyle.State_Raised + # if self.isChecked(): + # opt.state |= QStyle.State_On + if self.isDown(): + opt.state |= QStyle.State_Sunken + + # if (const QTabBar *tb = qobject_cast(parent())) { + # int index = tb->currentIndex(); + # QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb); + # if (tb->tabButton(index, position) == this) + # opt.state |= QStyle::State_Selected; + # } + + self.style().drawPrimitive(QStyle.PE_Widget, opt, p, self) + #self.style().drawPrimitive(QStyle.PE_IndicatorTabClose, opt, p, self) + #print self.palette().foreground().color().blue() + #c = self.style().styleHint( + # QStyle.SH_GroupBox_TextLabelColor, opt, self, None + #) + #c2 = QColor.fromRgba(0xffffffff & c) # stupid signed int + #bmp = restore_bitmap() + #p.setBackgroundMode(Qt.OpaqueMode) + #p.setBackground(c2) + #p.setPen(c2) + #p.drawPixmap(0, 0, bmp) + + +class MyHandler(QAbstractFileEngineHandler): + + def create(self, filename): + print filename + + +app = QApplication([]) +h = MyHandler() + +w = QFrame() +b = MyButton() +l = QVBoxLayout() +l.addWidget(b) +w.setLayout(l) + +w.setStyleSheet(""" + QFrame { + background: rgb(20, 20, 200); + } + MyButton { + background: yellow; + border: 1px solid red; + min-width: 40px; + max-width: 40px; + color: orange; + } + MyButton:hover { + border: 1px solid white; + } + MyButton:pressed { + background: red; + } + + """) +w.show() +app.exec_() diff --git a/enaml/qt/docking/button_bitmaps.py b/enaml/qt/docking/button_bitmaps.py new file mode 100644 index 000000000..fbfcd2ee4 --- /dev/null +++ b/enaml/qt/docking/button_bitmaps.py @@ -0,0 +1,85 @@ +#------------------------------------------------------------------------------ +# 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.QtGui import QBitmap, QImage + + +def _make_bitmap(data): + height = len(data) + width = len(data[0]) + img = QImage(width, height, QImage.Format_MonoLSB) + for y in xrange(height): + row = data[y] + for x in xrange(width): + idx = 0 if row[x] else 1 + img.setPixel(x, y, idx) + return QBitmap.fromImage(img) + + +_close_data = [ + [1, 1, 0, 0, 0, 0, 1, 1], + [0, 1, 1, 0, 0, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0], + [0, 1, 1, 0, 0, 1, 1, 0], + [1, 1, 0, 0, 0, 0, 1, 1], +] + + +_close_bmp = None + + +def close_bitmap(): + global _close_bmp + if _close_bmp is None: + _close_bmp = _make_bitmap(_close_data) + return _close_bmp + + +_maximize_data = [ + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1], +] + + +_maximize_bmp = None + + +def maximize_bitmap(): + global _maximize_bmp + if _maximize_bmp is None: + _maximize_bmp = _make_bitmap(_maximize_data) + return _maximize_bmp + + +_restore_data = [ + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 0], +] + + +_restore_bmp = None + + +def restore_bitmap(): + global _restore_bmp + if _restore_bmp is None: + _restore_bmp = _make_bitmap(_restore_data) + return _restore_bmp From 5fd3af51509da0b09a8baa55f686e4bcdf529146 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 16:59:29 -0400 Subject: [PATCH 2/9] update xbm handling --- enaml/qt/docking/button_bitmaps.py | 85 -------------------------- enaml/qt/docking/xbms.py | 96 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 85 deletions(-) delete mode 100644 enaml/qt/docking/button_bitmaps.py create mode 100644 enaml/qt/docking/xbms.py diff --git a/enaml/qt/docking/button_bitmaps.py b/enaml/qt/docking/button_bitmaps.py deleted file mode 100644 index fbfcd2ee4..000000000 --- a/enaml/qt/docking/button_bitmaps.py +++ /dev/null @@ -1,85 +0,0 @@ -#------------------------------------------------------------------------------ -# 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.QtGui import QBitmap, QImage - - -def _make_bitmap(data): - height = len(data) - width = len(data[0]) - img = QImage(width, height, QImage.Format_MonoLSB) - for y in xrange(height): - row = data[y] - for x in xrange(width): - idx = 0 if row[x] else 1 - img.setPixel(x, y, idx) - return QBitmap.fromImage(img) - - -_close_data = [ - [1, 1, 0, 0, 0, 0, 1, 1], - [0, 1, 1, 0, 0, 1, 1, 0], - [0, 0, 1, 1, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 1, 1, 0, 0], - [0, 1, 1, 0, 0, 1, 1, 0], - [1, 1, 0, 0, 0, 0, 1, 1], -] - - -_close_bmp = None - - -def close_bitmap(): - global _close_bmp - if _close_bmp is None: - _close_bmp = _make_bitmap(_close_data) - return _close_bmp - - -_maximize_data = [ - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 0, 0, 0, 0, 0, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1], - [1, 1, 1, 1, 1, 1, 1, 1], -] - - -_maximize_bmp = None - - -def maximize_bitmap(): - global _maximize_bmp - if _maximize_bmp is None: - _maximize_bmp = _make_bitmap(_maximize_data) - return _maximize_bmp - - -_restore_data = [ - [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 0, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 0, 0], -] - - -_restore_bmp = None - - -def restore_bitmap(): - global _restore_bmp - if _restore_bmp is None: - _restore_bmp = _make_bitmap(_restore_data) - return _restore_bmp diff --git a/enaml/qt/docking/xbms.py b/enaml/qt/docking/xbms.py new file mode 100644 index 000000000..7c06196b3 --- /dev/null +++ b/enaml/qt/docking/xbms.py @@ -0,0 +1,96 @@ +#------------------------------------------------------------------------------ +# 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, Int, Str + + +class XBM(Atom): + """ A simple class representing an XMB image. + + """ + #: The width of the xbm image. + width = Int() + + #: The height of the xbm image. + height = Int() + + #: The bytestring of image data. + data = Str() + + def __init__(self, width, height, data): + """ Initialize an XBM image. + + Parameters + ---------- + width : int + The width of the bitmap. + + height : int + The height of the bitmap. + + data : list + A list of 1s and 0s which represent the bitmap data. + The length must be equal to width * height. + + """ + assert len(data) == (width * height) + bytes = [] + for row in xrange(height): + val = 0 + offset = row * width + for col in xrange(width): + d = col % 8 + if col > 0 and d == 0: + bytes.append(chr(val)) + val = 0 + v = data[offset + col] + val |= v << (7 - d) + bytes.append(chr(val)) + self.width = width + self.height = height + self.data = ''.join(bytes) + + def toBitmap(self): + from PyQt4.QtCore import QSize + from PyQt4.QtGui import QBitmap, QImage + size = QSize(self.width, self.height) + return QBitmap.fromData(size, self.data, QImage.Format_Mono) + + +CLOSE_BUTTON = XBM(8, 7, [ + 1, 1, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 1, 1, +]) + + +MAXIMIZE_BUTTON = XBM(8, 7, [ + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +]) + + +RESTORE_BUTTON = XBM(10, 9, [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, +]) From c3255d9fc81c22d15b2ad920168905868a1ff171 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:00:07 -0400 Subject: [PATCH 3/9] replace icon button with bitmap button The bitmap button is more flexible since it can be styled via qt style sheets. --- enaml/qt/docking/q_bitmap_button.py | 129 ++++++++++++++++++++++++++++ enaml/qt/docking/q_icon_button.py | 60 ------------- 2 files changed, 129 insertions(+), 60 deletions(-) create mode 100644 enaml/qt/docking/q_bitmap_button.py delete mode 100644 enaml/qt/docking/q_icon_button.py diff --git a/enaml/qt/docking/q_bitmap_button.py b/enaml/qt/docking/q_bitmap_button.py new file mode 100644 index 000000000..155421dc9 --- /dev/null +++ b/enaml/qt/docking/q_bitmap_button.py @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------ +# 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 QPoint, QRect +from PyQt4.QtGui import QAbstractButton, QColor, QPainter, QStyle, QStyleOption + + +class QBitmapButton(QAbstractButton): + """ A button widget which renders a bitmap. + + This class is used to render the various maximize, restore, and + close buttons in the docking framework. Bitmap images are chosen + for rendering so that the button can be fully styled using Qt + style sheets. + + """ + _bitmap = None + + def bitmap(self): + """ Get the bitmap associated with the button. + + """ + return self._bitmap + + def setBitmap(self, bitmap): + """ Set the bitmap associate with the button. + + """ + self._bitmap = bitmap + self.update() + + def sizeHint(self): + """ Get the size hint for the bitmap button. + + The size hint of the button is equal to it's icon size. + + """ + return self.minimumSizeHint() + + def minimumSizeHint(self): + """ Get the minimum size hint for the bitmap button. + + The minimum size hint of the button is equal to it's icon size. + + """ + return self.iconSize() + + def enterEvent(self, event): + """ Handle the enter event for the button. + + """ + if self.isEnabled(): + self.update() + super(QBitmapButton, self).enterEvent(event) + + def leaveEvent(self, event): + """ Handle the leave event for the button. + + """ + if self.isEnabled(): + self.update() + super(QBitmapButton, self).leaveEvent(event) + + def styleOption(self): + """ Get a filled style option for the button. + + Returns + ------- + result : QStyleOption + A style option initialized for the current button state. + + """ + opt = QStyleOption() + opt.initFrom(self) + opt.state |= QStyle.State_AutoRaise + is_down = self.isDown() + is_enabled = self.isEnabled() + is_checked = self.isChecked() + under_mouse = self.underMouse() + if is_enabled and under_mouse and not is_checked and not is_down: + opt.state |= QStyle.State_Raised + if is_checked: + opt.state |= QStyle.State_On + if is_down: + opt.state |= QStyle.State_Sunken + return opt + + def drawBitmap(self, opt, painter): + """ Draw the bitmap for the button. + + The bitmap will be drawn with the foreground color set by + the style sheet and the style option. + + Parameters + ---------- + opt : QStyleOption + The style option to use for drawing. + + painter : QPainter + The painter to use for drawing. + + """ + bmp = self._bitmap + if bmp is not None: + # hack to get the current stylesheet foreground color + hint = QStyle.SH_GroupBox_TextLabelColor + fg = self.style().styleHint(hint, opt, self) + # mask signed to unsigned which 'fromRgba' requires + painter.setPen(QColor.fromRgba(0xffffffff & fg)) + size = self.size() + im_size = bmp.size() + x = size.width() / 2 - im_size.width() / 2 + y = size.height() / 2 - im_size.height() / 2 + source = QRect(QPoint(0, 0), im_size) + dest = QRect(QPoint(x, y), im_size) + painter.drawPixmap(dest, bmp, source) + + def paintEvent(self, event): + """ Handle the paint event for the button. + + """ + painter = QPainter(self) + opt = self.styleOption() + self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) + self.drawBitmap(opt, painter) diff --git a/enaml/qt/docking/q_icon_button.py b/enaml/qt/docking/q_icon_button.py deleted file mode 100644 index 2893fc459..000000000 --- a/enaml/qt/docking/q_icon_button.py +++ /dev/null @@ -1,60 +0,0 @@ -#------------------------------------------------------------------------------ -# 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. -#------------------------------------------------------------------------------ -import sys - -from PyQt4.QtCore import QSize -from PyQt4.QtGui import QAbstractButton, QPainter, QIcon - - -class QIconButton(QAbstractButton): - """ A button widget which renders tightly to its icon. - - """ - def sizeHint(self): - """ Get the size hint for the icon button. - - """ - return self.minimumSizeHint() - - def minimumSizeHint(self): - """ Get the minimum size hint for the icon button. - - """ - left, top, right, bottom = self.getContentsMargins() - return self.iconSize() + QSize(left + right, top + bottom) - - def enterEvent(self, event): - """ Handle the enter event for the button. - - """ - super(QIconButton, self).enterEvent(event) - if sys.platform == 'darwin': - self.repaint() - - def leaveEvent(self, event): - """ Handle the leave event for the button. - - """ - super(QIconButton, self).leaveEvent(event) - if sys.platform == 'darwin': - self.repaint() - - def paintEvent(self, event): - """ Handle the paint event for the button. - - """ - icon = self.icon() - if icon.isNull(): - return - if self.isDown(): - mode = QIcon.Selected - elif self.underMouse(): - mode = QIcon.Active - else: - mode = QIcon.Normal - icon.paint(QPainter(self), self.contentsRect(), mode=mode) From 1f52a89b327a0d2f777d9e639ccbf595156f1b8d Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:00:42 -0400 Subject: [PATCH 4/9] replace icon buttons with bitmap buttons --- enaml/qt/docking/q_dock_window.py | 49 ++++++++++--------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/enaml/qt/docking/q_dock_window.py b/enaml/qt/docking/q_dock_window.py index d5d67a849..7cf0996f4 100644 --- a/enaml/qt/docking/q_dock_window.py +++ b/enaml/qt/docking/q_dock_window.py @@ -5,18 +5,16 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from PyQt4.QtCore import Qt, QMargins, QPoint, QRect, pyqtSignal -from PyQt4.QtGui import QFrame, QHBoxLayout, QIcon, QLayout +from PyQt4.QtCore import Qt, QMargins, QPoint, QRect, QSize, pyqtSignal +from PyQt4.QtGui import QFrame, QHBoxLayout, QLayout from atom.api import Bool, Typed +from .q_bitmap_button import QBitmapButton from .q_dock_area import QDockArea from .q_dock_frame import QDockFrame from .q_dock_frame_layout import QDockFrameLayout -from .q_icon_button import QIconButton - -# Make sure the resources get registered. -from . import dock_resources +from .xbms import CLOSE_BUTTON, MAXIMIZE_BUTTON, RESTORE_BUTTON #: The maximum number of free windows to keep in the free list. @@ -64,37 +62,22 @@ def __init__(self, parent=None): super(QDockWindowButtons, self).__init__(parent) self._buttons = self.CloseButton | self.MaximizeButton - hovered = QIcon.Active - pressed = QIcon.Selected - - max_icon = QIcon() - max_icon.addFile(':dock_images/maxbtn_large_s.png') - max_icon.addFile(':dock_images/maxbtn_large_h.png', mode=hovered) - max_icon.addFile(':dock_images/maxbtn_large_p.png', mode=pressed) - - restore_icon = QIcon() - restore_icon.addFile(':dock_images/rstrbtn_large_s.png') - restore_icon.addFile(':dock_images/rstrbtn_large_h.png', mode=hovered) - restore_icon.addFile(':dock_images/rstrbtn_large_p.png', mode=pressed) - - close_icon = QIcon() - close_icon.addFile(':dock_images/closebtn_large_s.png') - close_icon.addFile(':dock_images/closebtn_large_h.png', mode=hovered) - close_icon.addFile(':dock_images/closebtn_large_p.png', mode=pressed) - - max_button = self._max_button = QIconButton(self) - max_button.setIcon(max_icon) - max_button.setIconSize(max_icon.availableSizes()[0]) + max_button = self._max_button = QBitmapButton(self) + max_button.setObjectName("dockwindow-maximize-button") + max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap()) + max_button.setIconSize(QSize(20, 15)) max_button.setVisible(self._buttons & self.MaximizeButton) - restore_button = self._restore_button = QIconButton(self) - restore_button.setIcon(restore_icon) - restore_button.setIconSize(restore_icon.availableSizes()[0]) + restore_button = self._restore_button = QBitmapButton(self) + restore_button.setObjectName("dockwindow-restore-button") + restore_button.setBitmap(RESTORE_BUTTON.toBitmap()) + restore_button.setIconSize(QSize(20, 15)) restore_button.setVisible(self._buttons & self.RestoreButton) - close_button = self._close_button = QIconButton() - close_button.setIcon(close_icon) - close_button.setIconSize(close_icon.availableSizes()[0]) + close_button = self._close_button = QBitmapButton(self) + close_button.setObjectName("dockwindow-close-button") + close_button.setBitmap(CLOSE_BUTTON.toBitmap()) + close_button.setIconSize(QSize(34, 15)) close_button.setVisible(self._buttons & self.CloseButton) layout = QHBoxLayout() From be2cca46a0afb74d50b5d5fab7b2112148462c44 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:00:58 -0400 Subject: [PATCH 5/9] replace icon buttons with bitmap buttons --- enaml/qt/docking/q_dock_title_bar.py | 38 +++++++++------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/enaml/qt/docking/q_dock_title_bar.py b/enaml/qt/docking/q_dock_title_bar.py index 0c10ec813..ee89822fd 100644 --- a/enaml/qt/docking/q_dock_title_bar.py +++ b/enaml/qt/docking/q_dock_title_bar.py @@ -6,14 +6,12 @@ # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ from PyQt4.QtCore import QSize, QMargins, pyqtSignal -from PyQt4.QtGui import QWidget, QFrame, QHBoxLayout, QIcon +from PyQt4.QtGui import QWidget, QFrame, QHBoxLayout -from .q_icon_button import QIconButton +from .q_bitmap_button import QBitmapButton from .q_icon_widget import QIconWidget from .q_text_label import QTextLabel - -# Make sure the resources get registered. -from . import dock_resources +from .xbms import CLOSE_BUTTON, MAXIMIZE_BUTTON, RESTORE_BUTTON class IDockTitleBar(QWidget): @@ -163,35 +161,23 @@ def __init__(self, parent=None): title_label = self._title_label = QTextLabel(self) - max_icon = QIcon() - max_icon.addFile(':dock_images/maxbtn_s.png') - max_icon.addFile(':dock_images/maxbtn_h.png', mode=QIcon.Active) - max_icon.addFile(':dock_images/maxbtn_p.png', mode=QIcon.Selected) - - restore_icon = QIcon() - restore_icon.addFile(':dock_images/rstrbtn_s.png') - restore_icon.addFile(':dock_images/rstrbtn_h.png', mode=QIcon.Active) - restore_icon.addFile(':dock_images/rstrbtn_p.png', mode=QIcon.Selected) - - close_icon = QIcon() - close_icon.addFile(':dock_images/closebtn_s.png') - close_icon.addFile(':dock_images/closebtn_h.png', mode=QIcon.Active) - close_icon.addFile(':dock_images/closebtn_p.png', mode=QIcon.Selected) - btn_size = QSize(14, 13) - max_button = self._max_button = QIconButton(self) - max_button.setIcon(max_icon) + max_button = self._max_button = QBitmapButton(self) + max_button.setObjectName('docktitlebar-maximize-button') + max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap()) max_button.setIconSize(btn_size) max_button.setVisible(self._buttons & self.MaximizeButton) - restore_button = self._restore_button = QIconButton(self) - restore_button.setIcon(restore_icon) + restore_button = self._restore_button = QBitmapButton(self) + restore_button.setObjectName('docktitlebar-restore-button') + restore_button.setBitmap(RESTORE_BUTTON.toBitmap()) restore_button.setIconSize(btn_size) restore_button.setVisible(self._buttons & self.RestoreButton) - close_button = self._close_button = QIconButton(self) - close_button.setIcon(close_icon) + close_button = self._close_button = QBitmapButton(self) + close_button.setObjectName('docktitlebar-close-button') + close_button.setBitmap(CLOSE_BUTTON.toBitmap()) close_button.setIconSize(btn_size) close_button.setVisible(self._buttons & self.CloseButton) From bd144ce737318bc1e2519fa3b90b05a613ba055f Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:01:22 -0400 Subject: [PATCH 6/9] replace tab close button with bitmap button --- enaml/qt/docking/q_dock_tab_widget.py | 61 ++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/enaml/qt/docking/q_dock_tab_widget.py b/enaml/qt/docking/q_dock_tab_widget.py index 15c088fc1..a8093e839 100644 --- a/enaml/qt/docking/q_dock_tab_widget.py +++ b/enaml/qt/docking/q_dock_tab_widget.py @@ -5,11 +5,36 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from PyQt4.QtCore import Qt, QPoint, QMetaObject, QEvent +from PyQt4.QtCore import Qt, QPoint, QSize, QMetaObject, QEvent from PyQt4.QtGui import ( - QApplication, QTabBar, QTabWidget, QMouseEvent, QResizeEvent + QApplication, QTabBar, QTabWidget, QMouseEvent, QResizeEvent, QStyle ) +from .q_bitmap_button import QBitmapButton +from .xbms import CLOSE_BUTTON + + +class QDockTabCloseButton(QBitmapButton): + """ A bitmap button subclass used as a dock tab close button. + + """ + def styleOption(self): + """ Get a filled style option for the button. + + Returns + ------- + result : QStyleOption + A style option initialized for the current button state. + + """ + opt = super(QDockTabCloseButton, self).styleOption() + parent = self.parent() + if isinstance(parent, QDockTabBar): + index = parent.currentIndex() + if parent.tabButton(index, QTabBar.RightSide) is self: + opt.state |= QStyle.State_Selected + return opt + class QDockTabBar(QTabBar): """ A custom QTabBar that manages safetly undocking a tab. @@ -59,25 +84,47 @@ def setCloseButtonVisible(self, index, visible): # A workaround is to send a dummy resize event. button.setVisible(visible) if not visible: - button.resize(0, 0) + button.resize(0, 0) else: - button.resize(button.sizeHint()) + button.resize(button.sizeHint()) size = self.size() event = QResizeEvent(size, size) QApplication.sendEvent(self, event) self.update() + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + def _onCloseButtonClicked(self): + """ Handle the 'clicked' signal on the tab close buttons. + + This handler will find the tab index for the clicked button + and emit the 'tabCloseRequested' signal with that index. + + """ + button = self.sender() + for index in xrange(self.count()): + if self.tabButton(index, QTabBar.RightSide) is button: + self.tabCloseRequested.emit(index) + #-------------------------------------------------------------------------- # Reimplementations #-------------------------------------------------------------------------- def tabInserted(self, index): """ Handle a tab insertion in the tab bar. - This handler will update the visibilty of close button for - the inserted tab. This method assumes that this tab bar is - properly parented by a QDockTabWidget. + This handler will create the close button for the tab and then + update its visibilty depending on whether or not the dock item + is closable. This method assumes that this tab bar is parented + by a QDockTabWidget. """ + button = QDockTabCloseButton(self) + button.setObjectName("docktab-close-button") + button.setBitmap(CLOSE_BUTTON.toBitmap()) + button.setIconSize(QSize(14, 13)) + button.clicked.connect(self._onCloseButtonClicked) + self.setTabButton(index, QTabBar.RightSide, button) visible = self.parent().widget(index).closable() self.setCloseButtonVisible(index, visible) From 6008da58f78e437eae3ea561b52dcc754750b120 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:03:21 -0400 Subject: [PATCH 7/9] expose the qt stylesheet directly for the DockArea The Qt stylesheet provides a lot of functionality which would be difficult and tedious to wrap. It's far simpler to allow the developer to define the qt stylesheet directly. --- enaml/qt/qt_dock_area.py | 133 +------- enaml/widgets/api.py | 5 +- enaml/widgets/dock_area.py | 12 +- enaml/widgets/dock_area_style.py | 194 ------------ enaml/widgets/dock_area_styles.py | 493 ++++++++++++++++++++++++++++++ examples/widgets/dock_area.enaml | 20 +- 6 files changed, 519 insertions(+), 338 deletions(-) delete mode 100644 enaml/widgets/dock_area_style.py create mode 100644 enaml/widgets/dock_area_styles.py diff --git a/enaml/qt/qt_dock_area.py b/enaml/qt/qt_dock_area.py index aa3dbea0e..eab7070da 100644 --- a/enaml/qt/qt_dock_area.py +++ b/enaml/qt/qt_dock_area.py @@ -5,8 +5,6 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from textwrap import dedent - from PyQt4.QtCore import QObject, QEvent, QSize, QTimer from PyQt4.QtGui import QTabWidget @@ -28,128 +26,6 @@ } -def make_style_sheet(style): - """ A function to generate the stylesheet for the give style. - - """ - - if style is None: - return '' - bg = ' background: rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - fg = ' color: rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - bd = ' border: 1px solid rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - items = {} - parts = [] - push = parts.append - def get(attr, which): - if getattr(style, attr) is not None: - items[attr] = which.format(getattr(style, attr)) - def push_item(attr): - push(items[attr]) - def push_item_if(attr): - if attr in items: - push(items[attr]) - get('dock_area_background', bg) - get('splitter_handle_background', bg) - get('dock_window_background', bg) - get('dock_window_border', bd) - get('dock_container_background', bg) - get('dock_container_border', bd) - get('dock_item_background', bg) - get('title_bar_background', bg) - get('title_bar_foreground', fg) - get('tab_background', bg) - get('tab_hover_background', bg) - get('tab_selected_background', bg) - get('tab_foreground', fg) - get('tab_hover_foreground', fg) - get('tab_selected_foreground', fg) - push('QDockArea {') - push(' padding: 5px;') - push_item_if('dock_area_background') - push('}') - if 'splitter_handle_background' in items: - push('QDockSplitterHandle {') - push_item('splitter_handle_background') - push('}') - if 'dock_window_background' in items or 'dock_window_border' in items: - push('QDockWindow {') - push_item_if('dock_window_background') - push_item_if('dock_window_border') - push('}') - if 'dock_container_background' in items: - push('QDockContainer {') - push_item('dock_container_background') - push('}') - if 'dock_container_border' in items: - push('QDockContainer[floating="true"] {') - push_item('dock_container_border') - push('}') - if 'dock_item_background' in items: - push('QDockItem {') - push_item('dock_item_background') - push('}') - if 'title_bar_background' in items: - push('QDockTitleBar {') - push_item('title_bar_background') - push('}') - if 'title_bar_foreground' in items or 'title_bar_font' in items: - push('QDockTitleBar > QTextLabel {') - push_item_if('title_bar_foreground') - # push_item_if('title_bar_font') - push('}') - push(dedent("""\ - /* Correct a bug in the pane size when using system styling */ - QDockTabWidget::pane { - } - QDockTabBar::close-button { - margin-bottom: 2px; - image: url(:dock_images/closebtn_s.png); - } - QDockTabBar::close-button:selected { - image: url(:dock_images/closebtn_b.png); - } - QDockTabBar::close-button:hover, - QDockTabBar::close-button:selected:hover { - image: url(:dock_images/closebtn_h.png); - } - QDockTabBar::close-button:pressed, - QDockTabBar::close-button:selected:pressed { - image: url(:dock_images/closebtn_p.png); - }""")) - if 'tab_background' in items or 'tab_foreground' in items: - push('QDockTabBar::tab {') - push_item_if('tab_background') - push_item_if('tab_foreground') - push('}') - push(dedent("""\ - QDockTabBar::tab:top, QDockTabBar::tab:bottom { - margin-right: 1px; - padding-left: 5px; - padding-right: 5px; - padding-bottom: 2px; - height: 17px; - } - - QDockTabBar::tab:left, QDockTabBar::tab:right { - margin-bottom: 1px; - padding-top: 5px; - padding-bottom: 5px; - width: 20px; - }""")) - if 'tab_hover_background' in items or 'tab_hover_foreground' in items: - push('QDockTabBar::tab:hover {') - push_item_if('tab_hover_background') - push_item_if('tab_hover_foreground') - push('}') - if 'tab_selected_background' in items or 'tab_selected_foreground' in items: - push('QDockTabBar::tab:selected {') - push_item_if('tab_selected_background') - push_item_if('tab_selected_foreground') - push('}') - return '\n'.join(parts) - - class DockFilter(QObject): """ A simple event filter used by the QtDockArea. @@ -215,7 +91,8 @@ def init_widget(self): super(QtDockArea, self).init_widget() d = self.declaration self.set_tab_position(d.tab_position) - self.set_style(d.style) + if d.style_sheet: + self.set_style_sheet(d.style_sheet) def init_layout(self): """ Initialize the layout of the underlying control. @@ -285,11 +162,11 @@ def set_tab_position(self, position): """ self.widget.setTabPosition(TAB_POSITIONS[position]) - def set_style(self, style): - """ Set the style for the underlying widget. + def set_style_sheet(self, style_sheet): + """ Set the style sheet for the underlying widget. """ - self.widget.setStyleSheet(make_style_sheet(style)) + self.widget.setStyleSheet(style_sheet) def save_layout(self): """ Save the current layout on the underlying widget. diff --git a/enaml/widgets/api.py b/enaml/widgets/api.py index 79ec3c705..fc22f2630 100644 --- a/enaml/widgets/api.py +++ b/enaml/widgets/api.py @@ -14,9 +14,8 @@ from .date_selector import DateSelector from .datetime_selector import DatetimeSelector from .dock_area import DockArea -from .dock_area_style import ( - DockAreaStyle, vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style +from .dock_area_styles import ( + VS_2010_STYLE, GREY_WIND_STYLE, NEW_MOON_STYLE, METRO_STYLE ) from .dock_item import DockItem from .dock_pane import DockPane diff --git a/enaml/widgets/dock_area.py b/enaml/widgets/dock_area.py index 791458522..d048b52c3 100644 --- a/enaml/widgets/dock_area.py +++ b/enaml/widgets/dock_area.py @@ -5,7 +5,9 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from atom.api import Coerced, Enum, Typed, ForwardTyped, observe, set_default +from atom.api import ( + Coerced, Enum, Typed, ForwardTyped, Unicode, observe, set_default +) from enaml.core.declarative import d_ from enaml.layout.dock_layout import ( @@ -13,7 +15,7 @@ ) from .constraints_widget import ConstraintsWidget, ProxyConstraintsWidget -from .dock_area_style import DockAreaStyle, vs_2010_style +from .dock_area_styles import VS_2010_STYLE from .dock_item import DockItem @@ -48,7 +50,7 @@ class ProxyDockArea(ProxyConstraintsWidget): def set_tab_position(self, position): raise NotImplementedError - def set_style(self, style): + def set_style_sheet(self, style_sheet): raise NotImplementedError def save_layout(self): @@ -77,7 +79,7 @@ class DockArea(ConstraintsWidget): #: The style to apply to the dock area. The default style resembles #: Visual Studio 2010. - style = d_(Typed(DockAreaStyle, factory=vs_2010_style)) + style_sheet = d_(Unicode(VS_2010_STYLE)) #: A Stack expands freely in height and width by default hug_width = set_default('ignore') @@ -202,7 +204,7 @@ def _update_layout(self, change): if change['type'] == 'update': self.apply_layout(change['value']) - @observe(('tab_position', 'style')) + @observe(('tab_position', 'style_sheet')) def _update_proxy(self, change): """ Update the proxy when the area state changes. diff --git a/enaml/widgets/dock_area_style.py b/enaml/widgets/dock_area_style.py deleted file mode 100644 index ab4fe3fbe..000000000 --- a/enaml/widgets/dock_area_style.py +++ /dev/null @@ -1,194 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 - -from enaml.colors import ColorMember, Color -from enaml.fonts import FontMember - - -class DockAreaStyle(Atom): - """ A class used to define the style to apply to the dock area. - - The developer is not required to explicitly specify all of the - style values. The toolkit will apply an appropriate default for - any value which is not explicitly provided. - - """ - #: The background color of a dock area. - dock_area_background = ColorMember() - - #: The background color of a splitter handle. - splitter_handle_background = ColorMember() - - #: The background color of a dock windows. - dock_window_background = ColorMember() - - #: The border color of a dock window. - dock_window_border = ColorMember() - - #: The background color of a dock container. - dock_container_background = ColorMember() - - #: The border color of a floating dock container. - dock_container_border = ColorMember() - - #: The background color of a dock item. - dock_item_background = ColorMember() - - #: The background color of a dock item title bar. - title_bar_background = ColorMember() - - #: The foreground color of a dock item title bar. - title_bar_foreground = ColorMember() - - #: The font of a dock item title bar. - title_bar_font = FontMember() - - #: The background color of a tab in a dock tab bar. - tab_background = ColorMember() - - #: The background color of a hovered tab in a dock tab bar. - tab_hover_background = ColorMember() - - #: The background color of a selected tab in a dock tab bar. - tab_selected_background = ColorMember() - - #: The foreground color of a tab in the dock tab bar. - tab_foreground = ColorMember() - - #: The foreground color of a hovered tab in a dock tab bar. - tab_hover_foreground = ColorMember() - - #: The foreground color of a selected tab in a dock tab bar. - tab_selected_foreground = ColorMember() - - -def vs_2010_style(): - """ A style which is inspired by Visual Studio 2010. - - """ - return DockAreaStyle( - dock_area_background=Color(49, 67, 98), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(53, 73, 106), - dock_window_border=Color(40, 60, 90), - dock_container_background=Color(53, 73, 106), - dock_container_border=Color(40, 60, 90), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(77, 96, 130), - title_bar_foreground=Color(250, 251, 254), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 15), - tab_hover_background=Color(76, 105, 153), - tab_selected_background=Color(240, 240, 240), - tab_foreground=Color(250, 251, 254), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def grey_wind_style(): - """ A mild grey and brown color scheme. - - Inspired by: - http://www.colourlovers.com/palette/2866138/Grey_Wind - - """ - return DockAreaStyle( - dock_area_background=Color(139, 131, 129), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(175, 182, 190), - dock_window_border=Color(144, 144, 152), - dock_container_background=Color(175, 178, 183), - dock_container_border=Color(144, 144, 152), - dock_item_background=Color(244, 244, 244), - title_bar_background=Color(144, 144, 152), - title_bar_foreground=Color(244, 244, 244), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 35), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(193, 191, 196), - tab_hover_foreground=Color(70, 70, 70), - tab_selected_background=Color(244, 244, 244), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def new_moon_style(): - """ A yellow, brown, and grey scheme which has lights and darks. - - Inspired by: - http://www.colourlovers.com/palette/90734/Newly_Risen_Moon - - """ - return DockAreaStyle( - dock_area_background=Color(54, 57, 59), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(197, 188, 142), - dock_window_border=Color(105, 103, 88), - dock_container_background=Color(62, 72, 75), - dock_container_border=Color(54, 57, 59), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(105, 103, 88), - title_bar_foreground=Color(244, 244, 244), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 30), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(197, 188, 142), - tab_selected_background=Color(240, 240, 240), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def daydreamer_style(): - """ A tempered olive colored theme. - - Inspired by: - http://www.colourlovers.com/palette/590207/d_a_y_d_r_e_a_m_e_r - - """ - return DockAreaStyle( - dock_area_background=Color(168, 168, 120), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(131, 144, 116), - dock_window_border=Color(6, 16, 19), - dock_container_background=Color(147, 158, 120), - dock_container_border=Color(6, 16, 19), - dock_item_background=Color(244, 244, 244), - title_bar_background=Color(131, 144, 116), - title_bar_foreground=Color(250, 250, 250), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 35), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(205, 205, 118), - tab_selected_background=Color(244, 244, 244), - tab_selected_foreground=Color(6, 16, 19), - ) - - -def metro_style(): - """ A style which is inspired by Windows Metro UIs. - - """ - return DockAreaStyle( - dock_area_background=Color(0xca, 0xca, 0xca), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(255, 255, 255), - dock_window_border=Color(0x66, 0x66, 0x66), - dock_container_background=Color(0xca, 0xca, 0xca), - dock_container_border=Color(0x66, 0x66, 0x66), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(53, 139, 202), - title_bar_foreground=Color(240, 240, 240), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(0x66, 0x66, 0x66), - tab_foreground=Color(240, 240, 240), - tab_hover_background=Color(0xff, 0xfb, 0x85, 200), - tab_hover_foreground=Color(0x33, 0x33, 0x33), - tab_selected_background=Color(240, 240, 240), - tab_selected_foreground=Color(0x33, 0x33, 0x33), - ) diff --git a/enaml/widgets/dock_area_styles.py b/enaml/widgets/dock_area_styles.py new file mode 100644 index 000000000..85ebbcf90 --- /dev/null +++ b/enaml/widgets/dock_area_styles.py @@ -0,0 +1,493 @@ +#------------------------------------------------------------------------------ +# 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. +#------------------------------------------------------------------------------ + + +#: A stylesheet inspired by Visual Studio 2010 +VS_2010_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(49, 67, 98); + border: 1px solid rgb(40, 60, 90); + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(53, 73, 106); + border: 1px solid rgb(40, 60, 90); + } + + QDockContainer { + background: rgb(53, 73, 106); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(40, 60, 90); + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(77, 96, 130); + } + + QDockTitleBar > QTextLabel { + color: rgb(250, 251, 254); + font: 9pt "Segoe UI"; + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar { + font: 9pt "Segoe UI"; + } + + QDockTabBar::tab { + background: rgba(255, 255, 255, 15); + color: rgb(250, 251, 254); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgba(255, 255, 255, 70); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton, + QDockWindowButtons QBitmapButton { + color: rgb(250, 251, 254); + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #3665B3; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: #3D6099; + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + border: 1px solid rgb(229, 195, 101); + background: rgb(250, 251, 254); + color: black; + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + background: rgb(255, 229, 128); + } + + QBitmapButton#docktab-close-button:selected { + color: black; + } +""" + +#: A mild grey and brown stylesheet. +#: Inspired by http://www.colourlovers.com/palette/2866138/Grey_Wind +GREY_WIND_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(175, 178, 183); + border: 1px solid rgb(161, 164, 168); + } + + QDockWindow > QDockArea { + border: 1px solid rgb(129, 121, 119); + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(139, 131, 129); + border: 1px solid rgb(129, 121, 119); + } + + QDockContainer { + background: rgb(175, 178, 183); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(144, 144, 152); + } + + QDockItem { + background: rgb(244, 244, 244); + } + + QDockTitleBar { + background: rgb(144, 144, 152); + } + + QDockTitleBar > QTextLabel { + color: rgb(244, 244, 244); + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar::tab { + background: rgba(0, 0, 0, 20); + color: rgb(244, 244, 244); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgb(144, 144, 152); + } + + QDockTabBar::tab:selected { + background: rgb(244, 244, 244); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(250, 251, 254); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: rgb(80, 80, 80); + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(80, 80, 80); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: rgb(250, 251, 254); + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: rgb(175, 178, 183); + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: rgb(144, 144, 152); + } +""" + + +#: A yellow, brown, and grey stylesheet. +#: Inspired by http://www.colourlovers.com/palette/90734/Newly_Risen_Moon +NEW_MOON_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(54, 57, 59); + border: 1px solid rgb(45, 45, 45); + } + + QDockWindow > QDockArea { + border: 1px solid #9E935D; + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(197, 188, 142); + border: 1px solid #9E935D; + } + + QDockContainer { + background: rgb(54, 57, 59); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(45, 45, 45); + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(105, 103, 88); + } + + QDockTitleBar > QTextLabel { + color: rgb(240, 240, 240); + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar::tab { + background: rgba(255, 255, 255, 30); + color: rgb(240, 240, 240); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgba(197, 188, 142, 170); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(240, 240, 240); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: rgb(50, 50, 50); + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(100, 100, 100); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: rgb(240, 240, 240); + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #9E935D; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: rgb(105, 103, 88); + } +""" + + +#: A stylesheet inspired by Windows Metro. +METRO_STYLE = u""" + QDockArea { + padding: 5px; + background: #C0C0C0; + border: 1px solid #B0B0B0; + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: white; + border: 1px solid #B0B0B0; + } + + QDockContainer { + background: #C0C0C0; + } + + QDockContainer[floating="true"] { + border: 1px solid #B0B0B0; + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(53, 139, 202); + } + + QDockTitleBar > QTextLabel { + color: rgb(240, 240, 240); + font: 9pt "Segoe UI"; + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar { + font: 9pt "Segoe UI"; + } + + QDockTabBar::tab { + background: #666666; + background: #838587; + color: rgb(240, 240, 240); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgb(53, 139, 202); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(240, 240, 240); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(100, 100, 100); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: white; + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #3665B3; + color: white; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: #3D6099; + } +""" diff --git a/examples/widgets/dock_area.enaml b/examples/widgets/dock_area.enaml index 9924b3bb4..9e8cd12b0 100644 --- a/examples/widgets/dock_area.enaml +++ b/examples/widgets/dock_area.enaml @@ -22,11 +22,18 @@ from enaml.layout.api import ( ) from enaml.widgets.api import ( Window, Container, DockArea, DockItem, PushButton, Field, Html, Slider, - ObjectCombo, vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style + ObjectCombo, VS_2010_STYLE, GREY_WIND_STYLE, NEW_MOON_STYLE, METRO_STYLE ) +STYLES = { + 'VS 2010': VS_2010_STYLE, + 'Grey Wind': GREY_WIND_STYLE, + 'New Moon': NEW_MOON_STYLE, + 'Metro': METRO_STYLE, +} + + enamldef MyDockArea(DockArea): layout = hdocksplit( vdocksplit('Item 1', 'Item 3', 'Item 5'), @@ -129,10 +136,7 @@ enamldef Main(Window): names.append(name) area.find('Item 1').split('left', *names) ObjectCombo: style_c: - items = [ - vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style, - ] - to_string = lambda func: func.__name__.replace('_', ' ') + items = STYLES.keys() + selected = 'VS 2010' MyDockArea: area: - style << style_c.selected() + style_sheet << STYLES[style_c.selected] From 43e360e8b584825fcee4171366b602a92347a254 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:03:32 -0400 Subject: [PATCH 8/9] fix an item comparison bug --- enaml/qt/qt_object_combo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enaml/qt/qt_object_combo.py b/enaml/qt/qt_object_combo.py index c45d10a51..4060776c3 100644 --- a/enaml/qt/qt_object_combo.py +++ b/enaml/qt/qt_object_combo.py @@ -137,7 +137,7 @@ def refresh_items(self): widget.addItem(text) else: widget.addItem(qicon, text) - if item is selected: + if item == selected: target_index = index widget.setCurrentIndex(target_index) finally: From 0f8918f731e68f5813616888dbd3b858a22682de Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Sun, 26 May 2013 17:04:23 -0400 Subject: [PATCH 9/9] get rid of testing file --- button_test.py | 84 -------------------------------------------------- 1 file changed, 84 deletions(-) delete mode 100644 button_test.py diff --git a/button_test.py b/button_test.py deleted file mode 100644 index 3bdc5ac60..000000000 --- a/button_test.py +++ /dev/null @@ -1,84 +0,0 @@ - -from enaml.qt.docking.button_bitmaps import restore_bitmap - -from PyQt4.QtCore import QSize, QAbstractFileEngineHandler, Qt -from PyQt4.QtGui import ( - QApplication, QAbstractButton, QFrame, QVBoxLayout, QPainter, - QStyleOption, QStyle, QColor -) - -class MyButton(QAbstractButton): - - def sizeHint(self): - return QSize(20, 20) - - def paintEvent(self, event): - p = QPainter(self) - opt = QStyleOption() - opt.initFrom(self) - # opt.state |= QStyle.State_AutoRaise - # if (self.isEnabled() and self.underMouse() and not - # self.isChecked() and not self.isDown()): - # opt.state |= QStyle.State_Raised - # if self.isChecked(): - # opt.state |= QStyle.State_On - if self.isDown(): - opt.state |= QStyle.State_Sunken - - # if (const QTabBar *tb = qobject_cast(parent())) { - # int index = tb->currentIndex(); - # QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb); - # if (tb->tabButton(index, position) == this) - # opt.state |= QStyle::State_Selected; - # } - - self.style().drawPrimitive(QStyle.PE_Widget, opt, p, self) - #self.style().drawPrimitive(QStyle.PE_IndicatorTabClose, opt, p, self) - #print self.palette().foreground().color().blue() - #c = self.style().styleHint( - # QStyle.SH_GroupBox_TextLabelColor, opt, self, None - #) - #c2 = QColor.fromRgba(0xffffffff & c) # stupid signed int - #bmp = restore_bitmap() - #p.setBackgroundMode(Qt.OpaqueMode) - #p.setBackground(c2) - #p.setPen(c2) - #p.drawPixmap(0, 0, bmp) - - -class MyHandler(QAbstractFileEngineHandler): - - def create(self, filename): - print filename - - -app = QApplication([]) -h = MyHandler() - -w = QFrame() -b = MyButton() -l = QVBoxLayout() -l.addWidget(b) -w.setLayout(l) - -w.setStyleSheet(""" - QFrame { - background: rgb(20, 20, 200); - } - MyButton { - background: yellow; - border: 1px solid red; - min-width: 40px; - max-width: 40px; - color: orange; - } - MyButton:hover { - border: 1px solid white; - } - MyButton:pressed { - background: red; - } - - """) -w.show() -app.exec_()