Repository: spyder-ide/spyder

Issue:
WaitSpinner color under the dark theme should be white and suggestion for improving the look of the spinner
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

## Problem Description

The wait spinner in the `Find` plugin should be white under the dark theme.

![waiting_spinner_dark](https://user-images.githubusercontent.com/10170372/59516741-001f5900-8e90-11e9-8b52-73115b1b5645.gif)

Also, while working on another project, I made some changes to the look of the spinner. I personally like it better like that, but what do you think?

![waiting_spinner_modif](https://user-images.githubusercontent.com/10170372/59516773-16c5b000-8e90-11e9-8127-3fa36155cd26.gif)

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder 4.0.0.dev0 (Commit: f17a9b15c) 
* Python 3.7.1 64-bit | Qt 5.9.6 | PyQt5 5.9.2 | Windows 10 



Discussion:
Hi yes! Agreed is much better!
It would be good to define some usage guidelines if we need to use the spinner somewhere else

Resolving patch:
diff --git a/spyder/plugins/findinfiles/widgets.py b/spyder/plugins/findinfiles/widgets.py
--- a/spyder/plugins/findinfiles/widgets.py
+++ b/spyder/plugins/findinfiles/widgets.py
@@ -41,9 +41,8 @@
 from spyder.widgets.comboboxes import PatternComboBox
 from spyder.widgets.onecolumntree import OneColumnTree
 from spyder.utils.misc import regexp_error_msg
-from spyder.utils.qthelpers import create_toolbutton
+from spyder.utils.qthelpers import create_toolbutton, create_waitspinner
 from spyder.config.gui import get_font
-from spyder.widgets.waitingspinner import QWaitingSpinner
 
 
 ON = 'on'
@@ -911,9 +910,7 @@ def __init__(self, parent):
         QWidget.__init__(self, parent)
 
         self.status_text = QLabel(self)
-        self.spinner = QWaitingSpinner(self, centerOnParent=False)
-        self.spinner.setNumberOfLines(12)
-        self.spinner.setInnerRadius(2)
+        self.spinner = create_waitspinner(parent=self)
         layout = QHBoxLayout()
         layout.addWidget(self.spinner)
         layout.addWidget(self.status_text)
diff --git a/spyder/utils/qthelpers.py b/spyder/utils/qthelpers.py
--- a/spyder/utils/qthelpers.py
+++ b/spyder/utils/qthelpers.py
@@ -7,6 +7,7 @@
 """Qt utilities"""
 
 # Standard library imports
+from math import pi
 import os
 import os.path as osp
 import re
@@ -24,11 +25,12 @@
 
 # Local imports
 from spyder.config.base import get_image_path, running_in_mac_app
-from spyder.config.gui import get_shortcut
+from spyder.config.gui import get_shortcut, is_dark_interface
 from spyder.utils import programs
 from spyder.utils import icon_manager as ima
 from spyder.utils.icon_manager import get_icon, get_std_icon
 from spyder.py3compat import is_text_string, to_text_string
+from spyder.widgets.waitingspinner import QWaitingSpinner
 
 # Note: How to redirect a signal from widget *a* to widget *b* ?
 # ----
@@ -214,6 +216,30 @@ def create_toolbutton(parent, text=None, shortcut=None, icon=None, tip=None,
     return button
 
 
+def create_waitspinner(size=32, n=11, parent=None):
+    """
+    Create a wait spinner with the specified size built with n circling dots.
+    """
+    dot_padding = 1
+
+    # To calculate the size of the dots, we need to solve the following
+    # system of two equations in two variables.
+    # (1) middle_circumference = pi * (size - dot_size)
+    # (2) middle_circumference = n * (dot_size + dot_padding)
+    dot_size = (pi * size - n * dot_padding) / (n + pi)
+    inner_radius = (size - 2 * dot_size) / 2
+
+    spinner = QWaitingSpinner(parent, centerOnParent=False)
+    spinner.setTrailSizeDecreasing(True)
+    spinner.setNumberOfLines(n)
+    spinner.setLineLength(dot_size)
+    spinner.setLineWidth(dot_size)
+    spinner.setInnerRadius(inner_radius)
+    spinner.setColor(Qt.white if is_dark_interface() else Qt.black)
+
+    return spinner
+
+
 def action2button(action, autoraise=True, text_beside_icon=False, parent=None):
     """Create a QToolButton directly from a QAction object"""
     if parent is None:
diff --git a/spyder/widgets/waitingspinner.py b/spyder/widgets/waitingspinner.py
--- a/spyder/widgets/waitingspinner.py
+++ b/spyder/widgets/waitingspinner.py
@@ -36,9 +36,9 @@
 
 import math
 
-from qtpy.QtCore import *
-from qtpy.QtGui import *
-from qtpy.QtWidgets import *
+from qtpy.QtCore import QRect, Qt, QTimer
+from qtpy.QtGui import QColor, QPainter
+from qtpy.QtWidgets import QWidget
 
 
 class QWaitingSpinner(QWidget):
@@ -55,6 +55,7 @@ def __init__(self, parent, centerOnParent=True,
         self._roundness = 100.0
         self._minimumTrailOpacity = 3.14159265358979323846
         self._trailFadePercentage = 80.0
+        self._trailSizeDecreasing = False
         self._revolutionsPerSecond = 1.57079632679489661923
         self._numberOfLines = 20
         self._lineLength = 10
@@ -92,9 +93,19 @@ def paintEvent(self, QPaintEvent):
             distance = self.lineCountDistanceFromPrimary(i, self._currentCounter, self._numberOfLines)
             color = self.currentLineColor(distance, self._numberOfLines, self._trailFadePercentage,
                                           self._minimumTrailOpacity, self._color)
+
+            # Compute the scaling factor to apply to the size and thickness
+            # of the lines in the trail.
+            if self._trailSizeDecreasing:
+                sf = (self._numberOfLines - distance) / self._numberOfLines
+            else:
+                sf = 1
+
             painter.setBrush(color)
-            painter.drawRoundedRect(QRect(0, -self._lineWidth / 2, self._lineLength, self._lineWidth), self._roundness,
-                                    self._roundness, Qt.RelativeSize)
+            rect = QRect(0, -self._lineWidth / 2,
+                         sf * self._lineLength, sf * self._lineWidth)
+            painter.drawRoundedRect(
+                rect, self._roundness, self._roundness, Qt.RelativeSize)
             painter.restore()
 
     def start(self):
@@ -158,6 +169,13 @@ def numberOfLines(self):
     def lineLength(self):
         return self._lineLength
 
+    def isTrailSizeDecreasing(self):
+        """
+        Return whether the length and thickness of the trailing lines
+        are decreasing.
+        """
+        return self._trailSizeDecreasing
+
     def lineWidth(self):
         return self._lineWidth
 
@@ -180,6 +198,13 @@ def setRevolutionsPerSecond(self, revolutionsPerSecond):
     def setTrailFadePercentage(self, trail):
         self._trailFadePercentage = trail
 
+    def setTrailSizeDecreasing(self, value):
+        """
+        Set whether the length and thickness of the trailing lines
+        are decreasing.
+        """
+        self._trailSizeDecreasing = value
+
     def setMinimumTrailOpacity(self, minimumTrailOpacity):
         self._minimumTrailOpacity = minimumTrailOpacity
 


Test patch: