Display a count of errors at the bottom of the screen

You can test it by typing in the console:
log error test
log warning test

Click on the count hide / show the console and reset the counters.

Also now status bar is a dedicated class we can easyly extend it.

Ref #1864
This commit is contained in:
Julien Duponchelle
2017-02-17 16:24:58 +01:00
parent 00e402f28c
commit 0c052542b3
7 changed files with 80581 additions and 79903 deletions

View File

@@ -52,6 +52,7 @@ from .utils.analytics import AnalyticsClient
from .dialogs.appliance_wizard import ApplianceWizard
from .dialogs.new_appliance_dialog import NewApplianceDialog
from .dialogs.notif_dialog import NotifDialog, NotifDialogHandler
from .status_bar import StatusBarHandler
from .registry.appliance import ApplianceError
log = logging.getLogger(__name__)
@@ -78,6 +79,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# Setup logger
logging.getLogger().addHandler(NotifDialogHandler(NotifDialog(self)))
logging.getLogger().addHandler(StatusBarHandler(self.uiStatusBar))
self._open_file_at_startup = open_file

92
gns3/status_bar.py Normal file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from .qt import QtWidgets, QtGui, qslot
class StatusBarHandler(logging.StreamHandler):
def __init__(self, widget):
super().__init__()
self._widget = widget
self.setLevel(logging.WARNING)
def emit(self, record):
if record.levelno > logging.WARNING:
self._widget.addError()
else:
self._widget.addWarning()
class StatusBar(QtWidgets.QStatusBar):
"""
The status bar
"""
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self._parent = parent
self._errors = 0
self._warnings = 0
self._errors_button = QtWidgets.QPushButton()
self._errors_button.setFlat(True)
self._errors_button.setIcon(QtGui.QIcon(":/icons/warning.svg"))
self._errors_button.clicked.connect(self._errorButtonPushedSlot)
self.addPermanentWidget(self._errors_button)
self._refresh()
def addError(self):
"""
Increment error count
"""
self._errors += 1
self._refresh()
def addWarning(self):
"""
Increment warning count
"""
self._warnings += 1
self._refresh()
def _refresh(self):
if self._errors == 0 and self._warnings == 0:
self._errors_button.setText("")
self._errors_button.hide()
return
self._errors_button.show()
text = ""
if self._errors == 1:
text += "1 error"
elif self._errors > 1:
text += "{} errors".format(self._errors)
if self._warnings == 1:
text += " 1 warning"
elif self._warnings > 1:
text += " {} warnings".format(self._warnings)
self._errors_button.setText(text)
@qslot
def _errorButtonPushedSlot(self, *args, **kwargs):
self._parent.uiConsoleDockWidget.toggleViewAction().trigger()
self._warnings = 0
self._errors = 0
self._refresh()

View File

@@ -173,7 +173,7 @@ background-none;
<addaction name="uiToolsMenu"/>
<addaction name="uiHelpMenu"/>
</widget>
<widget class="QStatusBar" name="uiStatusBar"/>
<widget class="StatusBar" name="uiStatusBar"/>
<widget class="QToolBar" name="uiGeneralToolBar">
<property name="windowTitle">
<string>General</string>
@@ -1205,6 +1205,11 @@ background-none;
<extends>QGraphicsView</extends>
<header>..graphics_view.h</header>
</customwidget>
<customwidget>
<class>StatusBar</class>
<extends>QStatusBar</extends>
<header>..status_bar.h</header>
</customwidget>
<customwidget>
<class>NodesView</class>
<extends>QTreeWidget</extends>

View File

