Skip to content

Commit

Permalink
Refactor Main Window status bar to improve information presentation (#…
Browse files Browse the repository at this point in the history
…5451)

closes #5417
  • Loading branch information
Czaki committed Jan 24, 2023
1 parent a06077f commit 180992f
Showing 1 changed file with 155 additions and 60 deletions.
215 changes: 155 additions & 60 deletions napari/_qt/widgets/qt_viewer_status_bar.py
@@ -1,14 +1,9 @@
"""Status bar widget on the viewer MainWindow"""
from typing import TYPE_CHECKING, Optional

from qtpy.QtCore import Qt
from qtpy.QtWidgets import (
QHBoxLayout,
QLabel,
QSizePolicy,
QStatusBar,
QWidget,
)
from typing import TYPE_CHECKING, Optional, cast

from qtpy.QtCore import QEvent, Qt
from qtpy.QtGui import QFontMetrics, QResizeEvent
from qtpy.QtWidgets import QLabel, QStatusBar, QWidget
from superqt import QElidingLabel

from napari._qt.dialogs.qt_activity_dialog import ActivityToggleItem
Expand All @@ -22,48 +17,45 @@ class ViewerStatusBar(QStatusBar):
def __init__(self, parent: '_QtMainWindow') -> None:
super().__init__(parent=parent)

main_widget = QWidget()

layout = QHBoxLayout()

self._status = QLabel(trans._('Ready'))
self._status.setContentsMargins(0, 0, 0, 0)

self._layer_base = QElidingLabel(trans._(''))
self._layer_base.setObjectName('layer_base status')
self._layer_base.setElideMode(Qt.TextElideMode.ElideMiddle)
self._layer_base.setMinimumSize(100, 16)
self._layer_base.setContentsMargins(0, 0, 0, 0)
self._layer_base.setSizePolicy(
QSizePolicy.Minimum, QSizePolicy.Maximum
)

self._plugin_reader = QElidingLabel(trans._(''))
self._plugin_reader.setObjectName('plugin-reader status')
self._plugin_reader.setMinimumSize(80, 16)
self._plugin_reader.setContentsMargins(0, 0, 0, 0)
self._plugin_reader.setElideMode(Qt.TextElideMode.ElideMiddle)

self._source_type = QLabel('')
self._source_type.setObjectName('source-type status')
self._source_type.setContentsMargins(0, 0, 0, 0)

self._coordinates = QLabel('')
self._coordinates = QElidingLabel('')
self._coordinates.setObjectName('coordinates status')
self._coordinates.setMinimumSize(100, 16)
self._coordinates.setContentsMargins(0, 0, 0, 0)

layout.addWidget(self._status)
layout.addWidget(self._layer_base)
layout.addWidget(self._source_type)
layout.addWidget(self._plugin_reader)
layout.addWidget(self._coordinates)
layout.addStretch(0)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)

main_widget.setLayout(layout)
self._help = QElidingLabel('')
self._help.setObjectName('help status')
self._help.setAlignment(
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
)

main_widget = StatusBarWidget(
self._status,
self._layer_base,
self._source_type,
self._plugin_reader,
self._coordinates,
self._help,
)
self.addWidget(main_widget, 1)
self._help = QElidingLabel('')
self._help.setAlignment(Qt.AlignmentFlag.AlignRight)
self._help.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
layout.addWidget(self._help, 1)

