Skip to content

Commit

Permalink
Merge pull request #3855 from irgolic/popups-to-non-intrusive-notific…
Browse files Browse the repository at this point in the history
…ations

[ENH] Replace popups with non-intrusive notifications
  • Loading branch information
ales-erjavec authored Jun 17, 2019
2 parents e60df9b + 2b216b4 commit 67fafb0
Show file tree
Hide file tree
Showing 7 changed files with 804 additions and 101 deletions.
123 changes: 80 additions & 43 deletions Orange/canvas/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@

import pkg_resources

from AnyQt.QtGui import QFont, QColor, QPalette, QDesktopServices
from AnyQt.QtWidgets import QMessageBox
from AnyQt.QtGui import QFont, QColor, QPalette, QDesktopServices, QIcon
from AnyQt.QtCore import (
Qt, QDir, QUrl, QSettings, QThread, pyqtSignal, QT_VERSION
)
Expand All @@ -39,6 +38,8 @@
from Orange.canvas.registry import qt
from Orange.canvas.registry import WidgetRegistry, set_global_registry
from Orange.canvas.registry import cache
from Orange.canvas.utils.overlay import NotificationWidget, NotificationOverlay
from Orange.widgets import gui

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -180,32 +181,56 @@ def make_sql_logger(level=logging.INFO):
sql_log.addHandler(handler)


def show_survey():
# If run for the first time, open a browser tab with a survey
settings = QSettings()
show_survey = settings.value("startup/show-survey", True, type=bool)
def setup_notifications():
settings = config.settings()

# If run for the fifth time, prompt short survey
show_survey = settings["startup/show-short-survey"] and \
settings["startup/launch-count"] >= 5
if show_survey:
question = QMessageBox(
QMessageBox.Question,
'Orange Survey',
'We would like to know more about how our software is used.\n\n'
'Would you care to fill our short 1-minute survey?',
QMessageBox.Yes | QMessageBox.No)
question.setDefaultButton(QMessageBox.Yes)
later = question.addButton('Ask again later', QMessageBox.NoRole)
question.setEscapeButton(later)

def handle_response(result):
if result == QMessageBox.Yes:
surveyDialogButtons = NotificationWidget.Ok | NotificationWidget.Close
surveyDialog = NotificationWidget(icon=QIcon(gui.resource_filename("icons/information.png")),
title="Survey",
text="We want to understand our users better.\n"
"Would you like to take a short survey?",
standardButtons=surveyDialogButtons)

def handle_survey_response(button):
if surveyDialog.buttonRole(button) == NotificationWidget.AcceptRole:
success = QDesktopServices.openUrl(
QUrl("https://orange.biolab.si/survey/short.html"))
settings.setValue("startup/show-survey", not success)
else:
settings.setValue("startup/show-survey", result != QMessageBox.No)
settings["startup/show-short-survey"] = not success
elif surveyDialog.buttonRole(button) == NotificationWidget.RejectRole:
settings["startup/show-short-survey"] = False

surveyDialog.clicked.connect(handle_survey_response)

NotificationOverlay.registerNotification(surveyDialog)

question.finished.connect(handle_response)
question.show()
return question
# data collection permission
if not settings["error-reporting/permission-requested"]:
permDialogButtons = NotificationWidget.Ok | NotificationWidget.Close
permDialog = NotificationWidget(icon=QIcon(gui.resource_filename(
"../../distribute/icon-48.png")),
title="Anonymous Usage Statistics",
text="Do you wish to opt-in to sharing "
"statistics about how you use Orange?\n"
"All data is anonymized and used "
"exclusively for understanding how users "
"interact with Orange.",
standardButtons=permDialogButtons)
btnOK = permDialog.button(NotificationWidget.AcceptRole)
btnOK.setText("Allow")

