We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ValidationError when trying to plot more than 16 different colors
### 🐛 Bug Report

Dear napari team,

when I try to plot points with more than 16 different colors I get following error:

```python

File [AppData\Local\miniconda3\envs\xparse\lib\site-packages\napari\utils\events\evented_model.py:242](/AppData/Local/miniconda3/envs/xparse/lib/site-packages/napari/utils/events/evented_model.py:242), in EventedModel.__init__(self, **kwargs)
    [241] -> None:
--> [242]     super().__init__(**kwargs)
    [244]     self._events.source = self
    [245]     # add event emitters for each field which is mutable

File [AppData\Local\miniconda3\envs\xparse\lib\site-packages\pydantic\main.py:341](/Local/miniconda3/envs/xparse/lib/site-packages/pydantic/main.py:341), in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for ColorManager
__root__
  Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe' (type=type_error)
``` 

For 16 different colors or less it is no problem.


### 💡 Steps to Reproduce

The bug can be reproduced using following code:

```python
from skimage import data
import numpy as np
import napari

# set parameters for point generation
n_points = 100
n_clusters = 17
points = np.random.rand(n_points, 2) * 100

# start viewer
viewer = napari.view_image(data.astronaut(), rgb=True)

# set point properties
point_properties = {
    'abc': np.random.choice([str(elem) for elem in np.arange(n_clusters)], n_points)
}

# add points
points_layer = viewer.add_points(
    points,
    properties=point_properties,
    face_color='abc',
    face_color_cycle=['magenta', 'green'],
    edge_width=0.1,
)
``` 

The number of clusters can be changed with `n_clusters`.

### 💡 Expected Behavior

I expect to be able to plot points with more than 16 different colors. Since I only offered a color cycle with two colors, I expect napari to behave the same for >16 points than <=16 points.

### 🌎 Environment

napari: 0.4.18
Platform: Windows-10-10.0.19045-SP0
Python: 3.9.18 (main, Sep 11 2023, 14:09:26) [MSC v.1916 64 bit (AMD64)]
Qt: 5.15.2
PyQt5: 5.15.9
NumPy: 1.25.2
SciPy: 1.9.3
Dask: 2023.9.2
VisPy: 0.12.2
magicgui: 0.7.3
superqt: 0.6.0
in-n-out: 0.1.8
app-model: 0.2.2
npe2: 0.7.2

OpenGL:
  - GL version:  4.6.0 Compatibility Profile Context 23.10.24.05.230830
  - MAX_TEXTURE_SIZE: 16384

Screens:
  - screen 1: resolution 2560x1600, scale 1.0
  - screen 2: resolution 3840x2160, scale 1.0
  - screen 3: resolution 2880x1800, scale 1.0

Settings path:
  - [AppData\Local\napari\xparse_57fe1a37b30a9e37de3a06866d324c7f56a92d1a\settings.yaml](/AppData/Local/napari/xparse_57fe1a37b30a9e37de3a06866d324c7f56a92d1a/settings.yaml)
Plugins:
  - napari: 0.4.18 (77 contributions)
  - napari-console: 0.0.8 (0 contributions)
  - napari-svg: 0.1.10 (2 contributions)
  - ome-types: 0.4.2 (2 contributions)

### 💡 Additional Context

Also it was really difficult to detect this error. I encountered the problem in a function that was included into a dock widget. And when I tried to add the points via the widget, simply nothing happened. No error message appeared. Is it possible to somehow show the error messages when executing functions from widgets?

