Skip to content

Commit

Permalink
Merge pull request #2514 from ales-erjavec/widget-status-bar-buttons
Browse files Browse the repository at this point in the history
[ENH] Widget status bar buttons
  • Loading branch information
janezd authored Oct 9, 2017
2 parents 53c1bf5 + 36d86ce commit bee63b7
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 64 deletions.
9 changes: 6 additions & 3 deletions Orange/canvas/scheme/widgetsscheme.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

import sip

from AnyQt.QtWidgets import QWidget, QShortcut, QLabel, QSizePolicy
from AnyQt.QtWidgets import QWidget, QShortcut, QLabel, QSizePolicy, QAction
from AnyQt.QtGui import QKeySequence, QWhatsThisClickedEvent

from AnyQt.QtCore import Qt, QObject, QCoreApplication, QTimer, QEvent
Expand Down Expand Up @@ -514,8 +514,11 @@ def create_widget_instance(self, node):
node.set_progress(widget.progressBarValue)

# Install a help shortcut on the widget
help_shortcut = QShortcut(QKeySequence("F1"), widget)
help_shortcut.activated.connect(self.__on_help_request)
help_action = widget.findChild(QAction, "action-help")
if help_action is not None:
help_action.setEnabled(True)
help_action.setVisible(True)
help_action.triggered.connect(self.__on_help_request)

# Up shortcut (activate/open parent)
up_shortcut = QShortcut(
Expand Down
5 changes: 5 additions & 0 deletions Orange/widgets/icons/chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions Orange/widgets/icons/help.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions Orange/widgets/icons/report.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions Orange/widgets/tests/test_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from unittest.mock import patch, MagicMock

from AnyQt.QtWidgets import QAction

from Orange.widgets.gui import OWComponent
from Orange.widgets.settings import Setting
from Orange.widgets.tests.base import WidgetTest
Expand Down Expand Up @@ -69,6 +71,12 @@ def test_widget_tests_do_not_use_stored_settings(self):
widget2 = self.create_widget(MyWidget)
self.assertEqual(widget2.field, 42)

def test_widget_help_action(self):
widget = self.create_widget(MyWidget)
help_action = widget.findChild(QAction, "action-help")
help_action.setEnabled(True)
help_action.setVisible(True)


class WidgetMsgTestCase(WidgetTest):

Expand Down
71 changes: 70 additions & 1 deletion Orange/widgets/utils/buttons.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from AnyQt.QtWidgets import QPushButton, QStyle, QStyleOptionButton
from AnyQt.QtWidgets import (
QPushButton, QAbstractButton, QFocusFrame, QStyle, QStylePainter,
QStyleOptionButton
)
from AnyQt.QtGui import QPalette, QIcon
from AnyQt.QtCore import Qt, QSize


Expand Down Expand Up @@ -59,3 +63,68 @@ def sizeHint(self):
style.sizeFromContents(QStyle.CT_PushButton, option,
size, self))
return sh


class SimpleButton(QAbstractButton):
"""
A simple icon button widget.
"""
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.__focusframe = None

def focusInEvent(self, event):
# reimplemented
event.accept()
if self.__focusframe is None:
self.__focusframe = QFocusFrame(self)
self.__focusframe.setWidget(self)
palette = self.palette()
palette.setColor(QPalette.Foreground,
palette.color(QPalette.Highlight))
self.__focusframe.setPalette(palette)

def focusOutEvent(self, event):
# reimplemented
event.accept()
if self.__focusframe is not None:
self.__focusframe.hide()
self.__focusframe.deleteLater()
self.__focusframe = None

def sizeHint(self):
# reimplemented
self.ensurePolished()
iconsize = self.iconSize()
icon = self.icon()
if not icon.isNull():
iconsize = icon.actualSize(iconsize)
return iconsize

def minimumSizeHint(self):
# reimplemented
return self.sizeHint()

def paintEvent(self, event):
painter = QStylePainter(self)
option = QStyleOptionButton()
option.initFrom(self)
option.text = ""
option.icon = self.icon()
option.iconSize = self.iconSize()
option.features = QStyleOptionButton.Flat
if self.isDown():
option.state |= QStyle.State_Sunken
painter.drawPrimitive(QStyle.PE_PanelButtonBevel, option)

if not option.icon.isNull():
if option.state & QStyle.State_Active:
mode = (QIcon.Normal if option.state & QStyle.State_MouseOver
else QIcon.Active)
else:
mode = QIcon.Disabled
if self.isChecked():
state = QIcon.On
else:
state = QIcon.Off
option.icon.paint(painter, option.rect, Qt.AlignCenter, mode, state)
58 changes: 3 additions & 55 deletions Orange/widgets/utils/overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@

from AnyQt.QtWidgets import (
QHBoxLayout, QPushButton, QLabel, QSizePolicy, QStyle, QAbstractButton,
QStyleOptionButton, QStylePainter, QFocusFrame, QWidget, QStyleOption
QWidget, QStyleOption
)
from AnyQt.QtGui import QIcon, QPixmap, QPainter
from AnyQt.QtCore import Qt, QSize, QRect, QPoint, QEvent
from AnyQt.QtCore import pyqtSignal as Signal, pyqtSlot as Slot

from .buttons import SimpleButton