def handle_permission_response(button):
if permDialog.buttonRole(button) != permDialog.DismissRole:
settings["error-reporting/permission-requested"] = True
if permDialog.buttonRole(button) == permDialog.AcceptRole:
settings["error-reporting/send-statistics"] = True

permDialog.clicked.connect(handle_permission_response)

NotificationOverlay.registerNotification(permDialog)


def check_for_updates():
Expand Down Expand Up @@ -249,31 +274,39 @@ def ua_string():

def compare_versions(latest):
version = pkg_resources.parse_version
if version(latest) <= version(current):
skipped = settings.value('startup/latest-skipped-version', "", type=str)
if version(latest) <= version(current) or \
latest == skipped:
return
question = QMessageBox(
QMessageBox.Information,
'Orange Update Available',
'A newer version of Orange is available.<br><br>'
'<b>Current version:</b> {}<br>'
'<b>Latest version:</b> {}'.format(current, latest),
textFormat=Qt.RichText)
ok = question.addButton('Download Now', question.AcceptRole)
question.setDefaultButton(ok)
question.addButton('Remind Later', question.RejectRole)
question.finished.connect(
lambda:
question.clickedButton() == ok and
QDesktopServices.openUrl(QUrl("https://orange.biolab.si/download/")))
question.show()

questionButtons = NotificationWidget.Ok | NotificationWidget.Close
question = NotificationWidget(icon=QIcon(gui.resource_filename('icons/Dlg_down3.png')),
title='Orange Update Available',
text='Current version: <b>{}</b><br>'
'Latest version: <b>{}</b>'.format(current, latest),
textFormat=Qt.RichText,
standardButtons=questionButtons,
acceptLabel="Download",
rejectLabel="Skip this Version")

def handle_click(b):
if question.buttonRole(b) != question.DismissRole:
settings.setValue('startup/latest-skipped-version', latest)
if question.buttonRole(b) == question.AcceptRole:
QDesktopServices.openUrl(QUrl("https://orange.biolab.si/download/"))

question.clicked.connect(handle_click)

NotificationOverlay.registerNotification(question)

thread = GetLatestVersion()
thread.resultReady.connect(compare_versions)
thread.start()
return thread
return None

def send_usage_statistics():

def send_usage_statistics():
class SendUsageStatistics(QThread):
def run(self):
try:
Expand All @@ -287,6 +320,7 @@ def run(self):
thread.start()
return thread


def main(argv=None):
if argv is None:
argv = sys.argv
Expand Down Expand Up @@ -433,6 +467,8 @@ def onrequest(url):

settings = QSettings()

settings.setValue('startup/launch-count', settings.value('startup/launch-count', 0, int) + 1)

stylesheet = options.stylesheet or defaultstylesheet
stylesheet_string = None

Expand Down Expand Up @@ -557,8 +593,10 @@ def show_message(message):
open_requests[-1])
canvas_window.load_scheme(open_requests[-1].toLocalFile())

# initialize notifications
setup_notifications()

# local references prevent destruction
survey = show_survey()
update_check = check_for_updates()
send_stat = send_usage_statistics()

Expand Down Expand Up @@ -591,7 +629,6 @@ def show_message(message):
status = 42

del canvas_window
del survey
del update_check
del send_stat

Expand Down
42 changes: 4 additions & 38 deletions Orange/canvas/application/canvasmain.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ def user_documents_path():
return QDesktopServices.storageLocation(
QDesktopServices.DocumentsLocation)

from ...widgets.utils.overlay import MessageOverlayWidget

from ..gui.dropshadow import DropShadowFrame
from ..gui.dock import CollapsibleDockWidget
from ..gui.quickhelp import QuickHelpTipEvent
from ..gui.utils import message_critical, message_question, \
message_warning, message_information

from ..utils.overlay import NotificationOverlay

from ..document.usagestatistics import UsageStatistics

from ..help import HelpManager
Expand Down Expand Up @@ -384,42 +384,7 @@ def touch():

self.setMinimumSize(600, 500)

