diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1b9c67094030305bd9970a6be62702ebec6385df..eb5731b7ddbac0999fbf95a3887a986e759b4e78 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -50,6 +50,7 @@ test_docs:
 test_install:
     stage: test
     image: continuumio/miniconda3:latest
+    before_script: []  # before_script not needed here!
     script:
         - pip install .[all]
 
@@ -70,7 +71,7 @@ pages:
 pypi:
     stage: deploy
     image: continuumio/miniconda3:latest
-    before_script: []
+    before_script: []  # before_script not needed here!
     script:
         - pip install twine
         - python setup.py sdist bdist_wheel
diff --git a/setup.cfg b/setup.cfg
index 95906f1bf28747410afef7c0331c779c5bd4ce09..9bc36ea6945cfb05f09deca4579ebcc9b30e9833 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -36,7 +36,6 @@ packages = find:
 python_requires = >=3.7
 setup_requires =
     setuptools
-    matplotlib>=3
 tests_require =
     coverage
     pytest
@@ -51,10 +50,10 @@ install_requires =
     scipy
     Pillow
 
+[options.packages.find]
+where = src
+
 [options.extras_require]
-3Dplot =
-    qt==4.8
-    mayavi==4.5
 io =
     hyperspy
 fftw =
@@ -62,8 +61,6 @@ fftw =
 colors =
     cmocean
 all =
-    qt==4.8
-    mayavi==4.5
     hyperspy
     pyfftw
     cmocean
diff --git a/setup.py b/setup.py
index bf06da17f2fb7fa50196f22bae993373f1bfefa7..77335139311e09186089325e653d7c8fa111d0ac 100644
--- a/setup.py
+++ b/setup.py
@@ -2,9 +2,8 @@
 # coding=utf-8
 """Setup for testing, building, distributing and installing the 'EMPyRe'-package"""
 
+
 import os
-import glob
-import shutil
 import subprocess
 from setuptools import setup, config
 