@@ -1,36 +1,37 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/main_window.ui'
# Form implementation generated from reading ui file '/Users/noplay/code/gns3/gns3-gui/gns3/ui/main_window.ui'
#
# Created: Fri Feb 17 17:08:13 2017
# by: PyQt5 UI code generator 5.2.1
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowModality(QtCore.Qt.NonModal)
MainWindow.resize(984, 715)
MainWindow.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
MainWindow.setStyleSheet("#toolBar_Devices QToolButton {\n"
"width: 50px;\n"
"height: 55px;\n"
"border:solid 1px black opacity 0.4;\n"
"background-none;\n"
"}\n"
"\n"
"#toolBar_General QToolButton {\n"
"width: 36px;\n"
"height: 36px;\n"
"border:solid 1px black opacity 0.4;\n"
"background-none;\n"
"}\n"
"\n"
"")
MainWindow.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks|QtWidgets.QMainWindow.AnimatedDocks)
"width: 50px;\n"
"height: 55px;\n"
"border:solid 1px black opacity 0.4;\n"
"background-none;\n"
"}\n"
"\n"
"#toolBar_General QToolButton {\n"
"width: 36px;\n"
"height: 36px;\n"
"border:solid 1px black opacity 0.4;\n"
"background-none;\n"
"}\n"
"\n"
"")
MainWindow.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks | QtWidgets.QMainWindow.AnimatedDocks)
self.uiCentralWidget = QtWidgets.QWidget(MainWindow)
self.uiCentralWidget.setObjectName("uiCentralWidget")
self.gridlayout = QtWidgets.QGridLayout(self.uiCentralWidget)
@@ -68,7 +69,7 @@ class Ui_MainWindow(object):
self.uiToolsMenu = QtWidgets.QMenu(self.uiMenuBar)
self.uiToolsMenu.setObjectName("uiToolsMenu")
MainWindow.setMenuBar(self.uiMenuBar)
self.uiStatusBar = QtWidgets.QStatusBar(MainWindow)
self.uiStatusBar = StatusBar(MainWindow)
self.uiStatusBar.setObjectName("uiStatusBar")
MainWindow.setStatusBar(self.uiStatusBar)
self.uiGeneralToolBar = QtWidgets.QToolBar(MainWindow)
@@ -82,13 +83,13 @@ class Ui_MainWindow(object):
self.uiNodesDockWidget.setEnabled(True)
self.uiNodesDockWidget.setVisible(True)
self.uiNodesDockWidget.setFloating(False)
self.uiNodesDockWidget.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea|QtCore.Qt.RightDockWidgetArea)
self.uiNodesDockWidget.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea)
self.uiNodesDockWidget.setObjectName("uiNodesDockWidget")
self.uiNodesDockWidgetContents = QtWidgets.QWidget()
self.uiNodesDockWidgetContents.setObjectName("uiNodesDockWidgetContents")
self.vboxlayout = QtWidgets.QVBoxLayout(self.uiNodesDockWidgetContents)
self.vboxlayout.setSpacing(0)
self.vboxlayout.setContentsMargins(0, 0, 0, 0)
self.vboxlayout.setSpacing(0)
self.vboxlayout.setObjectName("vboxlayout")
self.uiNodesView = NodesView(self.uiNodesDockWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
@@ -129,8 +130,8 @@ class Ui_MainWindow(object):
self.uiConsoleDockWidgetContents = QtWidgets.QWidget()
self.uiConsoleDockWidgetContents.setObjectName("uiConsoleDockWidgetContents")
self.vboxlayout1 = QtWidgets.QVBoxLayout(self.uiConsoleDockWidgetContents)
self.vboxlayout1.setSpacing(0)
self.vboxlayout1.setContentsMargins(0, 0, 0, 0)
self.vboxlayout1.setSpacing(0)
self.vboxlayout1.setObjectName("vboxlayout1")
self.uiConsoleTextEdit = ConsoleView(self.uiConsoleDockWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
@@ -212,22 +213,22 @@ class Ui_MainWindow(object):
self.uiOnlineHelpAction.setObjectName("uiOnlineHelpAction")
self.uiScreenshotAction = QtWidgets.QAction(MainWindow)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/icons/camera-photo.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon4.addPixmap(QtGui.QPixmap(":/icons/camera-photo-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon4.addPixmap(QtGui.QPixmap(":/icons/camera-photo.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiScreenshotAction.setIcon(icon4)
self.uiScreenshotAction.setObjectName("uiScreenshotAction")
self.uiStartAllAction = QtWidgets.QAction(MainWindow)
self.uiStartAllAction.setEnabled(True)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/icons/start.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon5.addPixmap(QtGui.QPixmap(":/icons/start-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon5.addPixmap(QtGui.QPixmap(":/icons/start.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiStartAllAction.setIcon(icon5)
self.uiStartAllAction.setObjectName("uiStartAllAction")
self.uiStopAllAction = QtWidgets.QAction(MainWindow)
self.uiStopAllAction.setEnabled(True)
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(":/icons/stop.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon6.addPixmap(QtGui.QPixmap(":/icons/stop-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon6.addPixmap(QtGui.QPixmap(":/icons/stop.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiStopAllAction.setIcon(icon6)
self.uiStopAllAction.setObjectName("uiStopAllAction")
self.uiConsoleAllAction = QtWidgets.QAction(MainWindow)
@@ -241,14 +242,14 @@ class Ui_MainWindow(object):
self.uiAboutQtAction.setObjectName("uiAboutQtAction")
self.uiZoomInAction = QtWidgets.QAction(MainWindow)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(":/icons/zoom-in.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon8.addPixmap(QtGui.QPixmap(":/icons/zoom-in-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon8.addPixmap(QtGui.QPixmap(":/icons/zoom-in.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiZoomInAction.setIcon(icon8)
self.uiZoomInAction.setObjectName("uiZoomInAction")
self.uiZoomOutAction = QtWidgets.QAction(MainWindow)
icon9 = QtGui.QIcon()
icon9.addPixmap(QtGui.QPixmap(":/icons/zoom-out.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon9.addPixmap(QtGui.QPixmap(":/icons/zoom-out-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon9.addPixmap(QtGui.QPixmap(":/icons/zoom-out.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiZoomOutAction.setIcon(icon9)
self.uiZoomOutAction.setObjectName("uiZoomOutAction")
self.uiZoomResetAction = QtWidgets.QAction(MainWindow)
@@ -265,8 +266,8 @@ class Ui_MainWindow(object):
self.uiPreferencesAction.setObjectName("uiPreferencesAction")
self.uiSuspendAllAction = QtWidgets.QAction(MainWindow)
icon11 = QtGui.QIcon()
icon11.addPixmap(QtGui.QPixmap(":/icons/pause.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon11.addPixmap(QtGui.QPixmap(":/icons/pause-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon11.addPixmap(QtGui.QPixmap(":/icons/pause.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiSuspendAllAction.setIcon(icon11)
self.uiSuspendAllAction.setObjectName("uiSuspendAllAction")
self.uiAddNoteAction = QtWidgets.QAction(MainWindow)
@@ -294,15 +295,15 @@ class Ui_MainWindow(object):
self.uiDrawRectangleAction = QtWidgets.QAction(MainWindow)
self.uiDrawRectangleAction.setCheckable(True)
icon16 = QtGui.QIcon()
icon16.addPixmap(QtGui.QPixmap(":/icons/rectangle.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon16.addPixmap(QtGui.QPixmap(":/icons/rectangle-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon16.addPixmap(QtGui.QPixmap(":/icons/rectangle.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiDrawRectangleAction.setIcon(icon16)
self.uiDrawRectangleAction.setObjectName("uiDrawRectangleAction")
self.uiDrawEllipseAction = QtWidgets.QAction(MainWindow)
self.uiDrawEllipseAction.setCheckable(True)
icon17 = QtGui.QIcon()
icon17.addPixmap(QtGui.QPixmap(":/icons/ellipse.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon17.addPixmap(QtGui.QPixmap(":/icons/ellipse-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon17.addPixmap(QtGui.QPixmap(":/icons/ellipse.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiDrawEllipseAction.setIcon(icon17)
self.uiDrawEllipseAction.setObjectName("uiDrawEllipseAction")
self.uiShowPortNamesAction = QtWidgets.QAction(MainWindow)
@@ -344,32 +345,32 @@ class Ui_MainWindow(object):
self.uiCheckForUpdateAction.setObjectName("uiCheckForUpdateAction")
self.uiBrowseRoutersAction = QtWidgets.QAction(MainWindow)
icon23 = QtGui.QIcon()
icon23.addPixmap(QtGui.QPixmap(":/icons/router.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon23.addPixmap(QtGui.QPixmap(":/icons/router-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon23.addPixmap(QtGui.QPixmap(":/icons/router.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiBrowseRoutersAction.setIcon(icon23)
self.uiBrowseRoutersAction.setObjectName("uiBrowseRoutersAction")
self.uiBrowseSwitchesAction = QtWidgets.QAction(MainWindow)
icon24 = QtGui.QIcon()
icon24.addPixmap(QtGui.QPixmap(":/icons/switch.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon24.addPixmap(QtGui.QPixmap(":/icons/switch-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon24.addPixmap(QtGui.QPixmap(":/icons/switch.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiBrowseSwitchesAction.setIcon(icon24)
self.uiBrowseSwitchesAction.setObjectName("uiBrowseSwitchesAction")
self.uiBrowseEndDevicesAction = QtWidgets.QAction(MainWindow)
icon25 = QtGui.QIcon()
icon25.addPixmap(QtGui.QPixmap(":/icons/PC.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon25.addPixmap(QtGui.QPixmap(":/icons/PC-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon25.addPixmap(QtGui.QPixmap(":/icons/PC.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiBrowseEndDevicesAction.setIcon(icon25)
self.uiBrowseEndDevicesAction.setObjectName("uiBrowseEndDevicesAction")
self.uiBrowseSecurityDevicesAction = QtWidgets.QAction(MainWindow)
icon26 = QtGui.QIcon()
icon26.addPixmap(QtGui.QPixmap(":/icons/firewall.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon26.addPixmap(QtGui.QPixmap(":/icons/firewall-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon26.addPixmap(QtGui.QPixmap(":/icons/firewall.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiBrowseSecurityDevicesAction.setIcon(icon26)
self.uiBrowseSecurityDevicesAction.setObjectName("uiBrowseSecurityDevicesAction")
self.uiBrowseAllDevicesAction = QtWidgets.QAction(MainWindow)
icon27 = QtGui.QIcon()
icon27.addPixmap(QtGui.QPixmap(":/icons/browse-all-icons.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon27.addPixmap(QtGui.QPixmap(":/icons/browse-all-icons-hover.png"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon27.addPixmap(QtGui.QPixmap(":/icons/browse-all-icons.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.uiBrowseAllDevicesAction.setIcon(icon27)
self.uiBrowseAllDevicesAction.setObjectName("uiBrowseAllDevicesAction")
self.uiAddLinkAction = QtWidgets.QAction(MainWindow)
@@ -377,8 +378,8 @@ class Ui_MainWindow(object):
icon28 = QtGui.QIcon()
icon28.addPixmap(QtGui.QPixmap(":/icons/connection-new-hover.svg"), QtGui.QIcon.Active, QtGui.QIcon.Off)
icon28.addPixmap(QtGui.QPixmap(":/icons/connection-new.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
icon28.addPixmap(QtGui.QPixmap(":/icons/cancel-connection.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
self.uiAddLinkAction.setIcon(icon28)
self.uiAddLinkAction.setObjectName("uiAddLinkAction")
self.uiFitInViewAction = QtWidgets.QAction(MainWindow)
@@ -687,9 +688,10 @@ class Ui_MainWindow(object):
self.uiWebInterfaceAction.setText(_translate("MainWindow", "Web interface"))
self.uiDrawLineAction.setText(_translate("MainWindow", "Drawn line"))
from ..console_view import ConsoleView
from ..topology_summary_view import TopologySummaryView
from ..compute_summary_view import ComputeSummaryView
from ..console_view import ConsoleView
from ..graphics_view import GraphicsView
from ..nodes_view import NodesView
from ..status_bar import StatusBar
from ..topology_summary_view import TopologySummaryView
from . import resources_rc

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" y2="50" gradientUnits="userSpaceOnUse" x2="42" y1="18" x1="12">
<stop stop-color="#ddd" offset="0"/>
<stop stop-color="#fff" offset="1"/>
</linearGradient>
<linearGradient id="e" x2="0" gradientUnits="userSpaceOnUse" y2="46.2" y1="41.2">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="b" y2="51" gradientUnits="userSpaceOnUse" y1="24" x2="38" x1="6">
<stop stop-color="#e88" offset="0"/>
<stop stop-color="#d33" offset="1"/>
</linearGradient>
<radialGradient id="f" gradientUnits="userSpaceOnUse" cy="41.2" cx="39.1" gradientTransform="matrix(2 0 0 1.28 -39.1 -11.6)" r="3.9">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</radialGradient>
</defs>
<g opacity=".4">
<path id="d" fill="url(#f)" d="m39.1 46.1c3.23 0 7.9-2.24 7.9-5s-3.7-5-7.9-5z"/>
<use x="-48" xlink:href="#d" transform="scale(-1,1)"/>
<path fill="url(#e)" d="m8.9 36.1h30.2v10h-30.2z"/>
</g>
<g stroke-linejoin="round">
<g stroke="#900" stroke-width="6">
<path id="c" d="m7 40h34l-17-32z"/>
</g>
<use stroke-width="4" stroke="url(#b)" xlink:href="#c"/>
<use stroke-width="2" stroke="#c00" xlink:href="#c" fill="#c00"/>
<path stroke="url(#a)" d="m9.8 38.3h28.4l-14.2-26.7z" fill="url(#a)"/>
</g>
<path d="m22.75 30.4l-0.5-10h3.5l-0.6 10z"/>
<circle cy="33.58" cx="24" r="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -28,6 +28,7 @@
<file alias="icons/default.svg">icons/cancel.svg</file>
<file>icons/configuration.svg</file>
<file>icons/cancel.svg</file>
<file>icons/warning.svg</file>
<file>icons/cancel-connection.svg</file>
<file>icons/connection-new.svg</file>
<file>icons/connection-new-hover.svg</file>