# ask for anonymous data collection permission
def requestDataCollectionPermission():
permDialogButtons = MessageOverlayWidget.AcceptRole | MessageOverlayWidget.RejectRole
permDialog = MessageOverlayWidget(parent=w,
text="Do you wish to share anonymous usage "
"statistics to help improve Orange?",
wordWrap=True,
standardButtons=permDialogButtons)
btnOK = permDialog.button(MessageOverlayWidget.AcceptRole)
btnOK.setText("Allow")

def respondToRequest():
settings["error-reporting/permission-requested"] = True

def shareData():
settings["error-reporting/send-statistics"] = True

permDialog.clicked.connect(respondToRequest)
permDialog.accepted.connect(shareData)

permDialog.setStyleSheet("""
MessageOverlayWidget {
background: qlineargradient(
x1: 0, y1: 0, x2: 0, y2: 1,
stop:0 #666, stop:0.3 #6D6D6D, stop:1 #666)
}
MessageOverlayWidget QLabel#text-label {
color: white;
}""")

permDialog.setWidget(w)
permDialog.show()

settings = config.settings()
if not settings["error-reporting/permission-requested"]:
requestDataCollectionPermission()
self.notification_overlay = NotificationOverlay(self.scheme_widget)

def setup_actions(self):
"""Initialize main window actions.
Expand Down Expand Up @@ -1912,6 +1877,7 @@ def closeEvent(self, event):
settings.endGroup()
self.help_dock.close()
self.log_dock.close()
self.notification_overlay.close()
super().closeEvent(event)

__did_restore = False
Expand Down
21 changes: 16 additions & 5 deletions Orange/canvas/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def init():
("startup/check-updates", bool, True,
"Check for updates"),

("startup/launch-count", int, 0,
""),

("startup/show-short-survey", bool, True,
"Has the user not been asked to take a short survey yet"),

("stylesheet", str, "orange",
"QSS stylesheet to use"),

Expand Down Expand Up @@ -111,11 +117,14 @@ def init():
("quickmenu/trigger-on-any-key", bool, False,
"Show quick menu on double click."),

("logging/level", int, 1, "Logging level"),
("logging/level", int, 1,
"Logging level"),

("logging/show-on-error", bool, True, "Show log window on error"),
("logging/show-on-error", bool, True,
"Show log window on error"),

("logging/dockable", bool, True, "Allow log window to be docked"),
("logging/dockable", bool, True,
"Allow log window to be docked"),

("help/open-in-external-browser", bool, False,
"Open help in an external browser"),
Expand All @@ -135,9 +144,11 @@ def init():
("add-ons/pip-install-arguments", str, '',
'Arguments to pass to "pip install" when installing add-ons.'),

("network/http-proxy", str, '', 'HTTP proxy.'),
("network/http-proxy", str, '',
'HTTP proxy.'),

("network/https-proxy", str, '', 'HTTPS proxy.'),
("network/https-proxy", str, '',
'HTTPS proxy.'),
]

spec = [config_slot(*t) for t in spec]
Expand Down
17 changes: 15 additions & 2 deletions Orange/canvas/gui/stackedwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,25 @@ def maximumSize(self):
else:
return QStackedLayout.maximumSize(self)

def hasHeightForWidth(self) -> bool:
current = self.currentWidget()
if current is not None:
return current.hasHeightForWidth()
else:
return False

def heightForWidth(self, width: int) -> int:
current = self.currentWidget()
if current is not None:
return current.heightForWidth(width)
else:
return -1

def geometry(self):
# Reimplemented due to QTBUG-47107.
return QRect(self.__rect)

def setGeometry(self, rect):
# type: (QRect) -> None
def setGeometry(self, rect: QRect) -> None:
if rect == self.__rect:
return
self.__rect = QRect(rect)
Expand Down
Loading

0 comments on commit 67fafb0

Please sign in to comment.