prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  CSV export broken
  ### Short description
  Export CSV failed when the plot name has decode error characters.
  
  ### Code to reproduce
  ```python
  from pyqtgraph.Qt import QtGui, QtCore
  import numpy as np
  import pyqtgraph as pg
  
  #QtGui.QApplication.setGraphicsSystem('raster')
  app = QtGui.QApplication([])
  win = pg.GraphicsLayoutWidget(show=True, title="Basic plotting examples")
  win.resize(1000,600)
  win.setWindowTitle('pyqtgraph example: Plotting')
  
  
  pg.setConfigOptions(antialias=True)
  
  pw = win.addPlot(title="Scatter plot, axis labels, log scale")
  pw.addLegend()
  pw .plot(np.random.normal(size=100), pen=(255,0,0), name="\u00A0下加热体")
  
  QtGui.QApplication.instance().exec_()
  ```
  
  ### Expected behavior
  Export CSV Success
  
  ### Real behavior
  Export CSV Failed
  
  ```
  ---------------------------------------------------------------------------
  UnicodeEncodeError                        Traceback (most recent call last)
  c:\program files\python37\lib\site-packages\pyqtgraph\exporters\Exporter.py in fileSaveFinished(self, fileName)
       75                 fileName = fileName + '.' + selectedExt.lstrip('.')
       76
  ---> 77         self.export(fileName=fileName, **self.fileDialog.opts)
       78
       79     def getScene(self):
  
  c:\program files\python37\lib\site-packages\pyqtgraph\exporters\CSVExporter.py in export(self, fileName)
       58
       59         with open(fileName, 'w') as fd:
  ---> 60             fd.write(sep.join(header) + '\n')
       61             i = 0
       62             numFormat = '%%0.%dg' % self.params['precision']
  
  UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 1: illegal multibyte sequence
  ```
  
  ### Tested environment(s)
  
   * PyQtGraph version: 0.11.0.dev0+g2203933
   * Qt Python binding: PyQt5 5.13.2 Qt 5.13.2
   * Python version: Python 3.7.5 
   * NumPy version: 1.17.4
   * Operating system: Windows 7 X64
   * Installation method: pip  git+
  
  ### Additional context
  I use "\u00A0" because i want to add some space before label name in the legend.
  Could i use the csv export by "utf-8" but not "gbk" ?
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pyqtgraph/exporters/CSVExporter.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 from ..Qt import QtGui, QtCore
  3 from .Exporter import Exporter
  4 from ..parametertree import Parameter
  5 from .. import PlotItem
  6 
  7 __all__ = ['CSVExporter']
  8     
  9     
  10 class CSVExporter(Exporter):
  11     Name = "CSV from plot data"
  12     windows = []
  13     def __init__(self, item):
  14         Exporter.__init__(self, item)
  15         self.params = Parameter(name='params', type='group', children=[
  16             {'name': 'separator', 'type': 'list', 'value': 'comma', 'values': ['comma', 'tab']},
  17             {'name': 'precision', 'type': 'int', 'value': 10, 'limits': [0, None]},
  18             {'name': 'columnMode', 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']}
  19         ])
  20         
  21     def parameters(self):
  22         return self.params
  23     
  24     def export(self, fileName=None):
  25         
  26         if not isinstance(self.item, PlotItem):
  27             raise Exception("Must have a PlotItem selected for CSV export.")
  28         
  29         if fileName is None:
  30             self.fileSaveDialog(filter=["*.csv", "*.tsv"])
  31             return
  32 
  33         data = []
  34         header = []
  35 
  36         appendAllX = self.params['columnMode'] == '(x,y) per plot'
  37 
  38         for i, c in enumerate(self.item.curves):
  39             cd = c.getData()
  40             if cd[0] is None:
  41                 continue
  42             data.append(cd)
  43             if hasattr(c, 'implements') and c.implements('plotData') and c.name() is not None:
  44                 name = c.name().replace('"', '""') + '_'
  45                 xName, yName = '"'+name+'x"', '"'+name+'y"'
  46             else:
  47                 xName = 'x%04d' % i
  48                 yName = 'y%04d' % i
  49             if appendAllX or i == 0:
  50                 header.extend([xName, yName])
  51             else:
  52                 header.extend([yName])
  53 
  54         if self.params['separator'] == 'comma':
  55             sep = ','
  56         else:
  57             sep = '\t'
  58 
  59         with open(fileName, 'w') as fd:
  60             fd.write(sep.join(header) + '\n')
  61             i = 0
  62             numFormat = '%%0.%dg' % self.params['precision']
  63             numRows = max([len(d[0]) for d in data])
  64             for i in range(numRows):
  65                 for j, d in enumerate(data):
  66                     # write x value if this is the first column, or if we want
  67                     # x for all rows
  68                     if appendAllX or j == 0:
  69                         if d is not None and i < len(d[0]):
  70                             fd.write(numFormat % d[0][i] + sep)
  71                         else:
  72                             fd.write(' %s' % sep)
  73 
  74                     # write y value
  75                     if d is not None and i < len(d[1]):
  76                         fd.write(numFormat % d[1][i] + sep)
  77                     else:
  78                         fd.write(' %s' % sep)
  79                 fd.write('\n')
  80 
  81 
  82 CSVExporter.register()        
  83                 
  84         
  85
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/pyqtgraph/exporters/CSVExporter.py b/pyqtgraph/exporters/CSVExporter.py
  --- a/pyqtgraph/exporters/CSVExporter.py
  +++ b/pyqtgraph/exporters/CSVExporter.py
  @@ -3,6 +3,7 @@
   from .Exporter import Exporter
   from ..parametertree import Parameter
   from .. import PlotItem
  +from ..python2_3 import asUnicode
   
   __all__ = ['CSVExporter']
       
  @@ -57,7 +58,7 @@
               sep = '\t'
   
           with open(fileName, 'w') as fd:
  -            fd.write(sep.join(header) + '\n')
  +            fd.write(sep.join(map(asUnicode, header)) + '\n')
               i = 0
               numFormat = '%%0.%dg' % self.params['precision']
               numRows = max([len(d[0]) for d in data])
  
verification_info: {"golden_diff": "diff --git a/pyqtgraph/exporters/CSVExporter.py b/pyqtgraph/exporters/CSVExporter.py\n--- a/pyqtgraph/exporters/CSVExporter.py\n+++ b/pyqtgraph/exporters/CSVExporter.py\n@@ -3,6 +3,7 @@\n from .Exporter import Exporter\n from ..parametertree import Parameter\n from .. import PlotItem\n+from ..python2_3 import asUnicode\n \n __all__ = ['CSVExporter']\n     \n@@ -57,7 +58,7 @@\n             sep = '\\t'\n \n         with open(fileName, 'w') as fd:\n-            fd.write(sep.join(header) + '\\n')\n+            fd.write(sep.join(map(asUnicode, header)) + '\\n')\n             i = 0\n             numFormat = '%%0.%dg' % self.params['precision']\n             numRows = max([len(d[0]) for d in data])\n", "issue": "CSV export broken\n### Short description\r\nExport CSV failed when the plot name has decode error characters.\r\n\r\n### Code to reproduce\r\n```python\r\nfrom pyqtgraph.Qt import QtGui, QtCore\r\nimport numpy as np\r\nimport pyqtgraph as pg\r\n\r\n#QtGui.QApplication.setGraphicsSystem('raster')\r\napp = QtGui.QApplication([])\r\nwin = pg.GraphicsLayoutWidget(show=True, title=\"Basic plotting examples\")\r\nwin.resize(1000,600)\r\nwin.setWindowTitle('pyqtgraph example: Plotting')\r\n\r\n\r\npg.setConfigOptions(antialias=True)\r\n\r\npw = win.addPlot(title=\"Scatter plot, axis labels, log scale\")\r\npw.addLegend()\r\npw .plot(np.random.normal(size=100), pen=(255,0,0), name=\"\\u00A0\u4e0b\u52a0\u70ed\u4f53\")\r\n\r\nQtGui.QApplication.instance().exec_()\r\n```\r\n\r\n### Expected behavior\r\nExport CSV Success\r\n\r\n### Real behavior\r\nExport CSV Failed\r\n\r\n```\r\n---------------------------------------------------------------------------\r\nUnicodeEncodeError                        Traceback (most recent call last)\r\nc:\\program files\\python37\\lib\\site-packages\\pyqtgraph\\exporters\\Exporter.py in fileSaveFinished(self, fileName)\r\n     75                 fileName = fileName + '.' + selectedExt.lstrip('.')\r\n     76\r\n---> 77         self.export(fileName=fileName, **self.fileDialog.opts)\r\n     78\r\n     79     def getScene(self):\r\n\r\nc:\\program files\\python37\\lib\\site-packages\\pyqtgraph\\exporters\\CSVExporter.py in export(self, fileName)\r\n     58\r\n     59         with open(fileName, 'w') as fd:\r\n---> 60             fd.write(sep.join(header) + '\\n')\r\n     61             i = 0\r\n     62             numFormat = '%%0.%dg' % self.params['precision']\r\n\r\nUnicodeEncodeError: 'gbk' codec can't encode character '\\xa0' in position 1: illegal multibyte sequence\r\n```\r\n\r\n### Tested environment(s)\r\n\r\n * PyQtGraph version: 0.11.0.dev0+g2203933\r\n * Qt Python binding: PyQt5 5.13.2 Qt 5.13.2\r\n * Python version: Python 3.7.5 \r\n * NumPy version: 1.17.4\r\n * Operating system: Windows 7 X64\r\n * Installation method: pip  git+\r\n\r\n### Additional context\r\nI use \"\\u00A0\" because i want to add some space before label name in the legend.\r\nCould i use the csv export by \"utf-8\" but not \"gbk\" ?\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom ..Qt import QtGui, QtCore\nfrom .Exporter import Exporter\nfrom ..parametertree import Parameter\nfrom .. import PlotItem\n\n__all__ = ['CSVExporter']\n    \n    \nclass CSVExporter(Exporter):\n    Name = \"CSV from plot data\"\n    windows = []\n    def __init__(self, item):\n        Exporter.__init__(self, item)\n        self.params = Parameter(name='params', type='group', children=[\n            {'name': 'separator', 'type': 'list', 'value': 'comma', 'values': ['comma', 'tab']},\n            {'name': 'precision', 'type': 'int', 'value': 10, 'limits': [0, None]},\n            {'name': 'columnMode', 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']}\n        ])\n        \n    def parameters(self):\n        return self.params\n    \n    def export(self, fileName=None):\n        \n        if not isinstance(self.item, PlotItem):\n            raise Exception(\"Must have a PlotItem selected for CSV export.\")\n        \n        if fileName is None:\n            self.fileSaveDialog(filter=[\"*.csv\", \"*.tsv\"])\n            return\n\n        data = []\n        header = []\n\n        appendAllX = self.params['columnMode'] == '(x,y) per plot'\n\n        for i, c in enumerate(self.item.curves):\n            cd = c.getData()\n            if cd[0] is None:\n                continue\n            data.append(cd)\n            if hasattr(c, 'implements') and c.implements('plotData') and c.name() is not None:\n                name = c.name().replace('\"', '\"\"') + '_'\n                xName, yName = '\"'+name+'x\"', '\"'+name+'y\"'\n            else:\n                xName = 'x%04d' % i\n                yName = 'y%04d' % i\n            if appendAllX or i == 0:\n                header.extend([xName, yName])\n            else:\n                header.extend([yName])\n\n        if self.params['separator'] == 'comma':\n            sep = ','\n        else:\n            sep = '\\t'\n\n        with open(fileName, 'w') as fd:\n            fd.write(sep.join(header) + '\\n')\n            i = 0\n            numFormat = '%%0.%dg' % self.params['precision']\n            numRows = max([len(d[0]) for d in data])\n            for i in range(numRows):\n                for j, d in enumerate(data):\n                    # write x value if this is the first column, or if we want\n                    # x for all rows\n                    if appendAllX or j == 0:\n                        if d is not None and i < len(d[0]):\n                            fd.write(numFormat % d[0][i] + sep)\n                        else:\n                            fd.write(' %s' % sep)\n\n                    # write y value\n                    if d is not None and i < len(d[1]):\n                        fd.write(numFormat % d[1][i] + sep)\n                    else:\n                        fd.write(' %s' % sep)\n                fd.write('\\n')\n\n\nCSVExporter.register()        \n                \n        \n", "path": "pyqtgraph/exporters/CSVExporter.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom ..Qt import QtGui, QtCore\nfrom .Exporter import Exporter\nfrom ..parametertree import Parameter\nfrom .. import PlotItem\nfrom ..python2_3 import asUnicode\n\n__all__ = ['CSVExporter']\n    \n    \nclass CSVExporter(Exporter):\n    Name = \"CSV from plot data\"\n    windows = []\n    def __init__(self, item):\n        Exporter.__init__(self, item)\n        self.params = Parameter(name='params', type='group', children=[\n            {'name': 'separator', 'type': 'list', 'value': 'comma', 'values': ['comma', 'tab']},\n            {'name': 'precision', 'type': 'int', 'value': 10, 'limits': [0, None]},\n            {'name': 'columnMode', 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']}\n        ])\n        \n    def parameters(self):\n        return self.params\n    \n    def export(self, fileName=None):\n        \n        if not isinstance(self.item, PlotItem):\n            raise Exception(\"Must have a PlotItem selected for CSV export.\")\n        \n        if fileName is None:\n            self.fileSaveDialog(filter=[\"*.csv\", \"*.tsv\"])\n            return\n\n        data = []\n        header = []\n\n        appendAllX = self.params['columnMode'] == '(x,y) per plot'\n\n        for i, c in enumerate(self.item.curves):\n            cd = c.getData()\n            if cd[0] is None:\n                continue\n            data.append(cd)\n            if hasattr(c, 'implements') and c.implements('plotData') and c.name() is not None:\n                name = c.name().replace('\"', '\"\"') + '_'\n                xName, yName = '\"'+name+'x\"', '\"'+name+'y\"'\n            else:\n                xName = 'x%04d' % i\n                yName = 'y%04d' % i\n            if appendAllX or i == 0:\n                header.extend([xName, yName])\n            else:\n                header.extend([yName])\n\n        if self.params['separator'] == 'comma':\n            sep = ','\n        else:\n            sep = '\\t'\n\n        with open(fileName, 'w') as fd:\n            fd.write(sep.join(map(asUnicode, header)) + '\\n')\n            i = 0\n            numFormat = '%%0.%dg' % self.params['precision']\n            numRows = max([len(d[0]) for d in data])\n            for i in range(numRows):\n                for j, d in enumerate(data):\n                    # write x value if this is the first column, or if we want\n                    # x for all rows\n                    if appendAllX or j == 0:\n                        if d is not None and i < len(d[0]):\n                            fd.write(numFormat % d[0][i] + sep)\n                        else:\n                            fd.write(' %s' % sep)\n\n                    # write y value\n                    if d is not None and i < len(d[1]):\n                        fd.write(numFormat % d[1][i] + sep)\n                    else:\n                        fd.write(' %s' % sep)\n                fd.write('\\n')\n\n\nCSVExporter.register()        \n                \n        \n", "path": "pyqtgraph/exporters/CSVExporter.py"}]}