Beside that it is really great to work with napari! Thanks a lot in advance!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `napari/layers/utils/color_manager_utils.py`
Content:
```
1 from typing import Any, Dict, Tuple, Union
2 
3 import numpy as np
4 
5 from napari.utils.colormaps import Colormap
6 from napari.utils.translations import trans
7 
8 
9 def guess_continuous(color_map: np.ndarray) -> bool:
10     """Guess if the property is continuous (return True) or categorical (return False)
11 
12     The property is guessed as continuous if it is a float or contains over 16 elements.
13 
14     Parameters
15     ----------
16     color_map : np.ndarray
17         The property values to guess if they are continuous
18 
19     Returns
20     -------
21     continuous : bool
22         True of the property is guessed to be continuous, False if not.
23     """
24     # if the property is a floating type, guess continuous
25     return (
26         issubclass(color_map.dtype.type, np.floating)
27         or len(np.unique(color_map)) > 16
28     )
29 
30 
31 def is_color_mapped(color, properties):
32     """determines if the new color argument is for directly setting or cycle/colormap"""
33     if isinstance(color, str):
34         return color in properties
35     if isinstance(color, dict):
36         return True
37     if isinstance(color, (list, np.ndarray)):
38         return False
39 
40     raise ValueError(
41         trans._(
42             'face_color should be the name of a color, an array of colors, or the name of an property',
43             deferred=True,
44         )
45     )
46 
47 
48 def map_property(
49     prop: np.ndarray,
50     colormap: Colormap,
51     contrast_limits: Union[None, Tuple[float, float]] = None,
52 ) -> Tuple[np.ndarray, Tuple[float, float]]:
53     """Apply a colormap to a property
54 
55     Parameters
56     ----------
57     prop : np.ndarray
58         The property to be colormapped
59     colormap : napari.utils.Colormap
60         The colormap object to apply to the property
61     contrast_limits : Union[None, Tuple[float, float]]
62         The contrast limits for applying the colormap to the property.
63         If a 2-tuple is provided, it should be provided as (lower_bound, upper_bound).
64         If None is provided, the contrast limits will be set to (property.min(), property.max()).
65         Default value is None.
66     """
67 
68     if contrast_limits is None:
69         contrast_limits = (prop.min(), prop.max())
70     normalized_properties = np.interp(prop, contrast_limits, (0, 1))
71     mapped_properties = colormap.map(normalized_properties)
72 
73     return mapped_properties, contrast_limits
74 
75 
76 def _validate_colormap_mode(
77     values: Dict[str, Any]
78 ) -> Tuple[np.ndarray, Dict[str, Any]]:
79     """Validate the ColorManager field values specific for colormap mode
80     This is called by the root_validator in ColorManager
81 
82     Parameters
83     ----------
84     values : dict
85         The field values that are passed to the ColorManager root validator
86 
87     Returns
88     -------
89     colors : np.ndarray
90         The (Nx4) color array to set as ColorManager.colors
91     values : dict
92     """
93     color_properties = values['color_properties'].values
94     cmap = values['continuous_colormap']
95     if len(color_properties) > 0:
96         if values['contrast_limits'] is None:
97             colors, contrast_limits = map_property(
98                 prop=color_properties,
99                 colormap=cmap,
100             )
101             values['contrast_limits'] = contrast_limits
102         else:
103             colors, _ = map_property(
104                 prop=color_properties,
105                 colormap=cmap,
106                 contrast_limits=values['contrast_limits'],
107             )
108     else:
109         colors = np.empty((0, 4))
110         current_prop_value = values['color_properties'].current_value
111         if current_prop_value is not None:
112             values['current_color'] = cmap.map(current_prop_value)[0]
113 
114     if len(colors) == 0:
115         colors = np.empty((0, 4))
116 
117     return colors, values
118 
119 
120 def _validate_cycle_mode(
121     values: Dict[str, Any]
122 ) -> Tuple[np.ndarray, Dict[str, Any]]:
123     """Validate the ColorManager field values specific for color cycle mode
124     This is called by the root_validator in ColorManager
125 
126     Parameters
127     ----------
128     values : dict
129         The field values that are passed to the ColorManager root validator
130 
131     Returns
132     -------
133     colors : np.ndarray
134         The (Nx4) color array to set as ColorManager.colors
135     values : dict
136     """
137     color_properties = values['color_properties'].values
138     cmap = values['categorical_colormap']
139     if len(color_properties) == 0:
140         colors = np.empty((0, 4))
141         current_prop_value = values['color_properties'].current_value
142         if current_prop_value is not None:
143             values['current_color'] = cmap.map(current_prop_value)[0]
144     else:
145         colors = cmap.map(color_properties)
146     values['categorical_colormap'] = cmap
147 
148     return colors, values
149
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/napari/layers/utils/color_manager_utils.py b/napari/layers/utils/color_manager_utils.py
--- a/napari/layers/utils/color_manager_utils.py
+++ b/napari/layers/utils/color_manager_utils.py
@@ -22,9 +22,9 @@
         True of the property is guessed to be continuous, False if not.
     """
     # if the property is a floating type, guess continuous
-    return (
-        issubclass(color_map.dtype.type, np.floating)
-        or len(np.unique(color_map)) > 16
+    return issubclass(color_map.dtype.type, np.floating) or (
+        len(np.unique(color_map)) > 16
+        and isinstance(color_map.dtype.type, np.integer)
     )

{"golden_diff": "diff --git a/napari/layers/utils/color_manager_utils.py b/napari/layers/utils/color_manager_utils.py\n--- a/napari/layers/utils/color_manager_utils.py\n+++ b/napari/layers/utils/color_manager_utils.py\n@@ -22,9 +22,9 @@\n         True of the property is guessed to be continuous, False if not.\n     \"\"\"\n     # if the property is a floating type, guess continuous\n-    return (\n-        issubclass(color_map.dtype.type, np.floating)\n-        or len(np.unique(color_map)) > 16\n+    return issubclass(color_map.dtype.type, np.floating) or (\n+        len(np.unique(color_map)) > 16\n+        and isinstance(color_map.dtype.type, np.integer)\n     )\n", "issue": "ValidationError when trying to plot more than 16 different colors\n### \ud83d\udc1b Bug Report\r\n\r\nDear napari team,\r\n\r\nwhen I try to plot points with more than 16 different colors I get following error:\r\n\r\n```python\r\n\r\nFile [AppData\\Local\\miniconda3\\envs\\xparse\\lib\\site-packages\\napari\\utils\\events\\evented_model.py:242](/AppData/Local/miniconda3/envs/xparse/lib/site-packages/napari/utils/events/evented_model.py:242), in EventedModel.__init__(self, **kwargs)\r\n    [241] -> None:\r\n--> [242]     super().__init__(**kwargs)\r\n    [244]     self._events.source = self\r\n    [245]     # add event emitters for each field which is mutable\r\n\r\nFile [AppData\\Local\\miniconda3\\envs\\xparse\\lib\\site-packages\\pydantic\\main.py:341](/Local/miniconda3/envs/xparse/lib/site-packages/pydantic/main.py:341), in pydantic.main.BaseModel.__init__()\r\n\r\nValidationError: 1 validation error for ColorManager\r\n__root__\r\n  Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe' (type=type_error)\r\n``` \r\n\r\nFor 16 different colors or less it is no problem.\r\n\r\n\r\n### \ud83d\udca1 Steps to Reproduce\r\n\r\nThe bug can be reproduced using following code:\r\n\r\n```python\r\nfrom skimage import data\r\nimport numpy as np\r\nimport napari\r\n\r\n# set parameters for point generation\r\nn_points = 100\r\nn_clusters = 17\r\npoints = np.random.rand(n_points, 2) * 100\r\n\r\n# start viewer\r\nviewer = napari.view_image(data.astronaut(), rgb=True)\r\n\r\n# set point properties\r\npoint_properties = {\r\n    'abc': np.random.choice([str(elem) for elem in np.arange(n_clusters)], n_points)\r\n}\r\n\r\n# add points\r\npoints_layer = viewer.add_points(\r\n    points,\r\n    properties=point_properties,\r\n    face_color='abc',\r\n    face_color_cycle=['magenta', 'green'],\r\n    edge_width=0.1,\r\n)\r\n``` \r\n\r\nThe number of clusters can be changed with `n_clusters`.\r\n\r\n### \ud83d\udca1 Expected Behavior\r\n\r\nI expect to be able to plot points with more than 16 different colors. Since I only offered a color cycle with two colors, I expect napari to behave the same for >16 points than <=16 points.\r\n\r\n### \ud83c\udf0e Environment\r\n\r\nnapari: 0.4.18\r\nPlatform: Windows-10-10.0.19045-SP0\r\nPython: 3.9.18 (main, Sep 11 2023, 14:09:26) [MSC v.1916 64 bit (AMD64)]\r\nQt: 5.15.2\r\nPyQt5: 5.15.9\r\nNumPy: 1.25.2\r\nSciPy: 1.9.3\r\nDask: 2023.9.2\r\nVisPy: 0.12.2\r\nmagicgui: 0.7.3\r\nsuperqt: 0.6.0\r\nin-n-out: 0.1.8\r\napp-model: 0.2.2\r\nnpe2: 0.7.2\r\n\r\nOpenGL:\r\n  - GL version:  4.6.0 Compatibility Profile Context 23.10.24.05.230830\r\n  - MAX_TEXTURE_SIZE: 16384\r\n\r\nScreens:\r\n  - screen 1: resolution 2560x1600, scale 1.0\r\n  - screen 2: resolution 3840x2160, scale 1.0\r\n  - screen 3: resolution 2880x1800, scale 1.0\r\n\r\nSettings path:\r\n  - [AppData\\Local\\napari\\xparse_57fe1a37b30a9e37de3a06866d324c7f56a92d1a\\settings.yaml](/AppData/Local/napari/xparse_57fe1a37b30a9e37de3a06866d324c7f56a92d1a/settings.yaml)\r\nPlugins:\r\n  - napari: 0.4.18 (77 contributions)\r\n  - napari-console: 0.0.8 (0 contributions)\r\n  - napari-svg: 0.1.10 (2 contributions)\r\n  - ome-types: 0.4.2 (2 contributions)\r\n\r\n### \ud83d\udca1 Additional Context\r\n\r\nAlso it was really difficult to detect this error. I encountered the problem in a function that was included into a dock widget. And when I tried to add the points via the widget, simply nothing happened. No error message appeared. Is it possible to somehow show the error messages when executing functions from widgets?\r\n\r\nBeside that it is really great to work with napari! Thanks a lot in advance!\n", "before_files": [{"content": "from typing import Any, Dict, Tuple, Union\n\nimport numpy as np\n\nfrom napari.utils.colormaps import Colormap\nfrom napari.utils.translations import trans\n\n\ndef guess_continuous(color_map: np.ndarray) -> bool:\n    \"\"\"Guess if the property is continuous (return True) or categorical (return False)\n\n    The property is guessed as continuous if it is a float or contains over 16 elements.\n\n    Parameters\n    ----------\n    color_map : np.ndarray\n        The property values to guess if they are continuous\n\n    Returns\n    -------\n    continuous : bool\n        True of the property is guessed to be continuous, False if not.\n    \"\"\"\n    # if the property is a floating type, guess continuous\n    return (\n        issubclass(color_map.dtype.type, np.floating)\n        or len(np.unique(color_map)) > 16\n    )\n\n\ndef is_color_mapped(color, properties):\n    \"\"\"determines if the new color argument is for directly setting or cycle/colormap\"\"\"\n    if isinstance(color, str):\n        return color in properties\n    if isinstance(color, dict):\n        return True\n    if isinstance(color, (list, np.ndarray)):\n        return False\n\n    raise ValueError(\n        trans._(\n            'face_color should be the name of a color, an array of colors, or the name of an property',\n            deferred=True,\n        )\n    )\n\n\ndef map_property(\n    prop: np.ndarray,\n    colormap: Colormap,\n    contrast_limits: Union[None, Tuple[float, float]] = None,\n) -> Tuple[np.ndarray, Tuple[float, float]]:\n    \"\"\"Apply a colormap to a property\n\n    Parameters\n    ----------\n    prop : np.ndarray\n        The property to be colormapped\n    colormap : napari.utils.Colormap\n        The colormap object to apply to the property\n    contrast_limits : Union[None, Tuple[float, float]]\n        The contrast limits for applying the colormap to the property.\n        If a 2-tuple is provided, it should be provided as (lower_bound, upper_bound).\n        If None is provided, the contrast limits will be set to (property.min(), property.max()).\n        Default value is None.\n    \"\"\"\n\n    if contrast_limits is None:\n        contrast_limits = (prop.min(), prop.max())\n    normalized_properties = np.interp(prop, contrast_limits, (0, 1))\n    mapped_properties = colormap.map(normalized_properties)\n\n    return mapped_properties, contrast_limits\n\n\ndef _validate_colormap_mode(\n    values: Dict[str, Any]\n) -> Tuple[np.ndarray, Dict[str, Any]]:\n    \"\"\"Validate the ColorManager field values specific for colormap mode\n    This is called by the root_validator in ColorManager\n\n    Parameters\n    ----------\n    values : dict\n        The field values that are passed to the ColorManager root validator\n\n    Returns\n    -------\n    colors : np.ndarray\n        The (Nx4) color array to set as ColorManager.colors\n    values : dict\n    \"\"\"\n    color_properties = values['color_properties'].values\n    cmap = values['continuous_colormap']\n    if len(color_properties) > 0:\n        if values['contrast_limits'] is None:\n            colors, contrast_limits = map_property(\n                prop=color_properties,\n                colormap=cmap,\n            )\n            values['contrast_limits'] = contrast_limits\n        else:\n            colors, _ = map_property(\n                prop=color_properties,\n                colormap=cmap,\n                contrast_limits=values['contrast_limits'],\n            )\n    else:\n        colors = np.empty((0, 4))\n        current_prop_value = values['color_properties'].current_value\n        if current_prop_value is not None:\n            values['current_color'] = cmap.map(current_prop_value)[0]\n\n    if len(colors) == 0:\n        colors = np.empty((0, 4))\n\n    return colors, values\n\n\ndef _validate_cycle_mode(\n    values: Dict[str, Any]\n) -> Tuple[np.ndarray, Dict[str, Any]]:\n    \"\"\"Validate the ColorManager field values specific for color cycle mode\n    This is called by the root_validator in ColorManager\n\n    Parameters\n    ----------\n    values : dict\n        The field values that are passed to the ColorManager root validator\n\n    Returns\n    -------\n    colors : np.ndarray\n        The (Nx4) color array to set as ColorManager.colors\n    values : dict\n    \"\"\"\n    color_properties = values['color_properties'].values\n    cmap = values['categorical_colormap']\n    if len(color_properties) == 0:\n        colors = np.empty((0, 4))\n        current_prop_value = values['color_properties'].current_value\n        if current_prop_value is not None:\n            values['current_color'] = cmap.map(current_prop_value)[0]\n    else:\n        colors = cmap.map(color_properties)\n    values['categorical_colormap'] = cmap\n\n    return colors, values\n", "path": "napari/layers/utils/color_manager_utils.py"}], "after_files": [{"content": "from typing import Any, Dict, Tuple, Union\n\nimport numpy as np\n\nfrom napari.utils.colormaps import Colormap\nfrom napari.utils.translations import trans\n\n\ndef guess_continuous(color_map: np.ndarray) -> bool:\n    \"\"\"Guess if the property is continuous (return True) or categorical (return False)\n\n    The property is guessed as continuous if it is a float or contains over 16 elements.\n\n    Parameters\n    ----------\n    color_map : np.ndarray\n        The property values to guess if they are continuous\n\n    Returns\n    -------\n    continuous : bool\n        True of the property is guessed to be continuous, False if not.\n    \"\"\"\n    # if the property is a floating type, guess continuous\n    return issubclass(color_map.dtype.type, np.floating) or (\n        len(np.unique(color_map)) > 16\n        and isinstance(color_map.dtype.type, np.integer)\n    )\n\n\ndef is_color_mapped(color, properties):\n    \"\"\"determines if the new color argument is for directly setting or cycle/colormap\"\"\"\n    if isinstance(color, str):\n        return color in properties\n    if isinstance(color, dict):\n        return True\n    if isinstance(color, (list, np.ndarray)):\n        return False\n\n    raise ValueError(\n        trans._(\n            'face_color should be the name of a color, an array of colors, or the name of an property',\n            deferred=True,\n        )\n    )\n\n\ndef map_property(\n    prop: np.ndarray,\n    colormap: Colormap,\n    contrast_limits: Union[None, Tuple[float, float]] = None,\n) -> Tuple[np.ndarray, Tuple[float, float]]:\n    \"\"\"Apply a colormap to a property\n\n    Parameters\n    ----------\n    prop : np.ndarray\n        The property to be colormapped\n    colormap : napari.utils.Colormap\n        The colormap object to apply to the property\n    contrast_limits : Union[None, Tuple[float, float]]\n        The contrast limits for applying the colormap to the property.\n        If a 2-tuple is provided, it should be provided as (lower_bound, upper_bound).\n        If None is provided, the contrast limits will be set to (property.min(), property.max()).\n        Default value is None.\n    \"\"\"\n\n    if contrast_limits is None:\n        contrast_limits = (prop.min(), prop.max())\n    normalized_properties = np.interp(prop, contrast_limits, (0, 1))\n    mapped_properties = colormap.map(normalized_properties)\n\n    return mapped_properties, contrast_limits\n\n\ndef _validate_colormap_mode(\n    values: Dict[str, Any]\n) -> Tuple[np.ndarray, Dict[str, Any]]:\n    \"\"\"Validate the ColorManager field values specific for colormap mode\n    This is called by the root_validator in ColorManager\n\n    Parameters\n    ----------\n    values : dict\n        The field values that are passed to the ColorManager root validator\n\n    Returns\n    -------\n    colors : np.ndarray\n        The (Nx4) color array to set as ColorManager.colors\n    values : dict\n    \"\"\"\n    color_properties = values['color_properties'].values\n    cmap = values['continuous_colormap']\n    if len(color_properties) > 0:\n        if values['contrast_limits'] is None:\n            colors, contrast_limits = map_property(\n                prop=color_properties,\n                colormap=cmap,\n            )\n            values['contrast_limits'] = contrast_limits\n        else:\n            colors, _ = map_property(\n                prop=color_properties,\n                colormap=cmap,\n                contrast_limits=values['contrast_limits'],\n            )\n    else:\n        colors = np.empty((0, 4))\n        current_prop_value = values['color_properties'].current_value\n        if current_prop_value is not None:\n            values['current_color'] = cmap.map(current_prop_value)[0]\n\n    if len(colors) == 0:\n        colors = np.empty((0, 4))\n\n    return colors, values\n\n\ndef _validate_cycle_mode(\n    values: Dict[str, Any]\n) -> Tuple[np.ndarray, Dict[str, Any]]:\n    \"\"\"Validate the ColorManager field values specific for color cycle mode\n    This is called by the root_validator in ColorManager\n\n    Parameters\n    ----------\n    values : dict\n        The field values that are passed to the ColorManager root validator\n\n    Returns\n    -------\n    colors : np.ndarray\n        The (Nx4) color array to set as ColorManager.colors\n    values : dict\n    \"\"\"\n    color_properties = values['color_properties'].values\n    cmap = values['categorical_colormap']\n    if len(color_properties) == 0:\n        colors = np.empty((0, 4))\n        current_prop_value = values['color_properties'].current_value\n        if current_prop_value is not None:\n            values['current_color'] = cmap.map(current_prop_value)[0]\n    else:\n        colors = cmap.map(color_properties)\n    values['categorical_colormap'] = cmap\n\n    return colors, values\n", "path": "napari/layers/utils/color_manager_utils.py"}]}