class OverlayWidget(QWidget):
"""
Expand Down Expand Up @@ -198,60 +200,6 @@ def __on_destroyed(self):
self.hide()


class SimpleButton(QAbstractButton):
"""
A simple icon button widget.
"""
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.__focusframe = None

def focusInEvent(self, event):
# reimplemented
event.accept()
self.__focusframe = QFocusFrame(self)
self.__focusframe.setWidget(self)

def focusOutEvent(self, event):
# reimplemented
event.accept()
self.__focusframe.deleteLater()
self.__focusframe = None

def sizeHint(self):
# reimplemented
self.ensurePolished()
iconsize = self.iconSize()
icon = self.icon()
if not icon.isNull():
iconsize = icon.actualSize(iconsize)
return iconsize

def minimumSizeHint(self):
# reimplemented
return self.sizeHint()

def paintEvent(self, event):
# reimplemented
painter = QStylePainter(self)
option = QStyleOptionButton()
option.initFrom(self)
option.icon = self.icon()
option.iconSize = self.iconSize()

icon = self.icon()

if not icon.isNull():
if option.state & QStyle.State_Active:
mode = (QIcon.Normal if option.state & QStyle.State_MouseOver
else QIcon.Active)
else:
mode = QIcon.Disabled
pixmap = icon.pixmap(option.iconSize, mode, )

painter.drawItemPixmap(option.rect, Qt.AlignCenter, pixmap)


class MessageWidget(QWidget):
"""
A widget displaying a simple message to the user.
Expand Down
22 changes: 22 additions & 0 deletions Orange/widgets/utils/tests/test_buttons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from AnyQt.QtCore import Qt
from AnyQt.QtGui import QFocusEvent
from AnyQt.QtWidgets import QStyle, QApplication
from Orange.widgets.tests.base import GuiTest
from Orange.widgets.utils import buttons


class SimpleButtonTest(GuiTest):
def test_button(self):
# Run through various state change and drawing code for coverage
b = buttons.SimpleButton()
b.setIcon(b.style().standardIcon(QStyle.SP_ComputerIcon))

QApplication.sendEvent(b, QFocusEvent(QFocusEvent.FocusIn))
QApplication.sendEvent(b, QFocusEvent(QFocusEvent.FocusOut))

b.grab()
b.setDown(True)
b.grab()
b.setCheckable(True)
b.setChecked(True)
b.grab()
41 changes: 36 additions & 5 deletions Orange/widgets/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from AnyQt.QtWidgets import (
QWidget, QDialog, QVBoxLayout, QSizePolicy, QApplication, QStyle,
QShortcut, QSplitter, QSplitterHandle, QPushButton, QStatusBar,
QProgressBar
QProgressBar, QAction
)
from AnyQt.QtCore import (
Qt, QByteArray, QSettings, QUrl, pyqtSignal as Signal
Expand All @@ -33,6 +33,7 @@
from Orange.widgets.utils.signals import \
WidgetSignalsMixin, Input, Output, AttributeList
from Orange.widgets.utils.overlay import MessageOverlayWidget, OverlayWidget
from Orange.widgets.utils.buttons import SimpleButton

# Msg is imported and renamed, so widgets can import it from this module rather
# than the one with the mixin (Orange.widgets.utils.messages). Assignment is
Expand Down Expand Up @@ -170,6 +171,7 @@ class OWWidget(QDialog, OWComponent, Report, ProgressBarMixin,
contextOpened = Signal()
contextClosed = Signal()

# pylint: disable=protected-access
def __new__(cls, *args, captionTitle=None, **kwargs):
self = super().__new__(cls, None, cls.get_flags())
QDialog.__init__(self, None, self.get_flags())
Expand Down Expand Up @@ -208,6 +210,12 @@ def __new__(cls, *args, captionTitle=None, **kwargs):
self.__msgwidget = None
self.__msgchoice = 0

self.__help_action = QAction(
"Help", self, objectName="action-help", toolTip="Show help",
enabled=False, visible=False, shortcut=QKeySequence(Qt.Key_F1)
)
self.addAction(self.__help_action)

self.left_side = None
self.controlArea = self.mainArea = self.buttonsArea = None
self.splitter = None
Expand All @@ -219,6 +227,7 @@ def __new__(cls, *args, captionTitle=None, **kwargs):

sc = QShortcut(QKeySequence.Copy, self)
sc.activated.connect(self.copy_to_clipboard)

if self.controlArea is not None:
# Otherwise, the first control has focus
self.controlArea.setFocus(Qt.ActiveWindowFocusReason)
Expand Down Expand Up @@ -319,10 +328,6 @@ def _insert_buttons_area(self):
self.buttonsArea = gui.widgetBox(
self.left_side, addSpace=0, spacing=9,
orientation=self.buttons_area_orientation)
if self.graphButton is not None:
self.buttonsArea.layout().addWidget(self.graphButton)
if self.report_button is not None:
self.buttonsArea.layout().addWidget(self.report_button)

def _insert_main_area(self):
self.mainArea = gui.vBox(
Expand Down Expand Up @@ -380,6 +385,32 @@ def set_basic_layout(self):
sb.setSizeGripEnabled(self.resizing_enabled)
c.layout().addWidget(sb)

help = self.__help_action
help_button = SimpleButton(
icon=QIcon(gui.resource_filename("icons/help.svg")),
toolTip="Show widget help", visible=help.isVisible(),
)
@help.changed.connect
def _():
help_button.setVisible(help.isVisible())
help_button.setEnabled(help.isEnabled())
help_button.clicked.connect(help.trigger)
sb.addWidget(help_button)

if self.graph_name is not None:
b = SimpleButton(
icon=QIcon(gui.resource_filename("icons/chart.svg")),
toolTip="Save Image",
)
b.clicked.connect(self.save_graph)
sb.addWidget(b)
if hasattr(self, "send_report"):
b = SimpleButton(
icon=QIcon(gui.resource_filename("icons/report.svg")),
toolTip="Report"
)
b.clicked.connect(self.show_report)
sb.addWidget(b)
self.message_bar = MessagesWidget(self)
self.message_bar.setSizePolicy(QSizePolicy.Preferred,
QSizePolicy.Preferred)
Expand Down

0 comments on commit bee63b7

Please sign in to comment.