self._activity_item = ActivityToggleItem()
self._activity_item._activityBtn.clicked.connect(
Expand All @@ -78,46 +70,32 @@ def setHelpText(self, text: str) -> None:

def setStatusText(
self,
text: str = None,
layer_base=None,
text: str = "",
layer_base: str = "",
source_type=None,
plugin=None,
coordinates=None,
plugin: str = "",
coordinates: str = "",
) -> None:
# The method used to set a single value as the status and not
# all the layer information.
self._status.setText(text)

if text:
self._status.setText(text)
else:
self._status.setText('')

if layer_base:
self._layer_base.show()
self._layer_base.setText(layer_base)
else:
self._layer_base.hide()
self._layer_base.setVisible(bool(layer_base))
self._layer_base.setText(layer_base)

self._source_type.setVisible(bool(source_type))
if source_type:
self._source_type.show()
self._source_type.setText(f'{source_type}: ')
else:
self._source_type.hide()

if plugin:
self._plugin_reader.show()
self._plugin_reader.setText(plugin)
else:
self._plugin_reader.hide()
self._plugin_reader.setVisible(bool(plugin))

if coordinates:
self._coordinates.show()
self._coordinates.setText(coordinates)
else:
self._coordinates.hide()
self._plugin_reader.setText(plugin)

self._coordinates.setVisible(bool(coordinates))
self._coordinates.setText(coordinates)

def _toggle_activity_dock(self, visible: Optional[bool] = None):
par: _QtMainWindow = self.parent()
par = cast('_QtMainWindow', self.parent())
if visible is None:
visible = not par._activity_dialog.isVisible()
if visible:
Expand All @@ -129,3 +107,120 @@ def _toggle_activity_dock(self, visible: Optional[bool] = None):
else:
par._activity_dialog.hide()
self._activity_item._activityBtn.setArrowType(Qt.ArrowType.UpArrow)


class StatusBarWidget(QWidget):
def __init__(
self,
status_label: QLabel,
layer_label: QLabel,
source_label: QLabel,
plugin_label: QLabel,
coordinates_label: QLabel,
help_label: QLabel,
parent: QWidget = None,
):
super().__init__(parent=parent)
self._status_label = status_label
self._layer_label = layer_label
self._source_label = source_label
self._plugin_label = plugin_label
self._coordinates_label = coordinates_label
self._help_label = help_label

self._status_label.setParent(self)
self._layer_label.setParent(self)
self._source_label.setParent(self)
self._plugin_label.setParent(self)
self._coordinates_label.setParent(self)
self._help_label.setParent(self)

def resizeEvent(self, event: QResizeEvent) -> None:
super().resizeEvent(event)
self.do_layout()

def event(self, event: QEvent) -> bool:
if event.type() == QEvent.Type.LayoutRequest:
self.do_layout()
return super().event(event)

@staticmethod
def _calc_width(fm: QFontMetrics, label: QLabel) -> int:
# magical nuber +2 is from superqt code
# magical number +12 is from experiments
# Adding this values is required to avoid the text to be elided
# if there is enough space to show it.
return (
(
fm.boundingRect(label.text()).width()
+ label.margin() * 2
+ 2
+ 12
)
if label.isVisible()
else 0
)

def do_layout(self):
width = self.width()
height = self.height()

fm = QFontMetrics(self._status_label.font())

status_width = self._calc_width(fm, self._status_label)
layer_width = self._calc_width(fm, self._layer_label)
source_width = self._calc_width(fm, self._source_label)
plugin_width = self._calc_width(fm, self._plugin_label)
coordinates_width = self._calc_width(fm, self._coordinates_label)

base_width = (
status_width
+ layer_width
+ source_width
+ plugin_width
+ coordinates_width
)

help_width = max(0, width - base_width)

if coordinates_width:
help_width = 0

if base_width > width:
self._help_label.setVisible(False)
layer_width = max(
int((layer_width / base_width) * layer_width),
min(self._layer_label.minimumWidth(), layer_width),
)
source_width = max(
int((source_width / base_width) * source_width),
min(self._source_label.minimumWidth(), source_width),
)
plugin_width = max(
int((plugin_width / base_width) * plugin_width),
min(self._plugin_label.minimumWidth(), plugin_width),
)
coordinates_width = (
base_width
- status_width
- layer_width
- source_width
- plugin_width
)

else:
self._help_label.setVisible(True)

self._status_label.setGeometry(0, 0, status_width, height)
shift = status_width
self._layer_label.setGeometry(shift, 0, layer_width, height)
shift += layer_width
self._source_label.setGeometry(shift, 0, source_width, height)
shift += source_width
self._plugin_label.setGeometry(shift, 0, plugin_width, height)
shift += plugin_width
self._coordinates_label.setGeometry(
shift, 0, coordinates_width, height
)
shift += coordinates_width
self._help_label.setGeometry(shift, 0, help_width, height)

0 comments on commit 180992f

Please sign in to comment.