@@ -27,16 +26,3 @@ with open(os.path.join(os.path.dirname(__file__), 'src', 'empyre', 'version.py')
 # Run setup (reads metadata & options from setup.cfg):
 print(R'running setup.py')
 setup()
-
-
-import matplotlib as mpl
-# Find matplotlib styles:
-BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib')
-STYLE_PATH = os.path.join(os.getcwd(), 'mplstyles')
-style_files = glob.glob(os.path.join(STYLE_PATH, '*.mplstyle'))
-# Copy them to the local matplotlib styles folder:
-print(f'copying matplotlib stylesheets to {BASE_LIBRARY_PATH}')
-for style_path in style_files:
-    _, fname = os.path.split(style_path)
-    dest = os.path.join(BASE_LIBRARY_PATH, fname)
-    shutil.copy(style_path, dest)
diff --git a/src/empyre/vis/colors.py b/src/empyre/vis/colors.py
index 46b7d963bbd847f63e0b11f30dc6624d6ea87251..344a41fbdbfdeb82594ee6bfe3e6d1063ee5d1f5 100644
--- a/src/empyre/vis/colors.py
+++ b/src/empyre/vis/colors.py
@@ -24,6 +24,8 @@ from matplotlib.ticker import FixedLocator
 from mpl_toolkits.mplot3d import Axes3D  # noqa: F401
 from matplotlib.patches import Circle
 
+from .tools import use_style
+
 
 __all__ = ['Colormap3D', 'ColormapCubehelix', 'ColormapPerception', 'ColormapHLS', 'ColormapClassic',
            'ColormapTransparent', 'cmaps', 'CMAP_CIRCULAR_DEFAULT', 'interpolate_color']
@@ -132,7 +134,7 @@ class Colormap3D(colors.Colormap, metaclass=abc.ABCMeta):
         if grayscale:
             color_wheel = color_wheel.convert('LA')
         # Plot the color wheel:
-        with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+        with use_style('empyre-image'):  # Only works on axes created WITHIN context!
             if axis is None:  # If no axis is set, find the current or create a new one:
                 fig = plt.figure()
                 axis = fig.add_subplot(1, 1, 1, aspect='equal')
@@ -293,7 +295,7 @@ class ColormapCubehelix(colors.LinearSegmentedColormap, Colormap3D):
 
         """
         self._log.debug('Calling plot_helix')
-        with plt.style.context('empyre-plot'):
+        with use_style('empyre-plot'):
             fig = plt.figure(figsize=figsize, constrained_layout=True)
             gs = fig.add_gridspec(2, 1, height_ratios=[8, 1])
             # Main plot:
diff --git a/src/empyre/vis/decorators.py b/src/empyre/vis/decorators.py
index a234bb877a2b1d821274e6d21d8b999658c7c5e2..a3509fea671eb14a8e9cda58ccb4b51c05d3e62f 100644
--- a/src/empyre/vis/decorators.py
+++ b/src/empyre/vis/decorators.py
@@ -17,6 +17,7 @@ from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
 from mpl_toolkits.axes_grid1.inset_locator import inset_axes
 
 from . import colors
+from .tools import use_style
 from ..fields.field import Field
 
 
@@ -338,7 +339,7 @@ def colorbar(im, fig=None, cbar_axis=None, axes=None, position='right', pad=0.02
             bounds = [left, bottom-pad-thickness, right-left, thickness]
         cbar_axis = fig.add_axes(bounds)
     # Create the colorbar:
-    with plt.style.context('empyre-image'):
+    with use_style('empyre-image'):
         if position in ('left', 'right'):
             cb = plt.colorbar(im, cax=cbar_axis, orientation='vertical')
             cb.ax.yaxis.set_ticks_position(position)
diff --git a/mplstyles/empyre-image.mplstyle b/src/empyre/vis/mplstyles/empyre-image.mplstyle
similarity index 100%
rename from mplstyles/empyre-image.mplstyle
rename to src/empyre/vis/mplstyles/empyre-image.mplstyle
diff --git a/mplstyles/empyre-plot.mplstyle b/src/empyre/vis/mplstyles/empyre-plot.mplstyle
similarity index 100%
rename from mplstyles/empyre-plot.mplstyle
rename to src/empyre/vis/mplstyles/empyre-plot.mplstyle
diff --git a/mplstyles/empyre-save.mplstyle b/src/empyre/vis/mplstyles/empyre-save.mplstyle
similarity index 100%
rename from mplstyles/empyre-save.mplstyle
rename to src/empyre/vis/mplstyles/empyre-save.mplstyle
diff --git a/src/empyre/vis/plot2d.py b/src/empyre/vis/plot2d.py
index cdf98309aac3d64b27f248c7946ac7c85de8bd1f..2cbfa8ece13886cf7b721a6a096da8ab688f7100 100644
--- a/src/empyre/vis/plot2d.py
+++ b/src/empyre/vis/plot2d.py
@@ -13,6 +13,7 @@ from matplotlib.colors import LinearSegmentedColormap
 from PIL import Image
 
 from . import colors
+from .tools import use_style
 from ..fields.field import Field
 
 
@@ -68,7 +69,7 @@ def imshow(field, axis=None, cmap=None, **kwargs):
             cmap = cmocean.cm.balance
         except ImportError:
             _log.info('cmocean.balance not found, fallback to rRdBu!')
-            cmap = 'RdBu_r'  # '_r' for reverse!
+            cmap = plt.get_cmap('RdBu_r')  # '_r' for reverse!
     elif isinstance(cmap, str):  # make sure we have a Colormap object (and not a string):
         cmap = plt.get_cmap(cmap)
     if cmap.name.replace('_r', '') in DIVERGING_CMAPS:  # 'replace' also matches reverted cmaps!
@@ -89,7 +90,7 @@ def imshow(field, axis=None, cmap=None, **kwargs):
     dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale
     kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v))
     # Plot with the empyre style context:
-    with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+    with use_style('empyre-image'):  # Only works on axes created WITHIN context!
         if axis is None:  # If no axis is set, find the current or create a new one:
             axis = plt.gca()
         return axis.imshow(squeezed_field, cmap=cmap, **kwargs)
@@ -132,7 +133,7 @@ def contour(field, axis=None, **kwargs):
     kwargs.setdefault('linestyles', 'dotted')
     kwargs.setdefault('linewidths', 2)
     # Plot with the empyre style context:
-    with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+    with use_style('empyre-image'):  # Only works on axes created WITHIN context!
         if axis is None:  # If no axis is set, find the current or create a new one:
             axis = plt.gca()
             axis.set_aspect('equal')
@@ -197,7 +198,7 @@ def colorvec(field, axis=None, **kwargs):
     dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale
     kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v))
     # Plot with the empyre style context:
-    with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+    with use_style('empyre-image'):  # Only works on axes created WITHIN context!
         if axis is None:  # If no axis is set, find the current or create a new one:
             axis = plt.gca()
         return axis.imshow(Image.fromarray(rgb), **kwargs)
@@ -255,7 +256,7 @@ def cosine_contours(field, axis=None, gain='auto', cmap=None, **kwargs):
     dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale
     kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v))
     # Plot with the empyre style context:
-    with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+    with use_style('empyre-image'):  # Only works on axes created WITHIN context!
         if axis is None:  # If no axis is set, find the current or create a new one:
             axis = plt.gca()
         return axis.imshow(contours, cmap=cmap, **kwargs)
@@ -376,7 +377,7 @@ def quiver(field, axis=None, color_angles=False, cmap=None, n_bin='auto', bin_wi
     kwargs.setdefault('minshaft', 2)
     kwargs.setdefault('linewidths', 1)
     # Plot with the empyre style context:
-    with plt.style.context('empyre-image'):  # Only works on axes created WITHIN context!
+    with use_style('empyre-image'):  # Only works on axes created WITHIN context!
         if axis is None:  # If no axis is set, find the current or create a new one:
             axis = plt.gca()
         axis.set_xlim(0, u_lim)
diff --git a/src/empyre/vis/tools.py b/src/empyre/vis/tools.py
index 2d5b3a4ea35796c267b8d9ee006fcde297b9b95e..28cdde811f073d85735c10e81c7071fd221a9705 100644
--- a/src/empyre/vis/tools.py
+++ b/src/empyre/vis/tools.py
@@ -5,16 +5,21 @@
 """This module provides helper functions to the vis module."""
 
 
+import os
+import glob
+import shutil
 import logging
 from numbers import Number
+from contextlib import contextmanager
 
 import numpy as np
+import matplotlib as mpl
 import matplotlib.pyplot as plt
 
 from ..fields.field import Field
 
 
-__all__ = ['new', 'savefig', 'calc_figsize']
+__all__ = ['new', 'savefig', 'calc_figsize', 'use_style', 'copy_mpl_stylesheets']
 _log = logging.getLogger(__name__)
 
 
@@ -79,10 +84,10 @@ def new(nrows=1, ncols=1, mode='image', figsize=None, textwidth=None, width_scal
             assert isinstance(aspect, Number), 'aspect has to be None, a number or a field instance squeezable to 2D!'
         figsize = calc_figsize(width_scale=width_scale, aspect=aspect, textwidth=textwidth)
     if mode == 'image':
-        with plt.style.context('empyre-image'):
+        with use_style('empyre-image'):
             return plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize, **kwargs)
     else:  # mode == 'plot':
-        with plt.style.context('empyre-plot'):
+        with use_style('empyre-plot'):
             return plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize, **kwargs)
 
 
@@ -101,7 +106,7 @@ def savefig(fname, **kwargs):
 
     """
     _log.debug('Calling savefig')
-    with plt.style.context('empyre-save'):
+    with use_style('empyre-save'):
         plt.savefig(fname, **kwargs)
 
 
@@ -143,3 +148,48 @@ def calc_figsize(textwidth, width_scale=1, aspect=1):
         raise ValueError(f"aspect has to be either a number, 'same' or 'golden'! Was {aspect}!")
     fig_size = [fig_width, fig_height]  # Both in inches
     return fig_size
+
+
+@contextmanager
+def use_style(stylename):
+    """Context that uses a matplotlib stylesheet. Can fall back to local mpl stylesheets if necessary!
+
+    Parameters
+    ----------
+    stylename : str
+        A style specification.
+
+    Yields
+    -------
+    context
+        Context manager for using style settings temporarily.
+
+    """
+    try:  # Try to load the style directly (works if it is installed somewhere mpl looks for it):
+        with plt.style.context(stylename) as context:
+            yield context
+    except OSError:  # Stylesheet not found, use local ones:
+        mplstyle_path = os.path.join(os.path.dirname(__file__), 'mplstyles', f'{stylename}.mplstyle')
+        with plt.style.context(mplstyle_path) as context:
+            yield context
+
+
+def copy_mpl_stylesheets():
+    """Copy matplotlib styles to the users matplotlib config directory. Useful if you want to utilize them elsewhere.
+
+    Notes
+    -----
+    You might need to restart your Python session for the stylesheets to be recognized/found!
+
+    """
+    # Find matplotlib styles:
+    user_stylelib_path = os.path.join(mpl.get_configdir(), 'stylelib')
+    vis_dir = os.path.dirname(__file__)
+    style_files = glob.glob(os.path.join(vis_dir, 'mplstyles', '*.mplstyle'))
+    # Copy them to the local matplotlib styles folder:
+    if not os.path.exists(user_stylelib_path):
+        os.makedirs(user_stylelib_path)
+    for style_path in style_files:
+        _, fname = os.path.split(style_path)
+        dest = os.path.join(user_stylelib_path, fname)
+        shutil.copy(style_path, dest)