1
0

33 Коммитууд 171695cc38 ... 3f7fdd03d1

Эзэн SHA1 Мессеж Огноо
  Matze 171695cc38 merge fixes 6 жил өмнө
  Matze ae893b4ebb Merge branch 'kapture2' of http://git.ipe.kit.edu/patrick/KaptureControlGui into kapture2 6 жил өмнө
  Matthias Martin 512f2a8ccd PV Monitoring working 6 жил өмнө
  Matthias Martin e5e33220bd Epics 6 жил өмнө
  Matthias Martin 28a8108ef0 rename 6 жил өмнө
  Matthias Martin 700d5b0624 Widget allows to add and change PV List. 6 жил өмнө
  Matthias Martin 1ae723bb80 Started interface 6 жил өмнө
  Matthias Martin 7800745694 Epics is back 6 жил өмнө
  Matthias Martin 1382f12d9f Epics removed -- will be in new widged 6 жил өмнө
  Matthias Martin 5f096d840b First changes. 6 жил өмнө
  Martin, Matthias b8265d99c6 Update kcg 6 жил өмнө
  Patrick Schreiber e0f5cc24a2 Update README.md 6 жил өмнө
  Patrick Schreiber 809c20623e Merge branch 'follow_plot' 6 жил өмнө
  Patrick Schreiber ffd09b6923 Introduce 50ohm background subtraction for timescan 6 жил өмнө
  Patrick Schreiber 72f317da15 implement Follow plot 6 жил өмнө
  Patrick Schreiber 94cc266862 Optionally include Singleread in LeftBar 6 жил өмнө
  Patrick Schreiber 9d694d303b Update Version 6 жил өмнө
  Patrick Schreiber 37736dbf55 Fix continous read interval 6 жил өмнө
  Patrick Schreiber c4941a4118 Remove Bug when aborting to save config 6 жил өмнө
  Patrick Schreiber 3451bf3485 Introduce Threshold for Timescan Averaging 6 жил өмнө
  Miriam Brosi e2e8478961 Removed possible usage of reikna fft 6 жил өмнө
  Patrick Schreiber dd9a11916c Fix Save/Load Config 7 жил өмнө
  Miriam Brosi ae9b0e7d39 single read tick for board_id 0 if on by default now 7 жил өмнө
  Miriam Brosi 24e2187807 fixed bug: acquisition with ext. trigger was running in main thread, now fixed, now also stop acquisition working 7 жил өмнө
  Miriam Brosi 0f4f391f60 implemented workaround: acquisition with ext. trigger method2 now working; but stop acquisition signal still delayed 7 жил өмнө
  Miriam Brosi 8614b94f1f fixed board.flush_dma function and added calls at begining of acquisition and begining and end of external trigger function 7 жил өмнө
  Lorenzo Rota bdf94a2b17 added logging info that initialisation was skipped and the configuration and status was read from KAPTURE 7 жил өмнө
  Miriam Brosi 598af59917 improved update of statusbar after message 'Data is inconsistent' 7 жил өмнө
  Miriam Brosi ba62421d91 added feature: in time scan possibility to average only over one bunch (if and which can be selected in time scan widget) 7 жил өмнө
  Miriam Brosi e4923697d1 fixed bugs: time scan now displays (and returns for click) the correct numbers for coarse and fine delay also in partial scans, enabled auto range for switching between color and line plot. 7 жил өмнө
  Miriam Brosi a05e83f3a5 replace all 184 by bunches_per_turns from the config file 7 жил өмнө
  Miriam Brosi a022288e67 fixed bug: turn board off is now working 7 жил өмнө
  Miriam Brosi 6e0c51bad0 fixed bug: bitsTable.py was still using old write_pci command instead of pci.write 7 жил өмнө
48 өөрчлөгдсөн 10966 нэмэгдсэн , 11564 устгасан
  1. 17 17
      KCG/Documentation/source/_themes/sphinx_rtd_theme/__init__.py
  2. 336 336
      KCG/Documentation/source/conf.py
  3. 1 1
      KCG/VERSION
  4. 0 1
      KCG/__init__.py
  5. 4 1
      KCG/base/__init__.py
  6. 16 16
      KCG/base/backend/board/__init__.py
  7. 123 123
      KCG/base/backend/board/actions.py
  8. 489 489
      KCG/base/backend/board/board_config.py
  9. 167 167
      KCG/base/backend/board/boards_connected.py
  10. 204 204
      KCG/base/backend/board/communication.py
  11. 37 37
      KCG/base/backend/board/errors.py
  12. 165 165
      KCG/base/backend/board/sequences.py
  13. 18 18
      KCG/base/backend/board/status.py
  14. 180 180
      KCG/base/backend/board/utils.py
  15. 165 172
      KCG/base/backend/dataset.py
  16. 155 155
      KCG/base/backend/io.py
  17. 1503 1517
      KCG/base/backendinterface.py
  18. 443 443
      KCG/base/bitsTable.py
  19. 99 99
      KCG/base/callbacks.py
  20. 378 417
      KCG/base/controlwidget.py
  21. 24 24
      KCG/base/globals.py
  22. 266 266
      KCG/base/groupedelements.py
  23. 564 584
      KCG/base/kcg.py
  24. 720 738
      KCG/base/kcgwidget.py
  25. 697 704
      KCG/base/leftbar.py
  26. 360 333
      KCG/base/log.py
  27. 147 147
      KCG/base/loghandler.py
  28. 142 142
      KCG/base/multiWidget.py
  29. 200 212
      KCG/base/multipage.py
  30. 885 940
      KCG/base/plotWidget.py
  31. 155 158
      KCG/base/settings.py
  32. 178 178
      KCG/base/storage.py
  33. 29 18
      KCG/config.cfg
  34. 15 23
      KCG/config.py
  35. BIN
      KCG/icons/open-trash-can.png
  36. 0 1
      KCG/kcg.pro
  37. 151 153
      KCG/kcg.py
  38. 1 1
      KCG/widgets/__init__.py
  39. 486 493
      KCG/widgets/acquiresettings.py
  40. 0 388
      KCG/widgets/epics_widget.py
  41. 47 47
      KCG/widgets/example_widget.py
  42. 348 363
      KCG/widgets/initialconfig.py
  43. 178 178
      KCG/widgets/singleread.py
  44. 778 885
      KCG/widgets/timingWidget.py
  45. 93 0
      config.cfg
  46. 0 18
      epics.cfg
  47. 0 4
      kcg.py
  48. 2 8
      setup.py

+ 17 - 17
KCG/Documentation/source/_themes/sphinx_rtd_theme/__init__.py

@@ -1,17 +1,17 @@
-"""Sphinx ReadTheDocs theme.
-
-From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
-
-"""
-import os
-
-VERSION = (0, 1, 8)
-
-__version__ = ".".join(str(v) for v in VERSION)
-__version_full__ = __version__
-
-
-def get_html_theme_path():
-    """Return list of HTML theme paths."""
-    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
-    return cur_dir
+"""Sphinx ReadTheDocs theme.
+
+From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
+
+"""
+import os
+
+VERSION = (0, 1, 8)
+
+__version__ = ".".join(str(v) for v in VERSION)
+__version_full__ = __version__
+
+
+def get_html_theme_path():
+    """Return list of HTML theme paths."""
+    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+    return cur_dir

+ 336 - 336
KCG/Documentation/source/conf.py

@@ -1,336 +1,336 @@
-# -*- coding: utf-8 -*-
-#
-# KCG documentation build configuration file, created by
-# sphinx-quickstart on Wed Jul  1 18:18:47 2015.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
-#    'sphinx.ext.mathjax',
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = 'KCG'
-copyright = '2016, Patrick Schreiber'
-
-rst_epilog = """
-.. |gui_name| replace:: {gui_name}
-""".format(gui_name=project)
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.4'
-# The full version, including alpha/beta/rc tags.
-release = '0.4b1'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-# html_theme = 'default'
-html_theme = 'sphinx_rtd_theme'
-html_theme_path =['_themes', ]
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'KCGdoc'
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-  ('index', 'KCG.tex', 'KCG Documentation',
-   'Patrick Schreiber', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'kcg', 'KCG Documentation',
-     ['Patrick Schreiber'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  ('index', 'KCG', 'KCG Documentation',
-   'Patrick Schreiber', 'KCG', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
-
-
-# -- Options for Epub output ----------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = 'KCG'
-epub_author = 'Patrick Schreiber'
-epub_publisher = 'Patrick Schreiber'
-epub_copyright = '2015, Patrick Schreiber'
-
-# The basename for the epub file. It defaults to the project name.
-#epub_basename = u'KCG'
-
-# The HTML theme for the epub output. Since the default themes are not optimized
-# for small screen space, using the same theme for HTML and epub output is
-# usually not wise. This defaults to 'epub', a theme designed to save visual
-# space.
-#epub_theme = 'epub'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
-
-# A sequence of (type, uri, title) tuples for the guide element of content.opf.
-#epub_guide = ()
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
-
-# Choose between 'default' and 'includehidden'.
-#epub_tocscope = 'default'
-
-# Fix unsupported image types using the PIL.
-#epub_fix_images = False
-
-# Scale large images.
-#epub_max_image_width = 0
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#epub_show_urls = 'inline'
-
-# If false, no index is generated.
-#epub_use_index = True
+# -*- coding: utf-8 -*-
+#
+# KCG documentation build configuration file, created by
+# sphinx-quickstart on Wed Jul  1 18:18:47 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+#    'sphinx.ext.mathjax',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'KCG'
+copyright = u'2016, Patrick Schreiber'
+
+rst_epilog = """
+.. |gui_name| replace:: {gui_name}
+""".format(gui_name=project)
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.4'
+# The full version, including alpha/beta/rc tags.
+release = '0.4b1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+# html_theme = 'default'
+html_theme = 'sphinx_rtd_theme'
+html_theme_path =['_themes', ]
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+# html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'KCGdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+  ('index', 'KCG.tex', u'KCG Documentation',
+   u'Patrick Schreiber', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'kcg', u'KCG Documentation',
+     [u'Patrick Schreiber'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'KCG', u'KCG Documentation',
+   u'Patrick Schreiber', 'KCG', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'KCG'
+epub_author = u'Patrick Schreiber'
+epub_publisher = u'Patrick Schreiber'
+epub_copyright = u'2015, Patrick Schreiber'
+
+# The basename for the epub file. It defaults to the project name.
+#epub_basename = u'KCG'
+
+# The HTML theme for the epub output. Since the default themes are not optimized
+# for small screen space, using the same theme for HTML and epub output is
+# usually not wise. This defaults to 'epub', a theme designed to save visual
+# space.
+#epub_theme = 'epub'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#epub_cover = ()
+
+# A sequence of (type, uri, title) tuples for the guide element of content.opf.
+#epub_guide = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
+
+# Choose between 'default' and 'includehidden'.
+#epub_tocscope = 'default'
+
+# Fix unsupported image types using the PIL.
+#epub_fix_images = False
+
+# Scale large images.
+#epub_max_image_width = 0
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#epub_show_urls = 'inline'
+
+# If false, no index is generated.
+#epub_use_index = True

+ 1 - 1
KCG/VERSION

@@ -1 +1 @@
-0.3.2.197.0704-beta
+0.3.2.180.0414-beta

+ 0 - 1
KCG/__init__.py

@@ -1 +0,0 @@
-from . import base

+ 4 - 1
KCG/base/__init__.py

@@ -1 +1,4 @@
-from . import backend
+__author__ = 'blaxxun'
+"""
+Base Stuff for the Gui
+"""

+ 16 - 16
KCG/base/backend/board/__init__.py

@@ -1,16 +1,16 @@
-# make everything conveniently available under the name board
-from .actions import *
-from .board_config import *
-from .boards_connected import *
-from .communication import *
-from .errors import *
-from .sequences import *
-from .status import *
-from .utils import *
-
-
-HEADER_SIZE_BYTES = 32
-
-pci = PCI()
-
-
+# make everything conveniently available under the name board
+from actions import *
+from board_config import *
+from boards_connected import *
+from communication import *
+from errors import *
+from sequences import *
+from status import *
+from utils import *
+
+
+HEADER_SIZE_BYTES = 32
+
+pci = PCI()
+
+

+ 123 - 123
KCG/base/backend/board/actions.py

@@ -1,123 +1,123 @@
-"""
-Actions performed on the board.
-Fore some more actions also see sequences.py
-"""
-import time
-
-from .communication import *
-from .utils import wait_for_revolutions
-
-
-def acquire_data(board_id, filename, simulate=False):
-    """
-    Acquire data. This sets registers and reads data to a file
-    :param board_id: the id of the board to acquire with
-    :param filename: the file to write to
-    :param simulate: whether to use the pilotbunch simulator or not
-    """
-    if simulate:
-        start_pilot_bunch_emulator(board_id)
-
-    #NOTE: New Picosecond Capture does not need flush any more
-    #Get firmware info from registers and decide if we want to flush
-    #or not
-
-    #flush_dma(board_id)
-    start_acquisition(board_id)
-    wait_for_revolutions(board_id)
-    stop_acquisition(board_id)
-    enable_transfer(board_id)
-    pci.read(board_id, dma='dma0', destination=filename)
-    #flush_dma(board_id)
-
-
-def data_reset(board_id):
-    """
-    Reset data
-    :param board_id: the board to reset
-    :return:
-    """
-    log.vinfo('Data reset')
-    pci.write(board_id, '000003f5', hex_mask='7')
-    time.sleep(0.05)
-    pci.write(board_id, '000003f0', hex_mask='7')
-    time.sleep(0.05)
-
-
-def flush_dma(board_id, dma='dma0'):
-    """
-    Flush dma. This basically reads data to /dev/null
-    :param board_id: the id to flush dma
-    :param dma: the dma to flush
-    """
-    log.vinfo('Flushing DMA Pipeline')
-    pci.write(board_id, '03f0', hex_mask='CF0')
-    time.sleep(0.05)
-    pci.write(board_id, '007f0', hex_mask='CF0')
-    pci.read(board_id, dma=dma, destination='/dev/null')
-
-
-def stop_board(board_id):
-    """
-    Stop a board (turn it off)
-    :param board_id: the board to work with
-    """
-    pci.write(board_id, '0x01', '0x9040')
-    pci.stop_dma(board_id)
-
-
-def soft_reset(board_id):
-    """
-    Perform a soft reset on the board.
-    :param board_id: the board to reset
-    :return:
-    """
-    pci.write(board_id, '0x1', '0x9040', hex_mask='0x1')
-    time.sleep(1)
-    pci.write(board_id, '0x0', '0x9040', hex_mask='0x1')
-
-
-def start_pilot_bunch_emulator(board_id):
-    """
-    Start the pilot bunch emulator
-    :param board_id: the board to start it for
-    """
-    log.vinfo('Start pilot bunch emulator')
-    pci.write(board_id, '400003f0', hex_mask='400003F0')
-    time.sleep(0.005)
-    pci.write(board_id, '03f0', hex_mask='CF0')
-
-
-def start_acquisition(board_id):
-    """
-    Start an acquisition. This will only tell the board to acquire. It will not read data
-    :param board_id: the id to acquire with
-    """
-    log.vinfo('Start acquisition')
-    pci.write(board_id, '1', '4', hex_mask='1')  # Start DMA?
-    time.sleep(0.005)
-    pci.write(board_id, '00bf0', hex_mask='CF0')
-
-
-def stop_acquisition(board_id):
-    """
-    Stop an acquisition. This will only stop the acquisition on the board.
-    :param board_id: the board to stop for
-    """
-    log.vinfo('Stop acquisition')
-    pci.write(board_id, '003f0', hex_mask='CF0')
-    time.sleep(0.005)
-
-
-def enable_transfer(board_id):
-    """
-    Enable data transfer. This will not transfer data.
-    :param board_id: the id of the board to transfer data from
-    :return:
-    """
-    log.vinfo('Enable data transfer')
-    pci.write(board_id, '007f0', hex_mask='CF0')
-    time.sleep(0.005)
-
-
-
+"""
+Actions performed on the board.
+Fore some more actions also see sequences.py
+"""
+import time
+
+from communication import *
+from utils import wait_for_revolutions
+
+
+def acquire_data(board_id, filename, simulate=False):
+    """
+    Acquire data. This sets registers and reads data to a file
+    :param board_id: the id of the board to acquire with
+    :param filename: the file to write to
+    :param simulate: whether to use the pilotbunch simulator or not
+    """
+    if simulate:
+        start_pilot_bunch_emulator(board_id)
+
+    #NOTE: New Picosecond Capture does not need flush any more
+    #Get firmware info from registers and decide if we want to flush
+    #or not
+
+    #flush_dma(board_id)
+    start_acquisition(board_id)
+    wait_for_revolutions(board_id)
+    stop_acquisition(board_id)
+    enable_transfer(board_id)
+    pci.read(board_id, dma='dma0', destination=filename)
+    #flush_dma(board_id)
+
+
+def data_reset(board_id):
+    """
+    Reset data
+    :param board_id: the board to reset
+    :return:
+    """
+    log.vinfo('Data reset')
+    pci.write(board_id, '000003f5', hex_mask='7')
+    time.sleep(0.05)
+    pci.write(board_id, '000003f0', hex_mask='7')
+    time.sleep(0.05)
+
+
+def flush_dma(board_id, dma='dma0'):
+    """
+    Flush dma. This basically reads data to /dev/null
+    :param board_id: the id to flush dma
+    :param dma: the dma to flush
+    """
+    log.vinfo('Flushing DMA Pipeline')
+    pci.write(board_id, '03f0', hex_mask='CF0')
+    time.sleep(0.05)
+    pci.write(board_id, '007f0', hex_mask='CF0')
+    pci.read(board_id, dma=dma, destination='/dev/null')
+
+
+def stop_board(board_id):
+    """
+    Stop a board (turn it off)
+    :param board_id: the board to work with
+    """
+    pci.write(board_id, '0x01', '0x9040')
+    pci.stop_dma(board_id)
+
+
+def soft_reset(board_id):
+    """
+    Perform a soft reset on the board.
+    :param board_id: the board to reset
+    :return:
+    """
+    pci.write(board_id, '0x1', '0x9040', hex_mask='0x1')
+    time.sleep(1)
+    pci.write(board_id, '0x0', '0x9040', hex_mask='0x1')
+
+
+def start_pilot_bunch_emulator(board_id):
+    """
+    Start the pilot bunch emulator
+    :param board_id: the board to start it for
+    """
+    log.vinfo('Start pilot bunch emulator')
+    pci.write(board_id, '400003f0', hex_mask='400003F0')
+    time.sleep(0.005)
+    pci.write(board_id, '03f0', hex_mask='CF0')
+
+
+def start_acquisition(board_id):
+    """
+    Start an acquisition. This will only tell the board to acquire. It will not read data
+    :param board_id: the id to acquire with
+    """
+    log.vinfo('Start acquisition')
+    pci.write(board_id, '1', '4', hex_mask='1')  # Start DMA?
+    time.sleep(0.005)
+    pci.write(board_id, '00bf0', hex_mask='CF0')
+
+
+def stop_acquisition(board_id):
+    """
+    Stop an acquisition. This will only stop the acquisition on the board.
+    :param board_id: the board to stop for
+    """
+    log.vinfo('Stop acquisition')
+    pci.write(board_id, '003f0', hex_mask='CF0')
+    time.sleep(0.005)
+
+
+def enable_transfer(board_id):
+    """
+    Enable data transfer. This will not transfer data.
+    :param board_id: the id of the board to transfer data from
+    :return:
+    """
+    log.vinfo('Enable data transfer')
+    pci.write(board_id, '007f0', hex_mask='CF0')
+    time.sleep(0.005)
+
+
+

+ 489 - 489
KCG/base/backend/board/board_config.py

@@ -1,489 +1,489 @@
-"""
-Configuration for each board
-"""
-
-import configparser
-import numpy as np
-import logging
-from PyQt4 import QtGui, QtCore
-
-from .communication import *
-from .... import config as kcg_config
-
-
-class BoardConfiguration(QtGui.QWidget):
-    """
-    The Main configuration class for boards.
-    """
-    callback_signal = QtCore.pyqtSignal(str, list)
-
-    def __init__(self, identifier, config_file=None):
-        super(BoardConfiguration, self).__init__()
-        self.callback_signal.connect(self._notify_observers_receiver)
-        self.identifier = identifier
-        self._config = {}
-        self._observers = {}
-        self._observers_for_all = []
-        self._set_defaults()
-        self.set_default_observers()
-
-        if config_file:
-            self.load_config(config_file)
-
-    def _set_defaults(self):
-        """
-        Set default values
-        """
-        self._config ={
-            'fpga_delay_max': 15,
-            'fpga_delay': 0,
-            'fpga_delay_factor': 150,
-
-            'chip_delay_max': 31,
-            'chip_1_delay': 4,
-            'chip_2_delay': 4,
-            'chip_3_delay': 4,
-            'chip_4_delay': 4,
-            'chip_delay_factor': 3,
-
-            'th_delay_max': 15,
-            'th_delay': 3,
-            'th_delay_factor': 150,
-
-            'adc_delay_max': 15,
-            'adc_1_delay': 4,
-            'adc_2_delay': 4,
-            'adc_3_delay': 4,
-            'adc_4_delay': 4,
-            'adc_delay_factor': 150,
-
-            'th_to_adc_cycles': 7,
-            'adc_1_delay_individual': -1,
-
-            'orbits_observe': 100,
-            'orbits_skip': 2,
-            'acquisition_count': 10,
-            'orbits_wait_time': 15,
-            'trigger_skip': 0,
-            'trigger_timeout': 12,
-            'trigger_method': 1,
-            'use_trigger': False,
-            'build_spectrograms': False,
-            'pilot_bunch': False,
-            'header': True if kcg_config.save_header is True else False
-        }
-
-    def set_default_observers(self):
-        """
-        Set observers that are always used
-        """
-        self.observe(None, self.update_header, 'header')
-        self.observe(None, lambda x: pci.write(self.identifier, hex(x), '0x9020'), 'orbits_observe')
-        self.observe(None, lambda x: pci.write(self.identifier, hex(x), '0x9028'), 'orbits_skip')
-
-    def notify_all_observers(self):
-        """
-        Notify all observers not only the ones that are affected by a change
-        """
-        for key, value in list(self._config.items()):
-            self._notify_observers(key, value)
-            # observers = self._observers.get(key, None)
-            # if observers:
-            #     for (who, callback) in observers:
-            #         callback(self.get(key))
-
-
-    def load_config(self, filename):
-        """
-        Load a config from a file
-        :param filename: the configuration file
-        :return:
-        """
-        if filename:
-            config = configparser.RawConfigParser()
-            if not config.read(str(filename)):
-                return False
-
-            for key in list(self._config.keys()):
-                try:
-                    if type(self._config[key]) == int:
-                        self._config[key] = config.getint('Config', key)
-                    elif type(self._config[key]) == bool:
-                        self._config[key] = config.getboolean('Config', key)
-                    logging.vinfo("Read '%s' for '%s' from '%s'"%(str(self._config[key]), key, str(filename)))
-                except configparser.NoOptionError as e:
-                    pass
-                except configparser.NoSectionError as e:
-                    pass
-            return True
-        else:
-            return False
-
-    def save_config(self, filename):
-        """
-        Save the current configuration to a file
-        :param filename: the file to write to
-        """
-        if filename:
-            # with open(filename, 'w') as f:
-            try:
-                f = open(filename, 'w')
-                cp = configparser.RawConfigParser()
-                cp.add_section('Config')
-                for key in list(self._config.keys()):
-                    cp.set('Config', key, self._config[key])
-                f.write('#\n'
-                        '#  KCG   (KAPTURE Control Gui) Configuration file\n'
-                        '#\n'
-                        '#  (c) Karlsruhe Institute of Technology, 2015\n'
-                        '#  All rights reserved.\n'
-                        '#\n'
-                        '#  Applicable Gui Version(s): 1.0 - 1.0.2\n'
-                        '#\n'
-                        '#  Saved at: ' + time.asctime() + '\n'
-                        '#\n\n')
-                cp.write(f)
-            except (IOError, TypeError):
-                return False
-            return True
-        else:
-            return False
-
-    def get(self, key):
-        """
-        Get the configuration value for key
-        :param key: the key to get the value for
-        :return: the value of the configuration for key
-        """
-        if not key in self._config:
-            raise NoSuchKeyError(key+" is not registered in BoardConfiguration for board "+str(self.identifier))
-        return self._config.get(key, None)
-
-    def dump(self):
-        """
-        Dump all configuration values
-        :return: all configuration values as list
-        """
-        s = ""
-        for key in list(self._config.keys()):
-            s += key + ": " + str(self.get(key)) + ", "
-        return s[:-1]
-
-    def update(self, key, value):
-        """
-        Update the value for key in the configuration
-        :param key: the key to update
-        :param value: the value to set the configuration for key to
-        """
-        self._config[key] = value
-        self._notify_observers(key, value)
-
-    def updateSilent(self, key, value):
-        """
-        Update the configuration without notifying observers
-        :param key: the key to updae
-        :param value: the value to set the configuration of key to
-        """
-        self._config[key] = value
-
-    def observe(self, who, callback, key):
-        """
-        Register an observer. (A callback that is called when the according configuration value changes)
-        :param who: who is observing
-        :param callback: the callback to call on change of the configuration value
-        :param key: the key to observe
-        """
-        if key not in list(self._config.keys()):
-            raise ObserverError(str("Key '%s' is unknown." % key))
-
-        if self._observers.get(key, None) is None:
-            self._observers[key] = []
-
-        self._observers[key].append([who, callback])
-
-    def observe_all(self, callback):
-        """
-        Register a observer that is called when any key changes
-        :param callback: the callback to register
-        """
-        if callback not in self._observers_for_all:
-            self._observers_for_all.append(callback)
-        else:
-            raise ObserverError("Observer already registered")
-
-    def unobserve(self, who, key=None):
-        """
-        Remove an observer
-        :param who: the observing entity
-        :param key: the key to remove it from
-        """
-        if key is not None:
-            observers = np.array(self._observers.get(key, None))
-            if observers is None:
-                return
-            if who not in observers[:, 0]:
-                return
-            for i, _obs in enumerate(self._observers[key]):
-                if _obs[0] is who:
-                    del self._observers[key][i]
-                    if not self._observers[key]:
-                        del self._observers[key]
-            return
-
-        for _key in list(self._observers.keys()):
-            for i, _obs in enumerate(self._observers[_key]):
-                if _obs[0] is who:
-                    del self._observers[_key][i]
-                    if not self._observers[_key]:
-                        del self._observers[_key]
-
-    def unobserve_all_observer(self, callback):
-        """
-        Unobserve an observer that observes all keys.
-        :param callback: the callback to unobserve
-        """
-        if callback in self._observers_for_all:
-            del self._observers_for_all[self._observers_for_all.index(callback)]
-
-    def _notify_observers_receiver(self, key, value):
-        """
-        The pyqt signal slot for notifications of observers
-        :param key: the key that changed
-        :param value: the new value
-        """
-        observers = self._observers.get(str(key), None)
-        value = value[0]
-        if observers is None:
-            return
-        for (who, callback) in observers:
-            callback(value)
-
-        for cb in self._observers_for_all:
-            cb(key, value)
-
-    def _notify_observers(self, key, value):
-        """
-        Notify observers. This emits a pyqt signal to make it thread save
-        :param key: the key that changed
-        :param value: the new value
-        """
-        self.callback_signal.emit(key, [value])
-
-    def make_uint(self, value, maximum, name=None):
-        """
-        Convert a value to an uint
-        :param value: the value
-        :param maximum: the maximum of the returned value
-        :param name: the name of this value
-        :return: the converted value
-        """
-        if value is None:
-            raise ValueError(str("%s Value is invalid (None)" % name))
-
-        val = None
-        try:
-            val = int(value)
-        except ValueError:
-            raise ValueError(str("%s Value is not a valid number" % name))
-
-        if maximum is not None:
-            if val > maximum:
-                raise ValueError(str("%s Value is too large (>%i)" % (name, maximum)))
-
-        if val < 0:
-            raise ValueError(str("%s Values below 0 are not allowed" % name))
-
-        return val
-
-    def set_fpga_delay(self, value):
-        """
-        Set the fpga_delays on the board
-        :param value: the value to set the delays to
-        """
-        time_factor = self.make_uint(value, self.get('fpga_delay_max'), 'FPGA_Delay')
-        reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "0"
-        pci.write(self.identifier, reg_value, '0x9060')
-        logging.vinfo("Set FPGA clock delay %i * %i --> %i picoseconds" % (time_factor, self.get('fpga_delay_factor'), time_factor*self.get('fpga_delay_factor')))
-        self.update('fpga_delay', value)
-
-    def set_chip_delay(self, adcs, values):
-        """
-        Set the chip_delays on the board
-        :param adcs: the adcs to set the delays for
-        :param values: the value to set the delays to
-        """
-        if not adcs or not values:
-            logging.vinfo("Nothing to do for chip delay.")
-            return
-
-        _adcs = []
-        for adc in adcs:
-            _adcs.append(self.make_uint(adc, 3, 'ADC_'))
-
-        _values = []
-        for value in values:
-            _values.append(self.make_uint(value, self.get('chip_delay_max'), 'ADC_Value'))
-
-        a_v = list(zip(_adcs, _values))
-
-        factors = [None, None, None, None]
-        for (adc, value) in a_v:
-            factors[adc] = value
-
-        reg_value = ''
-        mask = ''
-        # Chip Delays are stored as 'ADC_4 ADC_3 ADC_2 ADC_1' in the register.
-        # Therefore, we need to traverse the factors array in reverse order
-        for value in reversed(factors):
-            if value is not None:
-                reg_value += '{0:02x}'.format(value)
-                mask += 'ff'
-            else:
-                reg_value += '00'
-                mask += '00'
-
-        pci.write(self.identifier, reg_value, '0x9080', hex_mask=mask)
-        s = "Setting ADC Delays:"
-        for (adc, value) in a_v:
-            s += ' ADC_%i Fine Delay: %i,' % (adc, value)
-        s = s[:-1]  # cut away the last dangling ','
-        logging.vinfo(s)
-        for (adc, value) in a_v:
-            s = 'chip_%i_delay'%(adc+1)
-            self.update(s, value)
-
-    def set_th_delay(self, value):
-        """
-        Set the track and hold delay on the board
-        :param value: the value to set the delay to
-        """
-        time_factor = self.make_uint(value, self.get('th_delay_max'), 'TH_Delay')
-        reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "3"
-        pci.write(self.identifier, reg_value, '0x9060')
-        logging.vinfo("Set T/H Signal delay %i * %i --> %i picoseconds" % (time_factor, self.get('th_delay_factor'), time_factor*self.get('th_delay_factor')))
-        self.update('th_delay', value)
-
-    def set_adc_delay(self, adc, value):
-        """
-        Set the adc delay for the given adc on the board
-        :param adc: the adc to change the delay for
-        :param value: the value to set the delay to
-        """
-        if adc is None or value is None:
-            logging.vinfo("Nothing to do for ADC delay.")
-            return
-        _adc = self.make_uint(adc, 3, 'ADC Number')
-        _val = self.make_uint(value, self.get('adc_delay_max'), 'ADC Delay')
-        reg_value = "0x000501" + '{0:01x}'.format(_val) + "%i" % (_adc+4)
-        pci.write(self.identifier, reg_value, '0x9060')
-        s = "Setting ADC_%i delay %i * %i --> %i picoseconds" % ((_adc+1), _val, self.get('adc_delay_factor'), _val*self.get('adc_delay_factor'))
-        logging.vinfo(s)
-        adc_s = 'adc_%i_delay'%(_adc+1)
-        self.update(adc_s, _val)
-
-    def set_delay(self, n, ignore_seperate_delay=False):
-        """
-        Set delays
-        :param n: ?
-        :param ignore_seperate_delay: ignore the setting of a seperate delay for adc 1
-        """
-        def write_delay(value, channel):
-            '''write the delays to the board'''
-            cmd = '00501' + '%01x' % value + str(channel)
-            pci.write(self.identifier, cmd, reg='0x9060')
-            time.sleep(0.005)
-
-        logging.vinfo("Setting T/H Delay: " + str(n))
-        write_delay(n, 3)
-        self.update('th_delay', n)
-
-        delay = n + self.get('th_to_adc_cycles')
-
-        if delay > self.get('adc_delay_max'):
-            delay -= self.get('adc_delay_max') + 1
-
-        write_delay(delay, 5)
-        self.update('adc_2_delay', delay)
-        write_delay(delay, 6)
-        self.update('adc_3_delay', delay)
-        write_delay(delay, 7)
-        self.update('adc_4_delay', delay)
-
-        #ADC 1 might have an individual delay
-        if self.get('adc_1_delay_individual') > 0:
-            try:
-                delay = n + self.make_uint(self.get('adc_1_delay_individual'), 16, 'ADC 1 individual delay')
-                logging.vinfo("Setting ADC1 individual delay to " + str(delay))
-            except ValueError:
-                logging.vinfo(r"'adc_1_delay_individual' not set or inactive. Using default.")
-
-            if delay > self.get('adc_delay_max'):
-                delay -= self.get('adc_delay_max') + 1
-        write_delay(delay, 4)
-        self.update('adc_1_delay', delay)
-
-    def update_header(self, state):
-        """
-        Set the flag to write Header to files when acquiring.
-        :param state: True to enabling header and False to disable
-        :return: -
-        """
-        try:
-            control = pci.read(self.identifier, 1, '0x9040')[0]
-            control_bits = '{0:032b}'.format(int(control, 16))
-            if state:
-                control_bits = control_bits[:3] + '1' + control_bits[4:]
-            else:
-                control_bits = control_bits[:3] + '0' + control_bits[4:]
-            dec_val_bits = int(control_bits, 2)
-            pci.write(self.identifier, hex(dec_val_bits), '0x9040')
-        except BoardError as e:
-            reason = str(e) if str(e) != '' else "Unknown"
-            logging.error("Error in Board Communication, was unable to write value to board "+reason)
-
-    def read_from_board(self):
-        """
-        Read values from board and update them in the configuration (Mainly used for skip init functionality)
-        """
-        try:
-            settings = ['chip_1_delay','chip_2_delay','chip_3_delay','chip_4_delay']
-            # --[ read fine/chip delays ]
-            val = pci.read(self.identifier, reg='9080')[0]
-            # --[ set chip_1_delay ]--
-            self.update('chip_1_delay', int(val[6:8], 16))
-            # --[ set chip_2_delay ]--
-            self.update('chip_2_delay', int(val[4:6], 16))
-            # --[ set chip_3_delay ]--
-            self.update('chip_3_delay', int(val[2:4], 16))
-            # --[ set chip_4_delay ]--
-            self.update('chip_4_delay', int(val[0:2], 16))
-
-            # --[ read and set th delay ]--
-            val = pci.read(self.identifier, reg='90a0')[0]
-            self.update('th_delay', int(val, 16))
-
-            # --[ check for seperate adc1 delay ]--
-            val = pci.read(self.identifier, reg='9088')[0]
-            if int(val, 16) != self.get('th_delay') + self.get('adc_1_delay_individual'):
-                self.update('adc_1_delay_individual', int(val, 16)-self.get('th_delay'))
-            else:
-                self.update('adc_1_delay_individual', -1)
-
-            # --[ read and set number of orbits to acquire ]--
-            val = pci.read(self.identifier, reg='9020')[0]
-            self.update('orbits_observe', int(val, 16))
-
-            # --[ read and set number of orbits to skip ]--
-            val = pci.read(self.identifier, reg='9028')[0]
-            self.update('orbits_skip', int(val, 16))
-
-            # --[ read and update header ]--
-            control = pci.read(self.identifier, 1, '0x9040')[0]
-            control_bits = '{0:032b}'.format(int(control, 16))
-            if control_bits[3] == '1':
-                self.update('header', True)
-            else:
-                self.update('header', False)
-        except IndexError:
-            error(0x002, "Could not Read data from Board. Pci returned wrong amount of data.")
-
+"""
+Configuration for each board
+"""
+
+import ConfigParser
+import numpy as np
+import logging
+from PyQt4 import QtGui, QtCore
+
+from communication import *
+from .... import config as kcg_config
+
+
+class BoardConfiguration(QtGui.QWidget):
+    """
+    The Main configuration class for boards.
+    """
+    callback_signal = QtCore.pyqtSignal(str, list)
+
+    def __init__(self, identifier, config_file=None):
+        super(BoardConfiguration, self).__init__()
+        self.callback_signal.connect(self._notify_observers_receiver)
+        self.identifier = identifier
+        self._config = {}
+        self._observers = {}
+        self._observers_for_all = []
+        self._set_defaults()
+        self.set_default_observers()
+
+        if config_file:
+            self.load_config(config_file)
+
+    def _set_defaults(self):
+        """
+        Set default values
+        """
+        self._config ={
+            'fpga_delay_max': 15,
+            'fpga_delay': 0,
+            'fpga_delay_factor': 150,
+
+            'chip_delay_max': 31,
+            'chip_1_delay': 4,
+            'chip_2_delay': 4,
+            'chip_3_delay': 4,
+            'chip_4_delay': 4,
+            'chip_delay_factor': 3,
+
+            'th_delay_max': 15,
+            'th_delay': 3,
+            'th_delay_factor': 150,
+
+            'adc_delay_max': 15,
+            'adc_1_delay': 4,
+            'adc_2_delay': 4,
+            'adc_3_delay': 4,
+            'adc_4_delay': 4,
+            'adc_delay_factor': 150,
+
+            'th_to_adc_cycles': 7,
+            'adc_1_delay_individual': -1,
+
+            'orbits_observe': 100,
+            'orbits_skip': 2,
+            'acquisition_count': 10,
+            'orbits_wait_time': 15,
+            'trigger_skip': 0,
+            'trigger_timeout': 12,
+            'trigger_method': 1,
+            'use_trigger': False,
+            'build_spectrograms': False,
+            'pilot_bunch': False,
+            'header': True if kcg_config.save_header is True else False
+        }
+
+    def set_default_observers(self):
+        """
+        Set observers that are always used
+        """
+        self.observe(None, self.update_header, 'header')
+        self.observe(None, lambda x: pci.write(self.identifier, hex(x), '0x9020'), 'orbits_observe')
+        self.observe(None, lambda x: pci.write(self.identifier, hex(x), '0x9028'), 'orbits_skip')
+
+    def notify_all_observers(self):
+        """
+        Notify all observers not only the ones that are affected by a change
+        """
+        for key, value in self._config.items():
+            self._notify_observers(key, value)
+            # observers = self._observers.get(key, None)
+            # if observers:
+            #     for (who, callback) in observers:
+            #         callback(self.get(key))
+
+
+    def load_config(self, filename):
+        """
+        Load a config from a file
+        :param filename: the configuration file
+        :return:
+        """
+        if filename:
+            config = ConfigParser.RawConfigParser()
+            if not config.read(str(filename)):
+                return False
+
+            for key in self._config.keys():
+                try:
+                    if type(self._config[key]) == int:
+                        self._config[key] = config.getint('Config', key)
+                    elif type(self._config[key]) == bool:
+                        self._config[key] = config.getboolean('Config', key)
+                    logging.vinfo("Read '%s' for '%s' from '%s'"%(str(self._config[key]), key, str(filename)))
+                except ConfigParser.NoOptionError as e:
+                    pass
+                except ConfigParser.NoSectionError as e:
+                    pass
+            return True
+        else:
+            return False
+
+    def save_config(self, filename):
+        """
+        Save the current configuration to a file
+        :param filename: the file to write to
+        """
+        if filename:
+            # with open(filename, 'w') as f:
+            try:
+                f = open(filename, 'w')
+                cp = ConfigParser.RawConfigParser()
+                cp.add_section('Config')
+                for key in self._config.keys():
+                    cp.set('Config', key, self._config[key])
+                f.write('#\n'
+                        '#  KCG   (KAPTURE Control Gui) Configuration file\n'
+                        '#\n'
+                        '#  (c) Karlsruhe Institute of Technology, 2015\n'
+                        '#  All rights reserved.\n'
+                        '#\n'
+                        '#  Applicable Gui Version(s): 1.0 - 1.0.2\n'
+                        '#\n'
+                        '#  Saved at: ' + time.asctime() + '\n'
+                        '#\n\n')
+                cp.write(f)
+            except (IOError, TypeError):
+                return False
+            return True
+        else:
+            return False
+
+    def get(self, key):
+        """
+        Get the configuration value for key
+        :param key: the key to get the value for
+        :return: the value of the configuration for key
+        """
+        if not key in self._config:
+            raise NoSuchKeyError(key+" is not registered in BoardConfiguration for board "+str(self.identifier))
+        return self._config.get(key, None)
+
+    def dump(self):
+        """
+        Dump all configuration values
+        :return: all configuration values as list
+        """
+        s = ""
+        for key in self._config.keys():
+            s += key + ": " + str(self.get(key)) + ", "
+        return s[:-1]
+
+    def update(self, key, value):
+        """
+        Update the value for key in the configuration
+        :param key: the key to update
+        :param value: the value to set the configuration for key to
+        """
+        self._config[key] = value
+        self._notify_observers(key, value)
+
+    def updateSilent(self, key, value):
+        """
+        Update the configuration without notifying observers
+        :param key: the key to updae
+        :param value: the value to set the configuration of key to
+        """
+        self._config[key] = value
+
+    def observe(self, who, callback, key):
+        """
+        Register an observer. (A callback that is called when the according configuration value changes)
+        :param who: who is observing
+        :param callback: the callback to call on change of the configuration value
+        :param key: the key to observe
+        """
+        if key not in self._config.keys():
+            raise ObserverError(str("Key '%s' is unknown." % key))
+
+        if self._observers.get(key, None) is None:
+            self._observers[key] = []
+
+        self._observers[key].append([who, callback])
+
+    def observe_all(self, callback):
+        """
+        Register a observer that is called when any key changes
+        :param callback: the callback to register
+        """
+        if callback not in self._observers_for_all:
+            self._observers_for_all.append(callback)
+        else:
+            raise ObserverError("Observer already registered")
+
+    def unobserve(self, who, key=None):
+        """
+        Remove an observer
+        :param who: the observing entity
+        :param key: the key to remove it from
+        """
+        if key is not None:
+            observers = np.array(self._observers.get(key, None))
+            if observers is None:
+                return
+            if who not in observers[:, 0]:
+                return
+            for i, _obs in enumerate(self._observers[key]):
+                if _obs[0] is who:
+                    del self._observers[key][i]
+                    if not self._observers[key]:
+                        del self._observers[key]
+            return
+
+        for _key in self._observers.keys():
+            for i, _obs in enumerate(self._observers[_key]):
+                if _obs[0] is who:
+                    del self._observers[_key][i]
+                    if not self._observers[_key]:
+                        del self._observers[_key]
+
+    def unobserve_all_observer(self, callback):
+        """
+        Unobserve an observer that observes all keys.
+        :param callback: the callback to unobserve
+        """
+        if callback in self._observers_for_all:
+            del self._observers_for_all[self._observers_for_all.index(callback)]
+
+    def _notify_observers_receiver(self, key, value):
+        """
+        The pyqt signal slot for notifications of observers
+        :param key: the key that changed
+        :param value: the new value
+        """
+        observers = self._observers.get(str(key), None)
+        value = value[0]
+        if observers is None:
+            return
+        for (who, callback) in observers:
+            callback(value)
+
+        for cb in self._observers_for_all:
+            cb(key, value)
+
+    def _notify_observers(self, key, value):
+        """
+        Notify observers. This emits a pyqt signal to make it thread save
+        :param key: the key that changed
+        :param value: the new value
+        """
+        self.callback_signal.emit(key, [value])
+
+    def make_uint(self, value, maximum, name=None):
+        """
+        Convert a value to an uint
+        :param value: the value
+        :param maximum: the maximum of the returned value
+        :param name: the name of this value
+        :return: the converted value
+        """
+        if value is None:
+            raise ValueError(str("%s Value is invalid (None)" % name))
+
+        val = None
+        try:
+            val = int(value)
+        except ValueError:
+            raise ValueError(str("%s Value is not a valid number" % name))
+
+        if maximum is not None:
+            if val > maximum:
+                raise ValueError(str("%s Value is too large (>%i)" % (name, maximum)))
+
+        if val < 0:
+            raise ValueError(str("%s Values below 0 are not allowed" % name))
+
+        return val
+
+    def set_fpga_delay(self, value):
+        """
+        Set the fpga_delays on the board
+        :param value: the value to set the delays to
+        """
+        time_factor = self.make_uint(value, self.get('fpga_delay_max'), 'FPGA_Delay')
+        reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "0"
+        pci.write(self.identifier, reg_value, '0x9060')
+        logging.vinfo("Set FPGA clock delay %i * %i --> %i picoseconds" % (time_factor, self.get('fpga_delay_factor'), time_factor*self.get('fpga_delay_factor')))
+        self.update('fpga_delay', value)
+
+    def set_chip_delay(self, adcs, values):
+        """
+        Set the chip_delays on the board
+        :param adcs: the adcs to set the delays for
+        :param values: the value to set the delays to
+        """
+        if not adcs or not values:
+            logging.vinfo("Nothing to do for chip delay.")
+            return
+
+        _adcs = []
+        for adc in adcs:
+            _adcs.append(self.make_uint(adc, 3, 'ADC_'))
+
+        _values = []
+        for value in values:
+            _values.append(self.make_uint(value, self.get('chip_delay_max'), 'ADC_Value'))
+
+        a_v = zip(_adcs, _values)
+
+        factors = [None, None, None, None]
+        for (adc, value) in a_v:
+            factors[adc] = value
+
+        reg_value = ''
+        mask = ''
+        # Chip Delays are stored as 'ADC_4 ADC_3 ADC_2 ADC_1' in the register.
+        # Therefore, we need to traverse the factors array in reverse order
+        for value in reversed(factors):
+            if value is not None:
+                reg_value += '{0:02x}'.format(value)
+                mask += 'ff'
+            else:
+                reg_value += '00'
+                mask += '00'
+
+        pci.write(self.identifier, reg_value, '0x9080', hex_mask=mask)
+        s = "Setting ADC Delays:"
+        for (adc, value) in a_v:
+            s += ' ADC_%i Fine Delay: %i,' % (adc, value)
+        s = s[:-1]  # cut away the last dangling ','
+        logging.vinfo(s)
+        for (adc, value) in a_v:
+            s = 'chip_%i_delay'%(adc+1)
+            self.update(s, value)
+
+    def set_th_delay(self, value):
+        """
+        Set the track and hold delay on the board
+        :param value: the value to set the delay to
+        """
+        time_factor = self.make_uint(value, self.get('th_delay_max'), 'TH_Delay')
+        reg_value = "0x000501" + '{0:01x}'.format(time_factor) + "3"
+        pci.write(self.identifier, reg_value, '0x9060')
+        logging.vinfo("Set T/H Signal delay %i * %i --> %i picoseconds" % (time_factor, self.get('th_delay_factor'), time_factor*self.get('th_delay_factor')))
+        self.update('th_delay', value)
+
+    def set_adc_delay(self, adc, value):
+        """
+        Set the adc delay for the given adc on the board
+        :param adc: the adc to change the delay for
+        :param value: the value to set the delay to
+        """
+        if adc is None or value is None:
+            logging.vinfo("Nothing to do for ADC delay.")
+            return
+        _adc = self.make_uint(adc, 3, 'ADC Number')
+        _val = self.make_uint(value, self.get('adc_delay_max'), 'ADC Delay')
+        reg_value = "0x000501" + '{0:01x}'.format(_val) + "%i" % (_adc+4)
+        pci.write(self.identifier, reg_value, '0x9060')
+        s = "Setting ADC_%i delay %i * %i --> %i picoseconds" % ((_adc+1), _val, self.get('adc_delay_factor'), _val*self.get('adc_delay_factor'))
+        logging.vinfo(s)
+        adc_s = 'adc_%i_delay'%(_adc+1)
+        self.update(adc_s, _val)
+
+    def set_delay(self, n, ignore_seperate_delay=False):
+        """
+        Set delays
+        :param n: ?
+        :param ignore_seperate_delay: ignore the setting of a seperate delay for adc 1
+        """
+        def write_delay(value, channel):
+            '''write the delays to the board'''
+            cmd = '00501' + '%01x' % value + str(channel)
+            pci.write(self.identifier, cmd, reg='0x9060')
+            time.sleep(0.005)
+
+        logging.vinfo("Setting T/H Delay: " + str(n))
+        write_delay(n, 3)
+        self.update('th_delay', n)
+
+        delay = n + self.get('th_to_adc_cycles')
+
+        if delay > self.get('adc_delay_max'):
+            delay -= self.get('adc_delay_max') + 1
+
+        write_delay(delay, 5)
+        self.update('adc_2_delay', delay)
+        write_delay(delay, 6)
+        self.update('adc_3_delay', delay)
+        write_delay(delay, 7)
+        self.update('adc_4_delay', delay)
+
+        #ADC 1 might have an individual delay
+        if self.get('adc_1_delay_individual') > 0:
+            try:
+                delay = n + self.make_uint(self.get('adc_1_delay_individual'), 16, 'ADC 1 individual delay')
+                logging.vinfo("Setting ADC1 individual delay to " + str(delay))
+            except ValueError:
+                logging.vinfo(r"'adc_1_delay_individual' not set or inactive. Using default.")
+
+            if delay > self.get('adc_delay_max'):
+                delay -= self.get('adc_delay_max') + 1
+        write_delay(delay, 4)
+        self.update('adc_1_delay', delay)
+
+    def update_header(self, state):
+        """
+        Set the flag to write Header to files when acquiring.
+        :param state: True to enabling header and False to disable
+        :return: -
+        """
+        try:
+            control = pci.read(self.identifier, 1, '0x9040')[0]
+            control_bits = '{0:032b}'.format(int(control, 16))
+            if state:
+                control_bits = control_bits[:3] + '1' + control_bits[4:]
+            else:
+                control_bits = control_bits[:3] + '0' + control_bits[4:]
+            dec_val_bits = int(control_bits, 2)
+            pci.write(self.identifier, hex(dec_val_bits), '0x9040')
+        except BoardError as e:
+            reason = str(e) if str(e) != '' else "Unknown"
+            logging.error("Error in Board Communication, was unable to write value to board "+reason)
+
+    def read_from_board(self):
+        """
+        Read values from board and update them in the configuration (Mainly used for skip init functionality)
+        """
+        try:
+            settings = ['chip_1_delay','chip_2_delay','chip_3_delay','chip_4_delay']
+            # --[ read fine/chip delays ]
+            val = pci.read(self.identifier, reg='9080')[0]
+            # --[ set chip_1_delay ]--
+            self.update('chip_1_delay', int(val[6:8], 16))
+            # --[ set chip_2_delay ]--
+            self.update('chip_2_delay', int(val[4:6], 16))
+            # --[ set chip_3_delay ]--
+            self.update('chip_3_delay', int(val[2:4], 16))
+            # --[ set chip_4_delay ]--
+            self.update('chip_4_delay', int(val[0:2], 16))
+
+            # --[ read and set th delay ]--
+            val = pci.read(self.identifier, reg='90a0')[0]
+            self.update('th_delay', int(val, 16))
+
+            # --[ check for seperate adc1 delay ]--
+            val = pci.read(self.identifier, reg='9088')[0]
+            if int(val, 16) != self.get('th_delay') + self.get('adc_1_delay_individual'):
+                self.update('adc_1_delay_individual', int(val, 16)-self.get('th_delay'))
+            else:
+                self.update('adc_1_delay_individual', -1)
+
+            # --[ read and set number of orbits to acquire ]--
+            val = pci.read(self.identifier, reg='9020')[0]
+            self.update('orbits_observe', int(val, 16))
+
+            # --[ read and set number of orbits to skip ]--
+            val = pci.read(self.identifier, reg='9028')[0]
+            self.update('orbits_skip', int(val, 16))
+
+            # --[ read and update header ]--
+            control = pci.read(self.identifier, 1, '0x9040')[0]
+            control_bits = '{0:032b}'.format(int(control, 16))
+            if control_bits[3] == '1':
+                self.update('header', True)
+            else:
+                self.update('header', False)
+        except IndexError:
+            error(0x002, "Could not Read data from Board. Pci returned wrong amount of data.")
+

+ 167 - 167
KCG/base/backend/board/boards_connected.py

@@ -1,167 +1,167 @@
-"""
-Discover and manage connected boards
-"""
-
-import glob
-import logging as log
-
-from .... import config
-from ... kcgwidget import error
-from .communication import pci
-
-DUMMY_PCI_i_OUTPUT = 'Vendor: 10ee, Device: 6028, Bus: 3, Slot: 0, Function: 0\n\
- Interrupt - Pin: 1, Line: 255\n\
- BAR 0 - MEM32, Start: 0xf5500000, Length: 0x  100000, Flags: 0x00040200\n\
-\n\
-DMA Engines: \n\
- DMA  0 C2S - Type: Packet, Address Width: 32 bits\n\
-\n\
-Banks: \n\
- 0x80 dma: DMA Registers\n\
-'
-
-class BoardsConnected(object):
-    """
-    Container for connected/available boards
-    This will work as a generator and yield the available board_ids
-    NOTE: this is subject to change. In the future this may yield board_names or tuples or something else
-    """
-    def __init__(self):
-        self._board_ids = {}
-        self._board_ids_reverse = {}
-        self._device_files = {}
-        self.count = 0
-
-    def _device_id(self, dev_file):
-        """
-        Get the device id
-        :param dev_file: the dev_file to use
-        :return: string for the dev id
-        """
-        info_string = pci.info(dev_file=dev_file)
-        # info_string = DUMMY_PCI_i_OUTPUT
-        try:
-            return info_string.split('Device: ')[1].split(',')[0]
-        except IndexError:
-            error(0x002, "Pci returned no or wrong information string. Device probably not found.", severe=True)
-
-    def discover(self):
-        """
-        Discover connected boards
-        This is either a dummy implementation that defaults to /dev/fpga0 for every board
-        it will create 'virtual' boards
-        OR:
-        If config.use_dev_fpga_for_detection == True use number of /dev/fpga devices and use them
-        """
-        def get(dict_, what):  # this will return the dict value for what (Key) or what if what not in dict_
-            return dict_.get(what, what)
-
-        if config.board_detection_method == 'dev':
-            searchstring = '/dev/fpga'
-            # searchstring = '/dev/lp'
-            device_list = glob.glob(searchstring+'*')
-            log.info("Found {num} devices.".format(num=len(device_list)))
-
-            self._board_ids = {int(i.replace(searchstring, '')): get(config.device_names, self._device_id(i))
-                               for i in device_list}
-            self._device_files = {int(i.replace(searchstring, '')): i for i in device_list}
-
-        elif config.board_detection_method == 'dummy':
-            if config.num_dummy_boards:
-                num = int(config.num_dummy_boards)
-            else:
-                num = 5
-            self._board_ids = {i: get(config.device_names, 'test'+str(i)) for i in range(num)}
-            self._device_files = {i: '/dev/fpga0' for i in self.board_ids}
-
-        elif config.board_detection_method == 'list':
-            self._board_ids = {i: get(config.device_names, self._device_id(d_f)) for i, d_f in enumerate(config.device_list)}
-            self._device_files = {i: d_f for i, d_f in enumerate(config.device_list)}
-
-        else:
-            raise config.MisconfigurationError("board_detection_method was misconfigured")
-
-        # if no boards detected create a dummy board (everything will be disabled in the gui) so the gui does
-        # not crash
-        if len(self._board_ids) == 0:
-            self._board_ids[0] = None
-
-        self._board_ids_reverse = {v: k for k, v in list(self._board_ids.items())}  # build reverse look up
-
-    @property
-    def has_boards(self):
-        """
-        Check if at least one board is connected
-        """
-        if len(self.board_ids) == 1 and self._board_ids[0] is None:
-            return False
-        elif len(self.board_ids) > 0:
-            return True
-        else:
-            return False
-
-    @property
-    def board_ids(self):
-        """
-        Get Board Ids as list
-        """
-        return list(self._board_ids.keys())
-
-    @property
-    def board_names(self):
-        """
-        Get Board Names as list
-        :return:
-        """
-        return list(self._board_ids.values())
-
-    def get_board_name_from_id(self, board_id):
-        """
-        Get the name of a board with the given board_id
-        :param board_id: the board to get the name for
-        :return: the name of the board with id board_id
-        """
-        return self._board_ids[board_id]
-
-    def get_board_id_from_name(self, board_name):
-        """
-        Get the id of a board with the name board_name
-        :param board_name: the name of the board to get the id for
-        :return: the id of the board with name board_name
-        """
-        return self._board_ids_reverse[board_name]
-
-    def get_device_file(self, board_id):
-        """
-        Get the device file (/dev/...) to use when communicating with the board
-        :param board_id: the id of the board
-        :return: the device file as string
-        """
-        return self._device_files[board_id]
-
-    @property
-    def multi_board(self):
-        """
-        If multiple boards are connected this is true else false
-        """
-        if len(self.board_ids) > 1:
-            return True
-        return False
-
-    def __iter__(self):
-        self.__cur_idx = 0
-        return self
-
-    def __next__(self):
-        self.__cur_idx += 1
-        if self.__cur_idx > len(self.board_ids):
-            raise StopIteration
-        return self.board_ids[self.__cur_idx-1]
-
-    def __getitem__(self, item):
-        return self.board_ids[item]
-
-
-available_boards = BoardsConnected()
-available_boards.discover()
-
+"""
+Discover and manage connected boards
+"""
+
+import glob
+import logging as log
+
+from .... import config
+from ... kcgwidget import error
+from communication import pci
+
+DUMMY_PCI_i_OUTPUT = 'Vendor: 10ee, Device: 6028, Bus: 3, Slot: 0, Function: 0\n\
+ Interrupt - Pin: 1, Line: 255\n\
+ BAR 0 - MEM32, Start: 0xf5500000, Length: 0x  100000, Flags: 0x00040200\n\
+\n\
+DMA Engines: \n\
+ DMA  0 C2S - Type: Packet, Address Width: 32 bits\n\
+\n\
+Banks: \n\
+ 0x80 dma: DMA Registers\n\
+'
+
+class BoardsConnected(object):
+    """
+    Container for connected/available boards
+    This will work as a generator and yield the available board_ids
+    NOTE: this is subject to change. In the future this may yield board_names or tuples or something else
+    """
+    def __init__(self):
+        self._board_ids = {}
+        self._board_ids_reverse = {}
+        self._device_files = {}
+        self.count = 0
+
+    def _device_id(self, dev_file):
+        """
+        Get the device id
+        :param dev_file: the dev_file to use
+        :return: string for the dev id
+        """
+        info_string = pci.info(dev_file=dev_file)
+        # info_string = DUMMY_PCI_i_OUTPUT
+        try:
+            return info_string.split('Device: ')[1].split(',')[0]
+        except IndexError:
+            error(0x002, "Pci returned no or wrong information string. Device probably not found.", severe=True)
+
+    def discover(self):
+        """
+        Discover connected boards
+        This is either a dummy implementation that defaults to /dev/fpga0 for every board
+        it will create 'virtual' boards
+        OR:
+        If config.use_dev_fpga_for_detection == True use number of /dev/fpga devices and use them
+        """
+        def get(dict_, what):  # this will return the dict value for what (Key) or what if what not in dict_
+            return dict_.get(what, what)
+
+        if config.board_detection_method == 'dev':
+            searchstring = '/dev/fpga'
+            # searchstring = '/dev/lp'
+            device_list = glob.glob(searchstring+'*')
+            log.info("Found {num} devices.".format(num=len(device_list)))
+
+            self._board_ids = {int(i.replace(searchstring, '')): get(config.device_names, self._device_id(i))
+                               for i in device_list}
+            self._device_files = {int(i.replace(searchstring, '')): i for i in device_list}
+
+        elif config.board_detection_method == 'dummy':
+            if config.num_dummy_boards:
+                num = int(config.num_dummy_boards)
+            else:
+                num = 5
+            self._board_ids = {i: get(config.device_names, 'test'+str(i)) for i in range(num)}
+            self._device_files = {i: '/dev/fpga0' for i in self.board_ids}
+
+        elif config.board_detection_method == 'list':
+            self._board_ids = {i: get(config.device_names, self._device_id(d_f)) for i, d_f in enumerate(config.device_list)}
+            self._device_files = {i: d_f for i, d_f in enumerate(config.device_list)}
+
+        else:
+            raise config.MisconfigurationError("board_detection_method was misconfigured")
+
+        # if no boards detected create a dummy board (everything will be disabled in the gui) so the gui does
+        # not crash
+        if len(self._board_ids) == 0:
+            self._board_ids[0] = None
+
+        self._board_ids_reverse = {v: k for k, v in self._board_ids.items()}  # build reverse look up
+
+    @property
+    def has_boards(self):
+        """
+        Check if at least one board is connected
+        """
+        if len(self.board_ids) == 1 and self._board_ids[0] is None:
+            return False
+        elif len(self.board_ids) > 0:
+            return True
+        else:
+            return False
+
+    @property
+    def board_ids(self):
+        """
+        Get Board Ids as list
+        """
+        return self._board_ids.keys()
+
+    @property
+    def board_names(self):
+        """
+        Get Board Names as list
+        :return:
+        """
+        return self._board_ids.values()
+
+    def get_board_name_from_id(self, board_id):
+        """
+        Get the name of a board with the given board_id
+        :param board_id: the board to get the name for
+        :return: the name of the board with id board_id
+        """
+        return self._board_ids[board_id]
+
+    def get_board_id_from_name(self, board_name):
+        """
+        Get the id of a board with the name board_name
+        :param board_name: the name of the board to get the id for
+        :return: the id of the board with name board_name
+        """
+        return self._board_ids_reverse[board_name]
+
+    def get_device_file(self, board_id):
+        """
+        Get the device file (/dev/...) to use when communicating with the board
+        :param board_id: the id of the board
+        :return: the device file as string
+        """
+        return self._device_files[board_id]
+
+    @property
+    def multi_board(self):
+        """
+        If multiple boards are connected this is true else false
+        """
+        if len(self.board_ids) > 1:
+            return True
+        return False
+
+    def __iter__(self):
+        self.__cur_idx = 0
+        return self
+
+    def next(self):
+        self.__cur_idx += 1
+        if self.__cur_idx > len(self.board_ids):
+            raise StopIteration
+        return self.board_ids[self.__cur_idx-1]
+
+    def __getitem__(self, item):
+        return self.board_ids[item]
+
+
+available_boards = BoardsConnected()
+available_boards.discover()
+

+ 204 - 204
KCG/base/backend/board/communication.py

@@ -1,204 +1,204 @@
-"""
-Communication part of the board package
-"""
-import subprocess
-import re
-import logging as log
-import time
-
-from .errors import *
-from ...kcgwidget import error
-
-
-class PCI(object):
-    """
-    The Interface to the pci command
-    """
-    def __init__(self):
-        pass
-
-    def _safe_call(self, cmd):
-        """
-        Actually call the pci command
-        :param cmd: the command line to execute as list
-        :return: the output of the command
-        """
-        log.debug(cmd)
-        try:
-            # if '-r' in cmd:
-            return subprocess.check_output(cmd)
-            # else:
-            #     subprocess.Popen(cmd, shell=False)
-        except subprocess.CalledProcessError as e:
-            raise BoardError(e.output)
-        except OSError as e:
-            if str(e) == "[Errno 2] No such file or directory":
-                error(0x003, "Pci command not found. Exiting.")
-                raise InterfaceNotFoundError("pci command was not found in your searchpath")
-            else:
-                raise BoardError('{}: {}'.format(' '.join(cmd), str(e)))
-
-    def _format(self, output):
-        """
-        Format output values (remove unnecessary parts of the output)
-        :param output: the unformatted output
-        :return: the formatted output
-        """
-        output = output.split("\n")
-        lines = []
-        for line in output:
-            if line and line != "\n":
-                lines.append(line.split(":  ")[1])
-        formatted = []
-        for line in lines:
-            if line and line != "\n":
-                formatted += re.findall(r'[\da-fA-F]{8}', line)
-        return formatted
-
-    def read(self, board_id, amount=1, reg=None, dma=None, destination=None, decimal=False, timeout=None):
-        """
-        Read from boards
-
-        :param board_id: id of the board to write to (mandatory)
-        :param amount: number of 32byte blocks to read (default is 1) [1]
-        :param reg: register to read from [1]
-        :param dma: the dma to read from [1]
-        :param destination: the destination to write the retrieved data to ('memory' to return the data)
-        :param decimal: whether to return the result as decimal number (default is False)
-        :param timeout: the timeout for the read (only useful when reading data from dma)
-        :return:
-
-        [1]: If neither reg nor dma are given reg will default to 0x9000 and data from registers will be read
-             If reg is given, data from registers will be read
-             If dma is given, data from dma will be read and amount is ignored
-             If both reg and dma are given, an error will be raised
-        """
-        # either reg or dma have to be None
-        assert reg is None or dma is None, "read was called with reg and dma arguments."
-
-        if not reg and not dma:
-            reg = '0x9000'
-        source = reg if reg else dma
-        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-r', source]
-
-        dst = '/dev/stdout' if destination == 'memory' else destination
-
-        if reg:
-            cmd_extend = ["-s%i" % amount]
-        else:
-            cmd_extend = ['--multipacket', '-o', dst]
-            if timeout:
-                cmd_extend.extend(['--timeout', str(timeout)])
-            log.vinfo('Write data to {}'.format(dst))
-
-        cmd.extend(cmd_extend)
-        output = self._safe_call(cmd)
-
-        if reg:
-            formatted = self._format(output)
-            if decimal:
-                if dst:
-                    with open(dst, 'r+') as f:  # to be consistent with use of destination
-                        f.write(str([int(x, 16) for x in formatted]))
-                    return
-                else:
-                    return [int(x, 16) for x in formatted]
-            else:
-                if dst:
-                    with open(dst, 'r+') as f:  # to be consistent with use of destination
-                        f.write(str(formatted))
-                    return
-                return formatted
-        else:
-            if dst == '/dev/stdout':
-                return output.split('Writting')[0]
-
-    def write(self, board_id, value, reg='0x9040', hex_mask='FFFFFFFF'):
-        """
-        Write to boards
-        :param board_id: id of the board to write to (mandatory)
-        :param value: value to write (mandatory)
-        :param reg: register to write to (optional, default is '0x9040')
-        :param hex_mask: hex mask to apply to value before writing (optional)
-        :return:
-        """
-        assert len(hex_mask) <= 8, "Hex Mask has more than 32 bit."
-
-        if hex_mask != 'FFFFFFFF':
-            prev_val = self.read(board_id, 1, reg)[0]
-            prev_bits = '{0:032b}'.format(int(prev_val, 16))
-            mask_bits = '{0:032b}'.format(int(hex_mask, 16))
-            value_bits = '{0:032b}'.format(int(value, 16))
-            new_bits = list(prev_bits)
-            for i, bit in enumerate(mask_bits):
-                if bit == '1':
-                    new_bits[i] = value_bits[i]
-            value = hex(int("".join(new_bits), 2))
-
-        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-w', reg, value]
-        self._safe_call(cmd)
-        log.debug('Written %s to register %s' % (value, reg))
-
-    def read_data_to_file(self, board_id, filename, dma='dma0', timeout=None):
-        """
-        Read data from board and write to file
-
-        :param board_id: the board to read from
-        :param filename: the filename to write to
-        :param dma: the dma to use?
-        :param timeout: if not None: the timeout for the underlying pci command
-        :return:
-        """
-        return self.read(board_id, dma=dma, destination=filename, timeout=timeout)
-
-    def read_data_to_variable(self, board_id, dma='dma0', timeout=None):
-        """
-        Read data and return it.
-
-        :param board_id: the board to read from
-        :param dma: the dma to use?
-        :param timeout: if not None: the timeout for the underlying pci command
-        :return: string with data read from board
-        """
-        return self.read(board_id, dma=dma, timeout=timeout, destination='memory')
-
-    def start_dma(self, board_id, dma='dma0r'):
-        """
-        Start dma engine.
-
-        :param board_id: the board to start the dma engine for
-        :param dma: the dma to use
-        :return:
-        """
-        log.vinfo('Start DMA')
-        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--start-dma', dma]
-        self._safe_call(cmd)
-        time.sleep(0.05)
-
-    def stop_dma(self, board_id, dma='dma0r'):
-        """
-        Stop dma engine.
-
-        :param board_id: the board to stop the dma engine for
-        :param dma: the dma to use
-        :return:
-        """
-        log.vinfo('Stop DMA')
-        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--stop-dma', dma]
-        self._safe_call(cmd)
-        time.sleep(0.05)
-
-    def info(self, board_id=None, dev_file=None):
-        """
-        Get Device info (output of pci -i)
-        :return: Information string returned by pci -i
-        """
-        assert board_id != dev_file, "info got both board_id and dev_file or got none of both"
-        if board_id is not None:
-            cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-i']
-        else:
-            cmd = ['pci', '-d', dev_file, '-i']
-        return self._safe_call(cmd)
-
-pci = PCI()
-from .boards_connected import available_boards  # this import has to be here as boards_connected imports pci
+"""
+Communication part of the board package
+"""
+import subprocess
+import re
+import logging as log
+import time
+
+from errors import *
+from ...kcgwidget import error
+
+
+class PCI(object):
+    """
+    The Interface to the pci command
+    """
+    def __init__(self):
+        pass
+
+    def _safe_call(self, cmd):
+        """
+        Actually call the pci command
+        :param cmd: the command line to execute as list
+        :return: the output of the command
+        """
+        log.debug(cmd)
+        try:
+            # if '-r' in cmd:
+            return subprocess.check_output(cmd)
+            # else:
+            #     subprocess.Popen(cmd, shell=False)
+        except subprocess.CalledProcessError as e:
+            raise BoardError(e.output)
+        except OSError as e:
+            if str(e) == "[Errno 2] No such file or directory":
+                error(0x003, "Pci command not found. Exiting.")
+                raise InterfaceNotFoundError("pci command was not found in your searchpath")
+            else:
+                raise BoardError('{}: {}'.format(' '.join(cmd), str(e)))
+
+    def _format(self, output):
+        """
+        Format output values (remove unnecessary parts of the output)
+        :param output: the unformatted output
+        :return: the formatted output
+        """
+        output = output.split("\n")
+        lines = []
+        for line in output:
+            if line and line != "\n":
+                lines.append(line.split(":  ")[1])
+        formatted = []
+        for line in lines:
+            if line and line != "\n":
+                formatted += re.findall(r'[\da-fA-F]{8}', line)
+        return formatted
+
+    def read(self, board_id, amount=1, reg=None, dma=None, destination=None, decimal=False, timeout=None):
+        """
+        Read from boards
+
+        :param board_id: id of the board to write to (mandatory)
+        :param amount: number of 32byte blocks to read (default is 1) [1]
+        :param reg: register to read from [1]
+        :param dma: the dma to read from [1]
+        :param destination: the destination to write the retrieved data to ('memory' to return the data)
+        :param decimal: whether to return the result as decimal number (default is False)
+        :param timeout: the timeout for the read (only useful when reading data from dma)
+        :return:
+
+        [1]: If neither reg nor dma are given reg will default to 0x9000 and data from registers will be read
+             If reg is given, data from registers will be read
+             If dma is given, data from dma will be read and amount is ignored
+             If both reg and dma are given, an error will be raised
+        """
+        # either reg or dma have to be None
+        assert reg is None or dma is None, "read was called with reg and dma arguments."
+
+        if not reg and not dma:
+            reg = '0x9000'
+        source = reg if reg else dma
+        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-r', source]
+
+        dst = '/dev/stdout' if destination == 'memory' else destination
+
+        if reg:
+            cmd_extend = ["-s%i" % amount]
+        else:
+            cmd_extend = ['--multipacket', '-o', dst]
+            if timeout:
+                cmd_extend.extend(['--timeout', str(timeout)])
+            log.vinfo('Write data to {}'.format(dst))
+
+        cmd.extend(cmd_extend)
+        output = self._safe_call(cmd)
+
+        if reg:
+            formatted = self._format(output)
+            if decimal:
+                if dst:
+                    with open(dst, 'r+') as f:  # to be consistent with use of destination
+                        f.write(str([int(x, 16) for x in formatted]))
+                    return
+                else:
+                    return [int(x, 16) for x in formatted]
+            else:
+                if dst:
+                    with open(dst, 'r+') as f:  # to be consistent with use of destination
+                        f.write(str(formatted))
+                    return
+                return formatted
+        else:
+            if dst == '/dev/stdout':
+                return output.split('Writting')[0]
+
+    def write(self, board_id, value, reg='0x9040', hex_mask='FFFFFFFF'):
+        """
+        Write to boards
+        :param board_id: id of the board to write to (mandatory)
+        :param value: value to write (mandatory)
+        :param reg: register to write to (optional, default is '0x9040')
+        :param hex_mask: hex mask to apply to value before writing (optional)
+        :return:
+        """
+        assert len(hex_mask) <= 8, "Hex Mask has more than 32 bit."
+
+        if hex_mask != 'FFFFFFFF':
+            prev_val = self.read(board_id, 1, reg)[0]
+            prev_bits = '{0:032b}'.format(int(prev_val, 16))
+            mask_bits = '{0:032b}'.format(int(hex_mask, 16))
+            value_bits = '{0:032b}'.format(int(value, 16))
+            new_bits = list(prev_bits)
+            for i, bit in enumerate(mask_bits):
+                if bit == '1':
+                    new_bits[i] = value_bits[i]
+            value = hex(int("".join(new_bits), 2))
+
+        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-w', reg, value]
+        self._safe_call(cmd)
+        log.debug('Written %s to register %s' % (value, reg))
+
+    def read_data_to_file(self, board_id, filename, dma='dma0', timeout=None):
+        """
+        Read data from board and write to file
+
+        :param board_id: the board to read from
+        :param filename: the filename to write to
+        :param dma: the dma to use?
+        :param timeout: if not None: the timeout for the underlying pci command
+        :return:
+        """
+        return self.read(board_id, dma=dma, destination=filename, timeout=timeout)
+
+    def read_data_to_variable(self, board_id, dma='dma0', timeout=None):
+        """
+        Read data and return it.
+
+        :param board_id: the board to read from
+        :param dma: the dma to use?
+        :param timeout: if not None: the timeout for the underlying pci command
+        :return: string with data read from board
+        """
+        return self.read(board_id, dma=dma, timeout=timeout, destination='memory')
+
+    def start_dma(self, board_id, dma='dma0r'):
+        """
+        Start dma engine.
+
+        :param board_id: the board to start the dma engine for
+        :param dma: the dma to use
+        :return:
+        """
+        log.vinfo('Start DMA')
+        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--start-dma', dma]
+        self._safe_call(cmd)
+        time.sleep(0.05)
+
+    def stop_dma(self, board_id, dma='dma0r'):
+        """
+        Stop dma engine.
+
+        :param board_id: the board to stop the dma engine for
+        :param dma: the dma to use
+        :return:
+        """
+        log.vinfo('Stop DMA')
+        cmd = ['pci', '-d', available_boards.get_device_file(board_id), '--stop-dma', dma]
+        self._safe_call(cmd)
+        time.sleep(0.05)
+
+    def info(self, board_id=None, dev_file=None):
+        """
+        Get Device info (output of pci -i)
+        :return: Information string returned by pci -i
+        """
+        assert board_id != dev_file, "info got both board_id and dev_file or got none of both"
+        if board_id is not None:
+            cmd = ['pci', '-d', available_boards.get_device_file(board_id), '-i']
+        else:
+            cmd = ['pci', '-d', dev_file, '-i']
+        return self._safe_call(cmd)
+
+pci = PCI()
+from boards_connected import available_boards  # this import has to be here as boards_connected imports pci

+ 37 - 37
KCG/base/backend/board/errors.py

@@ -1,37 +1,37 @@
-"""
-Errors for board actions
-"""
-class BoardError(Exception):
-    """
-    A General Error with the Board
-    """
-    pass
-
-
-class InterfaceNotFoundError(Exception):
-    """
-    Error if not pci command was found
-    """
-    pass
-
-
-class ObserverError(Exception):
-    """
-    Error in Observation of configuration values
-    """
-    pass
-
-
-class NoBoardId(Exception):
-    """
-    Error if not board id was given
-    """
-    pass
-
-
-class NoSuchKeyError(Exception):
-    """
-    Error if a key is requested that does not exist in configuration
-    """
-    pass
-
+"""
+Errors for board actions
+"""
+class BoardError(Exception):
+    """
+    A General Error with the Board
+    """
+    pass
+
+
+class InterfaceNotFoundError(Exception):
+    """
+    Error if not pci command was found
+    """
+    pass
+
+
+class ObserverError(Exception):
+    """
+    Error in Observation of configuration values
+    """
+    pass
+
+
+class NoBoardId(Exception):
+    """
+    Error if not board id was given
+    """
+    pass
+
+
+class NoSuchKeyError(Exception):
+    """
+    Error if a key is requested that does not exist in configuration
+    """
+    pass
+

+ 165 - 165
KCG/base/backend/board/sequences.py

@@ -1,165 +1,165 @@
-"""
-Sequences for various board actions
-These methods are generators that will yield as first element the number
-of actions (other yields) excluding it self
-They will then perform the next action in row (setting registers or reading values from boards etc)
-and will yield True or False depending on the result of the action performed
-"""
-import logging
-
-from . import *
-from .utils import *
-
-
-def startup_sequence(board_id):
-    """
-    Star the board
-    """
-    NUMBER = 4
-    yield NUMBER
-
-    pci.start_dma(board_id)
-    pci.write(board_id, '0x20001000', '0x9100')
-    pci.write(board_id, '0x05', '0x9040')
-    yield True
-
-    pci.write(board_id, '0x3C1', '0x9040')
-    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
-    yield True
-
-    pci.write(board_id, '0x3F1', '0x9040')
-    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
-    yield True
-
-    pci.write(board_id, '0x3F0', '0x9040')
-    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
-    yield True
-
-
-def calibration_sequence(board_id):
-    """
-    Calibrate the board
-    """
-    NUMBER = 15
-    yield NUMBER
-
-    # Removing Board Reset
-    pci.write(board_id, '0x2000003f0', '0x9040')
-    yield True
-
-    # SPI Fanout Programming...
-    pci.write(board_id, '0x083', '0x9068')
-    yield True
-
-    pci.write(board_id, '0x6a2', '0x9068')
-    yield True
-
-    # PLL calibration start...
-    # PLL Reset...
-    pci.write(board_id, '0x80000000', '0x9060')
-    yield True
-
-    # Set CH_0 (FPGA) clock FPGA ...
-    pci.write(board_id, '0x00050000', '0x9060')
-    yield True
-
-    # Set CH_3 clock fanout ...
-    pci.write(board_id, '0x00050003', '0x9060')
-    yield True
-
-    # Set CH_4 clock ADC 1 ...
-    pci.write(board_id, '0x00050004', '0x9060')
-    yield True
-
-    # Set CH_5 clock ADC 2 ...
-    pci.write(board_id, '0x00050005', '0x9060')
-    yield True
-
-    # Set CH_6 clock ADC 3 ...
-    pci.write(board_id, '0x00050006', '0x9060')
-    yield True
-
-    # Set CH_7 clock ADC 4 ...
-    pci.write(board_id, '0x00050007', '0x9060')
-    yield True
-
-    # Set R8 ...
-    pci.write(board_id, '0x10000908', '0x9060')
-    yield True
-
-    # Set R11 ...
-    pci.write(board_id, '0x0082800B', '0x9060')
-    yield True
-
-    # Set R13 ...
-    pci.write(board_id, '0x029F400D', '0x9060')
-    yield True
-
-    # Set R14 (F_out and Global_EN => ON) ...
-    pci.write(board_id, '0x0830040E', '0x9060')
-    yield True
-
-    # Set R15 ...
-    #New frequency divider
-    pci.write(board_id, '0xCC00100F', '0x9060')
-    #pci.write(board_id, '0xD000100F', '0x9060')
-    yield True
-
-
-def synchronisation_sequence(board_id):
-    """
-    Synchronize the board
-    """
-    NUMBER = 2
-    yield NUMBER
-
-    # Send the PLL sync signals ...
-    pci.write(board_id, '0x1003f0', '0x9040')
-    yield True
-
-    pci.write(board_id, '0x0003f0', '0x9040')
-    yield True
-
-def write_value_sequence(board_id):
-    """
-    Write values to the board
-    """
-    NUMBER = 8
-    yield NUMBER
-
-    # Set_FPGA_clock_delay.sh 0
-    get_board_config(board_id).set_fpga_delay(get_board_config(board_id).get('fpga_delay'))
-    yield True
-
-    # Set_Delay_chip.sh 16 16 16 16
-    factors = [get_board_config(board_id).get('chip_1_delay'), get_board_config(board_id).get('chip_2_delay'),
-               get_board_config(board_id).get('chip_3_delay'), get_board_config(board_id).get('chip_4_delay')]
-    get_board_config(board_id).set_chip_delay([0, 1, 2, 3], factors)
-    yield True
-
-    # Set_TH_Delay.sh 12
-    get_board_config(board_id).set_th_delay(get_board_config(board_id).get('th_delay'))
-    yield True
-
-    # Set_ADC_1_Delay.sh 4
-    get_board_config(board_id).set_adc_delay(0, get_board_config(board_id).get('adc_1_delay'))
-    yield True
-
-    # Set_ADC_2_Delay.sh 4
-    get_board_config(board_id).set_adc_delay(1, get_board_config(board_id).get('adc_2_delay'))
-    yield True
-
-    # Set_ADC_3_Delay.sh 4
-    get_board_config(board_id).set_adc_delay(2, get_board_config(board_id).get('adc_3_delay'))
-    yield True
-
-    # Set_ADC_4_Delay.sh 4
-    get_board_config(board_id).set_adc_delay(3, get_board_config(board_id).get('adc_4_delay'))
-    yield True
-
-    pci.write(board_id, '{0:08x}'.format(get_board_config(board_id).get('orbits_observe')), '0x9020')
-    yield True
-
-    pci.write(board_id, '{0:08x}'.format(get_board_config(board_id).get('orbits_skip')), '0x9028')
-    yield True
-
+"""
+Sequences for various board actions
+These methods are generators that will yield as first element the number
+of actions (other yields) excluding it self
+They will then perform the next action in row (setting registers or reading values from boards etc)
+and will yield True or False depending on the result of the action performed
+"""
+import logging
+
+from . import *
+from utils import *
+
+
+def startup_sequence(board_id):
+    """
+    Star the board
+    """
+    NUMBER = 4
+    yield NUMBER
+
+    pci.start_dma(board_id)
+    pci.write(board_id, '0x20001000', '0x9100')
+    pci.write(board_id, '0x05', '0x9040')
+    yield True
+
+    pci.write(board_id, '0x3C1', '0x9040')
+    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
+    yield True
+
+    pci.write(board_id, '0x3F1', '0x9040')
+    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
+    yield True
+
+    pci.write(board_id, '0x3F0', '0x9040')
+    logging.info("9040:  " + str(pci.read(board_id, 1, '0x9040')[0]))
+    yield True
+
+
+def calibration_sequence(board_id):
+    """
+    Calibrate the board
+    """
+    NUMBER = 15
+    yield NUMBER
+
+    # Removing Board Reset
+    pci.write(board_id, '0x2000003f0', '0x9040')
+    yield True
+
+    # SPI Fanout Programming...
+    pci.write(board_id, '0x083', '0x9068')
+    yield True
+
+    pci.write(board_id, '0x6a2', '0x9068')
+    yield True
+
+    # PLL calibration start...
+    # PLL Reset...
+    pci.write(board_id, '0x80000000', '0x9060')
+    yield True
+
+    # Set CH_0 (FPGA) clock FPGA ...
+    pci.write(board_id, '0x00050000', '0x9060')
+    yield True
+
+    # Set CH_3 clock fanout ...
+    pci.write(board_id, '0x00050003', '0x9060')
+    yield True
+
+    # Set CH_4 clock ADC 1 ...
+    pci.write(board_id, '0x00050004', '0x9060')
+    yield True
+
+    # Set CH_5 clock ADC 2 ...
+    pci.write(board_id, '0x00050005', '0x9060')
+    yield True
+
+    # Set CH_6 clock ADC 3 ...
+    pci.write(board_id, '0x00050006', '0x9060')
+    yield True
+
+    # Set CH_7 clock ADC 4 ...
+    pci.write(board_id, '0x00050007', '0x9060')
+    yield True
+
+    # Set R8 ...
+    pci.write(board_id, '0x10000908', '0x9060')
+    yield True
+
+    # Set R11 ...
+    pci.write(board_id, '0x0082800B', '0x9060')
+    yield True
+
+    # Set R13 ...
+    pci.write(board_id, '0x029F400D', '0x9060')
+    yield True
+
+    # Set R14 (F_out and Global_EN => ON) ...
+    pci.write(board_id, '0x0830040E', '0x9060')
+    yield True
+
+    # Set R15 ...
+    #New frequency divider
+    pci.write(board_id, '0xCC00100F', '0x9060')
+    #pci.write(board_id, '0xD000100F', '0x9060')
+    yield True
+
+
+def synchronisation_sequence(board_id):
+    """
+    Synchronize the board
+    """
+    NUMBER = 2
+    yield NUMBER
+
+    # Send the PLL sync signals ...
+    pci.write(board_id, '0x1003f0', '0x9040')
+    yield True
+
+    pci.write(board_id, '0x0003f0', '0x9040')
+    yield True
+
+def write_value_sequence(board_id):
+    """
+    Write values to the board
+    """
+    NUMBER = 8
+    yield NUMBER
+
+    # Set_FPGA_clock_delay.sh 0
+    get_board_config(board_id).set_fpga_delay(get_board_config(board_id).get('fpga_delay'))
+    yield True
+
+    # Set_Delay_chip.sh 16 16 16 16
+    factors = [get_board_config(board_id).get('chip_1_delay'), get_board_config(board_id).get('chip_2_delay'),
+               get_board_config(board_id).get('chip_3_delay'), get_board_config(board_id).get('chip_4_delay')]
+    get_board_config(board_id).set_chip_delay([0, 1, 2, 3], factors)
+    yield True
+
+    # Set_TH_Delay.sh 12
+    get_board_config(board_id).set_th_delay(get_board_config(board_id).get('th_delay'))
+    yield True
+
+    # Set_ADC_1_Delay.sh 4
+    get_board_config(board_id).set_adc_delay(0, get_board_config(board_id).get('adc_1_delay'))
+    yield True
+
+    # Set_ADC_2_Delay.sh 4
+    get_board_config(board_id).set_adc_delay(1, get_board_config(board_id).get('adc_2_delay'))
+    yield True
+
+    # Set_ADC_3_Delay.sh 4
+    get_board_config(board_id).set_adc_delay(2, get_board_config(board_id).get('adc_3_delay'))
+    yield True
+
+    # Set_ADC_4_Delay.sh 4
+    get_board_config(board_id).set_adc_delay(3, get_board_config(board_id).get('adc_4_delay'))
+    yield True
+
+    pci.write(board_id, '{0:08x}'.format(get_board_config(board_id).get('orbits_observe')), '0x9020')
+    yield True
+
+    pci.write(board_id, '{0:08x}'.format(get_board_config(board_id).get('orbits_skip')), '0x9028')
+    yield True
+

+ 18 - 18
KCG/base/backend/board/status.py

@@ -1,18 +1,18 @@
-"""
-Status for boards
-"""
-
-
-class StatusStorage(object):
-    """
-    Class used as Container for various storage purposes
-    """
-    def __getattr__(self, item):
-        try:
-            # return self.old_getattr(item)
-            return object.__getattribute__(self, item)
-        except AttributeError:
-            # print item+" was not set, None is returned"
-            return None
-
-
+"""
+Status for boards
+"""
+
+
+class StatusStorage(object):
+    """
+    Class used as Container for various storage purposes
+    """
+    def __getattr__(self, item):
+        try:
+            # return self.old_getattr(item)
+            return object.__getattribute__(self, item)
+        except AttributeError:
+            # print item+" was not set, None is returned"
+            return None
+
+

+ 180 - 180
KCG/base/backend/board/utils.py

@@ -1,180 +1,180 @@
-"""
-Helper methods
-"""
-import logging as log
-import time
-
-from .communication import pci
-from .errors import *
-from .status import StatusStorage
-from .board_config import BoardConfiguration
-from .... import config as kcg_config
-from .boards_connected import available_boards
-
-
-def get_dec_from_bits(bits, msb=-1, lsb=-1):
-    """
-    Get decimal values from a string represented binary string
-    :param bits: the string to convert
-    :param msb: the most significant bit to use
-    :param lsb: the least significant bit to use
-    :return: the decimal value
-    """
-    rtrnValue = 0
-    if (msb < 0 or lsb < 0):
-        rtrnValue = int(bits)
-    elif (msb < lsb) or (msb > 31) or (lsb > 31):
-        log.info("Bit range for msb and lsb of get_dec_from_bits was wrong. Not truncating")
-        rtrnValue = int(bits, 2)
-    else:
-        chunk = ('{0:032b}'.format(int(bits, 2)))[31-msb:32-lsb]
-        rtrnValue = int(chunk, 2)
-
-    return rtrnValue
-
-
-def get_status(board_id):
-    """
-    Get the satatus of the board (this is used for the status leds)
-    :param board_id: the id of the board
-    :return: dictionary with the bits for each led (lower case led names are the keys of this dict)
-    """
-    registers = pci.read(board_id, 3, '0x9050', decimal=True)
-    bits = []
-    bits += ['{0:032b}'.format(registers[0])]
-    bits += ['{0:032b}'.format(registers[1])]
-    bits += ['{0:032b}'.format(registers[2])]
-
-    status = {}
-
-    s1 = get_dec_from_bits(bits[0], 2, 0)
-    if s1 == 0:
-        # Pipeline in reset mode
-        # self.pipeline_led.set_tri()
-        status['pipeline'] = 2
-    elif s1 == 1:
-        # Pipeline is idle
-        # self.pipeline_led.set_on()
-        status['pipeline'] = 3
-    elif s1 == 6:
-        # Pipeline in error state
-        # self.pipeline_led.set_off()
-        status['pipeline'] = 1
-    else:
-        # Should not happen!
-        # self.pipeline_led.set_out()
-        status['pipeline'] = 0
-
-    s2 = get_dec_from_bits(bits[0], 29, 26)
-    if s2 == 0:
-        # Master Control in reset mode
-        # self.master_control_led.set_tri()
-        status['master_control'] = 2
-    elif s2 == 1:
-        # Master Control is idle
-        # self.master_control_led.set_on()
-        status['master_control'] = 3
-    elif s2 == 8:
-        # Master Control in error state
-        # self.master_control_led.set_off()
-        status['master_control'] = 1
-    else:
-        # Should not happen!
-        # self.master_control_led.set_out()
-        status['master_control'] = 0
-
-    s3 = get_dec_from_bits(bits[2], 15, 12)
-    if s3 == 15:
-        # Data Check Idle
-        # self.data_check_led.set_on()
-        status['data_check'] = 3
-    else:
-        # Data Check Error
-        # self.data_check_led.set_off()
-        status['data_check'] = 1
-
-    s4 = int(bits[0][7])
-    if s4 == 0:
-        # PLL_LD not active
-        # self.pll_ld_led.set_tri()
-        status['PLL_LD'] = 2
-    elif s4 == 1:
-        # PLL_LD is active
-        # self.pll_ld_led.set_on()
-        status['PLL_LD'] = 3
-    else:
-        status['PLL_LD'] = 0
-
-    return status
-
-
-def is_conneced(board_id):
-    """
-    Is the board connected?
-    :param board_id: the id for the board to check
-    :return: True if the board is connected else False
-    """
-    try:
-        pci.read(board_id, 1, '0x9040')
-        return True
-    except BoardError:
-        return False
-    except InterfaceNotFoundError:
-        return None
-
-
-def is_active(board_id):
-    """
-    Check if a board is active
-    :param board_id: the board to check for
-    :return: True if it is active else False
-    """
-    control = pci.read(board_id, 1, '0x9040')[0]
-    control_bits = '{0:032b}'.format(int(control, 16))
-    return control_bits[22:26] == "1111"
-
-
-def wait_for_revolutions(board_id):
-    """
-    Sleep as long as the revolutions in the accelerator last to not stop the acquisition before it ended.
-    :param board_id: the board to wait for?
-    :return:
-    """
-    n = pci.read(board_id, 1, '0x9020', decimal=True)[0]  # Get the amount of orbits to observe
-    # n = 1 # Use this for debugging purposes if no board is connected
-    spin_time_ns = kcg_config.tRev * n
-    time.sleep(spin_time_ns * 1.1)  # 10% Safety margin
-
-
-_status = []
-_configs = []
-
-
-def create_new_board_config(identifier):
-    """
-    This creates a new instance of BoardConfiguration and also a new instance of StatusStorage
-    :param identifier: the identifier for this board (not the id)
-    :return:
-    """
-    global _configs
-    global _status
-    _configs.append(BoardConfiguration(identifier))
-    _status.append(StatusStorage())
-
-
-def get_board_config(id):
-    """
-    Get the configuration instance for the board with the given id
-    :param id: the id of the desired board
-    :return: the instance of configuration
-    """
-    return _configs[id]
-
-
-def get_board_status(id):
-    """
-    Get the status storage instance for the board with the given id
-    :param id: the id of the desired board
-    :return: the instance of the status storage
-    """
-    return _status[id]
+"""
+Helper methods
+"""
+import logging as log
+import time
+
+from communication import pci
+from errors import *
+from status import StatusStorage
+from board_config import BoardConfiguration
+from .... import config as kcg_config
+from boards_connected import available_boards
+
+
+def get_dec_from_bits(bits, msb=-1, lsb=-1):
+    """
+    Get decimal values from a string represented binary string
+    :param bits: the string to convert
+    :param msb: the most significant bit to use
+    :param lsb: the least significant bit to use
+    :return: the decimal value
+    """
+    rtrnValue = 0
+    if (msb < 0 or lsb < 0):
+        rtrnValue = int(bits)
+    elif (msb < lsb) or (msb > 31) or (lsb > 31):
+        log.info("Bit range for msb and lsb of get_dec_from_bits was wrong. Not truncating")
+        rtrnValue = int(bits, 2)
+    else:
+        chunk = ('{0:032b}'.format(int(bits, 2)))[31-msb:32-lsb]
+        rtrnValue = int(chunk, 2)
+
+    return rtrnValue
+
+
+def get_status(board_id):
+    """
+    Get the satatus of the board (this is used for the status leds)
+    :param board_id: the id of the board
+    :return: dictionary with the bits for each led (lower case led names are the keys of this dict)
+    """
+    registers = pci.read(board_id, 3, '0x9050', decimal=True)
+    bits = []
+    bits += ['{0:032b}'.format(registers[0])]
+    bits += ['{0:032b}'.format(registers[1])]
+    bits += ['{0:032b}'.format(registers[2])]
+
+    status = {}
+
+    s1 = get_dec_from_bits(bits[0], 2, 0)
+    if s1 == 0:
+        # Pipeline in reset mode
+        # self.pipeline_led.set_tri()
+        status['pipeline'] = 2
+    elif s1 == 1:
+        # Pipeline is idle
+        # self.pipeline_led.set_on()
+        status['pipeline'] = 3
+    elif s1 == 6:
+        # Pipeline in error state
+        # self.pipeline_led.set_off()
+        status['pipeline'] = 1
+    else:
+        # Should not happen!
+        # self.pipeline_led.set_out()
+        status['pipeline'] = 0
+
+    s2 = get_dec_from_bits(bits[0], 29, 26)
+    if s2 == 0:
+        # Master Control in reset mode
+        # self.master_control_led.set_tri()
+        status['master_control'] = 2
+    elif s2 == 1:
+        # Master Control is idle
+        # self.master_control_led.set_on()
+        status['master_control'] = 3
+    elif s2 == 8:
+        # Master Control in error state
+        # self.master_control_led.set_off()
+        status['master_control'] = 1
+    else:
+        # Should not happen!
+        # self.master_control_led.set_out()
+        status['master_control'] = 0
+
+    s3 = get_dec_from_bits(bits[2], 15, 12)
+    if s3 == 15:
+        # Data Check Idle
+        # self.data_check_led.set_on()
+        status['data_check'] = 3
+    else:
+        # Data Check Error
+        # self.data_check_led.set_off()
+        status['data_check'] = 1
+
+    s4 = int(bits[0][7])
+    if s4 == 0:
+        # PLL_LD not active
+        # self.pll_ld_led.set_tri()
+        status['PLL_LD'] = 2
+    elif s4 == 1:
+        # PLL_LD is active
+        # self.pll_ld_led.set_on()
+        status['PLL_LD'] = 3
+    else:
+        status['PLL_LD'] = 0
+
+    return status
+
+
+def is_conneced(board_id):
+    """
+    Is the board connected?
+    :param board_id: the id for the board to check
+    :return: True if the board is connected else False
+    """
+    try:
+        pci.read(board_id, 1, '0x9040')
+        return True
+    except BoardError:
+        return False
+    except InterfaceNotFoundError:
+        return None
+
+
+def is_active(board_id):
+    """
+    Check if a board is active
+    :param board_id: the board to check for
+    :return: True if it is active else False
+    """
+    control = pci.read(board_id, 1, '0x9040')[0]
+    control_bits = '{0:032b}'.format(int(control, 16))
+    return control_bits[22:26] == "1111"
+
+
+def wait_for_revolutions(board_id):
+    """
+    Sleep as long as the revolutions in the accelerator last to not stop the acquisition before it ended.
+    :param board_id: the board to wait for?
+    :return:
+    """
+    n = pci.read(board_id, 1, '0x9020', decimal=True)[0]  # Get the amount of orbits to observe
+    # n = 1 # Use this for debugging purposes if no board is connected
+    spin_time_ns = kcg_config.tRev * n
+    time.sleep(spin_time_ns * 1.1)  # 10% Safety margin
+
+
+_status = []
+_configs = []
+
+
+def create_new_board_config(identifier):
+    """
+    This creates a new instance of BoardConfiguration and also a new instance of StatusStorage
+    :param identifier: the identifier for this board (not the id)
+    :return:
+    """
+    global _configs
+    global _status
+    _configs.append(BoardConfiguration(identifier))
+    _status.append(StatusStorage())
+
+
+def get_board_config(id):
+    """
+    Get the configuration instance for the board with the given id
+    :param id: the id of the desired board
+    :return: the instance of configuration
+    """
+    return _configs[id]
+
+
+def get_board_status(id):
+    """
+    Get the status storage instance for the board with the given id
+    :param id: the id of the desired board
+    :return: the instance of the status storage
+    """
+    return _status[id]

+ 165 - 172
KCG/base/backend/dataset.py

@@ -1,172 +1,165 @@
-import logging
-import time
-
-import numpy as np
-from numpy.polynomial.polynomial import polyval
-
-from ... import config
-
-
-def _pad_array(array):
-    height, width = array.shape
-
-    # Miriam uses floor hence the padding is actually a cutting. Will wait for
-    # response if this is desired ...
-    pwidth = 2**np.floor(np.log2(width))
-    padded = np.zeros((height, pwidth))
-    padded[:, :width] = array[:, :pwidth]
-    return padded
-
-#try:
-    #import reikna.cluda
-    #import reikna.fft
-
-    #_plans = {}
-    #_in_buffers = {}
-    #_out_buffers = {}
-
-    #_api = reikna.cluda.ocl_api()
-    #_thr = _api.Thread.create()
-
-    #def _fft(array):
-        #start = time.time()
-        #padded = _pad_array(array).astype(np.complex64)
-        #height, width = padded.shape
-
-        #if width in _plans:
-            #fft = _plans[width]
-            #in_dev = _in_buffers[width]
-            #out_dev = _out_buffers[width]
-        #else:
-            #fft = reikna.fft.FFT(padded, axes=(1,)).compile(_thr)
-            #in_dev = _thr.to_device(padded)
-            #out_dev = _thr.empty_like(in_dev)
-            #_plans[width] = fft
-            #_in_buffers[width] = in_dev
-            #_out_buffers[width] = out_dev
-
-        #fft(out_dev, in_dev)
-        #logging.debug("GPU fft: {} s".format(time.time() - start))
-        #return out_dev.get()[:, :width / 2 + 1]
-    #logging.info("Using GPU based FFT!")
-
-#except ImportError:
-    #logging.debug("Failed to import reikna package. Falling back to Numpy FFT.")
-def _fft(array):
-    start = time.time()
-    freqs = np.fft.rfft(_pad_array(array))
-    logging.debug("np fft: {} s".format(time.time() - start))
-    return freqs
-
-
-BUNCHES_PER_TURN = config.bunches_per_turn
-HEADER_SIZE_BYTES = 32
-
-
-class DataSet(object):
-    def __init__(self, array, filename, header=None):
-        self.filename = filename
-        self.array = array
-        self._heatmaps = {}
-        self._ffts = {}
-        self.header = header
-
-    @property
-    def skipped_turns(self):
-        if self.header:
-            return self.header['skipped_turns']
-        else:
-            return 1
-
-    def bunch(self, number):
-        return self.array[self.array[:, 4] == number]
-
-    def num_bunches(self):
-        return self.array.shape[0]
-
-    def num_turns(self):
-        return self.num_bunches() / BUNCHES_PER_TURN
-
-    def heatmap(self, adc=1, frm=0, to=-1, bunch_frm=0, bunch_to=-1):
-        if not 1 <= adc <= 4:
-            raise ValueError('adc must be in [1,4]')
-
-        if not adc in self._heatmaps:
-            heatmap = self.array[:,adc-1].reshape(-1, BUNCHES_PER_TURN).transpose()
-            self._heatmaps[adc] = heatmap
-
-        return self._heatmaps[adc][bunch_frm:bunch_to, frm:to]
-
-    def fft(self, adc=1, frm=0, to=-1, drop_first_bin=False):
-        if not 1 <= adc <= 4:
-            raise ValueError('adc must be in [1,4]')
-
-        # if not adc in self._ffts:
-        #     heatmap = self.heatmap(adc, frm, to)
-        #     self._ffts[adc] = np.fft.fft2(heatmap, axes=[1])
-
-        # return self._ffts[adc]
-        heatmap = self.heatmap(adc, frm, to)
-        if drop_first_bin:
-            return _fft(heatmap)[:, 1:]
-        else:
-            return _fft(heatmap)
-
-    def fft_max_freq(self):
-        return (self.num_turns() // 2 + 1) * self.fft_freq_dist()
-    def fft_freq_dist(self):
-        return 1.0/(self.num_turns() * (self.skipped_turns + 1) * config.tRev)
-
-    def train(self, adc=1, frm=0, to=-1, **kwargs):
-        pdata = self.array[frm:to, :-1]
-        return pdata[:,adc-1]
-
-    def follow(self, adc=1, frm=0, to=-1, bunch=0, **kwargs):
-        """Follow one bunch through time"""
-        # pdata = self.array[frm:to, :-1]
-        pdata = self.array[bunch::BUNCHES_PER_TURN, adc-1]
-        pdata = pdata[frm:to]
-        return pdata
-
-    def combined(self, frm=0, to=-1, show_reconstructed=True):
-        array = self.array[frm:to, :]
-
-        N_s = 16
-        N_p = array.shape[0]
-
-        # Remove bunch number and flatten array
-        array = array[:,:4].reshape((-1, 1))
-        array = array.flatten()
-
-        # plot original data
-        orig_xs = np.arange(0, array.shape[0])
-        # axis.plot(orig_xs, array, '.')
-        ret = [np.array([orig_xs, array])]
-
-        # 4 rows for each ADC
-        ys = array.reshape((4, -1), order='F')
-
-        # multi-fit for each column of 4 ADCs, unfortunately, xs' are always the
-        # same, so we have to offset them later when computing the value
-        xs = [1, 2, 3, 4]
-        c = np.polynomial.polynomial.polyfit(xs, ys, 6)
-
-        smooth_xs = np.linspace(1, 4, N_s)
-        fitted_ys = polyval(smooth_xs, c, tensor=True)
-
-        if True:
-            # Output maxima
-            ys = np.max(fitted_ys, axis=1)
-            xs = 4 * ((np.argmax(fitted_ys, axis=1) + 1) / float(N_s)) + orig_xs[::4] - .5
-
-            # axis.plot(xs, ys, '.', color="red")
-            ret.append(np.array([xs, ys]))
-
-        if False:
-            # Debug output
-            ys = fitted_ys.reshape((-1, 1))
-            ys = ys.flatten()
-            xs = np.repeat(np.arange(0, 4 * N_p, 4), N_s) + np.tile(np.linspace(0, 3, N_s), N_p)
-            # axis.plot(xs, ys, '.', alpha=0.3)
-            ret.append(np.array([xs, ys]))
-        return ret
+import logging
+import time
+
+import numpy as np
+from numpy.polynomial.polynomial import polyval
+
+from ... import config
+
+
+def _pad_array(array):
+    height, width = array.shape
+
+    # Miriam uses floor hence the padding is actually a cutting. Will wait for
+    # response if this is desired ...
+    pwidth = 2**np.floor(np.log2(width))
+    padded = np.zeros((height, pwidth))
+    padded[:, :width] = array[:, :pwidth]
+    return padded
+
+try:
+    import reikna.cluda
+    import reikna.fft
+
+    _plans = {}
+    _in_buffers = {}
+    _out_buffers = {}
+
+    _api = reikna.cluda.ocl_api()
+    _thr = _api.Thread.create()
+
+    def _fft(array):
+        start = time.time()
+        padded = _pad_array(array).astype(np.complex64)
+        height, width = padded.shape
+
+        if width in _plans:
+            fft = _plans[width]
+            in_dev = _in_buffers[width]
+            out_dev = _out_buffers[width]
+        else:
+            fft = reikna.fft.FFT(padded, axes=(1,)).compile(_thr)
+            in_dev = _thr.to_device(padded)
+            out_dev = _thr.empty_like(in_dev)
+            _plans[width] = fft
+            _in_buffers[width] = in_dev
+            _out_buffers[width] = out_dev
+
+        fft(out_dev, in_dev)
+        logging.debug("GPU fft: {} s".format(time.time() - start))
+        return out_dev.get()[:, :width / 2 + 1]
+
+except ImportError:
+    logging.debug("Failed to import reikna package. Falling back to Numpy FFT.")
+    def _fft(array):
+        start = time.time()
+        freqs = np.fft.rfft(_pad_array(array))
+        logging.debug("np fft: {} s".format(time.time() - start))
+        return freqs
+
+
+BUNCHES_PER_TURN = config.bunches_per_turn
+HEADER_SIZE_BYTES = 32
+
+
+class DataSet(object):
+    def __init__(self, array, filename, header=None):
+        self.filename = filename
+        self.array = array
+        self._heatmaps = {}
+        self._ffts = {}
+        self.header = header
+
+    @property
+    def skipped_turns(self):
+        if self.header:
+            return self.header['skipped_turns']
+        else:
+            return 1
+
+    def bunch(self, number):
+        return self.array[self.array[:, 4] == number]
+
+    def num_bunches(self):
+        return self.array.shape[0]
+
+    def num_turns(self):
+        return self.num_bunches() / BUNCHES_PER_TURN
+
+    def heatmap(self, adc=1, frm=0, to=-1, bunch_frm=0, bunch_to=-1):
+        if not 1 <= adc <= 4:
+            raise ValueError('adc must be in [1,4]')
+
+        if not adc in self._heatmaps:
+            heatmap = self.array[:,adc-1].reshape(-1, BUNCHES_PER_TURN).transpose()
+            self._heatmaps[adc] = heatmap
+
+        return self._heatmaps[adc][bunch_frm:bunch_to, frm:to]
+
+    def fft(self, adc=1, frm=0, to=-1, drop_first_bin=False):
+        if not 1 <= adc <= 4:
+            raise ValueError('adc must be in [1,4]')
+
+        # if not adc in self._ffts:
+        #     heatmap = self.heatmap(adc, frm, to)
+        #     self._ffts[adc] = np.fft.fft2(heatmap, axes=[1])
+
+        # return self._ffts[adc]
+        heatmap = self.heatmap(adc, frm, to)
+        if drop_first_bin:
+            return _fft(heatmap)[:, 1:]
+        else:
+            return _fft(heatmap)
+
+    def fft_max_freq(self):
+        return (self.num_turns() // 2 + 1) * self.fft_freq_dist()
+    def fft_freq_dist(self):
+        return 1.0/(self.num_turns() * (self.skipped_turns + 1) * config.tRev)
+
+    def train(self, adc=1, frm=0, to=-1, **kwargs):
+        pdata = self.array[frm:to, :-1]
+        return pdata[:,adc-1]
+
+    def combined(self, frm=0, to=-1, show_reconstructed=True):
+        array = self.array[frm:to, :]
+
+        N_s = 16
+        N_p = array.shape[0]
+
+        # Remove bunch number and flatten array
+        array = array[:,:4].reshape((-1, 1))
+        array = array.flatten()
+
+        # plot original data
+        orig_xs = np.arange(0, array.shape[0])
+        # axis.plot(orig_xs, array, '.')
+        ret = [np.array([orig_xs, array])]
+
+        # 4 rows for each ADC
+        ys = array.reshape((4, -1), order='F')
+
+        # multi-fit for each column of 4 ADCs, unfortunately, xs' are always the
+        # same, so we have to offset them later when computing the value
+        xs = [1, 2, 3, 4]
+        c = np.polynomial.polynomial.polyfit(xs, ys, 6)
+
+        smooth_xs = np.linspace(1, 4, N_s)
+        fitted_ys = polyval(smooth_xs, c, tensor=True)
+
+        if True:
+            # Output maxima
+            ys = np.max(fitted_ys, axis=1)
+            xs = 4 * ((np.argmax(fitted_ys, axis=1) + 1) / float(N_s)) + orig_xs[::4] - .5
+
+            # axis.plot(xs, ys, '.', color="red")
+            ret.append(np.array([xs, ys]))
+
+        if False:
+            # Debug output
+            ys = fitted_ys.reshape((-1, 1))
+            ys = ys.flatten()
+            xs = np.repeat(np.arange(0, 4 * N_p, 4), N_s) + np.tile(np.linspace(0, 3, N_s), N_p)
+            # axis.plot(xs, ys, '.', alpha=0.3)
+            ret.append(np.array([xs, ys]))
+        return ret
+

+ 155 - 155
KCG/base/backend/io.py

@@ -1,155 +1,155 @@
-import os
-import math
-import logging
-
-import numpy as np
-
-# from ...config import bunches_per_turn as BUNCHES_PER_TURN
-from ... import config
-from .board import HEADER_SIZE_BYTES
-from .dataset import DataSet
-from . import board
-
-BUNCHES_PER_TURN = config.bunches_per_turn
-
-def is_data_consistent(dataset):
-    if len(dataset.array) == 0:
-        return False
-    bunch_numbers = dataset.array[:, -1]
-    expected = np.tile(np.arange(0, BUNCHES_PER_TURN), bunch_numbers.shape[0] / BUNCHES_PER_TURN)
-    wrong_indices = np.argwhere(bunch_numbers != expected)
-    if wrong_indices.shape[0] > 0:
-        first_error =  bunch_numbers.shape[0] - wrong_indices.shape[0]
-        logging.info('Data inconsistent at offset %i'%first_error)
-        np.savetxt('wrongdump', dataset.array[first_error - 3: first_error + 3])
-        filling = bunch_numbers[wrong_indices[0][0]:]
-        expected_filling = np.tile([222, 223], filling.shape[0] / 2)
-        wrong_filling_indices = np.argwhere(filling != expected_filling)
-        if wrong_filling_indices.shape[0] > 2:  # Some times filling does not start immediately... Why? I have NO IDEA!
-            return False
-        else:
-            return True
-    else:
-        return True
-
-
-def _cached_exist(filename):
-    return os.path.exists(os.path.abspath('{}.npy'.format(filename)))
-
-
-def decode_data(data):
-        # data = data[np.where(data != 0x01234567)]
-        data = data[np.where(data != 0xDEADDEAD)]  # This is the new filling
-
-        # Make sure we read multiple of fours
-        data = data[:int(4 * (math.floor(data.size / 4)))]
-
-        bunch_low = data & 0xfff
-        bunch_high = np.right_shift(data, 12) & 0xfff
-        bunch_number = np.right_shift(data, 24) & 0xfff
-
-        bunch_low = bunch_low.reshape(-1, 4)
-        bunch_high = bunch_high.reshape(-1, 4)
-
-        result = np.empty((bunch_low.shape[0] + bunch_high.shape[0], 5), dtype=np.uint16)
-        result[0::2,:4] = bunch_low
-        result[1::2,:4] = bunch_high
-        result[0::2, 4] = bunch_number[::4]
-        result[1::2, 4] = bunch_number[::4] + 1
-
-        result = result[:int(BUNCHES_PER_TURN * (math.floor(result.shape[0] / BUNCHES_PER_TURN))), :]
-        return result
-
-def data_has_header(data):
-    possible_header = data[0:board.HEADER_SIZE_BYTES/4]
-    back = possible_header[-1] & 0xF8888888 == 0xF8888888
-    front = possible_header[0] & 0xF8888888 == 0xF8888888
-    return (front, back)
-
-def get_num_of_skipped_turns(data, header_info):
-    header = data[0:board.HEADER_SIZE_BYTES/4]
-    if header_info[0]:
-        return header[-1] & 0b00111111
-    elif header_info[1]:
-        return header[0] & 0b00111111
-
-
-def parse_header(data, header_info):
-    """
-    Parse the Header and return the values in a dictionary
-    :param data: the data which contains a header
-    :return: dictionary with header entries
-    """
-    dic = {"skipped_turns": get_num_of_skipped_turns(data, header_info)}
-    return dic
-
-
-def read_from_file(filename, force=False, header=False, cache=False):
-    """
-    Read data from file
-    :param filename: file to read
-    :param force: force reread and do not take values from cache
-    :param header: only for backwards compatibility
-    :param cache: save cache
-    :return: dataset
-    """
-    if _cached_exist(filename) and not force:
-        cached_filename = '{}.npy'.format(filename)
-        logging.vinfo("Read pre-computed data from {}".format(cached_filename))
-        return DataSet(np.load(cached_filename), filename)
-
-    with open(filename, 'rb') as f:
-        logging.vinfo("Read data from {}".format(filename))
-
-        data = np.fromfile(f, dtype=np.uint32)
-
-        if len(data) == 0:
-            logging.error("File with 0b read.")
-            return DataSet(data, filename, header)
-        # If header is sent with the data, truncate it
-        header_info = data_has_header(data)
-        if True in header_info:
-            logging.vinfo("Header detected.")
-            # We read words of 4 bytes each
-            header = parse_header(data, header_info)
-            splice_words = HEADER_SIZE_BYTES / 4
-            data = data[splice_words:]
-        else:
-            logging.vinfo("No Header detected.")
-            header = None
-        result = decode_data(data)
-        dataset = DataSet(result, filename, header)
-
-        if cache:
-            logging.vinfo('Saving pre-computed data')
-            np.save('{}.npy'.format(filename), result)
-
-        return dataset
-
-
-def read_from_string(raw_data, force=False, header=False, cache=False, cache_filename="_heb_data_cache"):
-    if _cached_exist(cache_filename) and not force:
-        cache_file = '{}.npy'.format(cache_filename)
-        logging.vinfo("Read pre-computed data from {}".format(cache_file))
-        return DataSet(np.load(cache_file), cache_filename)
-
-    logging.vinfo("Read data directly from device.")
-    logging.vinfo("Read %i bytes of data" % len(raw_data))
-    data = np.fromstring(raw_data, dtype=np.uint32)
-    #If header is sent with the data, truncate it
-    header_info = data_has_header(data)
-    if True in header_info:
-            # We read words of 4 bytes each
-            header = parse_header(data, header_info)
-            splice_words = HEADER_SIZE_BYTES / 4
-            data = data[splice_words:]
-    else:
-        header = None
-    result = decode_data(data)
-    dataset = DataSet(result, "HEB Live Data", header)
-
-    if cache:
-        logging.vinfo('Saving pre-computed data')
-        np.save('{}.npy'.format(cache_filename), result)
-
-    return dataset
+import os
+import math
+import logging
+
+import numpy as np
+
+# from ...config import bunches_per_turn as BUNCHES_PER_TURN
+from ... import config
+from board import HEADER_SIZE_BYTES
+from dataset import DataSet
+import board
+
+BUNCHES_PER_TURN = config.bunches_per_turn
+
+def is_data_consistent(dataset):
+    if len(dataset.array) == 0:
+        return False
+    bunch_numbers = dataset.array[:, -1]
+    expected = np.tile(np.arange(0, BUNCHES_PER_TURN), bunch_numbers.shape[0] / BUNCHES_PER_TURN)
+    wrong_indices = np.argwhere(bunch_numbers != expected)
+    if wrong_indices.shape[0] > 0:
+        first_error =  bunch_numbers.shape[0] - wrong_indices.shape[0]
+        logging.info('Data inconsistent at offset %i'%first_error)
+        np.savetxt('wrongdump', dataset.array[first_error - 3: first_error + 3])
+        filling = bunch_numbers[wrong_indices[0][0]:]
+        expected_filling = np.tile([222, 223], filling.shape[0] / 2)
+        wrong_filling_indices = np.argwhere(filling != expected_filling)
+        if wrong_filling_indices.shape[0] > 2:  # Some times filling does not start immediately... Why? I have NO IDEA!
+            return False
+        else:
+            return True
+    else:
+        return True
+
+
+def _cached_exist(filename):
+    return os.path.exists(os.path.abspath('{}.npy'.format(filename)))
+
+
+def decode_data(data):
+        # data = data[np.where(data != 0x01234567)]
+        data = data[np.where(data != 0xDEADDEAD)]  # This is the new filling
+
+        # Make sure we read multiple of fours
+        data = data[:4 * int((math.floor(data.size / 4)))]
+
+        bunch_low = data & 0xfff
+        bunch_high = np.right_shift(data, 12) & 0xfff
+        bunch_number = np.right_shift(data, 24) & 0xfff
+
+        bunch_low = bunch_low.reshape(-1, 4)
+        bunch_high = bunch_high.reshape(-1, 4)
+
+        result = np.empty((bunch_low.shape[0] + bunch_high.shape[0], 5), dtype=np.uint16)
+        result[0::2,:4] = bunch_low
+        result[1::2,:4] = bunch_high
+        result[0::2, 4] = bunch_number[::4]
+        result[1::2, 4] = bunch_number[::4] + 1
+
+        result = result[:BUNCHES_PER_TURN * int((math.floor(result.shape[0] / BUNCHES_PER_TURN))), :]
+        return result
+
+def data_has_header(data):
+    possible_header = data[0:board.HEADER_SIZE_BYTES/4]
+    back = possible_header[-1] & 0xF8888888 == 0xF8888888
+    front = possible_header[0] & 0xF8888888 == 0xF8888888
+    return (front, back)
+
+def get_num_of_skipped_turns(data, header_info):
+    header = data[0:board.HEADER_SIZE_BYTES/4]
+    if header_info[0]:
+        return header[-1] & 0b00111111
+    elif header_info[1]:
+        return header[0] & 0b00111111
+
+
+def parse_header(data, header_info):
+    """
+    Parse the Header and return the values in a dictionary
+    :param data: the data which contains a header
+    :return: dictionary with header entries
+    """
+    dic = {"skipped_turns": get_num_of_skipped_turns(data, header_info)}
+    return dic
+
+
+def read_from_file(filename, force=False, header=False, cache=False):
+    """
+    Read data from file
+    :param filename: file to read
+    :param force: force reread and do not take values from cache
+    :param header: only for backwards compatibility
+    :param cache: save cache
+    :return: dataset
+    """
+    if _cached_exist(filename) and not force:
+        cached_filename = '{}.npy'.format(filename)
+        logging.vinfo("Read pre-computed data from {}".format(cached_filename))
+        return DataSet(np.load(cached_filename), filename)
+
+    with open(filename, 'rb') as f:
+        logging.vinfo("Read data from {}".format(filename))
+
+        data = np.fromfile(f, dtype=np.uint32)
+
+        if len(data) == 0:
+            logging.error("File with 0b read.")
+            return DataSet(data, filename, header)
+        # If header is sent with the data, truncate it
+        header_info = data_has_header(data)
+        if True in header_info:
+            logging.vinfo("Header detected.")
+            # We read words of 4 bytes each
+            header = parse_header(data, header_info)
+            splice_words = HEADER_SIZE_BYTES / 4
+            data = data[splice_words:]
+        else:
+            logging.vinfo("No Header detected.")
+            header = None
+        result = decode_data(data)
+        dataset = DataSet(result, filename, header)
+
+        if cache:
+            logging.vinfo('Saving pre-computed data')
+            np.save('{}.npy'.format(filename), result)
+
+        return dataset
+
+
+def read_from_string(raw_data, force=False, header=False, cache=False, cache_filename="_heb_data_cache"):
+    if _cached_exist(cache_filename) and not force:
+        cache_file = '{}.npy'.format(cache_filename)
+        logging.vinfo("Read pre-computed data from {}".format(cache_file))
+        return DataSet(np.load(cache_file), cache_filename)
+
+    logging.vinfo("Read data directly from device.")
+    logging.vinfo("Read %i bytes of data" % len(raw_data))
+    data = np.fromstring(raw_data, dtype=np.uint32)
+    #If header is sent with the data, truncate it
+    header_info = data_has_header(data)
+    if True in header_info:
+            # We read words of 4 bytes each
+            header = parse_header(data, header_info)
+            splice_words = HEADER_SIZE_BYTES / 4
+            data = data[splice_words:]
+    else:
+        header = None
+    result = decode_data(data)
+    dataset = DataSet(result, "HEB Live Data", header)
+
+    if cache:
+        logging.vinfo('Saving pre-computed data')
+        np.save('{}.npy'.format(cache_filename), result)
+
+    return dataset

+ 1503 - 1517
KCG/base/backendinterface.py

@@ -1,1517 +1,1503 @@
-"""
-This is the interface to the backend.
-It is used to make the backend easily interchangable.
-All Functions that interface directly with the backend are prefixed with bk\_
-Functions only used internal in this module will be prefixed _bif_
-"""
-
-import logging
-import time
-from datetime import datetime as dt
-import os
-import copy
-from PyQt4 import QtGui, QtCore
-import numpy as np
-from .backend import board
-from .backend.board import available_boards
-from .backend import io
-from .backend import dataset
-from .groupedelements import Buttons, Elements, live_plot_windows
-from . import storage
-from .. import config
-from . import kcgwidget as kcgw
-from .kcgwidget import error
-from .callbacks import callbacks
-from .log import log
-from .globals import glob as global_objects
-
-tr = kcgw.tr
-
-livePlotData = None
-
-
-def initStatus(st):
-    """
-    Initialize Status variables. These variables are used to transfer status variables over different modules.
-    :param st: variable to use (most likely a DummyStorage instance)
-    :return: -
-    """
-    st.continuous_read = False
-    st.calibrated = False
-    st.synced = False
-    st.defaults_set = False
-    st.status_text = tr("sw", "Ready")
-    st.time_scan = False
-    st.wait_on_trigger = False
-    st.last_file = None
-    st.board_connected = True
-    st.continuous_interval = 1000
-
-
-# -----------[ Backend Interface ]----------------------
-def _bif_enable_wait_cursor():
-    """
-    Show the "Wait Cursor"
-    """
-    QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
-
-
-def _bif_disable_wait_cursor():
-    """
-    Show the normal Cursor
-    """
-    QtGui.QApplication.restoreOverrideCursor()
-
-
-def _bif_continuous_read_is_enabled(board_id, popup_title_text=None):
-    """
-    Checks if continuous read is enabled and if yes shows a popup dialog to ask if it shall be disabled
-    :param board_id: id of the board do manipulate
-    :param popup_title_text: Text to display in the popup that asks to disable continuous read
-    :return: bool (True if continuous read is enabled and not disabled in popup else False)
-    """
-    if board.get_board_status(board_id).continuous_read:
-        if not available_boards.multi_board:
-            popup = PopupDialog(tr("Dialog", "Continuous read is currently active!\nStop continuous read and proceed?"),
-                                title=popup_title_text)
-        else:
-            popup = PopupDialog(tr("Dialog", "Board {board_id}\nContinuous read is currently active!\nStop continuous read and proceed?").format(board_id=board_id),
-                                title=popup_title_text)
-        popup.exec_()
-        popup.deleteLater()
-        if popup.get_return_value() is False:
-            return False
-        else:
-            _bif_set_continuous_read_inactive(board_id)
-            Elements.setChecked("continuousread_"+str(board_id), False)
-            return True
-    else:
-        return False
-
-
-def bk_status_readout():
-    """
-    Read Status for every connected board
-    """
-    if not available_boards.has_boards:
-        return
-    for brd in available_boards.board_ids:
-        _bif_status_readout(brd)
-
-
-def _bif_status_readout(board_id):
-    """
-    Read Status from board and update variables
-    as well as enable and disable corresponding Buttons
-    :return: -
-    """
-    # part_TODO: NOTE: Problem with this is that certain buttons will be greyed out until other buttons are pressed
-    # part_TODO: even if only the gui is restarted and the board is in the same state
-    if kcgw.testing:
-        return
-    if board.is_active(board_id):
-        Buttons.setEnabled("after_start_{}".format(board_id), True)
-        Buttons.setEnabled("start_board_{}".format(board_id), False)
-        Buttons.setEnabled("continuous_read_{}".format(board_id), True)
-
-        # MenuItems.setEnabled("continuous_read", True)
-    else:
-        board.get_board_status(board_id).calibrated = False
-        board.get_board_status(board_id).synced = False
-        board.get_board_status(board_id).defaults_set = False
-        Buttons.setEnabled("after_start_{}".format(board_id), False)
-        Buttons.setEnabled("continuous_read_{}".format(board_id), False)
-    Buttons.setEnabled("synchronize_{}".format(board_id), board.get_board_status(board_id).calibrated)
-    Buttons.setEnabled("set_defaults_{}".format(board_id), board.get_board_status(board_id).synced)
-    Buttons.setEnabled("acquire_{}".format(board_id), board.get_board_status(board_id).synced)
-    Buttons.setEnabled("acquireTrigger_{}".format(board_id), board.get_board_status(board_id).synced)
-    Elements.setEnabled("timing_{}".format(board_id), board.get_board_status(board_id).synced)
-
-    storage.get_board_specific_storage(board_id).update_LED()
-
-
-backup_readout = bk_status_readout
-
-
-class PopupDialog(QtGui.QDialog):
-    """
-    Simple Class to show a popup dialog.
-    """
-
-    def __init__(self, text, title=None, parent=None):
-        QtGui.QDialog.__init__(self, parent)
-        self.text = text
-        self.setWindowTitle(title if title else tr("Dialog", "User action required"))
-        self.return_value = False
-
-        size = QtCore.QSize(200, 200)
-        # self.setMaximumSize(size)
-        self.setMinimumSize(size)
-        box = QtGui.QVBoxLayout()
-        self.text_label = QtGui.QLabel(self.text)
-        self.text_label.setAlignment(QtCore.Qt.AlignCenter)
-        box.addWidget(self.text_label)
-        self.okay_btn = QtGui.QPushButton(tr("Button", "Ok"))
-        self.okay_btn.setStyleSheet("padding: 15px;")
-        self.okay_btn.clicked.connect(self.on_okay)
-        box.addWidget(self.okay_btn)
-        box.addSpacerItem(QtGui.QSpacerItem(1, 20))
-        self.cancel_btn = QtGui.QPushButton(tr("Button", "Cancel"))
-        self.cancel_btn.setStyleSheet("padding: 15px;")
-        self.cancel_btn.clicked.connect(self.on_cancel)
-        box.addWidget(self.cancel_btn)
-        self.setLayout(box)
-
-    def on_okay(self):
-        """
-        Handler for the press of the ok button
-        """
-        self.return_value = True
-        self.close()
-
-    def on_cancel(self):
-        """
-        Handler for the press of the cancel button
-        :return:
-        """
-        self.close()
-
-    def get_return_value(self):
-        """
-        Get True if the Window was closed with OK else False
-        :return: True if OK else False
-        """
-        return self.return_value
-
-
-def bk_start_board(board_id):
-    """
-    Start the Board.
-    This will set initial Registers to power up the Board.
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    _bif_enable_wait_cursor()
-    log(board_id=board_id, additional="Starting Board - following values are probably default values")
-
-    sequence = board.startup_sequence(board_id)
-    number = next(sequence)
-
-    if _bif_continuous_read_is_enabled(board_id, tr("Button", "Start Board")):
-        _bif_disable_wait_cursor()
-        return False
-
-    try:
-        if board.is_active(board_id):
-            _bif_disable_wait_cursor()
-            bk_status_readout()
-            return
-
-        logging.info("Activating Board")
-        next(sequence)
-        time.sleep(1.0)
-        dialog1 = PopupDialog(tr("Button", "Switch On the power supply --> FIRST <-- (on board {0})".format(board_id)))
-        dialog1.exec_()
-        dialog1.deleteLater()
-
-        if not dialog1.get_return_value():
-            logging.error("Starting procedure canceled")
-            _bif_disable_wait_cursor()
-            return False
-
-        logging.info("Switch ON T/Hs")
-        next(sequence)
-        time.sleep(0.1)
-        dialog2 = PopupDialog(tr("Dialog", "Switch On the power supply --> SECOND <-- (on board {0})".format(board_id)))
-        dialog2.exec_()
-        dialog2.deleteLater()
-
-        if not dialog2.get_return_value():
-            logging.info("Starting procedure canceled")
-            _bif_disable_wait_cursor()
-            return False
-
-        logging.info("Switch ON ADCs")
-        next(sequence)
-        time.sleep(0.1)
-
-        next(sequence)
-        time.sleep(1.0)
-
-        for step in sequence:
-            time.sleep(0.1)
-
-        logging.info("Board started successfully!")
-    except board.BoardError as e:
-        logging.error("Starting board failed: {}".format(str(e)))
-
-    bk_update_config(board_id, 'header', bk_get_config(board_id, 'header'))
-    _bif_disable_wait_cursor()
-    bk_status_readout()
-
-
-class _bif_ProgressBar(QtGui.QProgressBar):
-    """
-    Simple Progressbar class.
-    """
-
-    def __init__(self, min, max, text):
-        super(_bif_ProgressBar, self).__init__()
-        self.setRange(min, max)
-        self.setMaximumHeight(18)
-        # kcgw.statusbar.clearMessage()
-        self.label = QtGui.QLabel(text)
-        global_objects.get_global('statusbar').insertWidget(0, self.label)
-        global_objects.get_global('statusbar').insertWidget(1, self)
-        self.setValue(0)
-        QtGui.qApp.processEvents()
-
-    def remove(self, timeout=None):
-        """
-        Remove this instance of a progressbar
-        :param timeout: the time from calling this function to wanishing of the progressbar
-        :return: -
-        """
-
-        def remove_progressbar():
-            global_objects.get_global('statusbar').removeWidget(self)
-            global_objects.get_global('statusbar').removeWidget(self.label)
-            # kcgw.statusbar.showMessage(board.status.status_text)
-            self.destroy()
-
-        if timeout:
-            QtCore.QTimer.singleShot(timeout, remove_progressbar)
-        else:
-            remove_progressbar()
-
-
-# thread = None
-# cal = None
-def bk_calibrate(board_id, do_the_rest=None):
-    """
-    Send commands to the board that will enable it to calibrate itself.
-    This function checks if a read command is still running. BUT: It does not check if
-    the board is acquiring or something like this.
-    So Another instance of KCG can still be acquiring or calibrating at the same time. This can be dangerous.
-    :param board_id: id of the board do manipulate
-    :param do_the_rest: function to call after calibration. This is used when "Prepare Board" is pressed.
-    :return: -
-    """
-    log(board_id=board_id, additional="Calibrate")
-    thread = storage.get_board_specific_storage(board_id).setdefault('CalibrateThread', storage.ThreadStorage())
-    if thread.running:
-        logging.info("Calibration already running")
-        return
-    _bif_enable_wait_cursor()
-    if _bif_continuous_read_is_enabled(board_id, tr("Button", "Calibrate Board")):
-        _bif_disable_wait_cursor()
-        return
-
-    sequence = board.calibration_sequence(board_id)
-    number = next(sequence)
-    progressbar = _bif_ProgressBar(0, number, tr("sw", "Calibrating"))
-
-    class Calibrate(QtCore.QObject):
-        """
-        Class to use as thread class. NOTE: this is not used at the moment.
-        """
-        update_progressbar_signal = QtCore.pyqtSignal(int)
-        finished = QtCore.pyqtSignal()
-
-        def calibrate(self):
-            """
-            The method that is called inside the thread and that does the calibration
-            :return:
-            """
-            try:
-                logging.info('Started Board Calibration')
-                for idx, step in enumerate(sequence):
-                    time.sleep(0.5)
-                    self.update_progressbar_signal.emit(idx)
-                board.get_board_status(board_id).calibrated = True
-            except board.BoardError as e:
-                logging.error("Calibration failed: {}".format(str(e)))
-                # self.do_status_readout()
-                self.finished.emit()
-                return
-
-            logging.info("Board Calibration successful!")
-            self.finished.emit()
-
-    def thread_quit():
-        """
-        Method to handle the end of the calibration thread
-        :return:
-        """
-        thread.stop()
-        bk_status_readout()
-        progressbar.remove(0)
-        _bif_disable_wait_cursor()
-        if do_the_rest:  # execute sync and set defaults (this is set if prepare board was pressed)
-            do_the_rest(board_id)
-
-    cal = Calibrate()
-    thread.register(cal)
-    thread.connect('update_progressbar_signal', progressbar.setValue)
-    thread.connect('finished', thread_quit)
-    thread.start('calibrate')
-
-def bk_sync_board(board_id):
-    """
-    Sends commands to the board to sync with triggers.
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    _bif_enable_wait_cursor()
-    log(board_id=board_id, additional="Synchronize")
-    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Synchronize Board")):
-        _bif_disable_wait_cursor()
-        return
-
-    progressbar = _bif_ProgressBar(0, 100, tr("sw", "Synchronizing"))
-    sequence = board.synchronisation_sequence(board_id)
-    try:
-        next(sequence)  # skip number
-        logging.info("Synchronize PLLs")
-        next(sequence)
-        for i in range(1, 101):
-            time.sleep(0.01)
-            progressbar.setValue(i)
-        next(sequence)
-    except board.BoardError as e:
-        logging.error("Synchronization failed: {}".format(str(e)))
-        progressbar.remove(0)
-        _bif_disable_wait_cursor()
-        bk_status_readout()
-        return
-
-    progressbar.remove(0)
-
-    logging.info("Board synchronization successful!")
-    # self.set_defaults_button.setEnabled(True)
-    board.get_board_status(board_id).synced = True
-    _bif_disable_wait_cursor()
-    bk_status_readout()
-
-
-def bk_write_values(board_id, defaults=False):
-    """
-    Write values to board.
-    :param board_id: id of the board do manipulate
-    :param defaults: (bool) if True Writes default values
-    :return: -
-    """
-    _bif_enable_wait_cursor()
-    if defaults:
-        log(board_id=board_id, additional="Set Default Values")
-    else:
-        log(board_id=board_id, additional="Update Values on board")
-    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Update Values on Board")):
-        _bif_disable_wait_cursor()
-        return
-
-    sequence = board.write_value_sequence(board_id)
-    number = next(sequence)
-    if defaults:
-        board.get_board_config(board_id)._set_defaults()
-        progressbar = _bif_ProgressBar(0, number, tr("sw", "Setting Defaults"))
-        logging.info("Setting default Values")
-    else:
-        progressbar = _bif_ProgressBar(0, number, tr("sw", "Updating Values on Board"))
-        logging.info("Updating Values")
-
-    try:
-        for idx, step in enumerate(sequence):
-            time.sleep(0.1)
-            progressbar.setValue(idx)
-    except board.BoardError as e:
-        logging.error("Updating Values failed: {}".format(str(e)))
-        progressbar.remove(0)
-        _bif_disable_wait_cursor()
-        bk_status_readout()
-        return
-
-    board.get_board_status(board_id).defaults_set = True
-    progressbar.remove(0)
-
-    if defaults:
-        logging.info("Default values set successfully!")
-    else:
-        logging.info("Updated values successfully!")
-    _bif_disable_wait_cursor()
-    bk_status_readout()
-    board.get_board_config(board_id).notify_all_observers()
-
-
-def bk_stop_board(board_id):
-    """
-    Stops the board and shuts it down
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    _bif_enable_wait_cursor()
-    log(board_id=board_id, additional="Stop Board")
-    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
-        _bif_disable_wait_cursor()
-        return
-
-    try:
-        logging.info("Switching Off Board {}".format(board_id))
-        board.pci.write(board_id, '0x9040')
-        board.pci.stop_dma(board_id)
-        board.stop_board(board_id)
-        time.sleep(0.5)
-    except board.BoardError as e:
-        logging.error("Sequence failed: {}".format(str(e)))
-        _bif_disable_wait_cursor()
-        bk_status_readout()
-        return
-
-    logging.info("Board switched off successfully!")
-    Buttons.setEnabled("after_start_{}".format(board_id), False)
-    Buttons.setEnabled("start_board_{}".format(board_id), True)
-    board.get_board_status(board_id).calibrated = False
-    _bif_disable_wait_cursor()
-    bk_status_readout()
-
-def bk_soft_reset(board_id):
-    """
-    Perform a soft reset.
-    :param board_id: id of the board do manipulate
-    :return:
-    """
-    _bif_enable_wait_cursor()
-    log(board_id=board_id, additional="Soft Reset")
-    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
-        _bif_disable_wait_cursor()
-        return
-
-    try:
-        logging.info("Soft-Resetting Board {}...".format(board_id))
-        board.soft_reset(board_id)
-    except board.BoardError as e:
-        logging.error("Sequence failed: {}".format(str(e)))
-        _bif_disable_wait_cursor()
-        bk_status_readout()
-        return
-
-    _bif_disable_wait_cursor()
-    bk_status_readout()
-    board.get_board_config(board_id).update('header', True)  # reset header (might be reset by soft reset)
-    logging.info("Soft-Reset successful.")
-
-
-def bk_update_config(board_id, key, value, silent=False):
-    """
-    Interface to the update command of the BoardConfiguration class.
-    :param board_id: id of the board do manipulate
-    :param key: Key to update
-    :param value: Value to set for key
-    :param silent: (bool) if True do not inform observers on update
-    :return: -
-    """
-    try:
-        if silent:
-            board.get_board_config(board_id).updateSilent(key, value)
-        else:
-            board.get_board_config(board_id).update(key, value)
-    except board.BoardError as e:
-        logging.error("Setting value of {} failed: {}".format(key, str(e)))
-
-
-def bk_get_config(board_id, key):
-    """
-    Interface to the get command of the BoardConfiguration class.
-    :param board_id: id of the board do manipulate
-    :param key: Key to get the value for
-    :return: value stored for key
-    """
-    return board.get_board_config(board_id).get(key)
-
-
-def bk_get_board_status(board_id, status_variable):
-    """
-    Interface to the status class for each board.
-    :param board_id: id of the board do manipulate
-    :param status_variable: Key to get the value for
-    :return: value stored for key
-    """
-    return getattr(board.get_board_status(board_id), status_variable, None)
-
-
-def bk_get_status(board_id):
-    """
-    Interface to the get_status of the board
-    NOTE: This is not get_board_status
-    :return: status dictionary
-    """
-    return board.get_status(board_id)
-
-
-def bk_get_board_config(board_id):
-    """
-    Get the board config instance
-    :param board_id: the id of the board
-    :return: the config instance
-    """
-    return board.get_board_config(board_id)
-
-def bk_change_num_of_orbits(board_id, value, silent=False):
-    """
-    Send new number of orbits to board and update in config
-    :param board_id: id of the board do manipulate
-    :param value: the value to send
-    :param silent: (bool) if True do not inform observers on update
-    :return: -
-    """
-    bk_update_config(board_id, "orbits_observe", value, silent=silent)
-
-
-def bk_change_num_of_skipped_orbits(board_id, value, silent=False):
-    """
-    Send new number of orbits to skip to board and update in config
-    :param board_id: id of the board do manipulate
-    :param value: the value to send
-    :param silent: (bool) if True do not inform observers on update
-    :return: -
-    """
-    bk_update_config(board_id, "orbits_skip", value, silent=silent)
-
-
-def bk_change_count(board_id, value, silent=False):
-    """
-    Change the number of acquisitions you want to make.
-    :param board_id: id of the board do manipulate
-    :param value: (int) Number of acquisitions
-    :param silent: (bool) if True do not inform observers on update
-    :return: -
-    """
-    bk_update_config(board_id, "acquisition_count", value, silent=silent)
-
-
-def bk_change_wait(board_id, value, silent=False):
-    """
-    Change the time between acquisitions.
-    :param board_id: id of the board do manipulate
-    :param value: (bool) Time in seconds
-    :param silent: (bool) if True do not inform observers on update
-    :return: -
-    """
-    bk_update_config(board_id, "orbits_wait_time", value, silent=silent)
-
-
-def bk_change_build_spectrograms(board_id, value, silent=False):
-    """
-    Change if spectrograms are built or not)
-    :param board_id: id of the board do manipulate
-    :param value: (bool) True or False built or not
-    :param silent: (bool) if True do not inform observers on update
-    :return:
-    """
-    bk_update_config(board_id, "build_spectrograms", value, silent=silent)
-
-
-def bk_change_pilot_bunch(board_id, value, silent=False):
-    """
-    Change if pilot bunch is simulated
-    :param board_id: id of the board do manipulate
-    :param value: (bool) True or False to simulate or not
-    :param silent: (bool) if True do not inform observers on update
-    :return:
-    """
-    bk_update_config(board_id, "pilot_bunch", value, silent=silent)
-
-
-def _bif_iterate_spectrograms(board_id, path):
-    """
-    BROKEN (DOES NOT GET ANY DATA)
-    Built Spectrograms line by line
-    :param board_id: id of the board do manipulate
-    :param path: where to built the spectrogram
-    :return: -
-    """
-    return  # because it is broken
-    if not os.path.isdir(str(path)):
-        return
-
-    # how does this get data? dataset.data does not exist
-    transform = dataset.data.fft(1, frm=0, to=-1)
-    for i in range(config.bunches_per_turn - 1):
-        filename = os.path.join(storage.storage.save_location, storage.storage.subdirname, str(path), "%i.hsp" % i)
-        write_header = False
-        if not os.path.isfile(filename):
-            write_header = True
-        f = open(filename, 'ab')
-        if write_header:
-            f.write("#hsp\n")  # heb spectrogram magic number
-            f.write("#"+str(board.get_board_config(board_id).get("orbits_skip")))
-            f.write("\n")
-        line = transform[i, :]
-        f.write('{:0.3f} '.format(time.time()))
-        for e in line:
-            f.write("%s " % np.absolute(e))
-        f.write("\n")
-        f.close()
-
-
-def _bif_read_data_and_save(board_id):
-    """
-    Tell the pci command to start acquisition and save data
-    Also generates the filename from settings
-    :param board_id: id of the board do manipulate
-    :return:
-    """
-    now = time.time()
-    if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname)):
-        os.makedirs(str(storage.storage.save_location + '/' + storage.storage.subdirname))
-    filename = storage.storage.save_location + '/' + storage.storage.subdirname+'/{:0.3f}.out'.format(now)
-    board.get_board_status(board_id).last_file = filename
-
-    try:
-        simulate = board.get_board_config(board_id).get("pilot_bunch")
-        try:
-            board.acquire_data(board_id, filename, simulate=simulate)
-            if not os.path.isfile(filename):
-                    error(0x001, "No File Created")
-        except IndexError:
-            error(0x002, "Unexpected output of pci for number of orbits to observe. Returning")
-            return
-        _bif_read_and_update_data_from_file(board_id, filename)
-    except board.BoardError as e:
-        logging.error("Reading failed: {}".format(str(e)))
-
-
-def _bif_read_and_update_data_from_file(board_id, filename):
-    """
-    Proxy function for _bif_read_and_update to call with correct read_func
-    :param board_id: id of the board do manipulate
-    :param filename: filename to read data from
-    :return: -
-    """
-    _bif_read_and_update(board_id, io.read_from_file, str(filename))
-
-
-def _bif_read_and_update_data_from_string(board_id, raw_data):
-    """
-    Proxy function for _bif_read_and_update to call with correct read_func
-    :param board_id: id of the board do manipulate
-    :param raw_data: Data as string
-    :return: -
-    """
-    _bif_read_and_update(board_id, io.read_from_string, raw_data)
-
-
-def _bif_read_and_update(board_id, read_func, *args):
-    """
-    Function to read data from file or string (depending on read_func) and update plots etc.
-    :param board_id: id of the board do manipulate
-    :param read_func: function to use to read data
-    :param args: filename or raw_data (see _bif_read_and_update_from_{filename, string}
-    :return: -
-    """
-    _bif_enable_wait_cursor()
-
-    header = board.get_board_config(board_id).get('header')
-    # TODO: force_read: meaning ignore cache and read new -> in the old gui this was a commandline option
-    # TODO: cache_data: meaning cache already processed numpy data -> in the old gui this was a commandline option
-    if live_plot_windows.hasWindows(board_id):
-        data = read_func(*args, force=False, header=header, cache=False)
-
-        if not io.is_data_consistent(data):
-            callbacks.async_callback('update_consistency', False)
-            global_objects.get_global('statusbar').showMessage(tr("Dialog", "Data is inconsistent!"))
-            if read_func == io.read_from_string:
-                logging.info("Data is inconsistent")
-            else:
-                logging.info("Data is inconsistent - file: " + args[0])
-        else:
-            callbacks.async_callback('update_consistency', True)
-            global_objects.get_global('statusbar').showMessage(tr("Dialog", ""))
-
-        for plotwin in live_plot_windows.getWindows(board_id):
-            plotwin.plot_live(data=data)
-            QtGui.qApp.processEvents()
-    else:
-        callbacks.async_callback('update_consistency', None)
-
-    _bif_disable_wait_cursor()
-
-
-def bk_acquire(board_id):
-    """
-    Toggle Acqisition
-    :param board_id: id of the board do manipulate
-    :return:
-    """
-    if not bk_get_config(board_id, 'use_trigger'):
-        if board.get_board_status(board_id).acquisition == True:
-            log(board_id=board_id, additional="Manually Stopped Acquisition\nPerformed Acquisitions: " + str(storage.storage.current_acquisition))
-            _bif_stop_acquisition(board_id)
-        else:
-            _bif_start_acquisition(board_id)
-    else:
-        bk_toggle_wait_on_trigger(board_id)
-
-
-def _bif_stop_acquisition(board_id):
-    """
-    Stop acquisition
-    This does stop the timer started by _bif_start_acquisition()
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    board.get_board_status(board_id).acquisition = False
-    storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
-    storage.get_board_specific_storage(board_id).acquisition_timer.stop()
-    # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
-    #     if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
-    #         continue
-        # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
-        # elem.setText(tr("Button", "Start Acquisition"))
-    Elements.setEnabled('acquire_{}'.format(board_id), True)
-    callbacks.callback('acquisition_stopped', board_id)
-
-
-def _bif_start_acquisition(board_id):
-    """
-    Start acquisition.
-    This will start a timer to automatically acquire data.
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    log(board_id=board_id, additional="Started Acquisition")
-    board.get_board_status(board_id).acquisition = True
-    Elements.setEnabled('acquire_{}'.format(board_id), False)
-    # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
-    #     if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
-    #         continue
-        # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
-        # elem.setText(tr("Button", "Stop Acquisition"))
-    callbacks.callback('acquisition_started', board_id)
-    storage.get_board_specific_storage(board_id).acquisition_timer = QtCore.QTimer()
-    num_acquisitions = board.get_board_config(board_id).get("acquisition_count")
-    storage.get_board_specific_storage(board_id).acquisition_progressbar = \
-        _bif_ProgressBar(0, num_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
-    # storage.storage.acquisition_progressbar = acquisition_progressbar
-
-    # We increase already once because we do a single acquisition before the
-    # timer is started, otherwise we have to wait until the timer fires the
-    # first time.
-    try:
-        if isinstance(storage.storage.current_acquisition, dict):
-            storage.storage.current_acquisition[board_id] = 1
-        else:
-            storage.storage.current_acquisition = {board_id: 1}
-    except storage.StorageError:
-        storage.storage.current_acquisition = {board_id: 1}
-    _bif_read_data_and_save(board_id)
-
-    if board.get_board_config(board_id).get("build_spectrograms"):
-        spectrogram_dir = storage.storage.save_location + '/' + storage.storage.subdirname+"/spectrograms_{:0.3f}".format(time.time())
-        os.makedirs(spectrogram_dir)
-        _bif_iterate_spectrograms(board_id, spectrogram_dir) # TODO: not here?
-
-    storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
-
-    def on_timeout():
-        '''Handler for the timeout of the acquisition timer. This does the acquisition'''
-        if storage.storage.current_acquisition[board_id] < num_acquisitions:
-            storage.storage.current_acquisition[board_id] += 1
-            storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
-            _bif_read_data_and_save(board_id)
-            if board.get_board_config(board_id).get("build_spectrograms"):
-                _bif_iterate_spectrograms(board_id, spectrogram_dir)  # TODO: not here ?
-        else:
-            log(board_id=board_id, additional="Stopped Acquisition")
-            _bif_stop_acquisition(board_id)
-            storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
-
-    storage.get_board_specific_storage(board_id).acquisition_timer.timeout.connect(on_timeout)
-    storage.get_board_specific_storage(board_id).acquisition_timer.start(board.get_board_config(board_id).get('orbits_wait_time') * 1000)
-
-
-def bk_single_read(board_id):
-    """
-    Perform a single read of data
-    :param board_id: id of the board do manipulate
-    :return:
-    """
-    Elements.setEnabled("acquire_{}".format(board_id), False)
-    _bif_read_data_and_save(board_id)
-    log(board_id=board_id, additional="Single Read\nFilename: "+board.get_board_status(board_id).last_file.split('/')[-1])
-    Elements.setEnabled("acquire_{}".format(board_id), True)
-
-
-def _bif_set_continuous_read_active(board_id):
-    """
-    Enable continuous read
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    Elements.setEnabled("acquire_{}".format(board_id), False)
-    Elements.setEnabled("acquireTrigger_{}".format(board_id), False)
-    Elements.setEnabled("continuous_read_{}".format(board_id), True)
-    board.get_board_status(board_id).continuous_read = True
-
-
-def _bif_set_continuous_read_inactive(board_id):
-    """
-    Disable continuous read
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    if board.get_board_status(board_id).continuous_read:
-        board.get_board_status(board_id).continuous_read = False
-        storage.get_board_specific_storage(board_id).continuous_read_timer.stop()
-    Elements.setEnabled('acquire_{}'.format(board_id), True)
-    Elements.setEnabled('acquireTrigger_{}'.format(board_id), True)
-
-
-def bk_continuous_read(board_id, interval=100):
-    """
-    Toggle continuous read
-    :param board_id: id of the board do manipulate
-    :param interval: Time between two consecutive reads.
-    :return: -
-    """
-    if not board.get_board_status(board_id).continuous_read:
-        _bif_set_continuous_read_active(board_id)
-        _bif_continuous_read(board_id, interval)
-    else:
-        _bif_set_continuous_read_inactive(board_id)
-
-
-def _bif_continuous_read(board_id, interval=None):
-    """
-    Perform continuous read based on a timer.
-    :param interval:
-    :return:
-    """
-    if interval is not None:
-        # TODO: ueberall checken, dass der board specific storage verwendet wird
-        storage.get_board_specific_storage(board_id).continuous_interval = interval
-    storage.get_board_specific_storage(board_id).continuous_read_timer = QtCore.QTimer()
-    logging.info("Start continuous read")
-
-    def continuous_read_step():
-        if board.get_board_status(board_id).continuous_read:
-            _bif_read_data(board_id)
-            storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
-
-    storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
-
-
-def _bif_read_data(board_id):
-    """
-    Reads data acquired by board.
-    :param board_id: id of the board do manipulate
-    :return:
-    """
-    try:
-        if board.get_board_config(board_id).get('pilot_bunch'):
-            board.start_pilot_bunch_emulator(board_id)
-
-        board.start_acquisition(board_id)
-        try:
-            board.wait_for_revolutions(board_id)
-        except IndexError:
-            error(0x002, "Unexpected output of pci for number of orbits to observe. Returning")
-            return
-        board.stop_acquisition(board_id)
-        board.enable_transfer(board_id)
-        data_raw = board.pci.read_data_to_variable(board_id)
-        _bif_read_and_update_data_from_string(board_id, data_raw)
-    except board.BoardError as e:
-        logging.error("Reading failed for board {}: {}".format(str(board_id), str(e)))
-
-
-def bk_board_connected(board_id):
-    """
-    Interface to the board to check if it is connected.
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    if not available_boards.has_boards:
-        return False
-    else:
-        return board_id in available_boards.board_ids
-
-
-def bk_get_temperature(board_id):
-    """
-    Get Temperature from board and format it
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    fpga_temp_raw_hex = board.pci.read(board_id, 1, '0x9110')[0]
-    fpga_temp_raw_hex = fpga_temp_raw_hex[-3:]
-    fpga_temp_raw_bin = '{0:012b}'.format(int(fpga_temp_raw_hex, 16))
-    fpga_temp_encoded = board.get_dec_from_bits(fpga_temp_raw_bin, 9, 0)
-    fpga_temp_celsius = '{0:2.2f}'.format(((fpga_temp_encoded * 503.975) / 1024) - 273.15)
-    return fpga_temp_celsius
-
-
-backup_get_temp = bk_get_temperature
-
-
-def bk_time_scan(board_id, c_frm, c_to, f_frm, f_to, ts_pbar, plot_func, orbits_observe=None, orbits_skip=None, bucket_to_use=None, threshold_counts=None):
-    """
-    Toggle Timescan.
-    :param board_id: id of the board do manipulate
-    :param c_frm: (int) From value for Coarse scan
-    :param c_to:  (int) To value for Coarse scan
-    :param f_frm: (int) From value for Fine scan
-    :param f_to: (int) To value for fine scan
-    :param ts_pbar: Handle to the Timescan Progressbar
-    :param plot_func: Function to plot when timescan ended.
-    :param orbits_observe: Number of orbits to observe for the timescan (original values will be restored after timescan)
-    :param orbits_skip: Number of orbits to skipfor the timescan (original values will be restored after timescan)
-    :return: -
-    """
-    if board.get_board_status(board_id).time_scan:
-        _bif_stop_time_scan(board_id, ts_pbar)
-    else:
-        _bif_start_time_scan(board_id, c_frm, c_to, f_frm, f_to, ts_pbar, plot_func, orbits_observe, orbits_skip, bucket_to_use, threshold_counts)
-
-
-def _bif_stop_time_scan(board_id, ts_pbar):
-    """
-    Stop the timescan. This stops the timer.
-    :param board_id: id of the board do manipulate
-    :param ts_pbar: Timescan Progressbar handle
-    :return: -
-    """
-    Elements.getElements("start_time_scan_{}".format(board_id))[0].setText(tr("Button", "Start time scan"))
-    board.get_board_status(board_id).time_scan = False
-    board.get_board_config(board_id).set_delay(storage.storage.th_old[board_id])
-    board.get_board_config(board_id).set_chip_delay(
-            [0, 1, 2, 3],
-            [
-                storage.storage.chip_1_old[board_id],
-                storage.storage.chip_2_old[board_id],
-                storage.storage.chip_3_old[board_id],
-                storage.storage.chip_4_old[board_id]
-            ]
-    )
-    ts_pbar.reset()
-
-
-# tst = None  # Ugly but necessary for the thread not to be killed when function ends (which is almost immediately after start)
-# thread_ts = None
-
-
-def _bif_start_time_scan(board_id, c_frm, c_to, f_frm, f_to, timescan_progressbar, plot_func, orbits_observe, orbits_skip, bucket_to_use=None, threshold_counts=None):
-    """
-    Start the timscan. This starts the timer
-    :param board_id: id of the board do manipulate
-    :param c_frm: From value for coarse scan
-    :param c_to: To value for coarse scan
-    :param f_frm: From value for fine scan
-    :param f_to: To value for fine scan
-    :param timescan_progressbar: Handle for the timescan progressbar
-    :param plot_func: Function to use to plot the data
-    :param orbits_observe: Number of orbits to observe for the timescan (original values will be restored after timescan)
-    :param orbits_skip: Number of orbits to skip for the timescan (original values will be restored after timescan)
-    :param bucket_to_use: Number of the bucket whos data will be used to calculate the average signal at each timescan step (if None all bunches will be used)
-    :param threshold_counts: Skip buckets with adc counts between 2048 +- threshold_counts
-    :return: -
-    """
-    thread = storage.get_board_specific_storage(board_id).setdefault("TimeScanThread", storage.ThreadStorage())
-    # if thread_ts is not None:
-    #     logging.info("Time scan already running")
-    #     return
-    if thread.running:
-        logging.info("Time scan already running")
-        return
-
-    board.get_board_status(board_id).time_scan = True
-    Elements.getElements("start_time_scan_{}".format(board_id))[0].setText(tr("Button", "Stop time scan"))
-    if c_frm > c_to:
-        logging.info('Coarse Scan Interval is invalid: (%i > %i)' % (c_frm, c_to))
-        return
-    if f_frm > f_to:
-        logging.info('Fine Scan Interval is invalid: (%i > %i)' % (f_frm, f_to))
-        return
-
-    # the following could be made nicer with the use of setdefault and the use of get_board_specific_storage
-    if not hasattr(storage.storage, 'th_old'):
-        storage.storage.th_old = {}
-        storage.storage.chip_1_old = {}
-        storage.storage.chip_2_old = {}
-        storage.storage.chip_3_old = {}
-        storage.storage.chip_4_old = {}
-
-    storage.storage.th_old[board_id] = board.get_board_config(board_id).get('th_delay')
-    storage.storage.chip_1_old[board_id] = board.get_board_config(board_id).get('chip_1_delay')
-    storage.storage.chip_2_old[board_id] = board.get_board_config(board_id).get('chip_2_delay')
-    storage.storage.chip_3_old[board_id] = board.get_board_config(board_id).get('chip_3_delay')
-    storage.storage.chip_4_old[board_id] = board.get_board_config(board_id).get('chip_4_delay')
-
-    minimum = [None, None, None, None]
-    maximum = np.zeros((4, 3))
-    heatmap = np.zeros((4, (f_to - f_frm + 1), (c_to - c_frm + 1)))
-    timescan_progressbar.setRange(1, ((f_to - f_frm) + 1) * ((c_to - c_frm) + 1))
-
-    class thread_time_scan(QtCore.QObject):
-        '''Timescan Thread class'''
-        pbarSignal = QtCore.pyqtSignal(int)
-        stopSignal = QtCore.pyqtSignal()
-        finished = QtCore.pyqtSignal()
-
-        def __init__(self, c_frm, c_to, f_frm, f_to, timescan_progressbar, bucket_to_use, threshold_counts):
-            super(thread_time_scan, self).__init__()
-            self.c_frm = c_frm
-            self.c_to = c_to
-            self.f_frm = f_frm
-            self.f_to = f_to
-            self.timescan_progressbar = timescan_progressbar
-            self.bucket_to_use = bucket_to_use
-            self.threshold_counts = threshold_counts
-
-        def time_scan(self):
-            '''Method to run in the thread that does the timescan'''
-            Elements.setEnabled('acquire_{}'.format(board_id), False, exclude=Elements.getElements('start_time_scan_{}'.format(board_id)))
-            if orbits_observe:
-                if not hasattr(storage.storage, 'orbits_observe_before_timescan'):
-                    storage.storage.orbits_observe_before_timescan = {}
-                storage.storage.orbits_observe_before_timescan[board_id] = board.get_board_config(board_id).get("orbits_observe") # save old values to restore after timescan
-                board.get_board_config(board_id).update("orbits_observe", orbits_observe)
-                bk_change_num_of_orbits(board_id, orbits_observe)
-            if orbits_skip is not None:
-                if not hasattr(storage.storage, 'orbits_skip_before_timescan'):
-                    storage.storage.orbits_skip_before_timescan = {}
-                storage.storage.orbits_skip_before_timescan[board_id] = board.get_board_config(board_id).get("orbits_skip")
-                board.get_board_config(board_id).update("orbits_skip", orbits_skip)
-                bk_change_num_of_skipped_orbits(board_id, orbits_skip)
-
-            c_step = 0
-            for coarse in range(self.c_frm, self.c_to + 1):
-                try:
-                    board.get_board_config(board_id).set_delay(coarse)
-                except board.BoardError as e:
-                    self.stopSignal.emit()
-                    self.finished.emit()
-                    return
-
-                f_step = 0
-                for fine in range(self.f_frm, self.f_to + 1):
-                    board.get_board_config(board_id).set_chip_delay([0, 1, 2, 3], [fine, fine, fine, fine])
-
-                    try:
-                        if bk_get_config(board_id, 'pilot_bunch') is True:
-                            board.start_pilot_bunch_emulator(board_id)
-
-                        board.start_acquisition(board_id)
-                        try:
-                            board.wait_for_revolutions(board_id)  # Wait before asking for data
-                        except IndexError:
-                            error(0x002, "Unexpected output of pci for number of orbits to observe. Stopping Timescan")
-                            self.stopSignal.emit()
-                            return
-                        board.stop_acquisition(board_id)
-                        board.enable_transfer(board_id)
-
-                        # -----------[ IMPORTANT ]---------------------
-                        if not kcgw.testing:
-                            data_raw = board.pci.read_data_to_variable(board_id)
-                            board.flush_dma(board_id)
-                        # ----------------------------------------------
-                        else:
-                            f_name = "{InsertPathToPreAcquiredTimscanDataHere}" + str(
-                                coarse) + "_" + str(fine) + ".str"
-                            f = open(f_name, 'r')
-                            data_raw = f.read()
-
-                        # The PCI software not only prints the desired data but also some additional information.
-                        # This information has to be removed here.
-                        # To do so we split the output string from PCI at "Writting" (Note: Writting is correct as
-                        # this is a typo in the PCI driver)
-                        data = io.read_from_string(data_raw, force=True, cache=False)
-                    except board.BoardError as e:
-                        self.stopSignal.emit()
-                        self.finished.emit()
-                        return
-
-                    for adc in range(4):
-                        if self.bucket_to_use is None:
-                            if self.threshold_counts is not None:
-                                indexes = np.where(np.logical_or(data.array[:, adc] > 2048+self.threshold_counts, data.array[:, adc] < 2048-self.threshold_counts))[0]
-                                if indexes.shape[0] == 0:
-                                    buckets = data.array[:, adc]
-                                else:
-                                    buckets = data.array[indexes, adc]
-                            else:
-                                buckets = data.array[:, adc]
-                        else:
-                            buckets = data.array[self.bucket_to_use::config.bunches_per_turn, adc]
-                        heatmap[adc, f_step, c_step] = float(buckets.sum()) / buckets.shape[0]
-                        # Uncomment this to change back to the old (non functional) method of maxima determination
-                        # if heatmap[adc, f_step, c_step] > maximum[adc, 0]:
-                        #     maximum[adc, 0] = heatmap[adc, f_step, c_step]
-                        #     maximum[adc, 1] = coarse
-                        #     maximum[adc, 2] = fine
-                        # if minimum[adc] is None or minimum[adc] > heatmap[adc, f_step, c_step]:
-                        #     minimum[adc] = heatmap[adc, f_step, c_step]
-
-                    self.pbarSignal.emit(((c_step * (f_to - f_frm + 1)) + f_step) + 1)
-
-                    #GUI is blocked in our tight loop. Give it an opportunity to handle events
-                    # QtGui.QApplication.processEvents() # remove this if moved to thread again
-                    if board.get_board_status(board_id).time_scan is False:
-                        # Time Scan Stop is already performed by button press. Nothing else to do but leave
-                        self.finished.emit()
-                        return
-                    f_step += 1
-                c_step += 1
-            self.finished.emit()
-
-    def finished(timescan_progressbar):
-        '''Method to handle the end of the thread'''
-        thread.stop()
-        _bif_stop_time_scan(board_id, timescan_progressbar)
-        Elements.setEnabled('acquire_{}'.format(board_id), True, exclude=Elements.getElements('start_time_scan_{}'.format(board_id)))
-        if orbits_observe:
-            board.get_board_config(board_id).update("orbits_observe", storage.storage.orbits_observe_before_timescan[board_id]) # restore values
-            bk_change_num_of_orbits(board_id, storage.storage.orbits_observe_before_timescan[board_id])
-        if orbits_skip:
-            board.get_board_config(board_id).update("orbits_skip", storage.storage.orbits_skip_before_timescan[board_id])
-            bk_change_num_of_skipped_orbits(board_id, storage.storage.orbits_skip_before_timescan[board_id])
-            board.get_board_config(board_id).set_delay(storage.storage.th_old[board_id])
-
-        board.get_board_config(board_id).set_chip_delay(
-                [0, 1, 2, 3],
-                [
-                    storage.storage.chip_1_old[board_id],
-                    storage.storage.chip_2_old[board_id],
-                    storage.storage.chip_3_old[board_id],
-                    storage.storage.chip_4_old[board_id]
-                ]
-        )
-
-#        maximum = []
-        for adc, a in enumerate(heatmap):
-            f, c = np.unravel_index(np.argmax(a), a.shape)
-            maximum[adc] = [a[f, c], c+c_frm, f+f_frm]
-
-        m = [np.min(heatmap[heatmap != 0]), np.max(heatmap)]  # this gives the same levels for all 4 adcs
-        plot_func(heatmap, levels=m, ranges=[c_frm,c_to,f_frm,f_to], newTitle=str(tr("sw", "Coarserange:{c_f}-{c_t} ; Finerange:{f_f}-{f_t}")).format(
-            c_f=c_frm,
-            c_t=c_to,
-            f_f=f_frm,
-            f_t=f_to),
-                  maxima=maximum
-                  )
-
-        now = time.time()
-        if not os.path.isdir(str(os.path.join(storage.storage.save_location, storage.storage.subdirname, 'timescan'))):
-            os.makedirs((os.path.join(storage.storage.save_location, storage.storage.subdirname, 'timescan')))
-        filename = os.path.join(storage.storage.save_location, storage.storage.subdirname, 'timescan', 'timescan_{:0.3f}.out'.format(
-            now))
-        f = open(filename, 'wr')
-
-        for adc in range(4):
-            f.write("#ADC_%s\n" % adc)
-            for coarse, curr_cor in enumerate(np.transpose(heatmap[adc])):
-                for fine, value in enumerate(curr_cor):
-                    f.write("%i;%i;%f\n" % ((coarse + c_frm), (fine + f_frm), value))
-            f.write('\n')
-
-        f.close()
-        f = open(filename + '.gnuplot', 'wr')
-        f.write('set datafile separator ";"\n')
-        f.write('set multiplot layout 2,2\n')
-        f.write('unset key\n')
-        for i in range(4):
-            f.write('set label 1 "ADC_%i" at graph 0.7,0.95 font ",8"\n' % (i + 1))
-            f.write('plot "%s" every :::%i::%i using 3 with lines\n' % (filename, i, i))
-        f.write('unset multiplot\n')
-        f.close()
-
-        return
-
-    tst = thread_time_scan(c_frm, c_to, f_frm, f_to, timescan_progressbar, bucket_to_use, threshold_counts)
-    thread.register(tst)
-    thread.connect('pbarSignal', timescan_progressbar.setValue)
-    thread.connect('finished', lambda: finished(timescan_progressbar))
-    thread.connect('stopSignal', lambda: _bif_stop_time_scan(board_id, timescan_progressbar))
-    thread.start('time_scan')
-
-
-def bk_check_for_board(board_id):
-    """
-    Check if board is connected
-    Also overrides the bk_status_readout function with a function that does nothing (suppresses read attempts that
-    generate errors - if no board is connected, there is nothing to read from)
-    Also overrides the bk_get_temperature function as of the same reasons
-    :param board_id: id of the board do manipulate
-    :return: -
-    """
-    # global bk_status_readout, bk_get_temperature
-    board_status = bk_board_connected(board_id)
-    if board_status:
-        if not hasattr(board.get_board_status(board_id), 'board_connected') or \
-                not board.get_board_status(board_id).board_connected:
-            globals()['bk_status_readout'] = backup_readout
-            globals()['bk_get_temperature'] = backup_get_temp
-        board.get_board_status(board_id).board_connected = True
-
-    else:
-        Elements.setEnabled('no_board_{}'.format(board_id), False)
-
-        def do_nothing():
-            pass
-
-        def no_temp(board_id):
-            return "-"
-
-        globals()['bk_status_readout'] = do_nothing
-        globals()['bk_get_temperature'] = no_temp
-        board.get_board_status(board_id).board_connected = False
-        if board_status == False:
-            board.get_board_status(board_id).status_text = tr("sw", "Board {} not connected".format(board_id))
-        elif board_status == None:
-            board.get_board_status(board_id).status_text = tr("sw", "Software Interface not found")
-
-
-def bk_toggle_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
-    """
-    Toggle waiting for trigger signal to acquire
-    :param board_id: id of the board do manipulate
-    :param num_of_acquisitions: number of acquisitions to wait for
-    :param skip: how much trigger signals to skip between acquisitions
-    :param timeout: the timeout for the pci to wait for date
-    :param method: wait method to use
-            1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
-            NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
-            read sequentially
-    :return:
-    """
-    thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
-    if thread.running:
-        Elements.getElements("acquireTrigger_{}".format(board_id))[0].setText(tr("Button", "Stopping Acquisition"))
-        # FIXME: Button not updated otherwise:
-        QtGui.qApp.processEvents()
-        log(board_id=board_id, additional="Stop wait on trigger on board {}".format(board_id))
-        thread.quit()
-        thread.stop()
-        # for elem in Elements.getElements("acquire_{}".format(board_id)):
-        #     if isinstance(elem, QtGui.QShortcut):
-        #         continue
-            # elem.setText(tr("Button", "Stopping Acquisition"))
-            # elem.setEnabled(False)
-    else:
-        log(board_id=board_id, additional="Start wait on trigger on board {}".format(board_id))
-        # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
-        #     if isinstance(elem, QtGui.QShortcut):
-        #         continue
-            # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
-            # elem.setText(tr("Button", "Stop Acquisition"))
-        Elements.setEnabled('acquire_{}'.format(board_id), False)
-        callbacks.callback('acquisition_started', board_id)
-        _bif_start_wait_on_trigger(board_id, num_of_acquisitions, skip, timeout, method)
-
-
-def _bif_start_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
-    """
-    Start waiting on external acquisition trigger. This starts the timer
-    :param board_id: id of the board do manipulate
-    :param num_of_acquisitions: number of acquisitions to do
-    :param count_label: Handle for the countlabel
-    :param method: wait method to use
-            1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
-            NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
-            read sequentially
-    :return: -
-    """
-
-    # FIXme: This is a work around, for method 2 to work everytime a standard single read needs to be perform before acquisition is started
-    board.acquire_data(board_id, '/dev/null')
-    #with workaround no flush dema need, because it is done at end of board.acquire_data() anyway.
-    #board.flush_dma(board_id) # TODO: really at begining and end of function necessary?
-    thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
-    if thread.running:
-        logging.info("Wait already running on board {}".format(board_id))
-        return
-    log(board_id=board_id, additional="Start wait on trigger")
-    board.get_board_status(board_id).wait_on_trigger = True
-    if not os.path.isdir(str(os.path.join(storage.storage.save_location, storage.storage.subdirname))):
-        os.makedirs(str(os.path.join(storage.storage.save_location, storage.storage.subdirname)))
-
-    if not num_of_acquisitions:
-        num_of_acquisitions = bk_get_config(board_id, 'acquisition_count')
-    if not skip:
-        skip = bk_get_config(board_id, 'trigger_skip')
-    if not timeout:
-        timeout = bk_get_config(board_id, 'trigger_timeout')
-    if not method:
-        method = bk_get_config(board_id, 'trigger_method')
-
-    storage.get_board_specific_storage(board_id).trigger_progressbar = \
-        _bif_ProgressBar(0, num_of_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
-    board.pci.write(board_id, hex(num_of_acquisitions), "9024")
-    time.sleep(0.1)
-    board.pci.write(board_id, hex(skip), "902C")
-    # time.sleep(0.1)
-    # board.pci.write(board_id, 'ff0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
-
-    # This seems to sometimes lead to segfaults of python it self. An Idea to prevent this
-    # is to use copy.deepcopy in __init__. But there is no reason to think that that causes the problem. In fact
-    # I don't have an idea why it crashes.
-    # A possible reason could the os.rename be (or even the board.safe_call as that saves data to the disk) But I think
-    # this is rather unlikely
-    # ~~NOTE~~: the thread of calibration also triggered segfaults sometimes. But this seems to be miraculously solved.
-    # Something that is likely to cause the problem is the log.debug in board.safe_call
-    # Logging (using the logging module) is directly connected to the main thread and could cause problems
-    class thread_wait_on_signal(QtCore.QObject):
-        '''Class to run the wait on signal functionality in a thread'''
-        countUpdate = QtCore.pyqtSignal(int)
-        stopSignal = QtCore.pyqtSignal()
-        finished = QtCore.pyqtSignal()
-        liveplot = QtCore.pyqtSignal(int, str)  # This has to be changed if board_id is no integer
-
-        def __init__(self):
-            super(thread_wait_on_signal, self).__init__()
-            self.noa = None
-            self.path = None
-            self.timeout = None
-            self._quit = False
-
-        def init(self, num_of_acquisitions, path, timeout):
-            '''initialise a new run'''
-            self.noa = num_of_acquisitions
-            self.path = path
-            self.timeout = timeout
-            self._quit = False
-
-            # Elements.setEnabled('acquireTrigger_{}'.format(board_id), False) # exclude=Elements.getElements('wait_on_trigger_{}'.format(board_id)))
-
-        def wait_rw_simul(self): # Method 1
-            '''Wait simultaniously (with the pci command) for a trigger signal'''
-            board.pci.write(board_id, 'ff0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
-            for num_of_acq in range(self.noa):
-                # def step():
-                if self._quit:
-                    break
-                filename = self.path +'/{:0.3f}.out'.format(time.time())
-                board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
-                # rename with correct timestamp - last modified time
-                self.countUpdate.emit(num_of_acq + 1)
-
-                # file operations
-                if not os.path.isfile(filename):
-                    error(0x001, "No File Created")
-                    continue
-
-                newfile = '{path}/trigger_{num:05}_{htime}_{unixtime}.out'.format(
-                    num=num_of_acq,
-                    htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
-                    unixtime=int(os.path.getmtime(filename)),
-                    path=self.path
-                )
-                os.rename(filename, newfile)
-                if os.path.getsize(newfile) > 0:
-                    self.liveplot.emit(board_id, newfile)
-                else:
-                    logging.info("Acquired 0b, possible trigger timeout.")
-
-            self.finished.emit()
-
-        def wait_rw_seq(self): # Method 2
-            '''Wait sequentially (in the gui) for a trigger signal'''
-            for num_of_acq in range(self.noa):
-                board.pci.write(board_id, '00bf0', hex_mask='CF0')  # enable readout
-                pre_acq_num = board.pci.read(board_id, 1, '9034')[0]
-                time_a = time.time()
-                timeout = False
-                while pre_acq_num == board.pci.read(board_id, 1, '9034')[0]:
-                    if time.time() - time_a > self.timeout:
-                        timeout = True
-                        break
-                    if self._quit:
-                        self.finished.emit()
-                        return
-                if not timeout:
-                    board.pci.write(board_id, '000f0', hex_mask='8F0')  # disable readout
-                    board.pci.write(board_id, '007f0', hex_mask='CF0')  # enable transfer
-                    filename = self.path +'/{:0.3f}.out'.format(time.time())
-                    board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
-                    # board.pci.write(board_id, '000f0', hex_mask='4F0') # disable transfer
-                    self.countUpdate.emit(copy.deepcopy(num_of_acq+1))
-                    if self._quit:  # is this really the correct position? file is taken but not renamed!
-                        self.finished.emit()
-                        break
-
-                    if not os.path.isfile(filename):
-                        error(0x001, "No File Created")
-                        continue
-
-                    newfile = '{path}/trigger_{num:05}_{htime}_{unixtime}.out'.format(
-                        num=num_of_acq,
-                        htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
-                        unixtime=int(os.path.getmtime(filename)),
-                        path=self.path
-                    )
-                    os.rename(filename, newfile)
-                    self.liveplot.emit(board_id, newfile)
-                else:
-                    logging.info("Trigger timeout.")
-
-
-            self.finished.emit()
-
-        def quit(self):
-            '''quit this thread'''
-            self._quit = True
-
-        def __del__(self):
-            print('quite')
-            board.pci.write(board_id, '0', '9024')
-            time.sleep(0.1)
-            board.pci.write(board_id, '0', '902C')
-            time.sleep(0.1)
-            board.pci.write(board_id, '3f0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
-            board.flush_dma(board_id)
-
-    def finished():
-        '''Handle the end of the thread'''
-        board.pci.write(board_id, '0', '9024')
-        time.sleep(0.1)
-        board.pci.write(board_id, '0', '902C')
-        time.sleep(0.1)
-        board.pci.write(board_id, '3f0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
-        board.flush_dma(board_id)
-
-        thread.stop()
-        board.get_board_status(board_id).wait_on_trigger = False
-        storage.get_board_specific_storage(board_id).trigger_progressbar.remove(0)
-        log(board_id=board_id, additional="Stop wait on trigger")
-        Elements.setEnabled('acquire_{}'.format(board_id), True)
-        callbacks.callback('acquisition_stopped', board_id)
-
-        # for elem in Elements.getElements("acquire_{}".format(board_id)):
-        #     if isinstance(elem, QtGui.QShortcut):
-        #         continue
-            # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
-            # elem.setText(tr("Button", "Start Acquisition"))
-            # elem.setEnabled(True)
-
-        return
-
-    # twt = thread_wait_on_signal(num_of_acquisitions, storage.storage.save_location + '/' + storage.storage.subdirname,
-    #                             timeout)
-    if not thread.is_registered():
-        twt = thread_wait_on_signal()
-        thread.register(twt)
-    else:
-        thread.disconnect('countUpdate', 'finished', 'liveplot')
-    thread.init(num_of_acquisitions, storage.storage.save_location + '/' + storage.storage.subdirname, timeout)
-    # reconnect signals to make sure the correct versions of methods are called
-    thread.connect('countUpdate', storage.get_board_specific_storage(board_id).trigger_progressbar.setValue)
-    thread.connect('finished', finished)
-    thread.connect('liveplot', _bif_read_and_update_data_from_file)
-    if method == 1:
-        thread.start('wait_rw_simul')
-    elif method == 2:
-        thread.start('wait_rw_seq')
-    else:
-        raise ValueError("Wrong method")
+"""
+This is the interface to the backend.
+It is used to make the backend easily interchangable.
+All Functions that interface directly with the backend are prefixed with bk\_
+Functions only used internal in this module will be prefixed _bif_
+"""
+
+import logging
+import time
+from datetime import datetime as dt
+import os
+import copy
+from PyQt4 import QtGui, QtCore
+import numpy as np
+from backend import board
+from backend.board import available_boards
+from backend import io
+from backend import dataset
+from groupedelements import Buttons, Elements, live_plot_windows
+import storage
+from .. import config
+import kcgwidget as kcgw
+from kcgwidget import error
+from callbacks import callbacks
+from log import log
+from globals import glob as global_objects
+
+tr = kcgw.tr
+
+livePlotData = None
+
+
+def initStatus(st):
+    """
+    Initialize Status variables. These variables are used to transfer status variables over different modules.
+    :param st: variable to use (most likely a DummyStorage instance)
+    :return: -
+    """
+    st.continuous_read = False
+    st.calibrated = False
+    st.synced = False
+    st.defaults_set = False
+    st.status_text = tr("sw", "Ready")
+    st.time_scan = False
+    st.wait_on_trigger = False
+    st.last_file = None
+    st.board_connected = True
+    st.continuous_interval = 1000
+
+
+# -----------[ Backend Interface ]----------------------
+def _bif_enable_wait_cursor():
+    """
+    Show the "Wait Cursor"
+    """
+    QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
+
+
+def _bif_disable_wait_cursor():
+    """
+    Show the normal Cursor
+    """
+    QtGui.QApplication.restoreOverrideCursor()
+
+
+def _bif_continuous_read_is_enabled(board_id, popup_title_text=None):
+    """
+    Checks if continuous read is enabled and if yes shows a popup dialog to ask if it shall be disabled
+    :param board_id: id of the board do manipulate
+    :param popup_title_text: Text to display in the popup that asks to disable continuous read
+    :return: bool (True if continuous read is enabled and not disabled in popup else False)
+    """
+    if board.get_board_status(board_id).continuous_read:
+        if not available_boards.multi_board:
+            popup = PopupDialog(tr("Dialog", "Continuous read is currently active!\nStop continuous read and proceed?"),
+                                title=popup_title_text)
+        else:
+            popup = PopupDialog(tr("Dialog", "Board {board_id}\nContinuous read is currently active!\nStop continuous read and proceed?").format(board_id=board_id),
+                                title=popup_title_text)
+        popup.exec_()
+        popup.deleteLater()
+        if popup.get_return_value() is False:
+            return False
+        else:
+            _bif_set_continuous_read_inactive(board_id)
+            Elements.setChecked("continuousread_"+str(board_id), False)
+            return True
+    else:
+        return False
+
+
+def bk_status_readout():
+    """
+    Read Status for every connected board
+    """
+    if not available_boards.has_boards:
+        return
+    for brd in available_boards.board_ids:
+        _bif_status_readout(brd)
+
+
+def _bif_status_readout(board_id):
+    """
+    Read Status from board and update variables
+    as well as enable and disable corresponding Buttons
+    :return: -
+    """
+    # part_TODO: NOTE: Problem with this is that certain buttons will be greyed out until other buttons are pressed
+    # part_TODO: even if only the gui is restarted and the board is in the same state
+    if kcgw.testing:
+        return
+    if board.is_active(board_id):
+        Buttons.setEnabled("after_start_{}".format(board_id), True)
+        Buttons.setEnabled("start_board_{}".format(board_id), False)
+        Buttons.setEnabled("continuous_read_{}".format(board_id), True)
+
+        # MenuItems.setEnabled("continuous_read", True)
+    else:
+        board.get_board_status(board_id).calibrated = False
+        board.get_board_status(board_id).synced = False
+        board.get_board_status(board_id).defaults_set = False
+        Buttons.setEnabled("after_start_{}".format(board_id), False)
+        Buttons.setEnabled("continuous_read_{}".format(board_id), False)
+    Buttons.setEnabled("synchronize_{}".format(board_id), board.get_board_status(board_id).calibrated)
+    Buttons.setEnabled("set_defaults_{}".format(board_id), board.get_board_status(board_id).synced)
+    Buttons.setEnabled("acquire_{}".format(board_id), board.get_board_status(board_id).synced)
+    Buttons.setEnabled("acquireTrigger_{}".format(board_id), board.get_board_status(board_id).synced)
+    Elements.setEnabled("timing_{}".format(board_id), board.get_board_status(board_id).synced)
+
+    storage.get_board_specific_storage(board_id).update_LED()
+
+
+backup_readout = bk_status_readout
+
+
+class PopupDialog(QtGui.QDialog):
+    """
+    Simple Class to show a popup dialog.
+    """
+
+    def __init__(self, text, title=None, parent=None):
+        QtGui.QDialog.__init__(self, parent)
+        self.text = text
+        self.setWindowTitle(title if title else tr("Dialog", "User action required"))
+        self.return_value = False
+
+        size = QtCore.QSize(200, 200)
+        # self.setMaximumSize(size)
+        self.setMinimumSize(size)
+        box = QtGui.QVBoxLayout()
+        self.text_label = QtGui.QLabel(self.text)
+        self.text_label.setAlignment(QtCore.Qt.AlignCenter)
+        box.addWidget(self.text_label)
+        self.okay_btn = QtGui.QPushButton(tr("Button", "Ok"))
+        self.okay_btn.setStyleSheet("padding: 15px;")
+        self.okay_btn.clicked.connect(self.on_okay)
+        box.addWidget(self.okay_btn)
+        box.addSpacerItem(QtGui.QSpacerItem(1, 20))
+        self.cancel_btn = QtGui.QPushButton(tr("Button", "Cancel"))
+        self.cancel_btn.setStyleSheet("padding: 15px;")
+        self.cancel_btn.clicked.connect(self.on_cancel)
+        box.addWidget(self.cancel_btn)
+        self.setLayout(box)
+
+    def on_okay(self):
+        """
+        Handler for the press of the ok button
+        """
+        self.return_value = True
+        self.close()
+
+    def on_cancel(self):
+        """
+        Handler for the press of the cancel button
+        :return:
+        """
+        self.close()
+
+    def get_return_value(self):
+        """
+        Get True if the Window was closed with OK else False
+        :return: True if OK else False
+        """
+        return self.return_value
+
+
+def bk_start_board(board_id):
+    """
+    Start the Board.
+    This will set initial Registers to power up the Board.
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    _bif_enable_wait_cursor()
+    log(board_id=board_id, additional="Starting Board - following values are probably default values")
+
+    sequence = board.startup_sequence(board_id)
+    number = sequence.next()
+
+    if _bif_continuous_read_is_enabled(board_id, tr("Button", "Start Board")):
+        _bif_disable_wait_cursor()
+        return False
+
+    try:
+        if board.is_active(board_id):
+            _bif_disable_wait_cursor()
+            bk_status_readout()
+            return
+
+        logging.info("Activating Board")
+        sequence.next()
+        time.sleep(1.0)
+        dialog1 = PopupDialog(tr("Button", "Switch On the power supply --> FIRST <-- (on board {0})".format(board_id)))
+        dialog1.exec_()
+        dialog1.deleteLater()
+
+        if not dialog1.get_return_value():
+            logging.error("Starting procedure canceled")
+            _bif_disable_wait_cursor()
+            return False
+
+        logging.info("Switch ON T/Hs")
+        sequence.next()
+        time.sleep(0.1)
+        dialog2 = PopupDialog(tr("Dialog", "Switch On the power supply --> SECOND <-- (on board {0})".format(board_id)))
+        dialog2.exec_()
+        dialog2.deleteLater()
+
+        if not dialog2.get_return_value():
+            logging.info("Starting procedure canceled")
+            _bif_disable_wait_cursor()
+            return False
+
+        logging.info("Switch ON ADCs")
+        sequence.next()
+        time.sleep(0.1)
+
+        sequence.next()
+        time.sleep(1.0)
+
+        for step in sequence:
+            time.sleep(0.1)
+
+        logging.info("Board started successfully!")
+    except board.BoardError as e:
+        logging.error("Starting board failed: {}".format(str(e)))
+
+    bk_update_config(board_id, 'header', bk_get_config(board_id, 'header'))
+    _bif_disable_wait_cursor()
+    bk_status_readout()
+
+
+class _bif_ProgressBar(QtGui.QProgressBar):
+    """
+    Simple Progressbar class.
+    """
+
+    def __init__(self, min, max, text):
+        super(_bif_ProgressBar, self).__init__()
+        self.setRange(min, max)
+        self.setMaximumHeight(18)
+        # kcgw.statusbar.clearMessage()
+        self.label = QtGui.QLabel(text)
+        global_objects.get_global('statusbar').insertWidget(0, self.label)
+        global_objects.get_global('statusbar').insertWidget(1, self)
+        self.setValue(0)
+        QtGui.qApp.processEvents()
+
+    def remove(self, timeout=None):
+        """
+        Remove this instance of a progressbar
+        :param timeout: the time from calling this function to wanishing of the progressbar
+        :return: -
+        """
+
+        def remove_progressbar():
+            global_objects.get_global('statusbar').removeWidget(self)
+            global_objects.get_global('statusbar').removeWidget(self.label)
+            # kcgw.statusbar.showMessage(board.status.status_text)
+            self.destroy()
+
+        if timeout:
+            QtCore.QTimer.singleShot(timeout, remove_progressbar)
+        else:
+            remove_progressbar()
+
+
+# thread = None
+# cal = None
+def bk_calibrate(board_id, do_the_rest=None):
+    """
+    Send commands to the board that will enable it to calibrate itself.
+    This function checks if a read command is still running. BUT: It does not check if
+    the board is acquiring or something like this.
+    So Another instance of KCG can still be acquiring or calibrating at the same time. This can be dangerous.
+    :param board_id: id of the board do manipulate
+    :param do_the_rest: function to call after calibration. This is used when "Prepare Board" is pressed.
+    :return: -
+    """
+    log(board_id=board_id, additional="Calibrate")
+    thread = storage.get_board_specific_storage(board_id).setdefault('CalibrateThread', storage.ThreadStorage())
+    if thread.running:
+        logging.info("Calibration already running")
+        return
+    _bif_enable_wait_cursor()
+    if _bif_continuous_read_is_enabled(board_id, tr("Button", "Calibrate Board")):
+        _bif_disable_wait_cursor()
+        return
+
+    sequence = board.calibration_sequence(board_id)
+    number = sequence.next()
+    progressbar = _bif_ProgressBar(0, number, tr("sw", "Calibrating"))
+
+    class Calibrate(QtCore.QObject):
+        """
+        Class to use as thread class. NOTE: this is not used at the moment.
+        """
+        update_progressbar_signal = QtCore.pyqtSignal(int)
+        finished = QtCore.pyqtSignal()
+
+        def calibrate(self):
+            """
+            The method that is called inside the thread and that does the calibration
+            :return:
+            """
+            try:
+                logging.info('Started Board Calibration')
+                for idx, step in enumerate(sequence):
+                    time.sleep(0.5)
+                    self.update_progressbar_signal.emit(idx)
+                board.get_board_status(board_id).calibrated = True
+            except board.BoardError as e:
+                logging.error("Calibration failed: {}".format(str(e)))
+                # self.do_status_readout()
+                self.finished.emit()
+                return
+
+            logging.info("Board Calibration successful!")
+            self.finished.emit()
+
+    def thread_quit():
+        """
+        Method to handle the end of the calibration thread
+        :return:
+        """
+        thread.stop()
+        bk_status_readout()
+        progressbar.remove(0)
+        _bif_disable_wait_cursor()
+        if do_the_rest:  # execute sync and set defaults (this is set if prepare board was pressed)
+            do_the_rest(board_id)
+
+    cal = Calibrate()
+    thread.register(cal)
+    thread.connect('update_progressbar_signal', progressbar.setValue)
+    thread.connect('finished', thread_quit)
+    thread.start('calibrate')
+
+def bk_sync_board(board_id):
+    """
+    Sends commands to the board to sync with triggers.
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    _bif_enable_wait_cursor()
+    log(board_id=board_id, additional="Synchronize")
+    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Synchronize Board")):
+        _bif_disable_wait_cursor()
+        return
+
+    progressbar = _bif_ProgressBar(0, 100, tr("sw", "Synchronizing"))
+    sequence = board.synchronisation_sequence(board_id)
+    try:
+        sequence.next()  # skip number
+        logging.info("Synchronize PLLs")
+        sequence.next()
+        for i in range(1, 101):
+            time.sleep(0.01)
+            progressbar.setValue(i)
+        sequence.next()
+    except board.BoardError as e:
+        logging.error("Synchronization failed: {}".format(str(e)))
+        progressbar.remove(0)
+        _bif_disable_wait_cursor()
+        bk_status_readout()
+        return
+
+    progressbar.remove(0)
+
+    logging.info("Board synchronization successful!")
+    # self.set_defaults_button.setEnabled(True)
+    board.get_board_status(board_id).synced = True
+    _bif_disable_wait_cursor()
+    bk_status_readout()
+
+
+def bk_write_values(board_id, defaults=False):
+    """
+    Write values to board.
+    :param board_id: id of the board do manipulate
+    :param defaults: (bool) if True Writes default values
+    :return: -
+    """
+    _bif_enable_wait_cursor()
+    if defaults:
+        log(board_id=board_id, additional="Set Default Values")
+    else:
+        log(board_id=board_id, additional="Update Values on board")
+    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Update Values on Board")):
+        _bif_disable_wait_cursor()
+        return
+
+    sequence = board.write_value_sequence(board_id)
+    number = sequence.next()
+    if defaults:
+        board.get_board_config(board_id)._set_defaults()
+        progressbar = _bif_ProgressBar(0, number, tr("sw", "Setting Defaults"))
+        logging.info("Setting default Values")
+    else:
+        progressbar = _bif_ProgressBar(0, number, tr("sw", "Updating Values on Board"))
+        logging.info("Updating Values")
+
+    try:
+        for idx, step in enumerate(sequence):
+            time.sleep(0.1)
+            progressbar.setValue(idx)
+    except board.BoardError as e:
+        logging.error("Updating Values failed: {}".format(str(e)))
+        progressbar.remove(0)
+        _bif_disable_wait_cursor()
+        bk_status_readout()
+        return
+
+    board.get_board_status(board_id).defaults_set = True
+    progressbar.remove(0)
+
+    if defaults:
+        logging.info("Default values set successfully!")
+    else:
+        logging.info("Updated values successfully!")
+    _bif_disable_wait_cursor()
+    bk_status_readout()
+    board.get_board_config(board_id).notify_all_observers()
+
+
+def bk_stop_board(board_id):
+    """
+    Stops the board and shuts it down
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    _bif_enable_wait_cursor()
+    log(board_id=board_id, additional="Stop Board")
+    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
+        _bif_disable_wait_cursor()
+        return
+
+    try:
+        logging.info("Switching Off Board {}".format(board_id))
+        board.pci.write(board_id, '0x9040')
+        board.pci.stop_dma(board_id)
+        board.stop_board(board_id)
+        time.sleep(0.5)
+    except board.BoardError as e:
+        logging.error("Sequence failed: {}".format(str(e)))
+        _bif_disable_wait_cursor()
+        bk_status_readout()
+        return
+
+    logging.info("Board switched off successfully!")
+    Buttons.setEnabled("after_start_{}".format(board_id), False)
+    Buttons.setEnabled("start_board_{}".format(board_id), True)
+    board.get_board_status(board_id).calibrated = False
+    _bif_disable_wait_cursor()
+    bk_status_readout()
+
+def bk_soft_reset(board_id):
+    """
+    Perform a soft reset.
+    :param board_id: id of the board do manipulate
+    :return:
+    """
+    _bif_enable_wait_cursor()
+    log(board_id=board_id, additional="Soft Reset")
+    if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
+        _bif_disable_wait_cursor()
+        return
+
+    try:
+        logging.info("Soft-Resetting Board {}...".format(board_id))
+        board.soft_reset(board_id)
+    except board.BoardError as e:
+        logging.error("Sequence failed: {}".format(str(e)))
+        _bif_disable_wait_cursor()
+        bk_status_readout()
+        return
+
+    _bif_disable_wait_cursor()
+    bk_status_readout()
+    board.get_board_config(board_id).update('header', True)  # reset header (might be reset by soft reset)
+    logging.info("Soft-Reset successful.")
+
+
+def bk_update_config(board_id, key, value, silent=False):
+    """
+    Interface to the update command of the BoardConfiguration class.
+    :param board_id: id of the board do manipulate
+    :param key: Key to update
+    :param value: Value to set for key
+    :param silent: (bool) if True do not inform observers on update
+    :return: -
+    """
+    try:
+        if silent:
+            board.get_board_config(board_id).updateSilent(key, value)
+        else:
+            board.get_board_config(board_id).update(key, value)
+    except board.BoardError as e:
+        logging.error("Setting value of {} failed: {}".format(key, str(e)))
+
+
+def bk_get_config(board_id, key):
+    """
+    Interface to the get command of the BoardConfiguration class.
+    :param board_id: id of the board do manipulate
+    :param key: Key to get the value for
+    :return: value stored for key
+    """
+    return board.get_board_config(board_id).get(key)
+
+
+def bk_get_board_status(board_id, status_variable):
+    """
+    Interface to the status class for each board.
+    :param board_id: id of the board do manipulate
+    :param status_variable: Key to get the value for
+    :return: value stored for key
+    """
+    return getattr(board.get_board_status(board_id), status_variable, None)
+
+
+def bk_get_status(board_id):
+    """
+    Interface to the get_status of the board
+    NOTE: This is not get_board_status
+    :return: status dictionary
+    """
+    return board.get_status(board_id)
+
+
+def bk_get_board_config(board_id):
+    """
+    Get the board config instance
+    :param board_id: the id of the board
+    :return: the config instance
+    """
+    return board.get_board_config(board_id)
+
+def bk_change_num_of_orbits(board_id, value, silent=False):
+    """
+    Send new number of orbits to board and update in config
+    :param board_id: id of the board do manipulate
+    :param value: the value to send
+    :param silent: (bool) if True do not inform observers on update
+    :return: -
+    """
+    bk_update_config(board_id, "orbits_observe", value, silent=silent)
+
+
+def bk_change_num_of_skipped_orbits(board_id, value, silent=False):
+    """
+    Send new number of orbits to skip to board and update in config
+    :param board_id: id of the board do manipulate
+    :param value: the value to send
+    :param silent: (bool) if True do not inform observers on update
+    :return: -
+    """
+    bk_update_config(board_id, "orbits_skip", value, silent=silent)
+
+
+def bk_change_count(board_id, value, silent=False):
+    """
+    Change the number of acquisitions you want to make.
+    :param board_id: id of the board do manipulate
+    :param value: (int) Number of acquisitions
+    :param silent: (bool) if True do not inform observers on update
+    :return: -
+    """
+    bk_update_config(board_id, "acquisition_count", value, silent=silent)
+
+
+def bk_change_wait(board_id, value, silent=False):
+    """
+    Change the time between acquisitions.
+    :param board_id: id of the board do manipulate
+    :param value: (bool) Time in seconds
+    :param silent: (bool) if True do not inform observers on update
+    :return: -
+    """
+    bk_update_config(board_id, "orbits_wait_time", value, silent=silent)
+
+
+def bk_change_build_spectrograms(board_id, value, silent=False):
+    """
+    Change if spectrograms are built or not)
+    :param board_id: id of the board do manipulate
+    :param value: (bool) True or False built or not
+    :param silent: (bool) if True do not inform observers on update
+    :return:
+    """
+    bk_update_config(board_id, "build_spectrograms", value, silent=silent)
+
+
+def bk_change_pilot_bunch(board_id, value, silent=False):
+    """
+    Change if pilot bunch is simulated
+    :param board_id: id of the board do manipulate
+    :param value: (bool) True or False to simulate or not
+    :param silent: (bool) if True do not inform observers on update
+    :return:
+    """
+    bk_update_config(board_id, "pilot_bunch", value, silent=silent)
+
+
+def _bif_iterate_spectrograms(board_id, path):
+    """
+    BROKEN (DOES NOT GET ANY DATA)
+    Built Spectrograms line by line
+    :param board_id: id of the board do manipulate
+    :param path: where to built the spectrogram
+    :return: -
+    """
+    return  # because it is broken
+    if not os.path.isdir(str(path)):
+        return
+
+    # how does this get data? dataset.data does not exist
+    transform = dataset.data.fft(1, frm=0, to=-1)
+    for i in range(config.bunches_per_turn - 1):
+        filename = os.path.join(storage.storage.save_location, storage.storage.subdirname, str(path), "%i.hsp" % i)
+        write_header = False
+        if not os.path.isfile(filename):
+            write_header = True
+        f = open(filename, 'ab')
+        if write_header:
+            f.write("#hsp\n")  # heb spectrogram magic number
+            f.write("#"+str(board.get_board_config(board_id).get("orbits_skip")))
+            f.write("\n")
+        line = transform[i, :]
+        f.write('{:0.3f} '.format(time.time()))
+        for e in line:
+            f.write("%s " % np.absolute(e))
+        f.write("\n")
+        f.close()
+
+
+def _bif_read_data_and_save(board_id):
+    """
+    Tell the pci command to start acquisition and save data
+    Also generates the filename from settings
+    :param board_id: id of the board do manipulate
+    :return:
+    """
+    now = time.time()
+    if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname)):
+        os.makedirs(str(storage.storage.save_location + '/' + storage.storage.subdirname))
+    filename = storage.storage.save_location + '/' + storage.storage.subdirname+'/{:0.3f}.out'.format(now)
+    board.get_board_status(board_id).last_file = filename
+
+    try:
+        simulate = board.get_board_config(board_id).get("pilot_bunch")
+        try:
+            board.acquire_data(board_id, filename, simulate=simulate)
+            if not os.path.isfile(filename):
+                    error(0x001, "No File Created")
+        except IndexError:
+            error(0x002, "Unexpected output of pci for number of orbits to observe. Returning")
+            return
+        _bif_read_and_update_data_from_file(board_id, filename)
+    except board.BoardError as e:
+        logging.error("Reading failed: {}".format(str(e)))
+
+
+def _bif_read_and_update_data_from_file(board_id, filename):
+    """
+    Proxy function for _bif_read_and_update to call with correct read_func
+    :param board_id: id of the board do manipulate
+    :param filename: filename to read data from
+    :return: -
+    """
+    _bif_read_and_update(board_id, io.read_from_file, str(filename))
+
+
+def _bif_read_and_update_data_from_string(board_id, raw_data):
+    """
+    Proxy function for _bif_read_and_update to call with correct read_func
+    :param board_id: id of the board do manipulate
+    :param raw_data: Data as string
+    :return: -
+    """
+    _bif_read_and_update(board_id, io.read_from_string, raw_data)
+
+
+def _bif_read_and_update(board_id, read_func, *args):
+    """
+    Function to read data from file or string (depending on read_func) and update plots etc.
+    :param board_id: id of the board do manipulate
+    :param read_func: function to use to read data
+    :param args: filename or raw_data (see _bif_read_and_update_from_{filename, string}
+    :return: -
+    """
+    _bif_enable_wait_cursor()
+
+    header = board.get_board_config(board_id).get('header')
+    # TODO: force_read: meaning ignore cache and read new -> in the old gui this was a commandline option
+    # TODO: cache_data: meaning cache already processed numpy data -> in the old gui this was a commandline option
+    if live_plot_windows.hasWindows(board_id):
+        data = read_func(*args, force=False, header=header, cache=False)
+
+        if not io.is_data_consistent(data):
+            callbacks.async_callback('update_consistency', False)
+            global_objects.get_global('statusbar').showMessage(tr("Dialog", "Data is inconsistent!"))
+            if read_func == io.read_from_string:
+                logging.info("Data is inconsistent")
+            else:
+                logging.info("Data is inconsistent - file: " + args[0])
+        else:
+            callbacks.async_callback('update_consistency', True)
+            global_objects.get_global('statusbar').showMessage(tr("Dialog", ""))
+
+        for plotwin in live_plot_windows.getWindows(board_id):
+            plotwin.plot_live(data=data)
+            QtGui.qApp.processEvents()
+    else:
+        callbacks.async_callback('update_consistency', None)
+
+    _bif_disable_wait_cursor()
+
+
+def bk_acquire(board_id):
+    """
+    Toggle Acqisition
+    :param board_id: id of the board do manipulate
+    :return:
+    """
+    if not bk_get_config(board_id, 'use_trigger'):
+        if board.get_board_status(board_id).acquisition == True:
+            log(board_id=board_id, additional="Manually Stopped Acquisition\nPerformed Acquisitions: " + str(storage.storage.current_acquisition))
+            _bif_stop_acquisition(board_id)
+        else:
+            _bif_start_acquisition(board_id)
+    else:
+        bk_toggle_wait_on_trigger(board_id)
+
+
+def _bif_stop_acquisition(board_id):
+    """
+    Stop acquisition
+    This does stop the timer started by _bif_start_acquisition()
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    board.get_board_status(board_id).acquisition = False
+    storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
+    storage.get_board_specific_storage(board_id).acquisition_timer.stop()
+    # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
+    #     if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
+    #         continue
+        # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
+        # elem.setText(tr("Button", "Start Acquisition"))
+    Elements.setEnabled('acquire_{}'.format(board_id), True)
+    callbacks.callback('acquisition_stopped', board_id)
+
+
+def _bif_start_acquisition(board_id):
+    """
+    Start acquisition.
+    This will start a timer to automatically acquire data.
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    log(board_id=board_id, additional="Started Acquisition")
+    board.get_board_status(board_id).acquisition = True
+    Elements.setEnabled('acquire_{}'.format(board_id), False)
+    # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
+    #     if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
+    #         continue
+        # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
+        # elem.setText(tr("Button", "Stop Acquisition"))
+    callbacks.callback('acquisition_started', board_id)
+    storage.get_board_specific_storage(board_id).acquisition_timer = QtCore.QTimer()
+    num_acquisitions = board.get_board_config(board_id).get("acquisition_count")
+    storage.get_board_specific_storage(board_id).acquisition_progressbar = \
+        _bif_ProgressBar(0, num_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
+    # storage.storage.acquisition_progressbar = acquisition_progressbar
+
+    # We increase already once because we do a single acquisition before the
+    # timer is started, otherwise we have to wait until the timer fires the
+    # first time.
+    try:
+        if isinstance(storage.storage.current_acquisition, dict):
+            storage.storage.current_acquisition[board_id] = 1
+        else:
+            storage.storage.current_acquisition = {board_id: 1}
+    except storage.StorageError:
+        storage.storage.current_acquisition = {board_id: 1}
+    _bif_read_data_and_save(board_id)
+
+    if board.get_board_config(board_id).get("build_spectrograms"):
+        spectrogram_dir = storage.storage.save_location + '/' + storage.storage.subdirname+"/spectrograms_{:0.3f}".format(time.time())
+        os.makedirs(spectrogram_dir)
+        _bif_iterate_spectrograms(board_id, spectrogram_dir) # TODO: not here?
+
+    storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
+
+    def on_timeout():
+        '''Handler for the timeout of the acquisition timer. This does the acquisition'''
+        if storage.storage.current_acquisition[board_id] < num_acquisitions:
+            storage.storage.current_acquisition[board_id] += 1
+            storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
+            _bif_read_data_and_save(board_id)
+            if board.get_board_config(board_id).get("build_spectrograms"):
+                _bif_iterate_spectrograms(board_id, spectrogram_dir)  # TODO: not here ?
+        else:
+            log(board_id=board_id, additional="Stopped Acquisition")
+            _bif_stop_acquisition(board_id)
+            storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
+
+    storage.get_board_specific_storage(board_id).acquisition_timer.timeout.connect(on_timeout)
+    storage.get_board_specific_storage(board_id).acquisition_timer.start(board.get_board_config(board_id).get('orbits_wait_time') * 1000)
+
+
+def bk_single_read(board_id):
+    """
+    Perform a single read of data
+    :param board_id: id of the board do manipulate
+    :return:
+    """
+    Elements.setEnabled("acquire_{}".format(board_id), False)
+    _bif_read_data_and_save(board_id)
+    log(board_id=board_id, additional="Single Read\nFilename: "+board.get_board_status(board_id).last_file.split('/')[-1])
+    Elements.setEnabled("acquire_{}".format(board_id), True)
+
+
+def _bif_set_continuous_read_active(board_id):
+    """
+    Enable continuous read
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    Elements.setEnabled("acquire_{}".format(board_id), False)
+    Elements.setEnabled("acquireTrigger_{}".format(board_id), False)
+    Elements.setEnabled("continuous_read_{}".format(board_id), True)
+    board.get_board_status(board_id).continuous_read = True
+
+
+def _bif_set_continuous_read_inactive(board_id):
+    """
+    Disable continuous read
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    if board.get_board_status(board_id).continuous_read:
+        board.get_board_status(board_id).continuous_read = False
+        storage.get_board_specific_storage(board_id).continuous_read_timer.stop()
+    Elements.setEnabled('acquire_{}'.format(board_id), True)
+    Elements.setEnabled('acquireTrigger_{}'.format(board_id), True)
+
+
+def bk_continuous_read(board_id, interval=100):
+    """
+    Toggle continuous read
+    :param board_id: id of the board do manipulate
+    :param interval: Time between two consecutive reads.
+    :return: -
+    """
+    if not board.get_board_status(board_id).continuous_read:
+        _bif_set_continuous_read_active(board_id)
+        _bif_continuous_read(board_id, interval)
+    else:
+        _bif_set_continuous_read_inactive(board_id)
+
+
+def _bif_continuous_read(board_id, interval=None):
+    """
+    Perform continuous read based on a timer.
+    :param interval:
+    :return:
+    """
+    if interval is not None:
+        # TODO: ueberall checken, dass der board specific storage verwendet wird
+        storage.get_board_specific_storage(board_id).continuous_interval = interval
+    storage.get_board_specific_storage(board_id).continuous_read_timer = QtCore.QTimer()
+    logging.info("Start continuous read")
+
+    def continuous_read_step():
+        if board.get_board_status(board_id).continuous_read:
+            _bif_read_data(board_id)
+            storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
+
+    storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
+
+
+def _bif_read_data(board_id):
+    """
+    Reads data acquired by board.
+    :param board_id: id of the board do manipulate
+    :return:
+    """
+    try:
+        if board.get_board_config(board_id).get('pilot_bunch'):
+            board.start_pilot_bunch_emulator(board_id)
+
+        board.start_acquisition(board_id)
+        try:
+            board.wait_for_revolutions(board_id)
+        except IndexError:
+            error(0x002, "Unexpected output of pci for number of orbits to observe. Returning")
+            return
+        board.stop_acquisition(board_id)
+        board.enable_transfer(board_id)
+        data_raw = board.pci.read_data_to_variable(board_id)
+        _bif_read_and_update_data_from_string(board_id, data_raw)
+    except board.BoardError as e:
+        logging.error("Reading failed for board {}: {}".format(str(board_id), str(e)))
+
+
+def bk_board_connected(board_id):
+    """
+    Interface to the board to check if it is connected.
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    if not available_boards.has_boards:
+        return False
+    else:
+        return board_id in available_boards.board_ids
+
+
+def bk_get_temperature(board_id):
+    """
+    Get Temperature from board and format it
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    fpga_temp_raw_hex = board.pci.read(board_id, 1, '0x9110')[0]
+    fpga_temp_raw_hex = fpga_temp_raw_hex[-3:]
+    fpga_temp_raw_bin = '{0:012b}'.format(int(fpga_temp_raw_hex, 16))
+    fpga_temp_encoded = board.get_dec_from_bits(fpga_temp_raw_bin, 9, 0)
+    fpga_temp_celsius = '{0:2.2f}'.format(((fpga_temp_encoded * 503.975) / 1024) - 273.15)
+    return fpga_temp_celsius
+
+
+backup_get_temp = bk_get_temperature
+
+
+def bk_time_scan(board_id, c_frm, c_to, f_frm, f_to, ts_pbar, plot_func, orbits_observe=None, orbits_skip=None, bucket_to_use=None):
+    """
+    Toggle Timescan.
+    :param board_id: id of the board do manipulate
+    :param c_frm: (int) From value for Coarse scan
+    :param c_to:  (int) To value for Coarse scan
+    :param f_frm: (int) From value for Fine scan
+    :param f_to: (int) To value for fine scan
+    :param ts_pbar: Handle to the Timescan Progressbar
+    :param plot_func: Function to plot when timescan ended.
+    :param orbits_observe: Number of orbits to observe for the timescan (original values will be restored after timescan)
+    :param orbits_skip: Number of orbits to skipfor the timescan (original values will be restored after timescan)
+    :return: -
+    """
+    if board.get_board_status(board_id).time_scan:
+        _bif_stop_time_scan(board_id, ts_pbar)
+    else:
+        _bif_start_time_scan(board_id, c_frm, c_to, f_frm, f_to, ts_pbar, plot_func, orbits_observe, orbits_skip, bucket_to_use)
+
+
+def _bif_stop_time_scan(board_id, ts_pbar):
+    """
+    Stop the timescan. This stops the timer.
+    :param board_id: id of the board do manipulate
+    :param ts_pbar: Timescan Progressbar handle
+    :return: -
+    """
+    Elements.getElements("start_time_scan_{}".format(board_id))[0].setText(tr("Button", "Start time scan"))
+    board.get_board_status(board_id).time_scan = False
+    board.get_board_config(board_id).set_delay(storage.storage.th_old[board_id])
+    board.get_board_config(board_id).set_chip_delay(
+            [0, 1, 2, 3],
+            [
+                storage.storage.chip_1_old[board_id],
+                storage.storage.chip_2_old[board_id],
+                storage.storage.chip_3_old[board_id],
+                storage.storage.chip_4_old[board_id]
+            ]
+    )
+    ts_pbar.reset()
+
+
+# tst = None  # Ugly but necessary for the thread not to be killed when function ends (which is almost immediately after start)
+# thread_ts = None
+
+
+def _bif_start_time_scan(board_id, c_frm, c_to, f_frm, f_to, timescan_progressbar, plot_func, orbits_observe, orbits_skip, bucket_to_use=None):
+    """
+    Start the timscan. This starts the timer
+    :param board_id: id of the board do manipulate
+    :param c_frm: From value for coarse scan
+    :param c_to: To value for coarse scan
+    :param f_frm: From value for fine scan
+    :param f_to: To value for fine scan
+    :param timescan_progressbar: Handle for the timescan progressbar
+    :param plot_func: Function to use to plot the data
+    :param orbits_observe: Number of orbits to observe for the timescan (original values will be restored after timescan)
+    :param orbits_skip: Number of orbits to skip for the timescan (original values will be restored after timescan)
+    :param bucket_to_use: Number of the bucket whos data will be used to calculate the average signal at each timescan step (if None all bunches will be used)
+    :return: -
+    """
+    thread = storage.get_board_specific_storage(board_id).setdefault("TimeScanThread", storage.ThreadStorage())
+    # if thread_ts is not None:
+    #     logging.info("Time scan already running")
+    #     return
+    if thread.running:
+        logging.info("Time scan already running")
+        return
+
+    board.get_board_status(board_id).time_scan = True
+    Elements.getElements("start_time_scan_{}".format(board_id))[0].setText(tr("Button", "Stop time scan"))
+    if c_frm > c_to:
+        logging.info('Coarse Scan Interval is invalid: (%i > %i)' % (c_frm, c_to))
+        return
+    if f_frm > f_to:
+        logging.info('Fine Scan Interval is invalid: (%i > %i)' % (f_frm, f_to))
+        return
+
+    # the following could be made nicer with the use of setdefault and the use of get_board_specific_storage
+    if not hasattr(storage.storage, 'th_old'):
+        storage.storage.th_old = {}
+        storage.storage.chip_1_old = {}
+        storage.storage.chip_2_old = {}
+        storage.storage.chip_3_old = {}
+        storage.storage.chip_4_old = {}
+
+    storage.storage.th_old[board_id] = board.get_board_config(board_id).get('th_delay')
+    storage.storage.chip_1_old[board_id] = board.get_board_config(board_id).get('chip_1_delay')
+    storage.storage.chip_2_old[board_id] = board.get_board_config(board_id).get('chip_2_delay')
+    storage.storage.chip_3_old[board_id] = board.get_board_config(board_id).get('chip_3_delay')
+    storage.storage.chip_4_old[board_id] = board.get_board_config(board_id).get('chip_4_delay')
+
+    minimum = [None, None, None, None]
+    maximum = np.zeros((4, 3))
+    heatmap = np.zeros((4, (f_to - f_frm + 1), (c_to - c_frm + 1)))
+    timescan_progressbar.setRange(1, ((f_to - f_frm) + 1) * ((c_to - c_frm) + 1))
+
+    class thread_time_scan(QtCore.QObject):
+        '''Timescan Thread class'''
+        pbarSignal = QtCore.pyqtSignal(int)
+        stopSignal = QtCore.pyqtSignal()
+        finished = QtCore.pyqtSignal()
+
+        def __init__(self, c_frm, c_to, f_frm, f_to, timescan_progressbar, bucket_to_use):
+            super(thread_time_scan, self).__init__()
+            self.c_frm = c_frm
+            self.c_to = c_to
+            self.f_frm = f_frm
+            self.f_to = f_to
+            self.timescan_progressbar = timescan_progressbar
+            self.bucket_to_use = bucket_to_use
+
+        def time_scan(self):
+            '''Method to run in the thread that does the timescan'''
+            Elements.setEnabled('acquire_{}'.format(board_id), False, exclude=Elements.getElements('start_time_scan_{}'.format(board_id)))
+            if orbits_observe:
+                if not hasattr(storage.storage, 'orbits_observe_before_timescan'):
+                    storage.storage.orbits_observe_before_timescan = {}
+                storage.storage.orbits_observe_before_timescan[board_id] = board.get_board_config(board_id).get("orbits_observe") # save old values to restore after timescan
+                board.get_board_config(board_id).update("orbits_observe", orbits_observe)
+                bk_change_num_of_orbits(board_id, orbits_observe)
+            if orbits_skip is not None:
+                if not hasattr(storage.storage, 'orbits_skip_before_timescan'):
+                    storage.storage.orbits_skip_before_timescan = {}
+                storage.storage.orbits_skip_before_timescan[board_id] = board.get_board_config(board_id).get("orbits_skip")
+                board.get_board_config(board_id).update("orbits_skip", orbits_skip)
+                bk_change_num_of_skipped_orbits(board_id, orbits_skip)
+
+            c_step = 0
+            for coarse in range(self.c_frm, self.c_to + 1):
+                try:
+                    board.get_board_config(board_id).set_delay(coarse)
+                except board.BoardError as e:
+                    self.stopSignal.emit()
+                    self.finished.emit()
+                    return
+
+                f_step = 0
+                for fine in range(self.f_frm, self.f_to + 1):
+                    board.get_board_config(board_id).set_chip_delay([0, 1, 2, 3], [fine, fine, fine, fine])
+
+                    try:
+                        if bk_get_config(board_id, 'pilot_bunch') is True:
+                            board.start_pilot_bunch_emulator(board_id)
+
+                        board.start_acquisition(board_id)
+                        try:
+                            board.wait_for_revolutions(board_id)  # Wait before asking for data
+                        except IndexError:
+                            error(0x002, "Unexpected output of pci for number of orbits to observe. Stopping Timescan")
+                            self.stopSignal.emit()
+                            return
+                        board.stop_acquisition(board_id)
+                        board.enable_transfer(board_id)
+
+                        # -----------[ IMPORTANT ]---------------------
+                        if not kcgw.testing:
+                            data_raw = board.pci.read_data_to_variable(board_id)
+                            board.flush_dma(board_id)
+                        # ----------------------------------------------
+                        else:
+                            f_name = "{InsertPathToPreAcquiredTimscanDataHere}" + str(
+                                coarse) + "_" + str(fine) + ".str"
+                            f = open(f_name, 'r')
+                            data_raw = f.read()
+
+                        # The PCI software not only prints the desired data but also some additional information.
+                        # This information has to be removed here.
+                        # To do so we split the output string from PCI at "Writting" (Note: Writting is correct as
+                        # this is a typo in the PCI driver)
+                        data = io.read_from_string(data_raw, force=True, cache=False)
+                    except board.BoardError as e:
+                        self.stopSignal.emit()
+                        self.finished.emit()
+                        return
+
+                    for adc in range(4):
+                        if self.bucket_to_use is None:
+                            buckets = data.array[:, adc]
+                        else:
+                            buckets = data.array[self.bucket_to_use::config.bunches_per_turn, adc]
+                        heatmap[adc, f_step, c_step] = float(buckets.sum()) / buckets.shape[0]
+                        if heatmap[adc, f_step, c_step] > maximum[adc, 0]:
+                            maximum[adc, 0] = heatmap[adc, f_step, c_step]
+                            maximum[adc, 1] = coarse
+                            maximum[adc, 2] = fine
+                        if minimum[adc] is None or minimum[adc] > heatmap[adc, f_step, c_step]:
+                            minimum[adc] = heatmap[adc, f_step, c_step]
+
+                    self.pbarSignal.emit(((c_step * (f_to - f_frm + 1)) + f_step) + 1)
+
+                    #GUI is blocked in our tight loop. Give it an opportunity to handle events
+                    # QtGui.QApplication.processEvents() # remove this if moved to thread again
+                    if board.get_board_status(board_id).time_scan is False:
+                        # Time Scan Stop is already performed by button press. Nothing else to do but leave
+                        self.finished.emit()
+                        return
+                    f_step += 1
+                c_step += 1
+            self.finished.emit()
+
+    def finished(timescan_progressbar):
+        '''Method to handle the end of the thread'''
+        thread.stop()
+        _bif_stop_time_scan(board_id, timescan_progressbar)
+        Elements.setEnabled('acquire_{}'.format(board_id), True, exclude=Elements.getElements('start_time_scan_{}'.format(board_id)))
+        if orbits_observe:
+            board.get_board_config(board_id).update("orbits_observe", storage.storage.orbits_observe_before_timescan[board_id]) # restore values
+            bk_change_num_of_orbits(board_id, storage.storage.orbits_observe_before_timescan[board_id])
+        if orbits_skip:
+            board.get_board_config(board_id).update("orbits_skip", storage.storage.orbits_skip_before_timescan[board_id])
+            bk_change_num_of_skipped_orbits(board_id, storage.storage.orbits_skip_before_timescan[board_id])
+            board.get_board_config(board_id).set_delay(storage.storage.th_old[board_id])
+
+        board.get_board_config(board_id).set_chip_delay(
+                [0, 1, 2, 3],
+                [
+                    storage.storage.chip_1_old[board_id],
+                    storage.storage.chip_2_old[board_id],
+                    storage.storage.chip_3_old[board_id],
+                    storage.storage.chip_4_old[board_id]
+                ]
+        )
+
+
+        m = [np.min(heatmap[heatmap != 0]), np.max(heatmap)]  # this gives the same levels for all 4 adcs
+        plot_func(heatmap, levels=m, ranges=[c_frm,c_to,f_frm,f_to], newTitle=str(tr("sw", "Coarserange:{c_f}-{c_t} ; Finerange:{f_f}-{f_t}")).format(
+            c_f=c_frm,
+            c_t=c_to,
+            f_f=f_frm,
+            f_t=f_to),
+                  maxima=maximum
+                  )
+
+        now = time.time()
+        if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname + '/timescan')):
+            os.makedirs((storage.storage.save_location + '/' + storage.storage.subdirname + '/timescan'))
+        filename = storage.storage.save_location + '/' + storage.storage.subdirname + '/timescan/timescan_{:0.3f}.out'.format(
+            now)
+        f = open(filename, 'wr')
+
+        for adc in range(4):
+            f.write("#ADC_%s\n" % adc)
+            for coarse, curr_cor in enumerate(np.transpose(heatmap[adc])):
+                for fine, value in enumerate(curr_cor):
+                    f.write("%i;%i;%f\n" % ((coarse + c_frm), (fine + f_frm), value))
+            f.write('\n')
+
+        f.close()
+        f = open(filename + '.gnuplot', 'wr')
+        f.write('set datafile separator ";"\n')
+        f.write('set multiplot layout 2,2\n')
+        f.write('unset key\n')
+        for i in range(4):
+            f.write('set label 1 "ADC_%i" at graph 0.7,0.95 font ",8"\n' % (i + 1))
+            f.write('plot "%s" every :::%i::%i using 3 with lines\n' % (filename, i, i))
+        f.write('unset multiplot\n')
+        f.close()
+
+        return
+
+    tst = thread_time_scan(c_frm, c_to, f_frm, f_to, timescan_progressbar, bucket_to_use)
+    thread.register(tst)
+    thread.connect('pbarSignal', timescan_progressbar.setValue)
+    thread.connect('finished', lambda: finished(timescan_progressbar))
+    thread.connect('stopSignal', lambda: _bif_stop_time_scan(board_id, timescan_progressbar))
+    thread.start('time_scan')
+
+
+def bk_check_for_board(board_id):
+    """
+    Check if board is connected
+    Also overrides the bk_status_readout function with a function that does nothing (suppresses read attempts that
+    generate errors - if no board is connected, there is nothing to read from)
+    Also overrides the bk_get_temperature function as of the same reasons
+    :param board_id: id of the board do manipulate
+    :return: -
+    """
+    # global bk_status_readout, bk_get_temperature
+    board_status = bk_board_connected(board_id)
+    if board_status:
+        if not hasattr(board.get_board_status(board_id), 'board_connected') or \
+                not board.get_board_status(board_id).board_connected:
+            globals()['bk_status_readout'] = backup_readout
+            globals()['bk_get_temperature'] = backup_get_temp
+        board.get_board_status(board_id).board_connected = True
+
+    else:
+        Elements.setEnabled('no_board_{}'.format(board_id), False)
+
+        def do_nothing():
+            pass
+
+        def no_temp(board_id):
+            return "-"
+
+        globals()['bk_status_readout'] = do_nothing
+        globals()['bk_get_temperature'] = no_temp
+        board.get_board_status(board_id).board_connected = False
+        if board_status == False:
+            board.get_board_status(board_id).status_text = tr("sw", "Board {} not connected".format(board_id))
+        elif board_status == None:
+            board.get_board_status(board_id).status_text = tr("sw", "Software Interface not found")
+
+
+def bk_toggle_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
+    """
+    Toggle waiting for trigger signal to acquire
+    :param board_id: id of the board do manipulate
+    :param num_of_acquisitions: number of acquisitions to wait for
+    :param skip: how much trigger signals to skip between acquisitions
+    :param timeout: the timeout for the pci to wait for date
+    :param method: wait method to use
+            1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
+            NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
+            read sequentially
+    :return:
+    """
+    thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
+    if thread.running:
+        Elements.getElements("acquireTrigger_{}".format(board_id))[0].setText(tr("Button", "Stopping Acquisition"))
+        # FIXME: Button not updated otherwise:
+        QtGui.qApp.processEvents()
+        log(board_id=board_id, additional="Stop wait on trigger on board {}".format(board_id))
+        thread.quit()
+        thread.stop()
+        # for elem in Elements.getElements("acquire_{}".format(board_id)):
+        #     if isinstance(elem, QtGui.QShortcut):
+        #         continue
+            # elem.setText(tr("Button", "Stopping Acquisition"))
+            # elem.setEnabled(False)
+    else:
+        log(board_id=board_id, additional="Start wait on trigger on board {}".format(board_id))
+        # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
+        #     if isinstance(elem, QtGui.QShortcut):
+        #         continue
+            # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
+            # elem.setText(tr("Button", "Stop Acquisition"))
+        Elements.setEnabled('acquire_{}'.format(board_id), False)
+        callbacks.callback('acquisition_started', board_id)
+        _bif_start_wait_on_trigger(board_id, num_of_acquisitions, skip, timeout, method)
+
+
+def _bif_start_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
+    """
+    Start waiting on external acquisition trigger. This starts the timer
+    :param board_id: id of the board do manipulate
+    :param num_of_acquisitions: number of acquisitions to do
+    :param count_label: Handle for the countlabel
+    :param method: wait method to use
+            1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
+            NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
+            read sequentially
+    :return: -
+    """
+
+    # FIXme: This is a work around, for method 2 to work everytime a standard single read needs to be perform before acquisition is started
+    board.acquire_data(board_id, '/dev/null')
+    #with workaround no flush dema need, because it is done at end of board.acquire_data() anyway.
+    #board.flush_dma(board_id) # TODO: really at begining and end of function necessary?
+    thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
+    if thread.running:
+        logging.info("Wait already running on board {}".format(board_id))
+        return
+    log(board_id=board_id, additional="Start wait on trigger")
+    board.get_board_status(board_id).wait_on_trigger = True
+    if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname)):
+        os.makedirs(str(storage.storage.save_location + '/' + storage.storage.subdirname))
+
+    if not num_of_acquisitions:
+        num_of_acquisitions = bk_get_config(board_id, 'acquisition_count')
+    if not skip:
+        skip = bk_get_config(board_id, 'trigger_skip')
+    if not timeout:
+        timeout = bk_get_config(board_id, 'trigger_timeout')
+    if not method:
+        method = bk_get_config(board_id, 'trigger_method')
+
+    storage.get_board_specific_storage(board_id).trigger_progressbar = \
+        _bif_ProgressBar(0, num_of_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
+    board.pci.write(board_id, hex(num_of_acquisitions), "9024")
+    time.sleep(0.1)
+    board.pci.write(board_id, hex(skip), "902C")
+    # time.sleep(0.1)
+    # board.pci.write(board_id, 'ff0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
+
+    # This seems to sometimes lead to segfaults of python it self. An Idea to prevent this
+    # is to use copy.deepcopy in __init__. But there is no reason to think that that causes the problem. In fact
+    # I don't have an idea why it crashes.
+    # A possible reason could the os.rename be (or even the board.safe_call as that saves data to the disk) But I think
+    # this is rather unlikely
+    # ~~NOTE~~: the thread of calibration also triggered segfaults sometimes. But this seems to be miraculously solved.
+    # Something that is likely to cause the problem is the log.debug in board.safe_call
+    # Logging (using the logging module) is directly connected to the main thread and could cause problems
+    class thread_wait_on_signal(QtCore.QObject):
+        '''Class to run the wait on signal functionality in a thread'''
+        countUpdate = QtCore.pyqtSignal(int)
+        stopSignal = QtCore.pyqtSignal()
+        finished = QtCore.pyqtSignal()
+        liveplot = QtCore.pyqtSignal(int, str)  # This has to be changed if board_id is no integer
+
+        def __init__(self):
+            super(thread_wait_on_signal, self).__init__()
+            self.noa = None
+            self.path = None
+            self.timeout = None
+            self._quit = False
+
+        def init(self, num_of_acquisitions, path, timeout):
+            '''initialise a new run'''
+            self.noa = num_of_acquisitions
+            self.path = path
+            self.timeout = timeout
+            self._quit = False
+
+            # Elements.setEnabled('acquireTrigger_{}'.format(board_id), False) # exclude=Elements.getElements('wait_on_trigger_{}'.format(board_id)))
+
+        def wait_rw_simul(self): # Method 1
+            '''Wait simultaniously (with the pci command) for a trigger signal'''
+            board.pci.write(board_id, 'ff0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
+            for num_of_acq in xrange(self.noa):
+                # def step():
+                if self._quit:
+                    break
+                filename = self.path +'/{:0.3f}.out'.format(time.time())
+                board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
+                # rename with correct timestamp - last modified time
+                self.countUpdate.emit(num_of_acq + 1)
+
+                # file operations
+                if not os.path.isfile(filename):
+                    error(0x001, "No File Created")
+                    continue
+
+                newfile = '{path}/trigger_{num:05}_{htime}_{unixtime}.out'.format(
+                    num=num_of_acq,
+                    htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
+                    unixtime=int(os.path.getmtime(filename)),
+                    path=self.path
+                )
+                os.rename(filename, newfile)
+                if os.path.getsize(newfile) > 0:
+                    self.liveplot.emit(board_id, newfile)
+                else:
+                    logging.info("Acquired 0b, possible trigger timeout.")
+
+            self.finished.emit()
+
+        def wait_rw_seq(self): # Method 2
+            '''Wait sequentially (in the gui) for a trigger signal'''
+            for num_of_acq in xrange(self.noa):
+                board.pci.write(board_id, '00bf0', hex_mask='CF0')  # enable readout
+                pre_acq_num = board.pci.read(board_id, 1, '9034')[0]
+                time_a = time.time()
+                timeout = False
+                while pre_acq_num == board.pci.read(board_id, 1, '9034')[0]:
+                    if time.time() - time_a > self.timeout:
+                        timeout = True
+                        break
+                    if self._quit:
+                        self.finished.emit()
+                        return
+                if not timeout:
+                    board.pci.write(board_id, '000f0', hex_mask='8F0')  # disable readout
+                    board.pci.write(board_id, '007f0', hex_mask='CF0')  # enable transfer
+                    filename = self.path +'/{:0.3f}.out'.format(time.time())
+                    board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
+                    # board.pci.write(board_id, '000f0', hex_mask='4F0') # disable transfer
+                    self.countUpdate.emit(copy.deepcopy(num_of_acq+1))
+                    if self._quit:  # is this really the correct position? file is taken but not renamed!
+                        self.finished.emit()
+                        break
+
+                    if not os.path.isfile(filename):
+                        error(0x001, "No File Created")
+                        continue
+
+                    newfile = '{path}/trigger_{num:05}_{htime}_{unixtime}.out'.format(
+                        num=num_of_acq,
+                        htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
+                        unixtime=int(os.path.getmtime(filename)),
+                        path=self.path
+                    )
+                    os.rename(filename, newfile)
+                    self.liveplot.emit(board_id, newfile)
+                else:
+                    logging.info("Trigger timeout.")
+
+
+            self.finished.emit()
+
+        def quit(self):
+            '''quit this thread'''
+            self._quit = True
+
+        def __del__(self):
+            print ('quite')
+            board.pci.write(board_id, '0', '9024')
+            time.sleep(0.1)
+            board.pci.write(board_id, '0', '902C')
+            time.sleep(0.1)
+            board.pci.write(board_id, '3f0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
+            board.flush_dma(board_id)
+
+    def finished():
+        '''Handle the end of the thread'''
+        board.pci.write(board_id, '0', '9024')
+        time.sleep(0.1)
+        board.pci.write(board_id, '0', '902C')
+        time.sleep(0.1)
+        board.pci.write(board_id, '3f0', hex_mask='ff0')  # TODO: This writes t/h 3/4 but enable_transfer etc do not
+        board.flush_dma(board_id)
+
+        thread.stop()
+        board.get_board_status(board_id).wait_on_trigger = False
+        storage.get_board_specific_storage(board_id).trigger_progressbar.remove(0)
+        log(board_id=board_id, additional="Stop wait on trigger")
+        Elements.setEnabled('acquire_{}'.format(board_id), True)
+        callbacks.callback('acquisition_stopped', board_id)
+
+        # for elem in Elements.getElements("acquire_{}".format(board_id)):
+        #     if isinstance(elem, QtGui.QShortcut):
+        #         continue
+            # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
+            # elem.setText(tr("Button", "Start Acquisition"))
+            # elem.setEnabled(True)
+
+        return
+
+    # twt = thread_wait_on_signal(num_of_acquisitions, storage.storage.save_location + '/' + storage.storage.subdirname,
+    #                             timeout)
+    if not thread.is_registered():
+        twt = thread_wait_on_signal()
+        thread.register(twt)
+    else:
+        thread.disconnect('countUpdate', 'finished', 'liveplot')
+    thread.init(num_of_acquisitions, storage.storage.save_location + '/' + storage.storage.subdirname, timeout)
+    # reconnect signals to make sure the correct versions of methods are called
+    thread.connect('countUpdate', storage.get_board_specific_storage(board_id).trigger_progressbar.setValue)
+    thread.connect('finished', finished)
+    thread.connect('liveplot', _bif_read_and_update_data_from_file)
+    if method == 1:
+        thread.start('wait_rw_simul')
+    elif method == 2:
+        thread.start('wait_rw_seq')
+    else:
+        raise ValueError("Wrong method")

+ 443 - 443
KCG/base/bitsTable.py

@@ -1,443 +1,443 @@
-"""
-This defines the Tables used as Bits display
-"""
-
-from PyQt4 import QtGui, QtCore
-import logging
-from .backend import board
-from .backend.board import available_boards
-from . import backendinterface as bif
-from . import kcgwidget as kcgw
-tr = kcgw.tr
-
-class BitsDisplayTable(QtGui.QTableWidget):
-    """
-    Widget to use to display the Bits (as table)
-    """
-    def __init__(self, value, parent=None, optimalSize=True):
-        QtGui.QTableWidget.__init__(self, parent)
-        self.numbers = str(value)
-        if len(self.numbers) == 0:
-            raise ValueError("Cant create a table for a value of length 0.")
-        self.length = len(self.numbers)
-        self.do_style()
-        if optimalSize is True:
-            self.do_optimal_size()
-
-    def do_style(self):
-        self.horizontalHeader().setDefaultSectionSize(35)
-        self.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
-        self.horizontalHeader().setVisible(True)
-        self.verticalHeader().setDefaultSectionSize(17)
-        self.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
-        self.verticalHeader().setVisible(False)
-        self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
-        self.setRowCount(1)
-        self.setColumnCount(self.length)
-
-        # If self.length would be 5, this line would generate ('4', '3',
-        # '2', '1', '0')
-        headers = tuple([str(i) for i in reversed(list(range(0, self.length)))])
-        self.setHorizontalHeaderLabels(headers)
-
-        for i in range(len(self.numbers)):
-            item = QtGui.QTableWidgetItem(self.numbers[i])
-            item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
-            self.setItem(0, i, item)
-
-    def width(self):
-        width = 6
-        if self.verticalHeader().isHidden() is False:
-            width = self.verticalHeader().width() + 6
-
-        for i in range(self.columnCount()):
-            width = width + self.columnWidth(i)
-
-        return width
-
-    def height(self):
-        height = 6
-        if self.horizontalHeader().isHidden() is False:
-            height = self.horizontalHeader().height() + 6
-        for i in range(self.rowCount()):
-            height = height + self.rowHeight(i)
-
-        return height
-
-    def do_optimal_size(self):
-        size = QtCore.QSize(self.width(), self.height())
-        self.setMaximumSize(size)
-        self.setMinimumSize(size)
-
-    def stretch_to_width(self, width_in):
-        width = self.width()
-        if width >= width_in:
-            return
-
-        factor = width_in/float(width)
-        error = 0
-        for i in range(self.length):
-            current_cell_size = self.columnWidth(i)
-            new_cell_size = int(current_cell_size * factor)
-            error += new_cell_size - (current_cell_size * factor)
-            if (error >= 1.0) or (error <= -1.0):
-                new_cell_size -= int(error)
-                error -= int(error)
-            self.horizontalHeader().resizeSection(i, new_cell_size)
-
-        self.do_optimal_size()
-
-    def set_item(self, row, col, value):
-        item = QtGui.QTableWidgetItem(str(value))
-        item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
-        width = self._get_table_item_width(QtGui.QTableWidgetItem(value))
-        if width > self.columnWidth(col):
-            self.horizontalHeader().resizeSection(col, width)
-        self.setItem(row, col, item)
-
-    def set_numbers(self, value):
-        new_numbers = str(value)
-        if len(new_numbers) == 0:
-            raise ValueError("Cant create a table for a value of length 0.")
-        if len(new_numbers) != len(self.numbers):
-            raise ValueError("New Values for table don't match size."
-                             "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
-        self.numbers = new_numbers
-        for i in range(len(self.numbers)):
-            item = self.item(0, i)
-            item.setText(self.numbers[i])
-
-    def set_label(self, start, end, label, color=None):
-        if (start < 0) or (end > self.columnCount()-1) or (start > end):
-            raise ValueError("Invalid Start and End positions for Label: %s" % label)
-        if self.rowCount() < 2:
-            self.insertRow(1)
-            for i in range(self.length):
-                self.setItem(1, i, QtGui.QTableWidgetItem(''))
-
-        span = (end-start)+1
-        if span > 1:
-            self.setSpan(1, start, 1, span)
-        item = QtGui.QTableWidgetItem(label)
-        item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
-        if color:
-            item.setBackground(color)
-        self.setItem(1, start, item)
-
-        # Check if the label is larger then then cells it spans and resize the cells
-        # accordingly, if the label ends up larger then the cells.
-        label_width = self._get_table_item_width(QtGui.QTableWidgetItem(label))
-        cells_width = 0
-        for i in range(start, end+1):
-            cells_width = cells_width + self.columnWidth(i)
-
-        if label_width > cells_width:
-            new_cell_size = label_width/span
-            for i in range(start, end+1):
-                self.horizontalHeader().resizeSection(i, new_cell_size)
-
-        self.do_optimal_size()
-
-    def grey_out_column(self, column):
-        if (column < 0) or (column > self.length):
-            raise ValueError("Supplied column is out of range for this table")
-        for i in range(self.rowCount()):
-            self.item(i, column).setForeground(QtGui.QColor(120, 120, 120))
-            self.item(i, column).setBackground(QtGui.QColor(200, 200, 200))
-
-    def undo_grey_out_column(self, column):
-        if (column < 0) or (column > self.length):
-            raise ValueError("Supplied column is out of range for this table")
-        for i in range(self.rowCount()):
-            self.item(i, column).setForeground(QtGui.QColor(0, 0, 0))
-            self.item(i, column).setBackground(QtGui.QColor(255, 255, 255))
-
-    # Create a table just to insert our item and show how large its width ends up...
-    # We have to use this stupid workaround since QTableWidgetItem has no width() property...
-    def _get_table_item_width(self, item):
-        table = QtGui.QTableWidget()
-        table.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents)
-        table.setRowCount(1)
-        table.setColumnCount(1)
-        table.setItem(0, 0, item)
-        width = table.columnWidth(0)
-        table.deleteLater()
-        return width
-
-
-class BitsEditTable(BitsDisplayTable):
-
-    def __init__(self, value, parent=None, optimalSize=True):
-        BitsDisplayTable.__init__(self, value, parent, optimalSize)
-        self.checkboxes = []
-        self.populate_checkboxes()
-        self.verticalHeader().setDefaultSectionSize(35)
-        self.do_optimal_size()
-
-    def populate_checkboxes(self):
-        for i in range(self.length):
-            widget = QtGui.QWidget()
-            self.checkboxes += [QtGui.QCheckBox()]
-            layout = QtGui.QHBoxLayout(widget)
-            layout.addWidget(self.checkboxes[i])
-            layout.setAlignment(QtCore.Qt.AlignCenter)
-            layout.setContentsMargins(0, 0, 0, 0)
-            widget.setLayout(layout)
-            self.setCellWidget(0, i, widget)
-
-    def set_numbers(self, value):
-            new_numbers = str(value)
-            if len(new_numbers) == 0:
-                raise ValueError("Cant create a table for a value of length 0.")
-            if len(new_numbers) != len(self.numbers):
-                raise ValueError("New Values for table dont match size."
-                                 "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
-            self.numbers = new_numbers
-            for i in range(len(self.numbers)):
-                if self.numbers[i] == '1':
-                    self.checkboxes[i].setChecked(True)
-                else:
-                    self.checkboxes[i].setChecked(False)
-
-    def get_bits(self):
-        bits = ''
-        for i in range(self.length):
-            if self.checkboxes[i].isChecked():
-                bits += '1'
-            else:
-                bits += '0'
-        return bits
-
-    def get_bit(self, bit):
-        if bit > self.length:
-            return None
-        return self.checkboxes[(self.length - bit) - 1].isChecked()
-
-    def clear_all_bits(self):
-        for i in range(self.length):
-            self.checkboxes[i].setChecked(False)
-
-
-class AdvancedBoardInterface(QtGui.QWidget):
-
-    def __init__(self, parent=None, board_id=None):
-        QtGui.QWidget.__init__(self, parent)
-        self.parent = parent
-        self.do_layout()
-        self.data_flow_pipeline_status = None
-        # self.do_status_update() # Do not update status at init - gets updated whenever page is "opened"
-        if board_id is None:
-            print("No Valid board id specified for AdvancedBoardInterface.")
-            raise ValueError("No Valid board id specified for AdvancedBoardInterface.")
-        self.board_id = board_id
-
-    def do_layout(self):
-
-        self.table_grid = QtGui.QGridLayout()
-        self.setLayout(self.table_grid)
-
-        self.table_grid.addWidget(QtGui.QLabel("Status1 Register 0x9050 (Readonly)"), 0, 0)
-        self.t1 = QtGui.QScrollArea()
-        self.table_grid.addWidget(self.t1, 1, 0)
-        self.status1_table = BitsDisplayTable(32*'0', self)
-        self.do_status1_table_layout(self.status1_table)
-        # self.table_grid.addWidget(self.status1_table, 1, 0)
-        self.t1.setWidget(self.status1_table)
-        self.t1.setFixedHeight(self.status1_table.height()+24)
-
-        self.table_grid.addWidget(QtGui.QLabel("Status2 Register 0x9054 (Readonly)"), 2, 0)
-        self.t2 = QtGui.QScrollArea()
-        self.table_grid.addWidget(self.t2, 3, 0)
-        self.status2_table = BitsDisplayTable(32*'0', self)
-        self.do_status2_table_layout(self.status2_table)
-        # self.table_grid.addWidget(self.status2_table, 3, 0)
-        self.t2.setWidget(self.status2_table)
-        self.t2.setFixedHeight(self.status2_table.height()+24)
-
-        self.table_grid.addWidget(QtGui.QLabel("Status3 Register 0x9058 (Readonly)"), 4, 0)
-        self.t3 = QtGui.QScrollArea()
-        self.table_grid.addWidget(self.t3, 5, 0)
-        self.status3_table = BitsDisplayTable(32*'0', self)
-        self.do_status3_table_layout(self.status3_table)
-        # self.table_grid.addWidget(self.status3_table, 5, 0)
-        self.t3.setWidget(self.status3_table)
-        self.t3.setFixedHeight(self.status3_table.height()+24)
-
-        # self.table_grid.addItem(QtGui.QSpacerItem(1, 20), 6, 0)
-        self.table_grid.addWidget(QtGui.QLabel("Control Register 0x9040"), 7, 0)
-
-        buttons_box = QtGui.QHBoxLayout()
-        buttons_box.setAlignment(QtCore.Qt.AlignLeft)
-        self.write_control_button = QtGui.QPushButton("Write Values to board")
-        self.write_control_button.setMaximumWidth(200)
-        buttons_box.addWidget(self.write_control_button)
-        self.clear_control_button = QtGui.QPushButton("Clear Input")
-        self.clear_control_button.setMaximumWidth(200)
-        buttons_box.addWidget(self.clear_control_button)
-        self.check_status_control_button = QtGui.QPushButton("Check Status")
-        self.check_status_control_button.setMaximumWidth(200)
-        buttons_box.addWidget(self.check_status_control_button)
-        buttons_widget = QtGui.QWidget()
-        buttons_widget.setLayout(buttons_box)
-        self.table_grid.addWidget(buttons_widget, 8, 0)
-
-        self.tedit = QtGui.QScrollArea()
-        self.table_grid.addWidget(self.tedit, 9, 0)
-        self.control_table = BitsEditTable(32*'0', self)
-        self.do_control_table_layout(self.control_table)
-        self.tedit.setWidget(self.control_table)
-        self.tedit.setFixedHeight(self.control_table.height()+24)
-        # self.table_grid.addWidget(self.control_table, 9, 0)
-
-        self.write_control_button.clicked.connect(self.send_control_to_board)
-        self.clear_control_button.clicked.connect(self.control_table.clear_all_bits)
-        self.check_status_control_button.clicked.connect(self.do_status_update)
-        self.check_status_control_button.setShortcut("F5")
-
-        width1 = self.status1_table.width()
-        width2 = self.status2_table.width()
-        width3 = self.status3_table.width()
-        width4 = self.control_table.width()
-        max_width = max(width1, max(width2, max(width3, width4)))
-        self.status1_table.stretch_to_width(max_width)
-        self.status2_table.stretch_to_width(max_width)
-        self.status3_table.stretch_to_width(max_width)
-        self.control_table.stretch_to_width(max_width)
-
-
-    def send_control_to_board(self):
-        if bif._bif_continuous_read_is_enabled(self.board_id, tr("Heading", "Write Registers")):
-            logging.log("Cant write to board while continuous readout is active")
-            return
-
-        bits = self.control_table.get_bits()
-        # print bits
-        dec_val_bits = int(bits, 2)
-        # print dec_val_bits
-        # self.parent.text_area.write("Writing to board Register 0x9040: %s" % ('0x{0:08x}'.format(dec_val_bits)))
-        logging.info("Writing to board Register 0x9040: %s" % ('0x{0:08x}'.format(dec_val_bits)))
-        try:
-            board.pci.write(self.board_id, hex(dec_val_bits), '0x9040')
-        except board.BoardError as e:
-            QtGui.QMessageBox.critical(self, "Board communication",
-                                       "Was unable to write value to board!\nReason: "+str(e)+"\nBoard: "+str(self.board_id))
-
-        # self.parent.do_status_readout()
-        self.do_status_update()
-
-    def do_status1_table_layout(self, table):
-        # from right to left
-        table.set_label(29, 31, "FSM_Data_Pipeline_Status", QtCore.Qt.green)
-        table.grey_out_column(28)
-        table.set_label(27, 27, "FULL", QtCore.Qt.green)
-        table.set_label(26, 26, "EMPTY", QtCore.Qt.green)
-        table.grey_out_column(25)
-        table.grey_out_column(24)
-        table.set_label(14, 23, "RD_data_Counts", QtCore.Qt.green)
-        table.set_label(13, 13, "OVR_ADC", QtGui.QColor(210, 210, 0))
-        table.grey_out_column(12)
-        table.grey_out_column(11)
-        table.grey_out_column(10)
-        table.grey_out_column(9)
-        table.grey_out_column(8)
-        table.set_label(7, 7, "PLL_LD", QtGui.QColor(210, 210, 0))
-        table.grey_out_column(6)
-        table.set_label(2, 5, "Master Control", QtCore.Qt.green)
-        table.grey_out_column(1)
-        table.set_label(0, 0, "1")
-
-    def do_status2_table_layout(self, table):
-        #from right to left
-        table.set_label(31, 31, "FIFO 128 255 empty", QtCore.Qt.green)
-        table.set_label(30, 30, "FIFO 128 255 full", QtCore.Qt.green)
-        table.grey_out_column(29)
-        table.grey_out_column(28)
-        table.set_label(17, 27, "wr data count 128 255", QtGui.QColor(210, 210, 0))
-        table.grey_out_column(16)
-        table.set_label(15, 15, "FIFO 255 64 empty", QtCore.Qt.green)
-        table.set_label(14, 14, "FIFO 255 64 full", QtCore.Qt.green)
-        table.grey_out_column(13)
-        table.grey_out_column(12)
-        table.set_label(2, 11, "rd data count 255 64", QtGui.QColor(210, 210, 0))
-        table.grey_out_column(1)
-        table.grey_out_column(0)
-
-    def do_status3_table_layout(self, table):
-        #from right to left
-        table.set_label(29, 31, "FSM_ARBITER_DDR3", QtCore.Qt.green)
-        table.grey_out_column(28)
-        table.set_label(25, 27, "FSM_WR_DDR3", QtCore.Qt.green)
-        table.grey_out_column(24)
-        table.set_label(21, 23, "FSM_R_DDR3", QtCore.Qt.green)
-        table.grey_out_column(20)
-        table.set_label(16, 19, "BC_ERROR", QtGui.QColor(210, 210, 0))
-        table.set_label(1, 15, "Number of wrong BC", QtGui.QColor(255, 255, 0))
-        table.grey_out_column(0)
-
-    def do_control_table_layout(self, table):
-        #from right to left
-        table.set_label(31, 31, "reset", QtGui.QColor(0, 255, 255))
-        table.grey_out_column(30)
-        table.grey_out_column(29)
-        table.grey_out_column(28)
-        table.set_label(27, 27, "ADC_1(A+D)", QtGui.QColor(0, 255, 255))
-        table.set_label(26, 26, "ADC_2(A+D)2", QtGui.QColor(0, 255, 255))
-        table.set_label(25, 25, "T/H_1", QtGui.QColor(210, 210, 0))
-        table.set_label(24, 24, "T/H_2", QtGui.QColor(210, 210, 0))
-        table.set_label(23, 23, "T/H_3", QtGui.QColor(210, 210, 0))
-        table.set_label(22, 22, "T/H_4", QtGui.QColor(210, 210, 0))
-        table.set_label(21, 21, "EN_data_Trans", QtCore.Qt.yellow)
-        table.set_label(20, 20, "EN_readout", QtCore.Qt.yellow)
-        table.set_label(18, 19, "ADC_1", QtCore.Qt.yellow)
-        table.set_label(16, 17, "ADC_2", QtCore.Qt.yellow)
-        table.set_label(14, 15, "ADC_3", QtCore.Qt.yellow)
-        table.set_label(12, 13, "ADC_4", QtCore.Qt.yellow)
-        table.grey_out_column(11)
-        table.grey_out_column(10)
-        table.set_label(9, 9, "Clock Selection", QtCore.Qt.yellow)
-        table.set_label(8, 8, "Sampling", QtCore.Qt.green)
-        table.set_label(7, 7, "Reset", QtCore.Qt.yellow)
-        table.set_label(6, 6, "ADC_CNT", QtCore.Qt.yellow)
-        table.grey_out_column(5)
-        table.grey_out_column(4)
-        table.set_label(3, 3, "Header", QtCore.Qt.green)
-        table.grey_out_column(2)
-        table.set_label(1, 1, "Pilot Bunch by FPGA", QtGui.QColor(0, 255, 255))
-        table.set_label(0, 0, "FPGA Temp monitor Reset", QtGui.QColor(0, 255, 255))
-
-    def update_status(self, registers):
-        try:
-            self.status1_table.set_numbers('{0:032b}'.format(registers[0]))
-            self.status2_table.set_numbers('{0:032b}'.format(registers[1]))
-            self.status3_table.set_numbers('{0:032b}'.format(registers[2]))
-        except Exception:
-            return
-
-    def do_status_update(self):
-        if board.get_board_status(self.board_id).board_connected:
-            registers = board.pci.read(self.board_id, 3, '0x9050', decimal=True)
-            # TODO: KEEPING read_pci as this entire thing will be removed hopefully
-            self.update_status(registers)
-            control = board.pci.read(self.board_id, 1, '0x9040')[0]
-            control_bits = '{0:032b}'.format(int(control, 16))
-            self.control_table.set_numbers(control_bits)
-
-
-class AdvanceControlView(kcgw.KCGWidgets):
-    def __init__(self):
-        super(AdvanceControlView, self).__init__()
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.tabs = QtGui.QTabWidget()
-        self.layout.addWidget(self.tabs)
-
-        self.tables = []
-
-        for bid in available_boards:
-            self.tables.append(AdvancedBoardInterface(board_id=bid))
-            self.tabs.addTab(self.tables[-1], str(bid))
-
-    def pages_update_function(self):
-        for table in self.tables:
-            table.do_status_update()
-
+"""
+This defines the Tables used as Bits display
+"""
+
+from PyQt4 import QtGui, QtCore
+import logging
+from backend import board
+from backend.board import available_boards
+import backendinterface as bif
+import kcgwidget as kcgw
+tr = kcgw.tr
+
+class BitsDisplayTable(QtGui.QTableWidget):
+    """
+    Widget to use to display the Bits (as table)
+    """
+    def __init__(self, value, parent=None, optimalSize=True):
+        QtGui.QTableWidget.__init__(self, parent)
+        self.numbers = str(value)
+        if len(self.numbers) == 0:
+            raise ValueError("Cant create a table for a value of length 0.")
+        self.length = len(self.numbers)
+        self.do_style()
+        if optimalSize is True:
+            self.do_optimal_size()
+
+    def do_style(self):
+        self.horizontalHeader().setDefaultSectionSize(35)
+        self.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
+        self.horizontalHeader().setVisible(True)
+        self.verticalHeader().setDefaultSectionSize(17)
+        self.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
+        self.verticalHeader().setVisible(False)
+        self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+        self.setRowCount(1)
+        self.setColumnCount(self.length)
+
+        # If self.length would be 5, this line would generate ('4', '3',
+        # '2', '1', '0')
+        headers = tuple([str(i) for i in reversed(range(0, self.length))])
+        self.setHorizontalHeaderLabels(headers)
+
+        for i in range(len(self.numbers)):
+            item = QtGui.QTableWidgetItem(self.numbers[i])
+            item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+            self.setItem(0, i, item)
+
+    def width(self):
+        width = 6
+        if self.verticalHeader().isHidden() is False:
+            width = self.verticalHeader().width() + 6
+
+        for i in range(self.columnCount()):
+            width = width + self.columnWidth(i)
+
+        return width
+
+    def height(self):
+        height = 6
+        if self.horizontalHeader().isHidden() is False:
+            height = self.horizontalHeader().height() + 6
+        for i in range(self.rowCount()):
+            height = height + self.rowHeight(i)
+
+        return height
+
+    def do_optimal_size(self):
+        size = QtCore.QSize(self.width(), self.height())
+        self.setMaximumSize(size)
+        self.setMinimumSize(size)
+
+    def stretch_to_width(self, width_in):
+        width = self.width()
+        if width >= width_in:
+            return
+
+        factor = width_in/float(width)
+        error = 0
+        for i in range(self.length):
+            current_cell_size = self.columnWidth(i)
+            new_cell_size = int(current_cell_size * factor)
+            error += new_cell_size - (current_cell_size * factor)
+            if (error >= 1.0) or (error <= -1.0):
+                new_cell_size -= int(error)
+                error -= int(error)
+            self.horizontalHeader().resizeSection(i, new_cell_size)
+
+        self.do_optimal_size()
+
+    def set_item(self, row, col, value):
+        item = QtGui.QTableWidgetItem(str(value))
+        item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+        width = self._get_table_item_width(QtGui.QTableWidgetItem(value))
+        if width > self.columnWidth(col):
+            self.horizontalHeader().resizeSection(col, width)
+        self.setItem(row, col, item)
+
+    def set_numbers(self, value):
+        new_numbers = str(value)
+        if len(new_numbers) == 0:
+            raise ValueError("Cant create a table for a value of length 0.")
+        if len(new_numbers) != len(self.numbers):
+            raise ValueError("New Values for table don't match size."
+                             "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
+        self.numbers = new_numbers
+        for i in range(len(self.numbers)):
+            item = self.item(0, i)
+            item.setText(self.numbers[i])
+
+    def set_label(self, start, end, label, color=None):
+        if (start < 0) or (end > self.columnCount()-1) or (start > end):
+            raise ValueError("Invalid Start and End positions for Label: %s" % label)
+        if self.rowCount() < 2:
+            self.insertRow(1)
+            for i in range(self.length):
+                self.setItem(1, i, QtGui.QTableWidgetItem(''))
+
+        span = (end-start)+1
+        if span > 1:
+            self.setSpan(1, start, 1, span)
+        item = QtGui.QTableWidgetItem(label)
+        item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+        if color:
+            item.setBackground(color)
+        self.setItem(1, start, item)
+
+        # Check if the label is larger then then cells it spans and resize the cells
+        # accordingly, if the label ends up larger then the cells.
+        label_width = self._get_table_item_width(QtGui.QTableWidgetItem(label))
+        cells_width = 0
+        for i in range(start, end+1):
+            cells_width = cells_width + self.columnWidth(i)
+
+        if label_width > cells_width:
+            new_cell_size = label_width/span
+            for i in range(start, end+1):
+                self.horizontalHeader().resizeSection(i, new_cell_size)
+
+        self.do_optimal_size()
+
+    def grey_out_column(self, column):
+        if (column < 0) or (column > self.length):
+            raise ValueError("Supplied column is out of range for this table")
+        for i in range(self.rowCount()):
+            self.item(i, column).setForeground(QtGui.QColor(120, 120, 120))
+            self.item(i, column).setBackground(QtGui.QColor(200, 200, 200))
+
+    def undo_grey_out_column(self, column):
+        if (column < 0) or (column > self.length):
+            raise ValueError("Supplied column is out of range for this table")
+        for i in range(self.rowCount()):
+            self.item(i, column).setForeground(QtGui.QColor(0, 0, 0))
+            self.item(i, column).setBackground(QtGui.QColor(255, 255, 255))
+
+    # Create a table just to insert our item and show how large its width ends up...
+    # We have to use this stupid workaround since QTableWidgetItem has no width() property...
+    def _get_table_item_width(self, item):
+        table = QtGui.QTableWidget()
+        table.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents)
+        table.setRowCount(1)
+        table.setColumnCount(1)
+        table.setItem(0, 0, item)
+        width = table.columnWidth(0)
+        table.deleteLater()
+        return width
+
+
+class BitsEditTable(BitsDisplayTable):
+
+    def __init__(self, value, parent=None, optimalSize=True):
+        BitsDisplayTable.__init__(self, value, parent, optimalSize)
+        self.checkboxes = []
+        self.populate_checkboxes()
+        self.verticalHeader().setDefaultSectionSize(35)
+        self.do_optimal_size()
+
+    def populate_checkboxes(self):
+        for i in range(self.length):
+            widget = QtGui.QWidget()
+            self.checkboxes += [QtGui.QCheckBox()]
+            layout = QtGui.QHBoxLayout(widget)
+            layout.addWidget(self.checkboxes[i])
+            layout.setAlignment(QtCore.Qt.AlignCenter)
+            layout.setContentsMargins(0, 0, 0, 0)
+            widget.setLayout(layout)
+            self.setCellWidget(0, i, widget)
+
+    def set_numbers(self, value):
+            new_numbers = str(value)
+            if len(new_numbers) == 0:
+                raise ValueError("Cant create a table for a value of length 0.")
+            if len(new_numbers) != len(self.numbers):
+                raise ValueError("New Values for table dont match size."
+                                 "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
+            self.numbers = new_numbers
+            for i in range(len(self.numbers)):
+                if self.numbers[i] == '1':
+                    self.checkboxes[i].setChecked(True)
+                else:
+                    self.checkboxes[i].setChecked(False)
+
+    def get_bits(self):
+        bits = ''
+        for i in range(self.length):
+            if self.checkboxes[i].isChecked():
+                bits += '1'
+            else:
+                bits += '0'
+        return bits
+
+    def get_bit(self, bit):
+        if bit > self.length:
+            return None
+        return self.checkboxes[(self.length - bit) - 1].isChecked()
+
+    def clear_all_bits(self):
+        for i in range(self.length):
+            self.checkboxes[i].setChecked(False)
+
+
+class AdvancedBoardInterface(QtGui.QWidget):
+
+    def __init__(self, parent=None, board_id=None):
+        QtGui.QWidget.__init__(self, parent)
+        self.parent = parent
+        self.do_layout()
+        self.data_flow_pipeline_status = None
+        # self.do_status_update() # Do not update status at init - gets updated whenever page is "opened"
+        if board_id is None:
+            print ("No Valid board id specified for AdvancedBoardInterface.")
+            raise ValueError("No Valid board id specified for AdvancedBoardInterface.")
+        self.board_id = board_id
+
+    def do_layout(self):
+
+        self.table_grid = QtGui.QGridLayout()
+        self.setLayout(self.table_grid)
+
+        self.table_grid.addWidget(QtGui.QLabel("Status1 Register 0x9050 (Readonly)"), 0, 0)
+        self.t1 = QtGui.QScrollArea()
+        self.table_grid.addWidget(self.t1, 1, 0)
+        self.status1_table = BitsDisplayTable(32*'0', self)
+        self.do_status1_table_layout(self.status1_table)
+        # self.table_grid.addWidget(self.status1_table, 1, 0)
+        self.t1.setWidget(self.status1_table)
+        self.t1.setFixedHeight(self.status1_table.height()+24)
+
+        self.table_grid.addWidget(QtGui.QLabel("Status2 Register 0x9054 (Readonly)"), 2, 0)
+        self.t2 = QtGui.QScrollArea()
+        self.table_grid.addWidget(self.t2, 3, 0)
+        self.status2_table = BitsDisplayTable(32*'0', self)
+        self.do_status2_table_layout(self.status2_table)
+        # self.table_grid.addWidget(self.status2_table, 3, 0)
+        self.t2.setWidget(self.status2_table)
+        self.t2.setFixedHeight(self.status2_table.height()+24)
+
+        self.table_grid.addWidget(QtGui.QLabel("Status3 Register 0x9058 (Readonly)"), 4, 0)
+        self.t3 = QtGui.QScrollArea()
+        self.table_grid.addWidget(self.t3, 5, 0)
+        self.status3_table = BitsDisplayTable(32*'0', self)
+        self.do_status3_table_layout(self.status3_table)
+        # self.table_grid.addWidget(self.status3_table, 5, 0)
+        self.t3.setWidget(self.status3_table)
+        self.t3.setFixedHeight(self.status3_table.height()+24)
+
+        # self.table_grid.addItem(QtGui.QSpacerItem(1, 20), 6, 0)
+        self.table_grid.addWidget(QtGui.QLabel("Control Register 0x9040"), 7, 0)
+
+        buttons_box = QtGui.QHBoxLayout()
+        buttons_box.setAlignment(QtCore.Qt.AlignLeft)
+        self.write_control_button = QtGui.QPushButton("Write Values to board")
+        self.write_control_button.setMaximumWidth(200)
+        buttons_box.addWidget(self.write_control_button)
+        self.clear_control_button = QtGui.QPushButton("Clear Input")
+        self.clear_control_button.setMaximumWidth(200)
+        buttons_box.addWidget(self.clear_control_button)
+        self.check_status_control_button = QtGui.QPushButton("Check Status")
+        self.check_status_control_button.setMaximumWidth(200)
+        buttons_box.addWidget(self.check_status_control_button)
+        buttons_widget = QtGui.QWidget()
+        buttons_widget.setLayout(buttons_box)
+        self.table_grid.addWidget(buttons_widget, 8, 0)
+
+        self.tedit = QtGui.QScrollArea()
+        self.table_grid.addWidget(self.tedit, 9, 0)
+        self.control_table = BitsEditTable(32*'0', self)
+        self.do_control_table_layout(self.control_table)
+        self.tedit.setWidget(self.control_table)
+        self.tedit.setFixedHeight(self.control_table.height()+24)
+        # self.table_grid.addWidget(self.control_table, 9, 0)
+
+        self.write_control_button.clicked.connect(self.send_control_to_board)
+        self.clear_control_button.clicked.connect(self.control_table.clear_all_bits)
+        self.check_status_control_button.clicked.connect(self.do_status_update)
+        self.check_status_control_button.setShortcut("F5")
+
+        width1 = self.status1_table.width()
+        width2 = self.status2_table.width()
+        width3 = self.status3_table.width()
+        width4 = self.control_table.width()
+        max_width = max(width1, max(width2, max(width3, width4)))
+        self.status1_table.stretch_to_width(max_width)
+        self.status2_table.stretch_to_width(max_width)
+        self.status3_table.stretch_to_width(max_width)
+        self.control_table.stretch_to_width(max_width)
+
+
+    def send_control_to_board(self):
+        if bif._bif_continuous_read_is_enabled(self.board_id, tr("Heading", "Write Registers")):
+            logging.log("Cant write to board while continuous readout is active")
+            return
+
+        bits = self.control_table.get_bits()
+        # print bits
+        dec_val_bits = int(bits, 2)
+        # print dec_val_bits
+        # self.parent.text_area.write("Writing to board Register 0x9040: %s" % ('0x{0:08x}'.format(dec_val_bits)))
+        logging.info("Writing to board Register 0x9040: %s" % ('0x{0:08x}'.format(dec_val_bits)))
+        try:
+            board.pci.write(self.board_id, hex(dec_val_bits), '0x9040')
+        except board.BoardError as e:
+            QtGui.QMessageBox.critical(self, "Board communication",
+                                       "Was unable to write value to board!\nReason: "+str(e)+"\nBoard: "+str(self.board_id))
+
+        # self.parent.do_status_readout()
+        self.do_status_update()
+
+    def do_status1_table_layout(self, table):
+        # from right to left
+        table.set_label(29, 31, "FSM_Data_Pipeline_Status", QtCore.Qt.green)
+        table.grey_out_column(28)
+        table.set_label(27, 27, "FULL", QtCore.Qt.green)
+        table.set_label(26, 26, "EMPTY", QtCore.Qt.green)
+        table.grey_out_column(25)
+        table.grey_out_column(24)
+        table.set_label(14, 23, "RD_data_Counts", QtCore.Qt.green)
+        table.set_label(13, 13, "OVR_ADC", QtGui.QColor(210, 210, 0))
+        table.grey_out_column(12)
+        table.grey_out_column(11)
+        table.grey_out_column(10)
+        table.grey_out_column(9)
+        table.grey_out_column(8)
+        table.set_label(7, 7, "PLL_LD", QtGui.QColor(210, 210, 0))
+        table.grey_out_column(6)
+        table.set_label(2, 5, "Master Control", QtCore.Qt.green)
+        table.grey_out_column(1)
+        table.set_label(0, 0, "1")
+
+    def do_status2_table_layout(self, table):
+        #from right to left
+        table.set_label(31, 31, "FIFO 128 255 empty", QtCore.Qt.green)
+        table.set_label(30, 30, "FIFO 128 255 full", QtCore.Qt.green)
+        table.grey_out_column(29)
+        table.grey_out_column(28)
+        table.set_label(17, 27, "wr data count 128 255", QtGui.QColor(210, 210, 0))
+        table.grey_out_column(16)
+        table.set_label(15, 15, "FIFO 255 64 empty", QtCore.Qt.green)
+        table.set_label(14, 14, "FIFO 255 64 full", QtCore.Qt.green)
+        table.grey_out_column(13)
+        table.grey_out_column(12)
+        table.set_label(2, 11, "rd data count 255 64", QtGui.QColor(210, 210, 0))
+        table.grey_out_column(1)
+        table.grey_out_column(0)
+
+    def do_status3_table_layout(self, table):
+        #from right to left
+        table.set_label(29, 31, "FSM_ARBITER_DDR3", QtCore.Qt.green)
+        table.grey_out_column(28)
+        table.set_label(25, 27, "FSM_WR_DDR3", QtCore.Qt.green)
+        table.grey_out_column(24)
+        table.set_label(21, 23, "FSM_R_DDR3", QtCore.Qt.green)
+        table.grey_out_column(20)
+        table.set_label(16, 19, "BC_ERROR", QtGui.QColor(210, 210, 0))
+        table.set_label(1, 15, "Number of wrong BC", QtGui.QColor(255, 255, 0))
+        table.grey_out_column(0)
+
+    def do_control_table_layout(self, table):
+        #from right to left
+        table.set_label(31, 31, "reset", QtGui.QColor(0, 255, 255))
+        table.grey_out_column(30)
+        table.grey_out_column(29)
+        table.grey_out_column(28)
+        table.set_label(27, 27, "ADC_1(A+D)", QtGui.QColor(0, 255, 255))
+        table.set_label(26, 26, "ADC_2(A+D)2", QtGui.QColor(0, 255, 255))
+        table.set_label(25, 25, "T/H_1", QtGui.QColor(210, 210, 0))
+        table.set_label(24, 24, "T/H_2", QtGui.QColor(210, 210, 0))
+        table.set_label(23, 23, "T/H_3", QtGui.QColor(210, 210, 0))
+        table.set_label(22, 22, "T/H_4", QtGui.QColor(210, 210, 0))
+        table.set_label(21, 21, "EN_data_Trans", QtCore.Qt.yellow)
+        table.set_label(20, 20, "EN_readout", QtCore.Qt.yellow)
+        table.set_label(18, 19, "ADC_1", QtCore.Qt.yellow)
+        table.set_label(16, 17, "ADC_2", QtCore.Qt.yellow)
+        table.set_label(14, 15, "ADC_3", QtCore.Qt.yellow)
+        table.set_label(12, 13, "ADC_4", QtCore.Qt.yellow)
+        table.grey_out_column(11)
+        table.grey_out_column(10)
+        table.set_label(9, 9, "Clock Selection", QtCore.Qt.yellow)
+        table.set_label(8, 8, "Sampling", QtCore.Qt.green)
+        table.set_label(7, 7, "Reset", QtCore.Qt.yellow)
+        table.set_label(6, 6, "ADC_CNT", QtCore.Qt.yellow)
+        table.grey_out_column(5)
+        table.grey_out_column(4)
+        table.set_label(3, 3, "Header", QtCore.Qt.green)
+        table.grey_out_column(2)
+        table.set_label(1, 1, "Pilot Bunch by FPGA", QtGui.QColor(0, 255, 255))
+        table.set_label(0, 0, "FPGA Temp monitor Reset", QtGui.QColor(0, 255, 255))
+
+    def update_status(self, registers):
+        try:
+            self.status1_table.set_numbers('{0:032b}'.format(registers[0]))
+            self.status2_table.set_numbers('{0:032b}'.format(registers[1]))
+            self.status3_table.set_numbers('{0:032b}'.format(registers[2]))
+        except Exception:
+            return
+
+    def do_status_update(self):
+        if board.get_board_status(self.board_id).board_connected:
+            registers = board.pci.read(self.board_id, 3, '0x9050', decimal=True)
+            # TODO: KEEPING read_pci as this entire thing will be removed hopefully
+            self.update_status(registers)
+            control = board.pci.read(self.board_id, 1, '0x9040')[0]
+            control_bits = '{0:032b}'.format(int(control, 16))
+            self.control_table.set_numbers(control_bits)
+
+
+class AdvanceControlView(kcgw.KCGWidgets):
+    def __init__(self):
+        super(AdvanceControlView, self).__init__()
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.tabs = QtGui.QTabWidget()
+        self.layout.addWidget(self.tabs)
+
+        self.tables = []
+
+        for bid in available_boards:
+            self.tables.append(AdvancedBoardInterface(board_id=bid))
+            self.tabs.addTab(self.tables[-1], str(bid))
+
+    def pages_update_function(self):
+        for table in self.tables:
+            table.do_status_update()
+

+ 99 - 99
KCG/base/callbacks.py

@@ -1,99 +1,99 @@
-from PyQt4 import QtCore
-
-
-class NoCallbackError(Exception):
-    pass
-
-
-class CallbackExistsError(Exception):
-    pass
-
-
-class CallbackHandler(QtCore.QObject):
-    """
-    Handler for custom callbacks.
-    It can handle synchronous callbacks as well as async callbacks (using pyqtSignals)
-    """
-
-    callback_signal = QtCore.pyqtSignal(str, list, dict)
-
-    def __init__(self):
-        super(CallbackHandler, self).__init__()
-        self._callbacks = {}
-        self.callback_signal.connect(self.__async_callback_receiver)
-
-    def callback(self, name, *args, **kwargs):
-        """
-        Call all registered callback method for name
-        This passes all additional arguments and keyword arguments down to the callbacks
-        NOTE: all callbacks therefore need to accept the same number of arguments
-        :param name: the name for which the callbacks are to be called
-        :param args: arguments to be passed to the callbacks
-        :param kwargs: keyword arguments to be passed to the callbacks
-        """
-        if name in self._callbacks:
-            for callback in self._callbacks[name]:
-                callback(*args, **kwargs)
-        else:
-            raise NoCallbackError("Callback " + name + " not registered.")
-
-    def __async_callback_receiver(self, name, *args):
-        """
-        Internal Method (Called when async callbacks are to be executed)
-        """
-        self.callback(str(name), *args[0], **args[1])
-
-    def async_callback(self, name, *args, **kwargs):
-        """
-        Perform a async callback (same as callback but through pyqtSignal and therefore allowed in threads)
-        :param name: the name for which the callbacks are to be called
-        :param args: arguments to be passed to the callbacks
-        :param kwargs: keyword arguments to be passed to the callbacks
-        """
-        if name in self._callbacks:
-            self.callback_signal.emit(name, list(args), dict(kwargs))
-        else:
-            raise NoCallbackError("Callback " + name + " not registered.")
-
-    def add_callback(self, name, callback):
-        """
-        Register a callback for name
-        :param name: the name to register against
-        :param callback: the callback to register
-        """
-        if name in self._callbacks:
-            if callback in self._callbacks[name]:
-                raise CallbackExistsError("Callback " + name + " already registered.")
-            else:
-                self._callbacks[name].append(callback)
-        else:
-            self._callbacks[name] = [callback, ]
-
-    def delete_callback(self, name, callback):
-        """
-        Delete a callback from name
-        if no callback for name is left the whole group is deleted
-        :param name: the name to delete the callback from
-        :param callback: the callback to delete
-        """
-        if name not in self._callbacks:
-            raise NoCallbackError("Callback " + name + " not registered.")
-        else:
-            if callback in self._callbacks[name]:
-                self._callbacks[name].remove(callback)
-            else:
-                raise NoCallbackError("Callback " + str(callback) + " not registered in " + name+".")
-            if len(self._callbacks[name]) == 0:
-                self.delete_callback_class(name)
-
-    def delete_callback_class(self, name):
-        """
-        Delete a whole callback class
-        :param name: the name of the class
-        """
-        if name in self._callbacks:
-            del self._callbacks[name]
-        else:
-            raise NoCallbackError("Callback " + name + " not registered.")
-
-callbacks = CallbackHandler()
+from PyQt4 import QtCore
+
+
+class NoCallbackError(Exception):
+    pass
+
+
+class CallbackExistsError(Exception):
+    pass
+
+
+class CallbackHandler(QtCore.QObject):
+    """
+    Handler for custom callbacks.
+    It can handle synchronous callbacks as well as async callbacks (using pyqtSignals)
+    """
+
+    callback_signal = QtCore.pyqtSignal(str, list, dict)
+
+    def __init__(self):
+        super(CallbackHandler, self).__init__()
+        self._callbacks = {}
+        self.callback_signal.connect(self.__async_callback_receiver)
+
+    def callback(self, name, *args, **kwargs):
+        """
+        Call all registered callback method for name
+        This passes all additional arguments and keyword arguments down to the callbacks
+        NOTE: all callbacks therefore need to accept the same number of arguments
+        :param name: the name for which the callbacks are to be called
+        :param args: arguments to be passed to the callbacks
+        :param kwargs: keyword arguments to be passed to the callbacks
+        """
+        if name in self._callbacks:
+            for callback in self._callbacks[name]:
+                callback(*args, **kwargs)
+        else:
+            raise NoCallbackError("Callback " + name + " not registered.")
+
+    def __async_callback_receiver(self, name, *args):
+        """
+        Internal Method (Called when async callbacks are to be executed)
+        """
+        self.callback(str(name), *args[0], **args[1])
+
+    def async_callback(self, name, *args, **kwargs):
+        """
+        Perform a async callback (same as callback but through pyqtSignal and therefore allowed in threads)
+        :param name: the name for which the callbacks are to be called
+        :param args: arguments to be passed to the callbacks
+        :param kwargs: keyword arguments to be passed to the callbacks
+        """
+        if name in self._callbacks:
+            self.callback_signal.emit(name, list(args), dict(kwargs))
+        else:
+            raise NoCallbackError("Callback " + name + " not registered.")
+
+    def add_callback(self, name, callback):
+        """
+        Register a callback for name
+        :param name: the name to register against
+        :param callback: the callback to register
+        """
+        if name in self._callbacks:
+            if callback in self._callbacks[name]:
+                raise CallbackExistsError("Callback " + name + " already registered.")
+            else:
+                self._callbacks[name].append(callback)
+        else:
+            self._callbacks[name] = [callback, ]
+
+    def delete_callback(self, name, callback):
+        """
+        Delete a callback from name
+        if no callback for name is left the whole group is deleted
+        :param name: the name to delete the callback from
+        :param callback: the callback to delete
+        """
+        if name not in self._callbacks:
+            raise NoCallbackError("Callback " + name + " not registered.")
+        else:
+            if callback in self._callbacks[name]:
+                self._callbacks[name].remove(callback)
+            else:
+                raise NoCallbackError("Callback " + str(callback) + " not registered in " + name+".")
+            if len(self._callbacks[name]) == 0:
+                self.delete_callback_class(name)
+
+    def delete_callback_class(self, name):
+        """
+        Delete a whole callback class
+        :param name: the name of the class
+        """
+        if name in self._callbacks:
+            del self._callbacks[name]
+        else:
+            raise NoCallbackError("Callback " + name + " not registered.")
+
+callbacks = CallbackHandler()

+ 378 - 417
KCG/base/controlwidget.py

@@ -1,418 +1,379 @@
-"""
-This module defines the Initial view of the gui
-"""
-
-from PyQt4 import QtGui, QtCore
-import logging
-
-from . import kcgwidget as kcgw
-from .kcgwidget import error
-from .backend import board
-from .backend.board import available_boards
-from .groupedelements import Checkboxes, Buttons, Elements
-from . import backendinterface as bif
-from .loghandler import LogArea
-from . import storage
-from .. import config
-
-tr = kcgw.tr
-
-
-# 888     88888888888888888b.
-# 888     888       888  "Y88b
-# 888     888       888    888
-# 888     8888888   888    888
-# 888     888       888    888
-# 888     888       888    888
-# 888     888       888  .d88P
-# 8888888888888888888888888P"
-
-class LED(QtGui.QWidget):
-    """
-    Produces a graphical LED
-    """
-    def __init__(self, parent=None, status=1, height=10, width=10):
-        """
-        Initialize a LED
-        :param parent: (QWidget) parent of this widget
-        :param status: (int) (0=out, 1=off, 2=orange, 3=on) initial status of this LED
-        :param height: (int) height of the LED
-        :param width: (int) width of the LED
-        :return: -
-        """
-        QtGui.QWidget.__init__(self, parent)
-        colorRGB=(255, 0, 0)
-        self.width = width
-        self.height = height
-        self.color = QtGui.QColor(colorRGB[0], colorRGB[1], colorRGB[2])
-        self.center = QtCore.QPoint(width, height)
-        self.setMinimumSize(2 * width, 2 * height)
-        self.setMaximumSize(2 * width, 2 * height)
-        if status == 3:
-            self.set_on()
-        elif status == 1:
-            self.set_off()
-        elif status == 0:
-            self.set_out()
-        elif status == 2:
-            self.set_tri()
-        self.status = status
-
-    def paintEvent(self, event):
-        paint = QtGui.QPainter()
-        paint.begin(self)
-        paint.setRenderHint(QtGui.QPainter.Antialiasing)
-
-        # draw a grey 'socket' for the LED
-        paint.setPen(QtGui.QColor(160, 160, 160))
-        paint.setBrush(QtGui.QColor(180, 180, 180))
-        paint.drawEllipse(self.center, self.width, self.height)
-
-        # draw the body of the LED
-        paint.setBrush(self.color)
-        paint.drawEllipse(self.center, self.width*0.85, self.height*0.85)
-
-    def set_on(self):
-        """
-        Set the LED to "on" state
-        :return: -
-        """
-        self.color = QtGui.QColor(0, 255, 0)
-        self.update()
-        self.status = 3
-
-    def set_off(self):
-        """
-        Set the LED to "off" state
-        :return: -
-        """
-        self.color = QtGui.QColor(255, 0, 0)
-        self.update()
-        self.status = 1
-
-    def set_out(self):
-        """
-        Set the LED to "OUT" state (that is like an LED without power)
-        :return: -
-        """
-        self.color = QtGui.QColor(150, 150, 150)
-        self.update()
-        self.status = 0
-
-    def set_tri(self):
-        """
-        Set the LED to "TRI" state (that is led is orange)
-        :return: -
-        """
-        self.color = QtGui.QColor(255, 255, 0)
-        self.update()
-        self.status = 2
-
-    def set_status(self, status):
-        """
-        Set the status of the led
-        :param status: status (in 0, 1, 2, 3)
-        """
-        if status == 0:
-            self.set_out()
-        elif status == 1:
-            self.set_off()
-        elif status == 2:
-            self.set_tri()
-        elif status == 3:
-            self.set_on()
-
-
-#  .d8888b. 888           888                   888     88888888888888888b.
-# d88P  Y88b888           888                   888     888       888  "Y88b
-# Y88b.     888           888                   888     888       888    888
-#  "Y888b.  888888 8888b. 888888888  888.d8888b 888     8888888   888    888
-#     "Y88b.888       "88b888   888  88888K     888     888       888    888
-#       "888888   .d888888888   888  888"Y8888b.888     888       888    888
-# Y88b  d88PY88b. 888  888Y88b. Y88b 888     X88888     888       888  .d88P
-#  "Y8888P"  "Y888"Y888888 "Y888 "Y88888 88888P'8888888888888888888888888P"
-
-class StatusLED(QtGui.QWidget):
-    """
-    Create a Status LED with Label next to it
-    """
-    def __init__(self, text, status=None):
-        """
-        Initialise StatusLED
-        :param text: label text next to the LED
-        :param status: initial status of the LED
-        :return: -
-        """
-        super(StatusLED, self).__init__()
-        self.layout = QtGui.QHBoxLayout()
-        self.label = QtGui.QLabel(text)
-        self.led = LED(status=status, width=9, height=9)
-        self.layout.addWidget(self.led)
-        self.layout.addWidget(self.label)
-        self.setLayout(self.layout)
-
-    def set_on(self):
-        """
-        See set_on of LED Class
-        """
-        self.led.set_on()
-
-    def set_off(self):
-        """
-        See set_off of LED Class
-        """
-        self.led.set_off()
-
-    def set_out(self):
-        """
-        See set_out of LED Class
-        """
-        self.led.set_out()
-
-    def set_tri(self):
-        """
-        See set_tri of LED Class
-        """
-        self.led.set_tri()
-
-    def set_status(self, status):
-        """
-        See set_status of LED Class
-        :param status: the status to set the led to
-        """
-        self.led.set_status(status)
-
-
-# 888888b.                              888 .d8888b.                 888                  888
-# 888  "88b                             888d88P  Y88b                888                  888
-# 888  .88P                             888888    888                888                  888
-# 8888888K.  .d88b.  8888b. 888d888 .d88888888        .d88b. 88888b. 888888888d888 .d88b. 888
-# 888  "Y88bd88""88b    "88b888P"  d88" 888888       d88""88b888 "88b888   888P"  d88""88b888
-# 888    888888  888.d888888888    888  888888    888888  888888  888888   888    888  888888
-# 888   d88PY88..88P888  888888    Y88b 888Y88b  d88PY88..88P888  888Y88b. 888    Y88..88P888
-# 8888888P"  "Y88P" "Y888888888     "Y88888 "Y8888P"  "Y88P" 888  888 "Y888888     "Y88P" 888
-
-class BoardControl(kcgw.KCGWidgets):
-    """
-    The main view of the gui for each board at startup
-    """
-    def __init__(self, board_id, single=False):
-        super(BoardControl, self).__init__()
-        self.board_id = board_id
-        self.layout = QtGui.QVBoxLayout()
-        self.setLayout(self.layout)
-
-        if not single:
-            self.header_layout = QtGui.QHBoxLayout()
-            left_line = QtGui.QFrame()
-            left_line.setFrameShape(QtGui.QFrame.HLine)
-            left_line.setFrameShadow(QtGui.QFrame.Sunken)
-            right_line = QtGui.QFrame()
-            right_line.setFrameShape(QtGui.QFrame.HLine)
-            right_line.setFrameShadow(QtGui.QFrame.Sunken)
-            self.header_layout.addWidget(left_line)
-            header_label = self.createLabel("Board: {}".format(available_boards.get_board_name_from_id(board_id)))
-            header_label.setFixedWidth(header_label.sizeHint().width())
-            self.header_layout.addWidget(header_label)
-            self.header_layout.addWidget(right_line)
-
-            self.layout.addLayout(self.header_layout)
-
-        self.mainControlLayout = QtGui.QHBoxLayout()
-        self.subControlLayout = QtGui.QHBoxLayout()
-        self.subControlWidget = QtGui.QWidget()
-        self.subControlWidget.setLayout(self.subControlLayout)
-        self.statusLayout = QtGui.QHBoxLayout()
-        self.layout.addLayout(self.mainControlLayout)
-        self.layout.addWidget(self.subControlWidget)
-        self.layout.addLayout(self.statusLayout)
-
-        # ----------[ LED Status ]---------------
-        self.pipeline_led = StatusLED(tr("Label", "DataFlow Pipeline"))
-        self.master_control_led = StatusLED(tr("Label", "Master Control"))
-        self.data_check_led = StatusLED(tr("Label", "Data Check"))
-        self.pll_ld_led = StatusLED(tr("Label", "PLL_LD"))
-        self.statusLayout.addWidget(self.pipeline_led)
-        self.statusLayout.addWidget(self.master_control_led)
-        self.statusLayout.addWidget(self.data_check_led)
-        self.statusLayout.addWidget(self.pll_ld_led)
-
-        # -----------[ Buttons ]--------------
-        self.all_in_one_button = self.createButton(text=tr("Button", "Prepare Board"), connect=self.all_in_one,
-                                                   tooltip=tr("Tooltip", "Start, Calibrate, Synchronize and set Defaults\nCtrl+A"))
-        self.toggleButton = self.createButton(text="", connect=self.toggle_sub_control)
-        self.toggleButton.setIcon(QtGui.QIcon(config.icon_path("chevron-bottom.svg")))
-        self.toggleButton.setFixedWidth(50)
-        # self.all_in_one_button.setShortcut("Ctrl+A")
-        # self.all_in_one_button.setObjectName("all_in_one")
-
-        self.skip_init_button = self.createButton(text=tr("Button", "Skip Initialisation"), connect=self.skip_init,
-                                                  tooltip=tr("Tooltip", "Skip Initialisation and read values from board.\n"
-                                                                        "NOTE: ONLY DO THIS IF BOARD WAS CALIBRATED AND SYNCHRONIZED BEFORE"))
-
-        self.start_button = self.createButton(text=tr("Button", "Start Board"), connect=lambda x: bif.bk_start_board(board_id=board_id))
-        self.calibrate_button = self.createButton(text=tr("Button", "Calibrate"), connect=lambda x: bif.bk_calibrate(board_id=board_id))
-        self.syncronize_button = self.createButton(text=tr("Button", "Synchronize"), connect=lambda x: bif.bk_sync_board(board_id=board_id))
-        self.set_default_button = self.createButton(text=tr("Button", "Set Defaults"), connect=lambda x: bif.bk_write_values(board_id=board_id, defaults=True))
-        self.soft_reset_button = self.createButton(text=tr("Button", "Soft Reset"), connect=lambda x: bif.bk_soft_reset(board_id=board_id))
-        self.off_button = self.createButton(text=tr("Button", "Board Off"), connect=lambda x: bif.bk_stop_board(board_id=board_id))
-        self.off_button.setObjectName("off")
-        self.check_status_button = self.createButton(text=tr("Button", "Check Status"), connect=lambda x: bif._bif_status_readout(board_id=board_id))
-
-        Buttons.addButton('start_board_{}'.format(board_id), self.start_button)
-        Buttons.addButton(['calibrate_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.calibrate_button)
-        Buttons.addButton(['synchronize_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.syncronize_button)
-        Buttons.addButton(['set_defaults_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.set_default_button)
-        Elements.addItem('no_board_{}'.format(board_id),
-                         [
-                             self.start_button,
-                             self.calibrate_button,
-                             self.syncronize_button,
-                             self.set_default_button,
-                             self.soft_reset_button,
-                             self.off_button,
-                             self.all_in_one_button
-                         ])
-
-        self.mainControlLayout.addWidget(self.skip_init_button)
-        self.mainControlLayout.addWidget(self.all_in_one_button)
-        self.mainControlLayout.addWidget(self.toggleButton)
-        self.mainControlLayout.addWidget(self.soft_reset_button)
-        self.mainControlLayout.addWidget(self.off_button)
-
-        self.statusLayout.addWidget(self.check_status_button)
-
-        self.subControlLayout.addWidget(self.start_button)
-        self.subControlLayout.addWidget(self.calibrate_button)
-        self.subControlLayout.addWidget(self.syncronize_button)
-        self.subControlLayout.addWidget(self.set_default_button)
-
-        # register the led updater function (used in backendinterface.bk_status_readout)
-        storage.get_board_specific_storage(board_id).update_LED = self.on_check
-        self.geo = self.saveGeometry()
-        self.subControlWidget.hide()
-
-    def toggle_sub_control(self):
-        """
-        Show or hide the subcontrol buttons
-        """
-        self.subControlWidget.setHidden(not self.subControlWidget.isHidden())
-        if self.subControlWidget.isHidden():
-            self.toggleButton.setIcon(QtGui.QIcon(config.icon_path("chevron-bottom.svg")))
-        else:
-            self.toggleButton.setIcon(QtGui.QIcon(config.icon_path("chevron-top.svg")))
-        self.parent().adjustSize()
-
-    def all_in_one(self):
-        """
-        Function that gets called when the Prepare Board Button is pressed.
-        It Starts the board, syncs it and sets defaults.
-        This is accomplished via the backendinterface module
-        :return: -
-        """
-        if bif.bk_start_board(self.board_id) is not False:
-
-            def do_the_rest(board_id):
-                bif.bk_sync_board(board_id)
-                bif.bk_write_values(board_id, defaults=True)
-            bif.bk_calibrate(self.board_id, do_the_rest)
-
-    def skip_init(self):
-        """
-        Skip board initialisation progress and read data from board. This will adjust the values in the gui to those
-        on the board (for every value that is stored on the board)
-        """
-        board.get_board_config(self.board_id).read_from_board()
-        board.get_board_status(self.board_id).calibrated = True
-        board.get_board_status(self.board_id).synced = True
-        board.get_board_status(self.board_id).defaults_set = True
-        bif.bk_status_readout()
-        logging.info("Initialisation skipped, configuration and status was read from KAPTURE.")
-
-    def on_check(self):
-        """
-        This function is the handler for the status leds on the ControlWidget View.
-        Parses the registers and sets the colors of the leds according.
-        :return: -
-        """
-        try:
-            status = bif.bk_get_status(self.board_id)
-        except IndexError:
-            error(0x002, "Pci returned not enough registers to update LEDs.")
-            return
-
-        for led, st in list(status.items()):
-            getattr(self, led.lower()+"_led").set_status(st)
-
-
-#  .d8888b.                 888                  888888       888d8b     888                888
-# d88P  Y88b                888                  888888   o   888Y8P     888                888
-# 888    888                888                  888888  d8b  888        888                888
-# 888        .d88b. 88888b. 888888888d888 .d88b. 888888 d888b 888888 .d88888 .d88b.  .d88b. 888888
-# 888       d88""88b888 "88b888   888P"  d88""88b888888d88888b888888d88" 888d88P"88bd8P  Y8b888
-# 888    888888  888888  888888   888    888  88888888888P Y88888888888  888888  88888888888888
-# Y88b  d88PY88..88P888  888Y88b. 888    Y88..88P8888888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.
-#  "Y8888P"  "Y88P" 888  888 "Y888888     "Y88P" 888888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888
-#                                                                                888
-#                                                                           Y8b d88P
-#                                                                            "Y88P"
-
-class ControlWidget(kcgw.KCGWidgets):
-    """
-    Main Widget that is shown at start of gui.
-    """
-    def __init__(self):
-        super(ControlWidget, self).__init__()
-
-        self.overlayout = QtGui.QVBoxLayout()
-        self.setLayout(self.overlayout)
-
-        self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
-        self.overlayout.addWidget(self.splitter)
-
-
-        self.board_control_list = {i: BoardControl(i, not available_boards.multi_board) for i in available_boards}
-        self.board_widget = QtGui.QWidget()
-        self.board_widget_layout = QtGui.QVBoxLayout()
-        self.board_widget.setLayout(self.board_widget_layout)
-        for bc in list(self.board_control_list.values()):
-            self.board_widget_layout.addWidget(bc)
-        self.board_widget_layout.addStretch(1)
-
-        if available_boards.multi_board:
-            self.scroll_widget = QtGui.QScrollArea()
-            # self.scroll_widget.setMaximumWidth(1.1 * self.board_control_list.values()[0].minimumSizeHint().width())
-            self.scroll_widget.setWidgetResizable(True)
-            self.scroll_widget.setWidget(self.board_widget)
-            self.scroll_widget_container = QtGui.QWidget()
-            self.scroll_widget_layout = QtGui.QHBoxLayout()
-            self.scroll_widget_layout.addWidget(self.scroll_widget)
-            self.scroll_widget_layout.setAlignment(QtCore.Qt.AlignLeft)
-            self.scroll_widget_container.setLayout(self.scroll_widget_layout)
-            self.splitter.addWidget(self.scroll_widget_container)
-        else:
-            self.splitter.addWidget(self.board_widget)
-
-        # self.log_area = QtGui.QTextEdit()
-        self.log_area = LogArea()
-        self.log_area.setReadOnly(True)
-        self.splitter.addWidget(self.log_area)
-        self.log_area.init_logging()
-        self.log_area.setKeywords([
-            "Activating Board",
-            "Started Board Calibration",
-            "Synchronize PLLs",
-            "Setting default Values",
-            "Updating Values"
-        ])
-
-        # self.setTabOrder(self.soft_reset_button, self.check_status_button)
-        # self.setTabOrder(self.check_status_button, self.off_button)
-
-        # ------------------[ Logging ]----------------------
-        # log_handler = LogHandler(self.log_area)
-        # self.logger = logging.getLogger()
-        # self.logger.addHandler(log_handler)
-        # self.logger.setLevel(config.log_level)
-        # logging.logger = self.logger
+"""
+This module defines the Initial view of the gui
+"""
+
+from PyQt4 import QtGui, QtCore
+import logging
+
+import kcgwidget as kcgw
+from kcgwidget import error
+from backend import board
+from backend.board import available_boards
+from groupedelements import Checkboxes, Buttons, Elements
+import backendinterface as bif
+from loghandler import LogArea
+import storage
+from .. import config
+
+tr = kcgw.tr
+
+
+class LED(QtGui.QWidget):
+    """
+    Produces a graphical LED
+    """
+    def __init__(self, parent=None, status=1, height=10, width=10):
+        """
+        Initialize a LED
+        :param parent: (QWidget) parent of this widget
+        :param status: (int) (0=out, 1=off, 2=orange, 3=on) initial status of this LED
+        :param height: (int) height of the LED
+        :param width: (int) width of the LED
+        :return: -
+        """
+        QtGui.QWidget.__init__(self, parent)
+        colorRGB=(255, 0, 0)
+        self.width = width
+        self.height = height
+        self.color = QtGui.QColor(colorRGB[0], colorRGB[1], colorRGB[2])
+        self.center = QtCore.QPoint(width, height)
+        self.setMinimumSize(2 * width, 2 * height)
+        self.setMaximumSize(2 * width, 2 * height)
+        if status == 3:
+            self.set_on()
+        elif status == 1:
+            self.set_off()
+        elif status == 0:
+            self.set_out()
+        elif status == 2:
+            self.set_tri()
+        self.status = status
+
+    def paintEvent(self, event):
+        paint = QtGui.QPainter()
+        paint.begin(self)
+        paint.setRenderHint(QtGui.QPainter.Antialiasing)
+
+        # draw a grey 'socket' for the LED
+        paint.setPen(QtGui.QColor(160, 160, 160))
+        paint.setBrush(QtGui.QColor(180, 180, 180))
+        paint.drawEllipse(self.center, self.width, self.height)
+
+        # draw the body of the LED
+        paint.setBrush(self.color)
+        paint.drawEllipse(self.center, self.width*0.85, self.height*0.85)
+
+    def set_on(self):
+        """
+        Set the LED to "on" state
+        :return: -
+        """
+        self.color = QtGui.QColor(0, 255, 0)
+        self.update()
+        self.status = 3
+
+    def set_off(self):
+        """
+        Set the LED to "off" state
+        :return: -
+        """
+        self.color = QtGui.QColor(255, 0, 0)
+        self.update()
+        self.status = 1
+
+    def set_out(self):
+        """
+        Set the LED to "OUT" state (that is like an LED without power)
+        :return: -
+        """
+        self.color = QtGui.QColor(150, 150, 150)
+        self.update()
+        self.status = 0
+
+    def set_tri(self):
+        """
+        Set the LED to "TRI" state (that is led is orange)
+        :return: -
+        """
+        self.color = QtGui.QColor(255, 255, 0)
+        self.update()
+        self.status = 2
+
+    def set_status(self, status):
+        """
+        Set the status of the led
+        :param status: status (in 0, 1, 2, 3)
+        """
+        if status == 0:
+            self.set_out()
+        elif status == 1:
+            self.set_off()
+        elif status == 2:
+            self.set_tri()
+        elif status == 3:
+            self.set_on()
+
+
+class StatusLED(QtGui.QWidget):
+    """
+    Create a Status LED with Label next to it
+    """
+    def __init__(self, text, status=None):
+        """
+        Initialise StatusLED
+        :param text: label text next to the LED
+        :param status: initial status of the LED
+        :return: -
+        """
+        super(StatusLED, self).__init__()
+        self.layout = QtGui.QHBoxLayout()
+        self.label = QtGui.QLabel(text)
+        self.led = LED(status=status, width=9, height=9)
+        self.layout.addWidget(self.led)
+        self.layout.addWidget(self.label)
+        self.setLayout(self.layout)
+
+    def set_on(self):
+        """
+        See set_on of LED Class
+        """
+        self.led.set_on()
+
+    def set_off(self):
+        """
+        See set_off of LED Class
+        """
+        self.led.set_off()
+
+    def set_out(self):
+        """
+        See set_out of LED Class
+        """
+        self.led.set_out()
+
+    def set_tri(self):
+        """
+        See set_tri of LED Class
+        """
+        self.led.set_tri()
+
+    def set_status(self, status):
+        """
+        See set_status of LED Class
+        :param status: the status to set the led to
+        """
+        self.led.set_status(status)
+
+
+class BoardControl(kcgw.KCGWidgets):
+    """
+    The main view of the gui for each board at startup
+    """
+    def __init__(self, board_id, single=False):
+        super(BoardControl, self).__init__()
+        self.board_id = board_id
+        self.layout = QtGui.QVBoxLayout()
+        self.setLayout(self.layout)
+
+        if not single:
+            self.header_layout = QtGui.QHBoxLayout()
+            left_line = QtGui.QFrame()
+            left_line.setFrameShape(QtGui.QFrame.HLine)
+            left_line.setFrameShadow(QtGui.QFrame.Sunken)
+            right_line = QtGui.QFrame()
+            right_line.setFrameShape(QtGui.QFrame.HLine)
+            right_line.setFrameShadow(QtGui.QFrame.Sunken)
+            self.header_layout.addWidget(left_line)
+            header_label = self.createLabel("Board: {}".format(available_boards.get_board_name_from_id(board_id)))
+            header_label.setFixedWidth(header_label.sizeHint().width())
+            self.header_layout.addWidget(header_label)
+            self.header_layout.addWidget(right_line)
+
+            self.layout.addLayout(self.header_layout)
+
+        self.mainControlLayout = QtGui.QHBoxLayout()
+        self.subControlLayout = QtGui.QHBoxLayout()
+        self.subControlWidget = QtGui.QWidget()
+        self.subControlWidget.setLayout(self.subControlLayout)
+        self.statusLayout = QtGui.QHBoxLayout()
+        self.layout.addLayout(self.mainControlLayout)
+        self.layout.addWidget(self.subControlWidget)
+        self.layout.addLayout(self.statusLayout)
+
+        # ----------[ LED Status ]---------------
+        self.pipeline_led = StatusLED(tr("Label", "DataFlow Pipeline"))
+        self.master_control_led = StatusLED(tr("Label", "Master Control"))
+        self.data_check_led = StatusLED(tr("Label", "Data Check"))
+        self.pll_ld_led = StatusLED(tr("Label", "PLL_LD"))
+        self.statusLayout.addWidget(self.pipeline_led)
+        self.statusLayout.addWidget(self.master_control_led)
+        self.statusLayout.addWidget(self.data_check_led)
+        self.statusLayout.addWidget(self.pll_ld_led)
+
+        # -----------[ Buttons ]--------------
+        self.all_in_one_button = self.createButton(text=tr("Button", "Prepare Board"), connect=self.all_in_one,
+                                                   tooltip=tr("Tooltip", "Start, Calibrate, Synchronize and set Defaults\nCtrl+A"))
+        self.toggleButton = self.createButton(text="", connect=self.toggle_sub_control)
+        self.toggleButton.setIcon(QtGui.QIcon(config.install_path+"icons/chevron-bottom.svg"))
+        self.toggleButton.setFixedWidth(50)
+        # self.all_in_one_button.setShortcut("Ctrl+A")
+        # self.all_in_one_button.setObjectName("all_in_one")
+
+        self.skip_init_button = self.createButton(text=tr("Button", "Skip Initialisation"), connect=self.skip_init,
+                                                  tooltip=tr("Tooltip", "Skip Initialisation and read values from board.\n"
+                                                                        "NOTE: ONLY DO THIS IF BOARD WAS CALIBRATED AND SYNCHRONIZED BEFORE"))
+
+        self.start_button = self.createButton(text=tr("Button", "Start Board"), connect=lambda x: bif.bk_start_board(board_id=board_id))
+        self.calibrate_button = self.createButton(text=tr("Button", "Calibrate"), connect=lambda x: bif.bk_calibrate(board_id=board_id))
+        self.syncronize_button = self.createButton(text=tr("Button", "Synchronize"), connect=lambda x: bif.bk_sync_board(board_id=board_id))
+        self.set_default_button = self.createButton(text=tr("Button", "Set Defaults"), connect=lambda x: bif.bk_write_values(board_id=board_id, defaults=True))
+        self.soft_reset_button = self.createButton(text=tr("Button", "Soft Reset"), connect=lambda x: bif.bk_soft_reset(board_id=board_id))
+        self.off_button = self.createButton(text=tr("Button", "Board Off"), connect=lambda x: bif.bk_stop_board(board_id=board_id))
+        self.off_button.setObjectName("off")
+        self.check_status_button = self.createButton(text=tr("Button", "Check Status"), connect=lambda x: bif._bif_status_readout(board_id=board_id))
+
+        Buttons.addButton('start_board_{}'.format(board_id), self.start_button)
+        Buttons.addButton(['calibrate_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.calibrate_button)
+        Buttons.addButton(['synchronize_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.syncronize_button)
+        Buttons.addButton(['set_defaults_{}'.format(board_id), 'after_start_{}'.format(board_id)], self.set_default_button)
+        Elements.addItem('no_board_{}'.format(board_id),
+                         [
+                             self.start_button,
+                             self.calibrate_button,
+                             self.syncronize_button,
+                             self.set_default_button,
+                             self.soft_reset_button,
+                             self.off_button,
+                             self.all_in_one_button
+                         ])
+
+        self.mainControlLayout.addWidget(self.skip_init_button)
+        self.mainControlLayout.addWidget(self.all_in_one_button)
+        self.mainControlLayout.addWidget(self.toggleButton)
+        self.mainControlLayout.addWidget(self.soft_reset_button)
+        self.mainControlLayout.addWidget(self.off_button)
+
+        self.statusLayout.addWidget(self.check_status_button)
+
+        self.subControlLayout.addWidget(self.start_button)
+        self.subControlLayout.addWidget(self.calibrate_button)
+        self.subControlLayout.addWidget(self.syncronize_button)
+        self.subControlLayout.addWidget(self.set_default_button)
+
+        # register the led updater function (used in backendinterface.bk_status_readout)
+        storage.get_board_specific_storage(board_id).update_LED = self.on_check
+        self.geo = self.saveGeometry()
+        self.subControlWidget.hide()
+
+    def toggle_sub_control(self):
+        """
+        Show or hide the subcontrol buttons
+        """
+        self.subControlWidget.setHidden(not self.subControlWidget.isHidden())
+        if self.subControlWidget.isHidden():
+            self.toggleButton.setIcon(QtGui.QIcon(config.install_path+"icons/chevron-bottom.svg"))
+        else:
+            self.toggleButton.setIcon(QtGui.QIcon(config.install_path+"icons/chevron-top.svg"))
+        self.parent().adjustSize()
+
+    def all_in_one(self):
+        """
+        Function that gets called when the Prepare Board Button is pressed.
+        It Starts the board, syncs it and sets defaults.
+        This is accomplished via the backendinterface module
+        :return: -
+        """
+        if bif.bk_start_board(self.board_id) is not False:
+
+            def do_the_rest(board_id):
+                bif.bk_sync_board(board_id)
+                bif.bk_write_values(board_id, defaults=True)
+            bif.bk_calibrate(self.board_id, do_the_rest)
+
+    def skip_init(self):
+        """
+        Skip board initialisation progress and read data from board. This will adjust the values in the gui to those
+        on the board (for every value that is stored on the board)
+        """
+        board.get_board_config(self.board_id).read_from_board()
+        board.get_board_status(self.board_id).calibrated = True
+        board.get_board_status(self.board_id).synced = True
+        board.get_board_status(self.board_id).defaults_set = True
+        bif.bk_status_readout()
+        logging.info("Initialisation skipped, configuration and status was read from KAPTURE.")
+
+    def on_check(self):
+        """
+        This function is the handler for the status leds on the ControlWidget View.
+        Parses the registers and sets the colors of the leds according.
+        :return: -
+        """
+        try:
+            status = bif.bk_get_status(self.board_id)
+        except IndexError:
+            error(0x002, "Pci returned not enough registers to update LEDs.")
+            return
+
+        for led, st in status.items():
+            getattr(self, led.lower()+"_led").set_status(st)
+
+
+class ControlWidget(kcgw.KCGWidgets):
+    """
+    Main Widget that is shown at start of gui.
+    """
+    def __init__(self):
+        super(ControlWidget, self).__init__()
+
+        self.overlayout = QtGui.QVBoxLayout()
+        self.setLayout(self.overlayout)
+
+        self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
+        self.overlayout.addWidget(self.splitter)
+
+
+        self.board_control_list = {i: BoardControl(i, not available_boards.multi_board) for i in available_boards}
+        self.board_widget = QtGui.QWidget()
+        self.board_widget_layout = QtGui.QVBoxLayout()
+        self.board_widget.setLayout(self.board_widget_layout)
+        for bc in self.board_control_list.values():
+            self.board_widget_layout.addWidget(bc)
+        self.board_widget_layout.addStretch(1)
+
+        if available_boards.multi_board:
+            self.scroll_widget = QtGui.QScrollArea()
+            # self.scroll_widget.setMaximumWidth(1.1 * self.board_control_list.values()[0].minimumSizeHint().width())
+            self.scroll_widget.setWidgetResizable(True)
+            self.scroll_widget.setWidget(self.board_widget)
+            self.scroll_widget_container = QtGui.QWidget()
+            self.scroll_widget_layout = QtGui.QHBoxLayout()
+            self.scroll_widget_layout.addWidget(self.scroll_widget)
+            self.scroll_widget_layout.setAlignment(QtCore.Qt.AlignLeft)
+            self.scroll_widget_container.setLayout(self.scroll_widget_layout)
+            self.splitter.addWidget(self.scroll_widget_container)
+        else:
+            self.splitter.addWidget(self.board_widget)
+
+        # self.log_area = QtGui.QTextEdit()
+        self.log_area = LogArea()
+        self.log_area.setReadOnly(True)
+        self.splitter.addWidget(self.log_area)
+        self.log_area.init_logging()
+        self.log_area.setKeywords([
+            "Activating Board",
+            "Started Board Calibration",
+            "Synchronize PLLs",
+            "Setting default Values",
+            "Updating Values"
+        ])
+
+        # self.setTabOrder(self.soft_reset_button, self.check_status_button)
+        # self.setTabOrder(self.check_status_button, self.off_button)
+
+        # ------------------[ Logging ]----------------------
+        # log_handler = LogHandler(self.log_area)
+        # self.logger = logging.getLogger()
+        # self.logger.addHandler(log_handler)
+        # self.logger.setLevel(config.log_level)
+        # logging.logger = self.logger
         # logging.logger.addHandler(log_handler)

+ 24 - 24
KCG/base/globals.py

@@ -1,24 +1,24 @@
-class Globals(object):
-    """
-    Object to make it easy to work with global values
-    """
-    def __init__(self):
-        self._globals = dict()
-
-    def get_global(self, item):
-        """
-        Get a global value
-        :param item: the item to get
-        :return: the value of the global value of item
-        """
-        return self._globals.get(item, None)
-
-    def set_global(self, key, value):
-        """
-        Set a global value
-        :param key: the key to set
-        :param value: the value to set it to
-        """
-        self._globals[key] = value
-
-glob = Globals()
+class Globals(object):
+    """
+    Object to make it easy to work with global values
+    """
+    def __init__(self):
+        self._globals = dict()
+
+    def get_global(self, item):
+        """
+        Get a global value
+        :param item: the item to get
+        :return: the value of the global value of item
+        """
+        return self._globals.get(item, None)
+
+    def set_global(self, key, value):
+        """
+        Set a global value
+        :param key: the key to set
+        :param value: the value to set it to
+        """
+        self._globals[key] = value
+
+glob = Globals()

+ 266 - 266
KCG/base/groupedelements.py

@@ -1,266 +1,266 @@
-"""
-Module to easily group elements of a gui
-"""
-from PyQt4 import QtGui
-import warnings
-from itertools import chain
-
-
-class GroupWarning(Warning):
-    """
-    General Warning Class for GroupedObjects
-    """
-    pass
-
-warnings.simplefilter('always', GroupWarning)
-
-
-class GroupedObjects:
-    """
-    This class enables grouping of objects to easily access them as groups throughout the gui.
-    """
-    def __init__(self):
-        """
-        Initialise this object
-        :return:
-        """
-        self._objects = {}
-        self._status = {}
-        self.warn = False
-        self.autoremove = False
-        self.exception_on_deleted = False
-        self.notify_deletion = False
-
-    def setFlags(self, flagDict):
-        """
-        Set Flags that define the behaviour of GroupedObjects in various events.
-        :param flagDict: Dictionary containing the Flags.
-                        Possible Flags are: (They are to be of type bool)
-
-                        * warn: If an element is deleted and the corresponding entry is encountered by setEnabled, a
-                            Warning is raised if warn is True
-                        * autoremove: If an element is deleted and the corresponding entry is encountered by setEnabled,
-                            the Element will be removed from GroupedObjects if autoremove is True
-                        * exception_on_deleted: If an element is deleted and the corresponding entry is encountered by
-                            setEnabled an Exception will be raised if exception_on_deleted is True
-                        * notify_deletion: If this is set to True a notification will be printed to STDOUT whenever an
-                            autoremove is performed (see above)
-        :return: -
-        """
-        self.warn = flagDict.get('warn', False)
-        self.autoremove = flagDict.get('autoremove', False)
-        self.exception_on_deleted = flagDict.get('exception_on_deleted', False)
-        self.notify_deletion = flagDict.get('notify_deletion', False)
-
-    def createEmptyGroup(self, group):
-        """
-        Create an empty group
-        :param group: (str) the name of the group
-        :return: -
-        """
-        if group in self._objects:
-            raise GroupWarning("Specified Group \""+group+"\" already in list")
-        else:
-            self._objects[group] = []
-
-    def addItem(self, group, item):
-        """
-        Add a item or items to a group and thus register with the GroupedObjects object
-        :param group: (list or str) List of groups or single group where the item is to be added
-        :param item: (single item or list) What item/s
-        :return: -
-        """
-        group = group if isinstance(group, list) else [group]
-        item = item if isinstance(item, list) else [item]
-        exclude = list(chain(*[self.getElements(i) for i in group]))
-        for gr in group:
-            if not (gr in self._status):
-                self._status[gr] = True
-            if gr in self._objects and isinstance(self._objects[gr], list):
-                self._objects[gr].extend(item)
-            else:
-                self._objects[gr] = item
-            if not self._status[gr]: # for the case when the status is set before the object is registered or the object is recreated
-                self.setEnabled(gr, False, exclude=exclude)
-
-    def setChecked(self, group, state):
-        """
-        Set the state of all the checkboxes in the group
-        :param group: What group
-        :param state: True for checked, False for unchecked
-        :return: -
-        """
-        if group in self._objects:
-            self._status[group] = state
-            for obj in self._objects[group]:
-                if isinstance(obj, QtGui.QCheckBox):
-                    obj.setChecked(state)
-        else:
-            warnings.warn("Specified Group \""+group+"\" not in list", GroupWarning, stacklevel=2)
-
-    def setEnabled(self, group, state, exclude=None):
-        """
-        Set the state of all the items in the group
-        :param group: What group
-        :param state: True for enabled, False for disabled
-        :param exclude: Exclude this item
-        :return: -
-        """
-        to_remove = []
-        if group in self._objects:
-            self._status[group] = state
-            if self._objects[group] == []:  # untested if explicit test for empty list is necessary
-                return
-            for obj in self._objects[group]:
-                try:
-                    if exclude and obj in exclude:
-                        continue
-                    if isinstance(obj, QtGui.QAction):
-                        obj.setEnabled(state)
-                    elif isinstance(obj, QtGui.QMenu):
-                        obj.menuAction().setVisible(state)
-                    else:
-                        obj.setEnabled(state)
-                except RuntimeError as e:
-                    if "deleted" in str(e):
-                        # self.removeItem(group, obj)
-                        if self.autoremove:
-                            to_remove.append([group, obj])
-                        if self.warn:
-                            warnings.warn(str(e), GroupWarning, stacklevel=3)
-                        if self.exception_on_deleted:
-                            raise e
-                    else:
-                        raise e
-            if to_remove and self.autoremove:
-                for rm in to_remove:
-                    if self.notify_deletion:
-                        print("Autoremoving element from group '" + rm[0] + "'")
-                    self.removeItem(*rm)
-        else:
-            self._status[group] = state
-            warnings.warn("Specified Group \""+group+"\" not in Elements", GroupWarning, stacklevel=2)
-
-    def addMenuItem(self, group, item):
-        """
-        Deprecated. Use addItem.
-        """
-        self.addItem(group, item)
-
-    def addButton(self, group, item):
-        """
-        Deprecated. Use addItem.
-        """
-        self.addItem(group, item)
-
-    def addCheckbox(self, group, item):
-        """
-        Deprecated. Use addItem.
-        """
-        self.addItem(group, item)
-
-    def removeItem(self, group, item):
-        """
-        Remove an element from a gropu
-        :param group: (list or str) list of groups or groupname
-        :param item:  (list or item) list of items or item to remove
-        :return: -
-        """
-        groups = group if isinstance(group, list) else [group]
-        items = item if isinstance(item, list) else [item]
-        for gr in groups:
-            if not gr in self._objects:
-                # return
-                continue
-            for it in items:
-                if it in self._objects[gr]:
-                    del self._objects[gr][self._objects[gr].index(it)]
-
-    def removeGroup(self, group):
-        """
-        Remove a group from GroupedObjects. If the group is not registered, a warning will be raised.
-        :param group: (str) the group to remove
-        :return:
-        """
-        if group in self._objects:
-            del self._objects[group]
-        else:
-            warnings.warn("Group was not registered - Nothing removed", GroupWarning, stacklevel=2)
-
-    def emptyGroup(self, group):
-        """
-        Unregister all elements from group. (This will delete the group and recreate it.
-        :param group: (str) the group to clean out.
-        :return: -
-        """
-        self.removeGroup(group)
-        self.createEmptyGroup(group)
-
-    def getElements(self, group):
-        """
-        Get the elements of a group as list.
-        :param group: (str) the gorup you want the elements of
-        :return: (list) Elements in group
-        """
-        if group in self._objects:
-            return self._objects[group]
-        else:
-            return []
-
-    def isEnabled(self, group):
-        """
-        Check if a group is enabled.
-        :param group: (str) the gorup to check.
-        :return: (bool) State of group
-        """
-        if group in self._status:
-            return self._status[group]
-        else:
-            return True  # Default for elements is enabled
-
-class LivePlotWindows():
-    """
-    Container class to hold open LivePlotWindows.
-    Added LivePlotWindows will automatically be plotted to on the event of new data.
-    """
-    def __init__(self):
-        self.plotWindows = {}
-
-    def addWindow(self, board_id, window):
-        """
-        Register a Window.
-        :param window: (PlotWidget) The window to be added.
-        :return: -
-        """
-        if board_id in self.plotWindows:
-            self.plotWindows[board_id].append(window)
-        else:
-            self.plotWindows[board_id] = [window, ]
-    def getWindows(self, board_id):
-        """
-        Get the list of registered plot windows.
-        :return: (list) List of plotWindows
-        """
-        return self.plotWindows[board_id]
-    def hasWindows(self, board_id):
-        """
-        Check if Windows are registered.
-        :return: (bool) True if there are windows and False if not.
-        """
-        if board_id in self.plotWindows and len(self.plotWindows[board_id]) > 0:
-            return True
-        else:
-            return False
-    def removeWindow(self, board_id, window):
-        """
-        Remove a window from open plot windows
-        :param window: the window to remove
-        :return:
-        """
-        del self.plotWindows[board_id][self.plotWindows[board_id].index(window)]
-
-# Every Element is acessible through every Variable set here. Checkboxes, Buttons and MenuItems variables are set
-# to improve readability of the code. They refer to the same Object
-
-Elements = Checkboxes = Buttons = MenuItems = GroupedObjects()
-live_plot_windows = LivePlotWindows()
+"""
+Module to easily group elements of a gui
+"""
+from PyQt4 import QtGui
+import warnings
+from itertools import chain
+
+
+class GroupWarning(Warning):
+    """
+    General Warning Class for GroupedObjects
+    """
+    pass
+
+warnings.simplefilter('always', GroupWarning)
+
+
+class GroupedObjects:
+    """
+    This class enables grouping of objects to easily access them as groups throughout the gui.
+    """
+    def __init__(self):
+        """
+        Initialise this object
+        :return:
+        """
+        self._objects = {}
+        self._status = {}
+        self.warn = False
+        self.autoremove = False
+        self.exception_on_deleted = False
+        self.notify_deletion = False
+
+    def setFlags(self, flagDict):
+        """
+        Set Flags that define the behaviour of GroupedObjects in various events.
+        :param flagDict: Dictionary containing the Flags.
+                        Possible Flags are: (They are to be of type bool)
+
+                        * warn: If an element is deleted and the corresponding entry is encountered by setEnabled, a
+                            Warning is raised if warn is True
+                        * autoremove: If an element is deleted and the corresponding entry is encountered by setEnabled,
+                            the Element will be removed from GroupedObjects if autoremove is True
+                        * exception_on_deleted: If an element is deleted and the corresponding entry is encountered by
+                            setEnabled an Exception will be raised if exception_on_deleted is True
+                        * notify_deletion: If this is set to True a notification will be printed to STDOUT whenever an
+                            autoremove is performed (see above)
+        :return: -
+        """
+        self.warn = flagDict.get('warn', False)
+        self.autoremove = flagDict.get('autoremove', False)
+        self.exception_on_deleted = flagDict.get('exception_on_deleted', False)
+        self.notify_deletion = flagDict.get('notify_deletion', False)
+
+    def createEmptyGroup(self, group):
+        """
+        Create an empty group
+        :param group: (str) the name of the group
+        :return: -
+        """
+        if group in self._objects:
+            raise GroupWarning("Specified Group \""+group+"\" already in list")
+        else:
+            self._objects[group] = []
+
+    def addItem(self, group, item):
+        """
+        Add a item or items to a group and thus register with the GroupedObjects object
+        :param group: (list or str) List of groups or single group where the item is to be added
+        :param item: (single item or list) What item/s
+        :return: -
+        """
+        group = group if isinstance(group, list) else [group]
+        item = item if isinstance(item, list) else [item]
+        exclude = list(chain(*[self.getElements(i) for i in group]))
+        for gr in group:
+            if not (gr in self._status):
+                self._status[gr] = True
+            if gr in self._objects and isinstance(self._objects[gr], list):
+                self._objects[gr].extend(item)
+            else:
+                self._objects[gr] = item
+            if not self._status[gr]: # for the case when the status is set before the object is registered or the object is recreated
+                self.setEnabled(gr, False, exclude=exclude)
+
+    def setChecked(self, group, state):
+        """
+        Set the state of all the checkboxes in the group
+        :param group: What group
+        :param state: True for checked, False for unchecked
+        :return: -
+        """
+        if group in self._objects:
+            self._status[group] = state
+            for obj in self._objects[group]:
+                if isinstance(obj, QtGui.QCheckBox):
+                    obj.setChecked(state)
+        else:
+            warnings.warn("Specified Group \""+group+"\" not in list", GroupWarning, stacklevel=2)
+
+    def setEnabled(self, group, state, exclude=None):
+        """
+        Set the state of all the items in the group
+        :param group: What group
+        :param state: True for enabled, False for disabled
+        :param exclude: Exclude this item
+        :return: -
+        """
+        to_remove = []
+        if group in self._objects:
+            self._status[group] = state
+            if self._objects[group] == []:  # untested if explicit test for empty list is necessary
+                return
+            for obj in self._objects[group]:
+                try:
+                    if exclude and obj in exclude:
+                        continue
+                    if isinstance(obj, QtGui.QAction):
+                        obj.setEnabled(state)
+                    elif isinstance(obj, QtGui.QMenu):
+                        obj.menuAction().setVisible(state)
+                    else:
+                        obj.setEnabled(state)
+                except RuntimeError, e:
+                    if "deleted" in str(e):
+                        # self.removeItem(group, obj)
+                        if self.autoremove:
+                            to_remove.append([group, obj])
+                        if self.warn:
+                            warnings.warn(str(e), GroupWarning, stacklevel=3)
+                        if self.exception_on_deleted:
+                            raise e
+                    else:
+                        raise e
+            if to_remove and self.autoremove:
+                for rm in to_remove:
+                    if self.notify_deletion:
+                        print ("Autoremoving element from group '" + rm[0] + "'")
+                    self.removeItem(*rm)
+        else:
+            self._status[group] = state
+            warnings.warn("Specified Group \""+group+"\" not in Elements", GroupWarning, stacklevel=2)
+
+    def addMenuItem(self, group, item):
+        """
+        Deprecated. Use addItem.
+        """
+        self.addItem(group, item)
+
+    def addButton(self, group, item):
+        """
+        Deprecated. Use addItem.
+        """
+        self.addItem(group, item)
+
+    def addCheckbox(self, group, item):
+        """
+        Deprecated. Use addItem.
+        """
+        self.addItem(group, item)
+
+    def removeItem(self, group, item):
+        """
+        Remove an element from a gropu
+        :param group: (list or str) list of groups or groupname
+        :param item:  (list or item) list of items or item to remove
+        :return: -
+        """
+        groups = group if isinstance(group, list) else [group]
+        items = item if isinstance(item, list) else [item]
+        for gr in groups:
+            if not gr in self._objects:
+                # return
+                continue
+            for it in items:
+                if it in self._objects[gr]:
+                    del self._objects[gr][self._objects[gr].index(it)]
+
+    def removeGroup(self, group):
+        """
+        Remove a group from GroupedObjects. If the group is not registered, a warning will be raised.
+        :param group: (str) the group to remove
+        :return:
+        """
+        if group in self._objects:
+            del self._objects[group]
+        else:
+            warnings.warn("Group was not registered - Nothing removed", GroupWarning, stacklevel=2)
+
+    def emptyGroup(self, group):
+        """
+        Unregister all elements from group. (This will delete the group and recreate it.
+        :param group: (str) the group to clean out.
+        :return: -
+        """
+        self.removeGroup(group)
+        self.createEmptyGroup(group)
+
+    def getElements(self, group):
+        """
+        Get the elements of a group as list.
+        :param group: (str) the gorup you want the elements of
+        :return: (list) Elements in group
+        """
+        if group in self._objects:
+            return self._objects[group]
+        else:
+            return []
+
+    def isEnabled(self, group):
+        """
+        Check if a group is enabled.
+        :param group: (str) the gorup to check.
+        :return: (bool) State of group
+        """
+        if group in self._status:
+            return self._status[group]
+        else:
+            return True  # Default for elements is enabled
+
+class LivePlotWindows():
+    """
+    Container class to hold open LivePlotWindows.
+    Added LivePlotWindows will automatically be plotted to on the event of new data.
+    """
+    def __init__(self):
+        self.plotWindows = {}
+
+    def addWindow(self, board_id, window):
+        """
+        Register a Window.
+        :param window: (PlotWidget) The window to be added.
+        :return: -
+        """
+        if board_id in self.plotWindows:
+            self.plotWindows[board_id].append(window)
+        else:
+            self.plotWindows[board_id] = [window, ]
+    def getWindows(self, board_id):
+        """
+        Get the list of registered plot windows.
+        :return: (list) List of plotWindows
+        """
+        return self.plotWindows[board_id]
+    def hasWindows(self, board_id):
+        """
+        Check if Windows are registered.
+        :return: (bool) True if there are windows and False if not.
+        """
+        if board_id in self.plotWindows and len(self.plotWindows[board_id]) > 0:
+            return True
+        else:
+            return False
+    def removeWindow(self, board_id, window):
+        """
+        Remove a window from open plot windows
+        :param window: the window to remove
+        :return:
+        """
+        del self.plotWindows[board_id][self.plotWindows[board_id].index(window)]
+
+# Every Element is acessible through every Variable set here. Checkboxes, Buttons and MenuItems variables are set
+# to improve readability of the code. They refer to the same Object
+
+Elements = Checkboxes = Buttons = MenuItems = GroupedObjects()
+live_plot_windows = LivePlotWindows()

+ 564 - 584
KCG/base/kcg.py

@@ -1,584 +1,564 @@
-from PyQt4 import QtGui, QtCore
-
-import os
-import logging
-
-# --------[ Backend ]---------
-from . import backendinterface as bif
-# --------[ Essentials ]---------
-from . import storage
-from .settings import Settings
-# --------[ Necessary Widgets ]------
-from . import kcgwidget as kcgw
-from .controlwidget import ControlWidget
-from .multiWidget import MultiWidget
-from .groupedelements import MenuItems, Elements
-from .backend.board import available_boards
-from .backend import board
-from .multipage import MultiPage
-from .globals import glob as global_objects
-from . import bitsTable as bt
-from . import log
-from ..widgets import initialconfig
-# ---------[ Widgets IMPORTANT!!! ]------------------
-# this enables widgets. If this is not imported (even though it is not directly used) no widgets will be available
-from ..widgets import *
-# from widgets import *  # copy in case the above line gets removed by ide
-# ---------[ IMPORTANT ]---------------------
-
-tr = kcgw.tr
-
-from .. import config
-import time
-import getpass
-
-
-def readconfig(parent):
-    """
-    Reads the config and evalues certain variables
-    Also: Validates config to check if all necessary values are there
-    :param parent: parent for popup windows
-    :return: -
-    """
-    nec_conf = ['acquireSettingsIcon', 'bunches_per_turn', 'default_log_entries', 'default_save_location', 'default_subdirectory_name',
-                 'force_ask', 'guiIcon', 'language', 'logCommentIcon',
-                 'logIcon', 'newPlotDataIcon', 'newPlotLiveIcon', 'save_header', 'show_advanced_control', 'singleReadIcon', 'startIcon',
-                 'stopIcon', 'style', 'tRev', 'timingIcon']
-    missing_conf = []
-    for c in nec_conf:
-        if c not in dir(config):
-            missing_conf.append(c)
-    if missing_conf:
-        class ConfigError(Exception):
-            pass
-        raise ConfigError('The Following variables are missing in config.py: "' + '", "'.join(missing_conf)+'"')
-
-    if config.language != "en_GB":
-        kcgw.translator.load(os.path.join(config.install_path,'lang',config.language))
-    else:
-        global tr
-        kcgw.tr = lambda _, x: x
-        tr = lambda _, x: x
-    dateG = "{d}.{m}.{y}"
-    dateGd = "{d}_{m}_{y}"
-    dateA = "{m}-{d}-{y}"
-    times = "{H}_{M}"
-    timel = "{H}_{M}_{S}"
-    session = ""
-    if "{ask}" in config.default_subdirectory_name:
-        status = False
-        while not status:
-            text, status = QtGui.QInputDialog.getText(parent, tr("Heading", "Subdirectory"),
-                                                      tr("Dialog", "Enter a name for the Subdirectory\n"
-                                                                            "in which data will be saved:\n"
-                                                                            "NOTE: You are being asked because it "
-                                                                            "was set this way in the config file."))
-            if not status and not config.force_ask:
-                config.default_subdirectory_name = "{user}_{dateGd}-{timel}"
-                break
-            else:
-                config.subdirectory_name = text.replace(" ", "_")
-                return
-    if "{sessionname}" in config.default_subdirectory_name:
-        status = False
-        while not status:
-            text, status = QtGui.QInputDialog.getText(parent, tr("Heading", "Sessionname"),
-                                                      tr("Dialog", "Enter Sessionname\n"
-                                                                           "NOTE: You are being asked because it "
-                                                                           "was set this way in the config file.:"))
-            if not status and not config.force_ask:
-                config.default_subdirectory_name = "{user}_{dateGd}-{timel}"
-                break
-            else:
-                session = text.replace(" ", "_")
-
-    config.default_subdirectory_name = config.default_subdirectory_name.format(
-        dateG=dateG, dateGd=dateGd, dateA=dateA, times=times, timel=timel,
-        d=time.strftime("%d"), m=time.strftime("%m"), y=time.strftime("%y"),
-        H=time.strftime("%H"), M=time.strftime("%M"), S=time.strftime("%S"),
-        timestamp=time.localtime(), user=getpass.getuser(), sessionname=session
-    )
-    config.subdirectory_name = config.default_subdirectory_name.format(
-        d=time.strftime("%d"), m=time.strftime("%m"), y=time.strftime("%y"),
-        H=time.strftime("%H"), M=time.strftime("%M"), S=time.strftime("%S"),
-        timestamp=time.localtime(), user=getpass.getuser()
-    )
-    if config.default_save_location == "pwd":
-        import os
-        config.save_location = os.getcwd()
-    else:
-        config.save_location = config.default_save_location
-    
-    storage.storage.offset_correction = config.fifty_ohm_timescan_datafile
-
-
-_MultiView_Name_ = "MultiView"
-
-class CentralWidget(kcgw.KCGWidgets):
-    """
-    Central Widget for the KCG gui main window
-    """
-    def __init__(self, parent=None):
-        super(CentralWidget, self).__init__(parent=parent)
-
-
-        # -------[ Create empty Groups to avoid warnings ]---------
-        MenuItems.createEmptyGroup('Setup/Control')
-        MenuItems.createEmptyGroup('Bits Table')
-        # -------[ END ]---------------
-
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.pagesWidget = MultiPage(self)
-        self.layout.addWidget(self.pagesWidget)
-        self.mainControlWidget = ControlWidget()
-        self.pagesWidget.addPage(self.mainControlWidget, "Setup/Control")
-        self.mainMultiWidget = MultiWidget()
-        self.pagesWidget.addPage(self.mainMultiWidget, "MultiView")
-        # self.tableWidget = bt.AdvancedBoardInterface(parent=self)
-        self.tableWidget = bt.AdvanceControlView()
-        self.tableWidget.hide()
-
-
-#  .d8888b. 888     8888888888
-# d88P  Y88b888     888  888
-# 888    888888     888  888
-# 888       888     888  888
-# 888  88888888     888  888
-# 888    888888     888  888
-# Y88b  d88PY88b. .d88P  888
-#  "Y8888P88 "Y88888P" 8888888
-
-
-class Gui(QtGui.QMainWindow):
-    """
-    Main Window of the KCG gui
-    """
-    def __init__(self):
-        super(Gui, self).__init__()
-
-        self.createEmptyGroups()
-        # -------[ Check for boards and create corresponding objects ]------
-        for board_id in available_boards:
-            board.create_new_board_config(board_id)
-            # board.get_board_config(board_id).observe(None, lambda x: bif.update_header(board_id), 'header') #  Set update_header as function to call when header config is changed
-        for board_id in available_boards:
-            bif.initStatus(board.get_board_status(board_id))  # fill status storage with correct variables
-
-        readconfig(self)
-
-        # ----------[ Set Variables and create objects ]-----------------
-        # self.storage = storage.Storage()
-        self.storage = storage.storage
-        # storage.storage = self.storage
-        self.settings = None  # (this holds the settings window) Only create Window when used
-        self.statusbar = self.statusBar()
-        # kcgw.statusbar = self.statusbar # set status bar to kcgw to easily access from other sources
-        global_objects.set_global('statusbar', self.statusbar)
-        self.pageIndicator = QtGui.QLabel()
-        self.statusbar.addPermanentWidget(self.pageIndicator)
-        self.cw = CentralWidget(self)
-        self.doMenu()
-        self.setCentralWidget(self.cw)
-
-        self.initUI()
-        self.finalizeInit()
-
-        self.after_start_status_handler()
-        self.setContentsMargins(0, -10, 0, 0)
-
-    def initUI(self):
-        """
-        Initialize ui
-        :return: -
-        """
-        self.setWindowTitle("KCG - Kapture Control Gui")
-        self.setWindowIcon(QtGui.QIcon(config.icon_path(config.guiIcon)))
-        # QtGui.QApplication.setStyle("Oxygen") # Make it look less blown up in Gnome for example
-
-    def createEmptyGroups(self):
-        """
-        This creates empty groups with the GroupedObjects class in groupedelements module.
-        This has to be done to avoid warnings when groups are enabled or disabled before creation.
-        :return: -
-        """
-        for board_id in available_boards:
-            Elements.createEmptyGroup("acquire_{}".format(board_id))
-            Elements.createEmptyGroup("timing_{}".format(board_id))
-            Elements.createEmptyGroup("no_board_{}".format(board_id))
-            Elements.createEmptyGroup("continuous_read_{}".format(board_id))
-
-    def finalizeInit(self):
-        """
-        Final things done at initialisation
-        :return: -
-        """
-        self.populate_storage()
-
-        with open(config.style_path("style.css")) as f:
-            styleSheet = f.read()
-        if config.style == 'blue':
-            with open(config.style_path('blue.css')) as f:
-                styleSheet += f.read()
-        self.setStyleSheet(styleSheet)
-
-
-
-
-        # evaluate config file regarding advanced_control
-        self.showAdvancedControl(config.show_advanced_control)
-        self.storage.advanced_control = config.show_advanced_control
-
-        if not os.path.isdir(os.path.join(storage.storage.save_location, storage.storage.subdirname)):
-            os.makedirs(     os.path.join(storage.storage.save_location, storage.storage.subdirname))
-        
-        self.measurementLogger = log.MeasurementLogger()
-        log.logger = self.measurementLogger
-
-        logStrings = []
-        functionAndParameter = []
-        for par in self.measurementLogger.predefined_parameters:  # get strings and functions in seperate lists
-            logStrings.append(par[0])
-            functionAndParameter.append(par[1])
-        for e in config.default_log_entries:  # for every entry:
-            if e in logStrings:
-                self.measurementLogger.register_parameter(e, functionAndParameter[logStrings.index(e)][0], functionAndParameter[logStrings.index(e)][1])
-        #if log.no_epics and log.epics_reachable:
-        #    logging.error("Epics installation not found. Logfiles will not contain information that is to be "
-        #                 "obtained via epics.")
-        #if not log.epics_reachable:
-        #    logging.error("Epics PVs could not be accessed. Check Internet connection and Epics PV provider. Logfiles will not contain"
-        #                "information that is to be obtained via epics.")
-
-        # activate Epics if defined
-        if config.use_epics:
-            from ..widgets import epics_widget
-            epics_widget.epicsConfig = epics_widget.EpicsConfig()
-
-
-    def doMenu(self):
-        """
-        Create and show the menu and it's entries
-        :return: -
-        """
-        self.menu = self.menuBar()
-
-        self.fileMenu = self.menu.addMenu("&"+tr("Button", "File"))
-        self.saveConfigAction = self.fileMenu.addAction(tr("Button", "Save Board Configuration"), self.saveConfig)
-        self.saveConfigAction = self.fileMenu.addAction(tr("Button", "Load Board Configuration"), self.loadConfig)
-        self.settingsAction = self.fileMenu.addAction(tr("Button", "Settings"), self.showSettings, "Ctrl+P")
-        self.configAction = self.fileMenu.addAction(tr("Button", "Rerun Configuration Wizard"), self.rerunConfig)
-        self.quitAction = self.fileMenu.addAction(QtGui.QIcon(config.icon_path("exit.png")), tr("Button", "Quit"), self.close, "Ctrl+Q")
-        self.menu.setCornerWidget(self.cw.pagesWidget.leftright)
-
-        # ----------[ Page specific Menu Entries ]-------------
-        self.multiMenu = self.menu.addMenu("&"+tr("Button", "Windows"))
-        MenuItems.addMenuItem(_MultiView_Name_, self.multiMenu)
-        self.plotAction = self.multiMenu.addAction(QtGui.QIcon(config.icon_path(config.newPlotLiveIcon)), tr("Button", "New Plot"), self.cw.mainMultiWidget.leftBar.add_plot)
-        self.addWindowMenuEntries()
-
-        if not available_boards.multi_board:
-            self.acquireMenu = self.menu.addMenu("&"+tr("Button", "Acquire"))
-            MenuItems.addMenuItem(_MultiView_Name_, self.acquireMenu)
-            self.startAcquisitionAction = self.acquireMenu.addAction(QtGui.QIcon(config.icon_path(config.startIcon)),
-                         tr("Button", "Start Acquisition"), lambda: bif.bk_acquire(available_boards[0]))
-            self.startAcquisitionAction.setObjectName("start_acquisition_action")
-
-            MenuItems.addMenuItem("continuous_read_{}".format(available_boards[0]), self.startAcquisitionAction)
-            MenuItems.addMenuItem("acquireTrigger_{}".format(available_boards[0]), self.startAcquisitionAction)
-
-        # -----[ disable Menu Items for MultiView as it is not the startup page ]-------------
-        # this could be avoided if menu is created before the multipage widget
-        MenuItems.setEnabled(_MultiView_Name_, False)
-
-        self.help = self.menu.addMenu("&"+tr("Button", "Help"))
-        import webbrowser
-        self.help.addAction(tr("Button", "Open Manual"), lambda: webbrowser.open(os.path.join(config.install_path, "Documentation","build","html","index.html")))
-        self.help.addAction(tr("Button", "About"), self.showAbout)
-
-    def _show_board_chooser(self):
-        selected_boards = []
-        if len(available_boards.board_ids) == 1:
-            return [available_boards.board_ids[0]]
-        chooser = QtGui.QDialog(self)
-        chooser.setWindowTitle("KCG - Choose Boards")
-
-        
-        chooser_layout = QtGui.QVBoxLayout()
-        chooser.setLayout(chooser_layout)
-        chooser_layout.addWidget(QtGui.QLabel("Choose Boards"))
-        boards = {}
-        for bid in available_boards.board_ids:
-            boards[bid] = QtGui.QCheckBox(str(bid), chooser)
-            chooser_layout.addWidget(boards[bid])
-        button = QtGui.QPushButton("OK", chooser)
-        def ok():
-            for bi, box in list(boards.items()):
-                if box.isChecked():
-                    selected_boards.append(bi)
-            chooser.close()
-        button.clicked.connect(ok)
-        chooser_layout.addWidget(button)
-        chooser.exec_()
-        return selected_boards
-
-
-    def saveConfig(self, board_id=None):
-        """
-        Save the current configuration to a configuration file
-        :param board_id: the board to save the configuration for
-        """
-
-        filenameDialog = QtGui.QFileDialog(self, tr("Heading", "Save Configuration"), '', 'KAPTURE Configuration File (*.kcf)')
-        filenameDialog.setDefaultSuffix("kcf")
-        filenameDialog.setAcceptMode(filenameDialog.AcceptSave)
-        filenameDialog.exec_()
-        filename = filenameDialog.selectedFiles()
-        if not filename:
-            return
-
-        if board_id is None:
-            board_id = self._show_board_chooser()
-        elif not isinstance(board_id, list):
-            board_id = [board_id]
-
-
-        fname = filename[0].split(".")
-        for bid in board_id:
-            if len(board_id) == 1:
-                fname_board = filename[0]
-            else:
-                fname_board = ".".join(map(str, fname[:-1]))+"_"+str(bid)+"."+fname[-1]
-            if not board.get_board_config(bid).save_config(fname_board):
-                QtGui.QMessageBox.critical(self, tr("Heading", "Error Saving Config"), tr("Dialog", "There was an error saving to a config file."))
-
-    def loadConfig(self, board_id=None):
-        """
-        Load the configuration for the given board from a file
-        :param board_id: the board to read the configuration for
-        """
-        filename = QtGui.QFileDialog.getOpenFileName(self, 'Open Configuration', '', 'KAPTURE Configuration File (*.kcf)')
-        if not filename:
-            return
-
-
-        if board_id is None:
-            board_id = self._show_board_chooser()
-        elif not isinstance(board_id, list):
-            board_id = [board_id]
-
-        for bid in board_id:
-            if board.get_board_config(bid).load_config(filename):
-                bif.bk_write_values(bid, defaults=False)
-            else:
-                QtGui.QMessageBox.critical(self, tr("Heading", "Error Loading Config"), tr("Dialog", "There was an error loading the config file, make sure it is valid and try again."))
-
-    def rerunConfig(self):
-        """
-        Rerun the initial configuration wizard
-        """
-        self.setupConfig = initialconfig.ConfigSetup(restart=True)
-        self.setupConfig.setWindowModality(QtCore.Qt.ApplicationModal)
-        def restart():
-            import subprocess
-            import sys
-            import os
-            try:
-                subprocess.Popen(['kcg'])
-            except OSError as exception:
-                try:
-                    path = config.install_path[:-4]+'kcg'
-                    subprocess.Popen([sys.executable, path])
-                except:
-                    print('ERROR: could not restart aplication:')
-                    print(('  %s' % str(exception)))
-                else:
-                    QtGui.qApp.quit()
-            else:
-                QtGui.qApp.quit()
-        self.setupConfig.success_signal.connect(restart)
-        self.setupConfig.show()
-
-    def showAbout(self):
-        """
-        Show the about window.
-        :return: -
-        """
-        version = open(os.path.join(config.install_path,"VERSION")).read()
-        about = QtGui.QDialog(self)
-        about.setWindowTitle("KCG - About")
-        about_label = QtGui.QLabel(tr("About", "KAPTURE Control Gui\n"
-                                      "KCG is a graphical control interface to the KAPTURE board\n\n"
-                                      "Author: Patrick Schreiber\n\n"
-                                      "Version:\n")+version)
-        about_label.setAlignment(QtCore.Qt.AlignCenter)
-        header_label = QtGui.QLabel(tr("About", "KCG"))
-        header_label.setStyleSheet("font-size: 25pt; text-align: center;")
-        header_label.setAlignment(QtCore.Qt.AlignCenter)
-        footer_label = QtGui.QLabel(tr("About", "\nKAPTURE - Karlsruhe Pulse-Taking and Ultrafast Readout Electronics"))
-        footer_label.setStyleSheet("font-size: 7pt;")
-        footer_label.setAlignment(QtCore.Qt.AlignRight)
-
-        about_layout = QtGui.QHBoxLayout()
-        about_text_layout = QtGui.QVBoxLayout()
-        about.setLayout(about_layout)
-
-        # pxm = QtGui.QPixmap(config.guiIcon)
-
-        # icon_layout = QtGui.QVBoxLayout()
-        # icon_label = QtGui.QLabel("")
-        # icon_label.setPixmap(pxm.scaled(QtCore.QSize(128, 128), QtCore.Qt.KeepAspectRatio))
-        # icon_label.setFixedSize(130, 130)
-        # icon_layout.addWidget(icon_label)
-        # icon_layout.addStretch(1)
-
-        # about_layout.addLayout(icon_layout)
-        about_layout.addLayout(about_text_layout)
-        about_text_layout.addWidget(header_label)
-        about_text_layout.addWidget(about_label)
-        about_text_layout.addWidget(footer_label)
-
-        about.setFixedSize(400, 230)
-        about.setStyleSheet("background-color: darkgrey;")
-        about.exec_()
-
-    def addWindowMenuEntries(self):
-        """
-        Adds Window Menu entries for custom widgets
-        :return: -
-        """
-        for f in kcgw.get_registered_widgets():
-            self.multiMenu.addAction(*f[:3]) # TODO: icon - ???
-
-    def showSettings(self):
-        """
-        Create and show settings window
-        :return: -
-        """
-        if self.settings: # use preopened window
-            self.settings.show()
-            self.settings.raise_()
-            self.settings.activateWindow()
-        else:
-            self.settings = Settings(self.storage)
-            self.settings.changed.connect(self.updateSettings)
-
-    def updateSettings(self, changedsettings):
-        """
-        Update settings in storage if settings were changed in the settings window.
-        :param changedsettings: list of settings that have been changed
-        :return: -
-        """
-        for setting in changedsettings:
-            if setting == 'language':
-                lang = getattr(self.storage, setting)
-                self.update_configuration_file({'language':'"'+str(lang)+'"'})
-                QtGui.QMessageBox.information(self, "Change Language", "Language change takes effect after Gui restart", 1)
-            if setting == 'advanced_control':
-                self.showAdvancedControl(getattr(self.storage, setting))
-#       if setting == 'offset_correction':
-#       config.fifty_ohm_timescan_datafile = getattr(self.storage, setting)
-            for bid in available_boards.board_ids:
-                try:
-                    if bif.bk_get_config(bid, setting) != None:
-                        bif.bk_update_config(bid, setting, getattr(self.storage, setting))
-                except board.NoSuchKeyError:
-                    pass
-
-    def showAdvancedControl(self, value):
-        """
-        Enable or disable advanced table control view (Tables for registers)
-        :param value: (bool) True to show and False to hide advanced view
-        :return: -
-        """
-        if value:
-            if self.cw.tableWidget.isHidden():
-                self.cw.pagesWidget.addPage(self.cw.tableWidget, 'Bits Table', set_to_first=False)
-                self.cw.tableWidget.show()
-        else:
-            if not self.cw.tableWidget.isHidden():
-                self.cw.pagesWidget.removePage(self.cw.tableWidget)
-                self.cw.tableWidget.hide()
-
-    def after_start_status_handler(self):
-        """
-        Method to check for boards and perform a status_readout after the gui is fully started
-        :return:
-        """
-        for bid in available_boards.board_ids:  # there is always at least a dummy board
-            bif.bk_check_for_board(bid)
-        bif.bk_status_readout()
-
-    def populate_storage(self):
-        """
-        Initially fills storage with predefined settings and configuration values
-        :return: -
-        """
-        self.storage.header = config.save_header
-        self.storage.subdirname = config.subdirectory_name
-        self.storage.save_location = config.save_location
-        self.storage.language = config.language
-        self.storage.advanced_control = False
-        def update_header(val):
-            '''Update header'''
-            self.storage.header = val
-            if self.settings:
-                self.settings.headerTick.setChecked(val)
-        board.get_board_config(available_boards[0]).observe(self.storage.header, update_header, 'header')  # TODO: header at one place for all boards? (here it uses the first board)
-
-    def update_configuration_file(self, new_conf):
-        """
-        Update variablevalues in config file
-        NOTE: this doesn't use standard ConfigParser as that would delete comments
-        :param new_conf: Dictionary with variable, value pair
-        :return:
-        """
-        import re
-        # filename = "config.py"
-        filename = os.path.join(os.path.expanduser("~"),".kcg","config.cfg")
-        RE = '(('+'|'.join(list(new_conf.keys()))+')\s*=)[^\r\n]*?(\r?\n|\r)'
-        pat = re.compile(RE)
-
-        def jojo(mat,dic = new_conf ):
-            return dic[mat.group(2)].join(mat.group(1,3))
-
-        with open(filename,'rb') as f:
-            content = f.read()
-
-        with open(filename,'wb') as f:
-            f.write(pat.sub(jojo,content))
-
-    def closeEvent(self, ev):
-        """
-        Handles closing of the GUI - this function is called by pyqt upon a close event.
-        Asks if user really wants to close the gui
-        :param ev: event
-        :return: -
-        """
-        extra = ""
-        for b in available_boards:
-            if board.get_board_status(b).wait_on_trigger:
-                extra += '\n'+tr('Dialog', 'Waiting on external trigger is still enabled.')
-            if board.get_board_status(b).continuous_read:
-                extra += '\n'+tr('Dialog', 'Continuous read is still enabled.')
-            if extra:
-                break
-        cl = None
-        if extra:
-            cl = QtGui.QMessageBox.critical(self, tr("Heading", "Close KCG"),
-                    tr("Dialog", "Close KCG?")+extra,
-                    QtGui.QMessageBox.No | QtGui.QMessageBox.Yes,
-                    QtGui.QMessageBox.No)
-        if not cl or cl == QtGui.QMessageBox.Yes:
-            cl = QtGui.QMessageBox.question(self, tr("Heading", "Close KCG"),
-                                            tr("Dialog", "Close KCG?\nYou will loose the state of open plots etc."),
-                                            QtGui.QMessageBox.No | QtGui.QMessageBox.Yes,
-                                            QtGui.QMessageBox.No)
-
-        if cl == QtGui.QMessageBox.Yes:
-            if self.settings:
-                self.settings.close()
-            ev.accept()
-        else:
-            ev.ignore()
-
+from PyQt4 import QtGui, QtCore
+
+import os
+import logging
+
+# --------[ Backend ]---------
+import backendinterface as bif
+# --------[ Essentials ]---------
+import storage
+from settings import Settings
+# --------[ Necessary Widgets ]------
+import kcgwidget as kcgw
+from controlwidget import ControlWidget
+from multiWidget import MultiWidget
+from groupedelements import MenuItems, Elements
+from backend.board import available_boards
+from backend import board
+from multipage import MultiPage
+from globals import glob as global_objects
+import bitsTable as bt
+import log
+from ..widgets import initialconfig
+# ---------[ Widgets IMPORTANT!!! ]------------------
+# this enables widgets. If this is not imported (even though it is not directly used) no widgets will be available
+from ..widgets import *
+# from widgets import *  # copy in case the above line gets removed by ide
+# ---------[ IMPORTANT ]---------------------
+
+tr = kcgw.tr
+
+from .. import config
+import time
+import getpass
+
+
+def readconfig(parent):
+    """
+    Reads the config and evalues certain variables
+    Also: Validates config to check if all necessary values are there
+    :param parent: parent for popup windows
+    :return: -
+    """
+
+    nec_conf = ['acquireSettingsIcon', 'bunches_per_turn', 'default_log_entries', 'default_save_location', 'default_subdirectory_name',
+                 'epics_base_path', 'epics_log_entry_pvs', 'epics_test_pv', 'force_ask', 'guiIcon', 'language', 'logCommentIcon',
+                 'logIcon', 'newPlotDataIcon', 'newPlotLiveIcon', 'save_header', 'show_advanced_control', 'singleReadIcon', 'startIcon',
+                 'stopIcon', 'style', 'tRev', 'timingIcon']
+    missing_conf = []
+    for c in nec_conf:
+        if c not in dir(config):
+            missing_conf.append(c)
+    if missing_conf:
+        class ConfigError(Exception):
+            pass
+        raise ConfigError('The Following variables are missing in config.py: "' + '", "'.join(missing_conf)+'"')
+
+    if config.language != "en_GB":
+        kcgw.translator.load(config.install_path+'lang/'+ config.language)
+    else:
+        global tr
+        kcgw.tr = lambda _, x: x
+        tr = lambda _, x: x
+    dateG = "{d}.{m}.{y}"
+    dateGd = "{d}_{m}_{y}"
+    dateA = "{m}-{d}-{y}"
+    times = "{H}_{M}"
+    timel = "{H}_{M}_{S}"
+    session = ""
+    if "{ask}" in config.default_subdirectory_name:
+        status = False
+        while not status:
+            text, status = QtGui.QInputDialog.getText(parent, tr("Heading", "Subdirectory"),
+                                                      tr("Dialog", "Enter a name for the Subdirectory\n"
+                                                                            "in which data will be saved:\n"
+                                                                            "NOTE: You are being asked because it "
+                                                                            "was set this way in the config file."))
+            if not status and not config.force_ask:
+                config.default_subdirectory_name = "{user}_{dateGd}-{timel}"
+                break
+            else:
+                config.subdirectory_name = text.replace(" ", "_")
+                return
+    if "{sessionname}" in config.default_subdirectory_name:
+        status = False
+        while not status:
+            text, status = QtGui.QInputDialog.getText(parent, tr("Heading", "Sessionname"),
+                                                      tr("Dialog", "Enter Sessionname\n"
+                                                                           "NOTE: You are being asked because it "
+                                                                           "was set this way in the config file.:"))
+            if not status and not config.force_ask:
+                config.default_subdirectory_name = "{user}_{dateGd}-{timel}"
+                break
+            else:
+                session = text.replace(" ", "_")
+
+    config.default_subdirectory_name = config.default_subdirectory_name.format(
+        dateG=dateG, dateGd=dateGd, dateA=dateA, times=times, timel=timel,
+        d=time.strftime("%d"), m=time.strftime("%m"), y=time.strftime("%y"),
+        H=time.strftime("%H"), M=time.strftime("%M"), S=time.strftime("%S"),
+        timestamp=time.localtime(), user=getpass.getuser(), sessionname=session
+    )
+    config.subdirectory_name = config.default_subdirectory_name.format(
+        d=time.strftime("%d"), m=time.strftime("%m"), y=time.strftime("%y"),
+        H=time.strftime("%H"), M=time.strftime("%M"), S=time.strftime("%S"),
+        timestamp=time.localtime(), user=getpass.getuser()
+    )
+    if config.default_save_location == "pwd":
+        import os
+        config.save_location = os.getcwd()
+    else:
+        config.save_location = config.default_save_location
+
+
+_MultiView_Name_ = "MultiView"
+
+class CentralWidget(kcgw.KCGWidgets):
+    """
+    Central Widget for the KCG gui main window
+    """
+    def __init__(self, parent=None):
+        super(CentralWidget, self).__init__(parent=parent)
+
+
+        # -------[ Create empty Groups to avoid warnings ]---------
+        MenuItems.createEmptyGroup('Setup/Control')
+        MenuItems.createEmptyGroup('Bits Table')
+        # -------[ END ]---------------
+
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.pagesWidget = MultiPage(self)
+        self.layout.addWidget(self.pagesWidget)
+        self.mainControlWidget = ControlWidget()
+        self.pagesWidget.addPage(self.mainControlWidget, "Setup/Control")
+        self.mainMultiWidget = MultiWidget()
+        self.pagesWidget.addPage(self.mainMultiWidget, "MultiView")
+        # self.tableWidget = bt.AdvancedBoardInterface(parent=self)
+        self.tableWidget = bt.AdvanceControlView()
+        self.tableWidget.hide()
+
+
+class Gui(QtGui.QMainWindow):
+    """
+    Main Window of the KCG gui
+    """
+    def __init__(self):
+        super(Gui, self).__init__()
+
+        self.createEmptyGroups()
+        # -------[ Check for boards and create corresponding objects ]------
+        for board_id in available_boards:
+            board.create_new_board_config(board_id)
+            # board.get_board_config(board_id).observe(None, lambda x: bif.update_header(board_id), 'header') #  Set update_header as function to call when header config is changed
+        for board_id in available_boards:
+            bif.initStatus(board.get_board_status(board_id))  # fill status storage with correct variables
+
+        readconfig(self)
+
+        # ----------[ Set Variables and create objects ]-----------------
+        # self.storage = storage.Storage()
+        self.storage = storage.storage
+        # storage.storage = self.storage
+        self.settings = None  # (this holds the settings window) Only create Window when used
+        self.statusbar = self.statusBar()
+        # kcgw.statusbar = self.statusbar # set status bar to kcgw to easily access from other sources
+        global_objects.set_global('statusbar', self.statusbar)
+        self.pageIndicator = QtGui.QLabel()
+        self.statusbar.addPermanentWidget(self.pageIndicator)
+        self.cw = CentralWidget(self)
+        self.doMenu()
+        self.setCentralWidget(self.cw)
+
+        self.initUI()
+        self.finalizeInit()
+
+        self.after_start_status_handler()
+        self.setContentsMargins(0, -10, 0, 0)
+
+    def initUI(self):
+        """
+        Initialize ui
+        :return: -
+        """
+        self.setWindowTitle("KCG - Kapture Control Gui")
+        self.setWindowIcon(QtGui.QIcon(config.install_path + config.guiIcon))
+        # QtGui.QApplication.setStyle("Oxygen") # Make it look less blown up in Gnome for example
+
+    def createEmptyGroups(self):
+        """
+        This creates empty groups with the GroupedObjects class in groupedelements module.
+        This has to be done to avoid warnings when groups are enabled or disabled before creation.
+        :return: -
+        """
+        for board_id in available_boards:
+            Elements.createEmptyGroup("acquire_{}".format(board_id))
+            Elements.createEmptyGroup("timing_{}".format(board_id))
+            Elements.createEmptyGroup("no_board_{}".format(board_id))
+            Elements.createEmptyGroup("continuous_read_{}".format(board_id))
+
+    def finalizeInit(self):
+        """
+        Final things done at initialisation
+        :return: -
+        """
+        self.populate_storage()
+
+        with open(config.install_path+"style/style.css") as f:
+            styleSheet = f.read()
+        if config.style == 'blue':
+            with open(config.install_path+'style/blue.css') as f:
+                styleSheet += f.read()
+        self.setStyleSheet(styleSheet)
+
+
+        # evaluate config file regarding advanced_control
+        self.showAdvancedControl(config.show_advanced_control)
+        self.storage.advanced_control = config.show_advanced_control
+
+        if not os.path.isdir(storage.storage.save_location + '/' + storage.storage.subdirname):
+            os.makedirs(storage.storage.save_location + '/' + storage.storage.subdirname)
+        self.measurementLogger = log.MeasurementLogger()
+        log.logger = self.measurementLogger
+        logStrings = []
+        functionAndParameter = []
+        for par in self.measurementLogger.predefined_parameters:  # get strings and functions in seperate lists
+            logStrings.append(par[0])
+            functionAndParameter.append(par[1])
+        for e in config.default_log_entries:  # for every entry:
+            if e in logStrings:
+                self.measurementLogger.register_parameter(e, functionAndParameter[logStrings.index(e)][0], functionAndParameter[logStrings.index(e)][1])
+        if not "disabled" in config.epics_base_path:
+            if log.no_epics and log.epics_reachable:
+                logging.error("Epics installation not found. Logfiles will not contain information that is to be "
+                             "obtained via epics.")
+            if not log.epics_reachable:
+                logging.error("Epics PVs could not be accessed. Check Internet connection and Epics PV provider. Logfiles will not contain"
+                            "information that is to be obtained via epics.")
+
+
+    def doMenu(self):
+        """
+        Create and show the menu and it's entries
+        :return: -
+        """
+        self.menu = self.menuBar()
+
+        self.fileMenu = self.menu.addMenu("&"+tr("Button", "File"))
+        self.saveConfigAction = self.fileMenu.addAction(tr("Button", "Save Board Configuration"), self.saveConfig)
+        self.saveConfigAction = self.fileMenu.addAction(tr("Button", "Load Board Configuration"), self.loadConfig)
+        self.settingsAction = self.fileMenu.addAction(tr("Button", "Settings"), self.showSettings, "Ctrl+P")
+        self.configAction = self.fileMenu.addAction(tr("Button", "Rerun Configuration Wizard"), self.rerunConfig)
+        self.quitAction = self.fileMenu.addAction(QtGui.QIcon(config.install_path + "icons/exit.png"), tr("Button", "Quit"), self.close, "Ctrl+Q")
+        self.menu.setCornerWidget(self.cw.pagesWidget.leftright)
+
+        # ----------[ Page specific Menu Entries ]-------------
+        self.multiMenu = self.menu.addMenu("&"+tr("Button", "Windows"))
+        MenuItems.addMenuItem(_MultiView_Name_, self.multiMenu)
+        self.plotAction = self.multiMenu.addAction(QtGui.QIcon(config.install_path + config.newPlotLiveIcon), tr("Button", "New Plot"), self.cw.mainMultiWidget.leftBar.add_plot)
+        self.addWindowMenuEntries()
+
+        if not available_boards.multi_board:
+            self.acquireMenu = self.menu.addMenu("&"+tr("Button", "Acquire"))
+            MenuItems.addMenuItem(_MultiView_Name_, self.acquireMenu)
+            self.startAcquisitionAction = self.acquireMenu.addAction(QtGui.QIcon(config.install_path + config.startIcon),
+                         tr("Button", "Start Acquisition"), lambda: bif.bk_acquire(available_boards[0]))
+            self.startAcquisitionAction.setObjectName("start_acquisition_action")
+
+            MenuItems.addMenuItem("continuous_read_{}".format(available_boards[0]), self.startAcquisitionAction)
+            MenuItems.addMenuItem("acquireTrigger_{}".format(available_boards[0]), self.startAcquisitionAction)
+
+        # -----[ disable Menu Items for MultiView as it is not the startup page ]-------------
+        # this could be avoided if menu is created before the multipage widget
+        MenuItems.setEnabled(_MultiView_Name_, False)
+
+        self.help = self.menu.addMenu("&"+tr("Button", "Help"))
+        import webbrowser
+        self.help.addAction(tr("Button", "Open Manual"), lambda: webbrowser.open(config.install_path + "Documentation/build/html/index.html"))
+        self.help.addAction(tr("Button", "About"), self.showAbout)
+
+    def _show_board_chooser(self):
+        selected_boards = []
+        if len(available_boards.board_ids) == 1:
+            return [available_boards.board_ids[0]]
+        chooser = QtGui.QDialog(self)
+        chooser.setWindowTitle("KCG - Choose Boards")
+
+        
+        chooser_layout = QtGui.QVBoxLayout()
+        chooser.setLayout(chooser_layout)
+        chooser_layout.addWidget(QtGui.QLabel("Choose Boards"))
+        boards = {}
+        for bid in available_boards.board_ids:
+            boards[bid] = QtGui.QCheckBox(str(bid), chooser)
+            chooser_layout.addWidget(boards[bid])
+        button = QtGui.QPushButton("OK", chooser)
+        def ok():
+            for bi, box in boards.items():
+                if box.isChecked():
+                    selected_boards.append(bi)
+            chooser.close()
+        button.clicked.connect(ok)
+        chooser_layout.addWidget(button)
+        chooser.exec_()
+        return selected_boards
+
+
+    def saveConfig(self, board_id=None):
+        """
+        Save the current configuration to a configuration file
+        :param board_id: the board to save the configuration for
+        """
+
+        filenameDialog = QtGui.QFileDialog(self, tr("Heading", "Save Configuration"), '', 'KAPTURE Configuration File (*.kcf)')
+        filenameDialog.setDefaultSuffix("kcf")
+        filenameDialog.setAcceptMode(filenameDialog.AcceptSave)
+        filenameDialog.exec_()
+        filename = filenameDialog.selectedFiles()
+        if not filename:
+            return
+
+        if board_id is None:
+            board_id = self._show_board_chooser()
+        elif not isinstance(board_id, list):
+            board_id = [board_id]
+
+
+        fname = filename[0].split(".")
+        for bid in board_id:
+            if len(board_id) == 1:
+                fname_board = filename[0]
+            else:
+                fname_board = ".".join(map(str, fname[:-1]))+"_"+str(bid)+"."+fname[-1]
+            if not board.get_board_config(bid).save_config(fname_board):
+                QtGui.QMessageBox.critical(self, tr("Heading", "Error Saving Config"), tr("Dialog", "There was an error saving to a config file."))
+
+    def loadConfig(self, board_id=None):
+        """
+        Load the configuration for the given board from a file
+        :param board_id: the board to read the configuration for
+        """
+        filename = QtGui.QFileDialog.getOpenFileName(self, 'Open Configuration', '', 'KAPTURE Configuration File (*.kcf)')
+        if not filename:
+            return
+
+
+        if board_id is None:
+            board_id = self._show_board_chooser()
+        elif not isinstance(board_id, list):
+            board_id = [board_id]
+
+        for bid in board_id:
+            if board.get_board_config(bid).load_config(filename):
+                bif.bk_write_values(bid, defaults=False)
+            else:
+                QtGui.QMessageBox.critical(self, tr("Heading", "Error Loading Config"), tr("Dialog", "There was an error loading the config file, make sure it is valid and try again."))
+
+    def rerunConfig(self):
+        """
+        Rerun the initial configuration wizard
+        """
+        self.setupConfig = initialconfig.ConfigSetup(restart=True)
+        self.setupConfig.setWindowModality(QtCore.Qt.ApplicationModal)
+        def restart():
+            import subprocess
+            import sys
+            import os
+            try:
+                subprocess.Popen(['kcg'])
+            except OSError as exception:
+                try:
+                    path = config.install_path[:-4]+'kcg'
+                    subprocess.Popen([sys.executable, path])
+                except:
+                    print('ERROR: could not restart aplication:')
+                    print('  %s' % str(exception))
+                else:
+                    QtGui.qApp.quit()
+            else:
+                QtGui.qApp.quit()
+        self.setupConfig.success_signal.connect(restart)
+        self.setupConfig.show()
+
+    def showAbout(self):
+        """
+        Show the about window.
+        :return: -
+        """
+        version = open(config.install_path+"VERSION").read()
+        about = QtGui.QDialog(self)
+        about.setWindowTitle("KCG - About")
+        about_label = QtGui.QLabel(tr("About", "KAPTURE Control Gui\n"
+                                      "KCG is a graphical control interface to the KAPTURE board\n\n"
+                                      "Author: Patrick Schreiber\n\n"
+                                      "Version:\n")+version)
+        about_label.setAlignment(QtCore.Qt.AlignCenter)
+        header_label = QtGui.QLabel(tr("About", "KCG"))
+        header_label.setStyleSheet("font-size: 25pt; text-align: center;")
+        header_label.setAlignment(QtCore.Qt.AlignCenter)
+        footer_label = QtGui.QLabel(tr("About", "\nKAPTURE - Karlsruhe Pulse-Taking and Ultrafast Readout Electronics"))
+        footer_label.setStyleSheet("font-size: 7pt;")
+        footer_label.setAlignment(QtCore.Qt.AlignRight)
+
+        about_layout = QtGui.QHBoxLayout()
+        about_text_layout = QtGui.QVBoxLayout()
+        about.setLayout(about_layout)
+
+        # pxm = QtGui.QPixmap(config.guiIcon)
+
+        # icon_layout = QtGui.QVBoxLayout()
+        # icon_label = QtGui.QLabel("")
+        # icon_label.setPixmap(pxm.scaled(QtCore.QSize(128, 128), QtCore.Qt.KeepAspectRatio))
+        # icon_label.setFixedSize(130, 130)
+        # icon_layout.addWidget(icon_label)
+        # icon_layout.addStretch(1)
+
+        # about_layout.addLayout(icon_layout)
+        about_layout.addLayout(about_text_layout)
+        about_text_layout.addWidget(header_label)
+        about_text_layout.addWidget(about_label)
+        about_text_layout.addWidget(footer_label)
+
+        about.setFixedSize(400, 230)
+        about.setStyleSheet("background-color: darkgrey;")
+        about.exec_()
+
+    def addWindowMenuEntries(self):
+        """
+        Adds Window Menu entries for custom widgets
+        :return: -
+        """
+        for f in kcgw.get_registered_widgets():
+            self.multiMenu.addAction(*f[:3]) # TODO: icon - ???
+
+    def showSettings(self):
+        """
+        Create and show settings window
+        :return: -
+        """
+        if self.settings: # use preopened window
+            self.settings.show()
+            self.settings.raise_()
+            self.settings.activateWindow()
+        else:
+            self.settings = Settings(self.storage)
+            self.settings.changed.connect(self.updateSettings)
+
+    def updateSettings(self, changedsettings):
+        """
+        Update settings in storage if settings were changed in the settings window.
+        :param changedsettings: list of settings that have been changed
+        :return: -
+        """
+        for setting in changedsettings:
+            if setting == 'language':
+                lang = getattr(self.storage, setting)
+                self.update_configuration_file({'language':'"'+str(lang)+'"'})
+                QtGui.QMessageBox.information(self, "Change Language", "Language change takes effect after Gui restart", 1)
+            if setting == 'advanced_control':
+                self.showAdvancedControl(getattr(self.storage, setting))
+
+            for bid in available_boards.board_ids:
+                try:
+                    if bif.bk_get_config(bid, setting) != None:
+                        bif.bk_update_config(bid, setting, getattr(self.storage, setting))
+                except board.NoSuchKeyError:
+                    pass
+
+    def showAdvancedControl(self, value):
+        """
+        Enable or disable advanced table control view (Tables for registers)
+        :param value: (bool) True to show and False to hide advanced view
+        :return: -
+        """
+        if value:
+            if self.cw.tableWidget.isHidden():
+                self.cw.pagesWidget.addPage(self.cw.tableWidget, 'Bits Table', set_to_first=False)
+                self.cw.tableWidget.show()
+        else:
+            if not self.cw.tableWidget.isHidden():
+                self.cw.pagesWidget.removePage(self.cw.tableWidget)
+                self.cw.tableWidget.hide()
+
+    def after_start_status_handler(self):
+        """
+        Method to check for boards and perform a status_readout after the gui is fully started
+        :return:
+        """
+        for bid in available_boards.board_ids:  # there is always at least a dummy board
+            bif.bk_check_for_board(bid)
+        bif.bk_status_readout()
+
+    def populate_storage(self):
+        """
+        Initially fills storage with predefined settings and configuration values
+        :return: -
+        """
+        self.storage.header = config.save_header
+        self.storage.subdirname = config.subdirectory_name
+        self.storage.save_location = config.save_location
+        self.storage.language = config.language
+        self.storage.advanced_control = False
+        def update_header(val):
+            '''Update header'''
+            self.storage.header = val
+            if self.settings:
+                self.settings.headerTick.setChecked(val)
+        board.get_board_config(available_boards[0]).observe(self.storage.header, update_header, 'header')  # TODO: header at one place for all boards? (here it uses the first board)
+
+    def update_configuration_file(self, new_conf):
+        """
+        Update variablevalues in config file
+        NOTE: this doesn't use standard ConfigParser as that would delete comments
+        :param new_conf: Dictionary with variable, value pair
+        :return:
+        """
+        import re
+        # filename = "config.py"
+        filename = os.path.expanduser("~")+"/.kcg/config.cfg"
+        RE = '(('+'|'.join(new_conf.keys())+')\s*=)[^\r\n]*?(\r?\n|\r)'
+        pat = re.compile(RE)
+
+        def jojo(mat,dic = new_conf ):
+            return dic[mat.group(2)].join(mat.group(1,3))
+
+        with open(filename,'rb') as f:
+            content = f.read()
+
+        with open(filename,'wb') as f:
+            f.write(pat.sub(jojo,content))
+
+    def closeEvent(self, ev):
+        """
+        Handles closing of the GUI - this function is called by pyqt upon a close event.
+        Asks if user really wants to close the gui
+        :param ev: event
+        :return: -
+        """
+        extra = ""
+        for b in available_boards:
+            if board.get_board_status(b).wait_on_trigger:
+                extra += '\n'+tr('Dialog', 'Waiting on external trigger is still enabled.')
+            if board.get_board_status(b).continuous_read:
+                extra += '\n'+tr('Dialog', 'Continuous read is still enabled.')
+            if extra:
+                break
+        cl = None
+        if extra:
+            cl = QtGui.QMessageBox.critical(self, tr("Heading", "Close KCG"),
+                    tr("Dialog", "Close KCG?")+extra,
+                    QtGui.QMessageBox.No | QtGui.QMessageBox.Yes,
+                    QtGui.QMessageBox.No)
+        if not cl or cl == QtGui.QMessageBox.Yes:
+            cl = QtGui.QMessageBox.question(self, tr("Heading", "Close KCG"),
+                                            tr("Dialog", "Close KCG?\nYou will loose the state of open plots etc."),
+                                            QtGui.QMessageBox.No | QtGui.QMessageBox.Yes,
+                                            QtGui.QMessageBox.No)
+
+        if cl == QtGui.QMessageBox.Yes:
+            if self.settings:
+                self.settings.close()
+            ev.accept()
+        else:
+            ev.ignore()
+

+ 720 - 738
KCG/base/kcgwidget.py

@@ -1,738 +1,720 @@
-"""
-Base Classes used in KCG.
-This module also contains various helpful Classes to make live easier ;)
-"""
-from PyQt4 import QtGui, QtCore, QtSvg
-import logging
-import sys
-
-from .. import config
-
-
-def tr(_, x):
-    """
-    Dummy Translation method that does not translate
-    :param _: unused
-    :param x: the string to "translate"
-    :return: x
-    """
-    return x
-
-
-
-
-class BigIconButton(QtGui.QPushButton):
-    """
-    This is a Button with a big Icon (that can fill the whole button)
-    """
-    def __init__(self, path, width, height, connect=None, tooltip=None, parent=None):
-        """
-        Setting various properties
-        :param path: (str) the path to the icon
-        :param width: (int) with of the button
-        :param height: (int) height of the button
-        :param connect: (callable) function to call when button is pressed
-        :param tooltip: (str) tool tip to show
-        :param parent: (QWidget) parent widget.
-        :return: -
-        """
-        super(BigIconButton, self).__init__(parent)
-        self.setIcon(QtGui.QIcon(path))
-        self.setFixedSize(width, height)
-        self.setIconSize(QtCore.QSize(width*0.7, height*0.7))
-        if connect:
-            self.clicked.connect(connect)
-        if tooltip:
-            self.setToolTip(tooltip)
-
-
-class clickLabel(QtGui.QLabel):
-    """
-    Clickable Label
-    """
-    clicked = QtCore.pyqtSignal()
-    def mouseReleaseEvent(self, QMouseEvent):
-        """
-        Reimplementation of QLabel.mouseReleaseEvent to make this clickable
-        """
-        self.clicked.emit()
-
-
-class ClickableHBoxLayout(QtGui.QHBoxLayout):
-    """
-    A clickable HBoxLayout
-    """
-    clicked = QtCore.pyqtSignal()
-    def event(self, QEvent):
-        """
-        Reimplementation of QHBoxLayout.event to make it clickable
-        """
-        self.clicked.emit()
-        if QEvent.type() == QtCore.QEvent.MouseButtonRelease:
-            self.clicked.emit()
-            return True
-        else:
-            super(ClickableHBoxLayout, self).event(QEvent)
-            return True
-
-
-class switchLabel(QtGui.QLabel):
-    """
-    This implements a Switch.
-    It switches between left and right.
-    """
-    clicked = QtCore.pyqtSignal()
-
-    def __init__(self, startRight=False): # self.state: True->Right False->Left
-        """
-        Initialise switchLabel
-        As a normal Button it emits the clicked event when clicked.
-        It does NOT have two events for each side at the moment.
-        :param startRight: (bool) whether the switch is initially set to the right position (default is left (False))
-        :return: -
-        """
-        super(switchLabel, self).__init__()
-        self.leftSwitch = QtGui.QPixmap(config.icon_path("SwitchButtonLeft.png"))
-        self.leftSwitch = self.leftSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
-        self.rightSwitch = QtGui.QPixmap(config.icon_path("SwitchButtonRight.png"))
-        self.rightSwitch = self.rightSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
-        if startRight:
-            self.setPixmap(self.rightSwitch)
-            self.state = True
-        else:
-            self.setPixmap(self.leftSwitch)
-            self.state = False
-
-    def mouseReleaseEvent(self, QMouseEvent):
-        """
-        Reimplementation of QLabel.mouseReleaseEvent to make it clickable
-        """
-        if self.state:
-            self.state = False
-            self.setPixmap(self.leftSwitch)
-        else:
-            self.state = True
-            self.setPixmap(self.rightSwitch)
-        self.clicked.emit()
-
-
-class Switch(QtGui.QWidget):
-    """
-    A Simple Switch
-    """
-    clicked = QtCore.pyqtSignal()
-
-    def __init__(self, leftLabel, rightLabel, startRight=False):
-        super(Switch, self).__init__()
-        self.layout = QtGui.QHBoxLayout()
-        self.switch = switchLabel(startRight)
-        self.switch.clicked.connect(self.clicked.emit)
-        self.setLayout(self.layout)
-        self.layout.addStretch(1)
-        self.layout.addWidget(QtGui.QLabel(leftLabel))
-        self.layout.addWidget(self.switch)
-        self.layout.addWidget(QtGui.QLabel(rightLabel))
-        self.layout.addStretch(1)
-
-    def state(self):
-        """
-        Get the state of this switch
-        :return: the state of this switch
-        """
-        return self.switch.state
-
-
-class ClickableSVG(QtGui.QWidget):
-    """
-    This implements a clickable SVG Image
-    """
-    clicked = QtCore.pyqtSignal()
-
-    def __init__(self, path, width, height, wwidth=None, wheight=None, parent=None):
-        """
-        Initialisation of ClickabeSVG
-        :param path: (str) path to the svg file
-        :param width: (int) width of the svg
-        :param height: (int) height of the svg
-        :param wwidth: (int) width of the widget (not the svg)
-        :param wheight: (int) height of the widget (not the svg)
-        :param parent: (QWidget) parent widget of the ClickableSVG
-        :return: -
-        """
-        super(ClickableSVG, self).__init__(parent)
-        self.svg = QtSvg.QSvgWidget(path)
-        self.svg.setFixedSize(width, height)
-        layout = QtGui.QHBoxLayout()
-        layout.addWidget(self.svg)
-        self.setLayout(layout)
-        if wwidth:
-            self.setFixedWidth(wwidth)
-        if wheight:
-            self.setFixedHeight(wheight)
-
-    def mouseReleaseEvent(self, QMouseEvent):
-        """
-        Reimplementation of QWidget.mouseReleaseEvent to make it clickable
-        """
-        self.clicked.emit()
-
-    def changeSvg(self, path):
-        """
-        Change the SVG of this widget
-        :param path: (str) path to the new svg file
-        :return: -
-        """
-        self.svg.load(path)
-
-
-# 888    d8P  .d8888b.  .d8888b.    888       888d8b     888                888
-# 888   d8P  d88P  Y88bd88P  Y88b   888   o   888Y8P     888                888
-# 888  d8P   888    888888    888   888  d8b  888        888                888
-# 888d88K    888       888          888 d888b 888888 .d88888 .d88b.  .d88b. 888888.d8888b
-# 8888888b   888       888  88888   888d88888b888888d88" 888d88P"88bd8P  Y8b888   88K
-# 888  Y88b  888    888888    888   88888P Y88888888888  888888  88888888888888   "Y8888b.
-# 888   Y88b Y88b  d88PY88b  d88P   8888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.      X88
-# 888    Y88b "Y8888P"  "Y8888P88   888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888 88888P'
-#                                                                888
-#                                                           Y8b d88P
-#                                                            "Y88P"
-#
-
-class KCGWidgets(QtGui.QWidget):
-    """
-    Base Class for alsmost all Widgets used in KCG.
-    It holds various properties as well as methods that simplify creation of certain objects such as labels, buttons ...
-    """
-    closeSignal = QtCore.pyqtSignal()
-
-    def __init__(self, name=None, parent=None):
-        """
-        Initialise this baseclass
-        :param name: (str) name of the widget
-        :param parent: (QWidget) parent of this widget
-        :return: -
-        """
-        super(KCGWidgets, self).__init__(parent)
-        self._id = None
-        self._type = None
-        self._name = None
-        if name:
-            self.theName = name
-
-    @property
-    def theType(self):
-        """
-        Type of this widget
-        """
-        return self._type
-
-    @property
-    def theId(self):
-        """
-        ID of this widget
-        """
-        return self._id
-
-    @property
-    def theName(self):
-        """
-        Name of this widget
-        """
-        return self._name
-
-    @theType.setter
-    def theType(self, t):
-        """
-        Setter for the type of this widget
-        :param t: (int) type
-        """
-        self._type = t
-
-    @theId.setter
-    def theId(self, i):
-        """
-        Setter for the id of this widget
-        :param i: (int) id
-        """
-        self._id = i
-
-    @theName.setter
-    def theName(self, n):
-        """
-        Setter for the name of this widget
-        :param n: (str) name
-        """
-        self._name = n
-
-    def createButton(self, text="", x=None, y=None, dimensions=None, tooltip="", connect=False, icon=None):
-        """
-        Create a Button
-        :param text: (str) Text to display on the button
-        :param x: (int) x-position
-        :param y: (int) y-position
-        :param dimensions: (QSize) dimensions of the button
-        :param tooltip: (str) tooltip to display
-        :param connect: (callable) connect the button pressed event to this callable
-        :param icon: (QIcon) Icon to display on the button
-        :return: -
-        """
-        button = QtGui.QPushButton(text, self)
-        if tooltip:
-            button.setToolTip(tooltip)
-        if not dimensions:
-            button.resize(button.sizeHint())
-        else:
-            button.resize(dimensions)
-        if x and y:
-            button.move(x, y)
-        if connect:
-            button.clicked.connect(connect)
-        if icon:
-            button.setIcon(icon)
-        return button
-
-    def createLabel(self, text=None, image=None, tooltip=None, click=False, connect=None):
-        """
-        Create a Label
-        :param text: (str) Text to display on this label
-        :param image: (QPixmap) Image to display on this label
-        :param tooltip: (str) tooltip to display
-        :param click: (bool) make this a clickable label?
-        :param connect: (callable) if click is true, connect the clicked event to this callable
-        :return: -
-        """
-        if click:
-            label = clickLabel(self)
-            if connect:
-                label.clicked.connect(connect)
-        else:
-            label = QtGui.QLabel(self)
-        if text:
-            label.setText(text)
-        if image:
-            label.setPixmap(image)
-        if tooltip:
-            label.setToolTip(tooltip)
-        return label
-
-    def createSpinbox(self, min, max, interval=1, start_value=0, connect=None):
-        """
-        create a Spinbox
-        :param min: (int) minimum Value
-        :param max: (int) maximum Value
-        :param interval: (int) interval
-        :param start_value: (int) start Value
-        :param connect: (callable) function to call on value change
-        :return: -
-        """
-        spinbox = QtGui.QSpinBox()
-        spinbox.setRange(min, max)
-        spinbox.setSingleStep(interval)
-        spinbox.setValue(start_value)
-        if connect:
-            spinbox.valueChanged.connect(connect)
-        return spinbox
-
-    def createInput(self, text=None, read_only=False, width=None):
-        """
-        Create Input
-        :param text: (str) Default Text
-        :param read_only: (bool) set input as read only
-        :param width: (int) width of the input field in pixels
-        :return: -
-        """
-        input = QtGui.QLineEdit()
-        if text:
-            input.setText(text)
-        if width:
-            input.setMinimumWidth(width)
-        input.setReadOnly(read_only)
-        return input
-
-    def createCheckbox(self, text="", tooltip="", checked=False, connect=None):
-        """
-        Create Checkbox
-        :param tooltip: (str) what tooltip text to display
-        :param checked: (bool) Checkt as default?
-        :param connect: (callable) function to connect
-        :return:
-        """
-        cb = QtGui.QCheckBox(text)
-        cb.setToolTip(tooltip)
-        cb.setChecked(checked)
-        if connect:
-            cb.clicked.connect(connect)
-        return cb
-
-    def createSwitch(self, startRight=False, connect=None):
-        """
-        Create a Switch
-        :param startRight: (bool) if this is True the initial setting is set to right (default is left)
-        :param connect: (callable) connect the switches clicked event to this callable
-        :return: -
-        """
-        sw = switchLabel(startRight)
-        if connect:
-            sw.clicked.connect(connect)
-        return sw
-
-    def closeEvent(self, event):
-        """
-        Reimplementation of QWidget.closeEvent
-        """
-        super(KCGWidgets, self).closeEvent(event)
-        self.closeSignal.emit()
-
-
-class KCGSubWidget(QtGui.QMdiSubWindow):
-    """
-    Base Class for Subwindows in the KCG Gui
-    """
-    _id = None
-    _name = None
-    _type = None
-
-    def __init__(self, name=None, unique_id=None, typ=None, minSize=False):
-        """
-        Initialise a Subwindow
-        :param name: (str) name of this window
-        :param unique_id: (int) unique id of this window
-        :param typ: (int) type of this window
-        :param minSize: (bool) whether the window is to be resized to its minimum size
-        :return: -
-        """
-        super(KCGSubWidget, self).__init__()
-        if name != None:
-            self.theName = name
-        if unique_id != None:
-            self.theId = unique_id
-        if typ != None:
-            self.theType = typ
-
-    @property
-    def theType(self):
-        """
-        Type of this widget
-        """
-        return self._type
-
-    @property
-    def theId(self):
-        """
-        ID of this widget
-        """
-        return self._id
-
-    @property
-    def theName(self):
-        """
-        Name of this widget
-        """
-        return self._name
-
-    @theType.setter
-    def theType(self, t):
-        """
-        Setter for the type of this widget
-        :param t: (int) type
-        """
-        self._type = t
-
-    @theId.setter
-    def theId(self, i):
-        """
-        Setter for the id of this widget
-        :param i: (int) id
-        """
-        self._id = i
-
-    @theName.setter
-    def theName(self, n):
-        """
-        Setter for the name of this widget
-        :param n: (str) name
-        """
-        self._name = n
-
-
-class AccordionClickLine(QtGui.QWidget):
-    """
-    A simple clickable line used in the accordion widget below
-    """
-    clicked = QtCore.pyqtSignal()
-    def __init__(self, text):
-        super(AccordionClickLine, self).__init__()
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.layout.setSpacing(0)
-        self.setContentsMargins(0, -5, 0, -5)
-        self.layout.addWidget(QtGui.QLabel(text))
-        self.layout.addStretch(1)
-        self.down_chev = QtSvg.QSvgWidget(config.icon_path("chevron-bottom.svg"))
-        self.down_chev.setFixedSize(10, 10)
-        self.left_chev = QtSvg.QSvgWidget(config.icon_path("chevron-left.svg"))
-        self.left_chev.setFixedSize(10, 10)
-        self.down_chev.hide()
-        self.layout.addWidget(self.left_chev)
-
-        self.expanded = True
-
-    def mouseReleaseEvent(self, QMouseEvent):
-        """
-        Reimplementation of QWidget.mouseReleaseEvent to make it clickable
-        """
-        self.clicked.emit()
-        super(AccordionClickLine, self).mouseReleaseEvent(QMouseEvent)
-
-    def expand(self, state):
-        """
-        Expand or deexpand the Accordion entry
-        :param state: True to expand False to deexpand
-        """
-        if state:  # if you want to expand
-            if not self.expanded:  # and it is not already expanded
-                self.layout.removeWidget(self.down_chev)
-                self.down_chev.hide()
-                self.left_chev.show()
-                self.layout.addWidget(self.left_chev)
-                self.expanded = True
-
-        else:  # if you want to unexpand
-            if self.expanded:  # and it is epanded
-                self.layout.removeWidget(self.left_chev)
-                self.left_chev.hide()
-                self.down_chev.show()
-                self.layout.addWidget(self.down_chev)
-                self.expanded = False
-
-
-class AccordionWidget(QtGui.QWidget):  # TODO: accordion or accordeon?
-    """
-    Simple accordion widget similar to QToolBox
-    """
-    def __init__(self):
-        """
-        Initialise the accordion widget
-        """
-        super(AccordionWidget, self).__init__()
-        self.layout = QtGui.QVBoxLayout()
-        self.layout.setSpacing(0)
-        self.setLayout(self.layout)
-        self.widgets = []
-        self.headers = []
-
-    def resizeEvent(self, QResizeEvent):
-        """
-        Reimplementation of QWidget.resizeEvent to make it look nice
-        """
-        self.setMaximumHeight(self.layout.sizeHint().height())
-        super(AccordionWidget, self).resizeEvent(QResizeEvent)
-
-    def addItem(self, widget, text):
-        """
-        Add a Widget to the Accordion widget
-        :param widget: the widget to add
-        :param text: the text for this widget
-        """
-        hline2 = QtGui.QFrame()
-        hline2.setFrameShape(QtGui.QFrame.HLine)
-        hline2.setFrameShadow(QtGui.QFrame.Sunken)
-        cidx = len(self.widgets)
-        header = AccordionClickLine(text)
-        header.clicked.connect(lambda: self.toggleIndex(cidx))
-        self.headers.append(header)
-        self.layout.addWidget(hline2)
-        self.layout.addWidget(header)
-        self.layout.addWidget(widget)
-        self.widgets.append(widget)
-
-    def toggleIndex(self, index):
-        """
-        Toggle the visibility of the widget with index index
-        :param index: the index to toggle
-        :return:
-        """
-        if self.widgets[index].isHidden():
-            self.expandIndex(index)
-        else:
-            self.hideIndex(index)
-
-    def expandIndex(self, index):
-        """
-        Expand the widget with index index
-        :param index: the index of the widget to show
-        """
-        self.widgets[index].show()
-        self.headers[index].expand(True)
-        self.resize(1, 1)  # this will trigger a resizeEvent and thus will set the correct size
-
-    def hideIndex(self, index):
-        """
-        Hide the widget with the index index
-        :param index: the index of the widget to hide
-        """
-        self.widgets[index].hide()
-        self.headers[index].expand(False)
-        self.resize(1, 1)  # this will trigger a resizeEvent and thus will set the correct size
-
-    def hideAll(self):
-        """
-        Hide all widgets
-        """
-        for wid, header in zip(self.widgets, self.headers):
-            wid.hide()
-            header.expand(False)
-
-    def showAll(self):
-        """
-        Show all widgets
-        """
-        for wid, header in zip(self.widgets, self.headers):
-            wid.show()
-            header.expand(True)
-
-
-class MultilineInputDialog(QtGui.QDialog):
-    """
-    Multiline Input Dialog
-    When using this dialog, create is and open it with get_text. this also returns the entered text.
-    """
-    def __init__(self):
-        super(MultilineInputDialog, self).__init__()
-        self.answer = False
-
-    def fill(self, heading, label):
-        """
-        Fill the widget with elements
-        :param heading: (str) the heading of this widget
-        :param label: (str) the text to show above the input field
-        :return: -
-        """
-        self.setWindowTitle(heading)
-        self.layout = QtGui.QVBoxLayout()
-        self.setLayout(self.layout)
-
-        self.tl = QtGui.QLabel(label)
-        self.layout.addWidget(self.tl)
-        self.ti = QtGui.QPlainTextEdit()
-        self.layout.addWidget(self.ti)
-
-        # Advanced method to activate function call on Ctrl+Return (in this case the ok function aka press ok button)
-        # def ctrlEnter(event):
-        #     if event.key() == QtCore.Qt.Key_Return:
-        #         if event.modifiers() == QtCore.Qt.ControlModifier:
-        #             self.ok()
-        #     QtGui.QPlainTextEdit.keyPressEvent(self.ti, event)
-        # self.ti.keyPressEvent = ctrlEnter
-
-        self.blayout = QtGui.QHBoxLayout()
-        self.layout.addLayout(self.blayout)
-        self.okButton = QtGui.QPushButton(tr("Button", "OK"))
-        self.okButton.pressed.connect(self.ok)
-        self.okButton.setShortcut("Ctrl+Return")
-        self.cancelButton = QtGui.QPushButton(tr("Button", "Cancel"))
-        self.cancelButton.pressed.connect(self.cancel)
-        self.cancelButton.setShortcut("Esc")
-        self.blayout.addWidget(self.okButton)
-        self.blayout.addWidget(self.cancelButton)
-
-    def ok(self):
-        """
-        This gets executed when the ok button is pressed
-        """
-        self.answer = True
-        self.close()
-
-    def cancel(self):
-        """
-        This gets executed when the cancel button is pressed
-        """
-        self.answer = False
-        self.close()
-
-    def get_text(self, heading, label):
-        """
-        This function is the main entry point.
-        :param heading: (str) the heading of the widget
-        :param label: (str) the text to show above the input field
-        :return: (unicode) the entered text
-        """
-        self.fill(heading, label)
-        self.exec_()
-        return str(self.ti.toPlainText()), self.answer
-
-
-class IdGenerator():
-    """
-    Generate Unique Id for every subwindow
-    """
-    highest_id = 0
-    def genid(self):
-        """
-        Generate a new Id
-        :return:
-        """
-        self.highest_id += 1
-        return self.highest_id
-
-idg = IdGenerator()  # declare idgenerator instance to use globally
-
-
-_registered_possible_widgets = []
-_registered_possible_widget_functions = []
-
-
-def register_widget_creation_function(creation_func):
-    """
-    Register the function to create a certain widget
-    :param creation_func: (callable) function to call when the widget is to be created
-    :return: -
-    """
-    _registered_possible_widget_functions.append(creation_func)
-
-
-def register_widget(icon, text, target, shortcut=None):
-    """
-    Register a widget
-    :param icon: (QIcon) Icon to show on the toolbar button
-    :param text: (str) tooltip for the toolbar button
-    :param target: (callable) the function to create the widget
-    :param shortcut: (str) keyboard shortcut to open this widget
-    :return: -
-    """
-    _registered_possible_widgets.append([icon, text, target, shortcut])
-
-
-def get_registered_widgets():
-    """
-    Get the registered widgets
-    :return: (list) list of registered widgets
-    """
-    return _registered_possible_widgets
-
-
-def get_registered_widget_functions():
-    """
-    Get the functions that are registered
-    :return: (list) list of functions
-    """
-    return _registered_possible_widget_functions
-
-
-def error(code, text, severe=False):
-    """
-    Log an error using the logging module
-    :param code: the error code
-    :param text: the text to show
-    :param severe: if it is a severe error that has to quit the program
-    :return:
-    """
-    if isinstance(code, str) and code[0:2] == '0x':
-        cde = code
-    elif isinstance(code, str):
-        cde = '0x'+code
-    else:
-        cde = '0x'+format(code, '03x')
-    logging.critical('{code}: {text}'.format(code=cde, text=text))
-    if severe:
-        sys.exit(int(cde, 16))
+"""
+Base Classes used in KCG.
+This module also contains various helpful Classes to make live easier ;)
+"""
+from PyQt4 import QtGui, QtCore, QtSvg
+import logging
+import sys
+
+from .. import config
+
+
+def tr(_, x):
+    """
+    Dummy Translation method that does not translate
+    :param _: unused
+    :param x: the string to "translate"
+    :return: x
+    """
+    return x
+
+
+class BigIconButton(QtGui.QPushButton):
+    """
+    This is a Button with a big Icon (that can fill the whole button)
+    """
+    def __init__(self, path, width, height, connect=None, tooltip=None, parent=None):
+        """
+        Setting various properties
+        :param path: (str) the path to the icon
+        :param width: (int) with of the button
+        :param height: (int) height of the button
+        :param connect: (callable) function to call when button is pressed
+        :param tooltip: (str) tool tip to show
+        :param parent: (QWidget) parent widget.
+        :return: -
+        """
+        super(BigIconButton, self).__init__(parent)
+        self.setIcon(QtGui.QIcon(path))
+        self.setFixedSize(width, height)
+        self.setIconSize(QtCore.QSize(width*0.7, height*0.7))
+        if connect:
+            self.clicked.connect(connect)
+        if tooltip:
+            self.setToolTip(tooltip)
+
+
+class clickLabel(QtGui.QLabel):
+    """
+    Clickable Label
+    """
+    clicked = QtCore.pyqtSignal()
+    def mouseReleaseEvent(self, QMouseEvent):
+        """
+        Reimplementation of QLabel.mouseReleaseEvent to make this clickable
+        """
+        self.clicked.emit()
+
+class ClickableHBoxLayout(QtGui.QHBoxLayout):
+    """
+    A clickable HBoxLayout
+    """
+    clicked = QtCore.pyqtSignal()
+    def event(self, QEvent):
+        """
+        Reimplementation of QHBoxLayout.event to make it clickable
+        """
+        self.clicked.emit()
+        if QEvent.type() == QtCore.QEvent.MouseButtonRelease:
+            self.clicked.emit()
+            return True
+        else:
+            super(ClickableHBoxLayout, self).event(QEvent)
+            return True
+
+class switchLabel(QtGui.QLabel):
+    """
+    This implements a Switch.
+    It switches between left and right.
+    """
+    clicked = QtCore.pyqtSignal()
+
+    def __init__(self, startRight=False): # self.state: True->Right False->Left
+        """
+        Initialise switchLabel
+        As a normal Button it emits the clicked event when clicked.
+        It does NOT have two events for each side at the moment.
+        :param startRight: (bool) whether the switch is initially set to the right position (default is left (False))
+        :return: -
+        """
+        super(switchLabel, self).__init__()
+        self.leftSwitch = QtGui.QPixmap(config.install_path+"icons/SwitchButtonLeft.png")
+        self.leftSwitch = self.leftSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
+        self.rightSwitch = QtGui.QPixmap(config.install_path+"icons/SwitchButtonRight.png")
+        self.rightSwitch = self.rightSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
+        if startRight:
+            self.setPixmap(self.rightSwitch)
+            self.state = True
+        else:
+            self.setPixmap(self.leftSwitch)
+            self.state = False
+
+    def mouseReleaseEvent(self, QMouseEvent):
+        """
+        Reimplementation of QLabel.mouseReleaseEvent to make it clickable
+        """
+        if self.state:
+            self.state = False
+            self.setPixmap(self.leftSwitch)
+        else:
+            self.state = True
+            self.setPixmap(self.rightSwitch)
+        self.clicked.emit()
+
+class Switch(QtGui.QWidget):
+    """
+    A Simple Switch
+    """
+    clicked = QtCore.pyqtSignal()
+
+    def __init__(self, leftLabel, rightLabel, startRight=False):
+        super(Switch, self).__init__()
+        self.layout = QtGui.QHBoxLayout()
+        self.switch = switchLabel(startRight)
+        self.switch.clicked.connect(self.clicked.emit)
+        self.setLayout(self.layout)
+        self.layout.addStretch(1)
+        self.layout.addWidget(QtGui.QLabel(leftLabel))
+        self.layout.addWidget(self.switch)
+        self.layout.addWidget(QtGui.QLabel(rightLabel))
+        self.layout.addStretch(1)
+
+    def state(self):
+        """
+        Get the state of this switch
+        :return: the state of this switch
+        """
+        return self.switch.state
+
+
+class ClickableSVG(QtGui.QWidget):
+    """
+    This implements a clickable SVG Image
+    """
+    clicked = QtCore.pyqtSignal()
+
+    def __init__(self, path, width, height, wwidth=None, wheight=None, parent=None):
+        """
+        Initialisation of ClickabeSVG
+        :param path: (str) path to the svg file
+        :param width: (int) width of the svg
+        :param height: (int) height of the svg
+        :param wwidth: (int) width of the widget (not the svg)
+        :param wheight: (int) height of the widget (not the svg)
+        :param parent: (QWidget) parent widget of the ClickableSVG
+        :return: -
+        """
+        super(ClickableSVG, self).__init__(parent)
+        self.svg = QtSvg.QSvgWidget(path)
+        self.svg.setFixedSize(width, height)
+        layout = QtGui.QHBoxLayout()
+        layout.addWidget(self.svg)
+        self.setLayout(layout)
+        if wwidth:
+            self.setFixedWidth(wwidth)
+        if wheight:
+            self.setFixedHeight(wheight)
+
+    def mouseReleaseEvent(self, QMouseEvent):
+        """
+        Reimplementation of QWidget.mouseReleaseEvent to make it clickable
+        """
+        self.clicked.emit()
+
+    def changeSvg(self, path):
+        """
+        Change the SVG of this widget
+        :param path: (str) path to the new svg file
+        :return: -
+        """
+        self.svg.load(path)
+
+
+class KCGWidgets(QtGui.QWidget):
+    """
+    Base Class for alsmost all Widgets used in KCG.
+    It holds various properties as well as methods that simplify creation of certain objects such as labels, buttons ...
+    """
+    closeSignal = QtCore.pyqtSignal()
+
+    def __init__(self, name=None, parent=None):
+        """
+        Initialise this baseclass
+        :param name: (str) name of the widget
+        :param parent: (QWidget) parent of this widget
+        :return: -
+        """
+        super(KCGWidgets, self).__init__(parent)
+        self._id = None
+        self._type = None
+        self._name = None
+        if name:
+            self.theName = name
+
+    @property
+    def theType(self):
+        """
+        Type of this widget
+        """
+        return self._type
+
+    @property
+    def theId(self):
+        """
+        ID of this widget
+        """
+        return self._id
+
+    @property
+    def theName(self):
+        """
+        Name of this widget
+        """
+        return self._name
+
+    @theType.setter
+    def theType(self, t):
+        """
+        Setter for the type of this widget
+        :param t: (int) type
+        """
+        self._type = t
+
+    @theId.setter
+    def theId(self, i):
+        """
+        Setter for the id of this widget
+        :param i: (int) id
+        """
+        self._id = i
+
+    @theName.setter
+    def theName(self, n):
+        """
+        Setter for the name of this widget
+        :param n: (str) name
+        """
+        self._name = n
+
+    def createButton(self, text="", x=None, y=None, dimensions=None, tooltip="", connect=False, icon=None):
+        """
+        Create a Button
+        :param text: (str) Text to display on the button
+        :param x: (int) x-position
+        :param y: (int) y-position
+        :param dimensions: (QSize) dimensions of the button
+        :param tooltip: (str) tooltip to display
+        :param connect: (callable) connect the button pressed event to this callable
+        :param icon: (QIcon) Icon to display on the button
+        :return: -
+        """
+        button = QtGui.QPushButton(text, self)
+        if tooltip:
+            button.setToolTip(tooltip)
+        if not dimensions:
+            button.resize(button.sizeHint())
+        else:
+            button.resize(dimensions)
+        if x and y:
+            button.move(x, y)
+        if connect:
+            button.clicked.connect(connect)
+        if icon:
+            button.setIcon(icon)
+        return button
+
+    def createLabel(self, text=None, image=None, tooltip=None, click=False, connect=None):
+        """
+        Create a Label
+        :param text: (str) Text to display on this label
+        :param image: (QPixmap) Image to display on this label
+        :param tooltip: (str) tooltip to display
+        :param click: (bool) make this a clickable label?
+        :param connect: (callable) if click is true, connect the clicked event to this callable
+        :return: -
+        """
+        if click:
+            label = clickLabel(self)
+            if connect:
+                label.clicked.connect(connect)
+        else:
+            label = QtGui.QLabel(self)
+        if text:
+            label.setText(text)
+        if image:
+            label.setPixmap(image)
+        if tooltip:
+            label.setToolTip(tooltip)
+        return label
+
+    def createSpinbox(self, min, max, interval=1, start_value=0, connect=None):
+        """
+        create a Spinbox
+        :param min: (int) minimum Value
+        :param max: (int) maximum Value
+        :param interval: (int) interval
+        :param start_value: (int) start Value
+        :param connect: (callable) function to call on value change
+        :return: -
+        """
+        spinbox = QtGui.QSpinBox()
+        spinbox.setRange(min, max)
+        spinbox.setSingleStep(interval)
+        spinbox.setValue(start_value)
+        if connect:
+            spinbox.valueChanged.connect(connect)
+        return spinbox
+
+    def createInput(self, text=None, read_only=False, width=None):
+        """
+        Create Input
+        :param text: (str) Default Text
+        :param read_only: (bool) set input as read only
+        :param width: (int) width of the input field in pixels
+        :return: -
+        """
+        input = QtGui.QLineEdit()
+        if text:
+            input.setText(text)
+        if width:
+            input.setMinimumWidth(width)
+        input.setReadOnly(read_only)
+        return input
+
+    def createCheckbox(self, text="", tooltip="", checked=False, connect=None):
+        """
+        Create Checkbox
+        :param tooltip: (str) what tooltip text to display
+        :param checked: (bool) Checkt as default?
+        :param connect: (callable) function to connect
+        :return:
+        """
+        cb = QtGui.QCheckBox(text)
+        cb.setToolTip(tooltip)
+        cb.setChecked(checked)
+        if connect:
+            cb.clicked.connect(connect)
+        return cb
+
+    def createSwitch(self, startRight=False, connect=None):
+        """
+        Create a Switch
+        :param startRight: (bool) if this is True the initial setting is set to right (default is left)
+        :param connect: (callable) connect the switches clicked event to this callable
+        :return: -
+        """
+        sw = switchLabel(startRight)
+        if connect:
+            sw.clicked.connect(connect)
+        return sw
+
+    def closeEvent(self, event):
+        """
+        Reimplementation of QWidget.closeEvent
+        """
+        super(KCGWidgets, self).closeEvent(event)
+        self.closeSignal.emit()
+
+
+class KCGSubWidget(QtGui.QMdiSubWindow):
+    """
+    Base Class for Subwindows in the KCG Gui
+    """
+    _id = None
+    _name = None
+    _type = None
+
+    def __init__(self, name=None, unique_id=None, typ=None, minSize=False):
+        """
+        Initialise a Subwindow
+        :param name: (str) name of this window
+        :param unique_id: (int) unique id of this window
+        :param typ: (int) type of this window
+        :param minSize: (bool) whether the window is to be resized to its minimum size
+        :return: -
+        """
+        super(KCGSubWidget, self).__init__()
+        if name != None:
+            self.theName = name
+        if unique_id != None:
+            self.theId = unique_id
+        if typ != None:
+            self.theType = typ
+
+    @property
+    def theType(self):
+        """
+        Type of this widget
+        """
+        return self._type
+
+    @property
+    def theId(self):
+        """
+        ID of this widget
+        """
+        return self._id
+
+    @property
+    def theName(self):
+        """
+        Name of this widget
+        """
+        return self._name
+
+    @theType.setter
+    def theType(self, t):
+        """
+        Setter for the type of this widget
+        :param t: (int) type
+        """
+        self._type = t
+
+    @theId.setter
+    def theId(self, i):
+        """
+        Setter for the id of this widget
+        :param i: (int) id
+        """
+        self._id = i
+
+    @theName.setter
+    def theName(self, n):
+        """
+        Setter for the name of this widget
+        :param n: (str) name
+        """
+        self._name = n
+
+
+class AccordionClickLine(QtGui.QWidget):
+    """
+    A simple clickable line used in the accordion widget below
+    """
+    clicked = QtCore.pyqtSignal()
+    def __init__(self, text):
+        super(AccordionClickLine, self).__init__()
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.layout.setSpacing(0)
+        self.setContentsMargins(0, -5, 0, -5)
+        self.layout.addWidget(QtGui.QLabel(text))
+        self.layout.addStretch(1)
+        self.down_chev = QtSvg.QSvgWidget(config.install_path+"icons/chevron-bottom.svg")
+        self.down_chev.setFixedSize(10, 10)
+        self.left_chev = QtSvg.QSvgWidget(config.install_path+"icons/chevron-left.svg")
+        self.left_chev.setFixedSize(10, 10)
+        self.down_chev.hide()
+        self.layout.addWidget(self.left_chev)
+
+        self.expanded = True
+
+    def mouseReleaseEvent(self, QMouseEvent):
+        """
+        Reimplementation of QWidget.mouseReleaseEvent to make it clickable
+        """
+        self.clicked.emit()
+        super(AccordionClickLine, self).mouseReleaseEvent(QMouseEvent)
+
+    def expand(self, state):
+        """
+        Expand or deexpand the Accordion entry
+        :param state: True to expand False to deexpand
+        """
+        if state:  # if you want to expand
+            if not self.expanded:  # and it is not already expanded
+                self.layout.removeWidget(self.down_chev)
+                self.down_chev.hide()
+                self.left_chev.show()
+                self.layout.addWidget(self.left_chev)
+                self.expanded = True
+
+        else:  # if you want to unexpand
+            if self.expanded:  # and it is epanded
+                self.layout.removeWidget(self.left_chev)
+                self.left_chev.hide()
+                self.down_chev.show()
+                self.layout.addWidget(self.down_chev)
+                self.expanded = False
+
+
+class AccordionWidget(QtGui.QWidget):  # TODO: accordion or accordeon?
+    """
+    Simple accordion widget similar to QToolBox
+    """
+    def __init__(self):
+        """
+        Initialise the accordion widget
+        """
+        super(AccordionWidget, self).__init__()
+        self.layout = QtGui.QVBoxLayout()
+        self.layout.setSpacing(0)
+        self.setLayout(self.layout)
+        self.widgets = []
+        self.headers = []
+
+    def resizeEvent(self, QResizeEvent):
+        """
+        Reimplementation of QWidget.resizeEvent to make it look nice
+        """
+        self.setMaximumHeight(self.layout.sizeHint().height())
+        super(AccordionWidget, self).resizeEvent(QResizeEvent)
+
+    def addItem(self, widget, text):
+        """
+        Add a Widget to the Accordion widget
+        :param widget: the widget to add
+        :param text: the text for this widget
+        """
+        hline2 = QtGui.QFrame()
+        hline2.setFrameShape(QtGui.QFrame.HLine)
+        hline2.setFrameShadow(QtGui.QFrame.Sunken)
+        cidx = len(self.widgets)
+        header = AccordionClickLine(text)
+        header.clicked.connect(lambda: self.toggleIndex(cidx))
+        self.headers.append(header)
+        self.layout.addWidget(hline2)
+        self.layout.addWidget(header)
+        self.layout.addWidget(widget)
+        self.widgets.append(widget)
+
+    def toggleIndex(self, index):
+        """
+        Toggle the visibility of the widget with index index
+        :param index: the index to toggle
+        :return:
+        """
+        if self.widgets[index].isHidden():
+            self.expandIndex(index)
+        else:
+            self.hideIndex(index)
+
+    def expandIndex(self, index):
+        """
+        Expand the widget with index index
+        :param index: the index of the widget to show
+        """
+        self.widgets[index].show()
+        self.headers[index].expand(True)
+        self.resize(1, 1)  # this will trigger a resizeEvent and thus will set the correct size
+
+    def hideIndex(self, index):
+        """
+        Hide the widget with the index index
+        :param index: the index of the widget to hide
+        """
+        self.widgets[index].hide()
+        self.headers[index].expand(False)
+        self.resize(1, 1)  # this will trigger a resizeEvent and thus will set the correct size
+
+    def hideAll(self):
+        """
+        Hide all widgets
+        """
+        for wid, header in zip(self.widgets, self.headers):
+            wid.hide()
+            header.expand(False)
+
+    def showAll(self):
+        """
+        Show all widgets
+        """
+        for wid, header in zip(self.widgets, self.headers):
+            wid.show()
+            header.expand(True)
+
+
+class MultilineInputDialog(QtGui.QDialog):
+    """
+    Multiline Input Dialog
+    When using this dialog, create is and open it with get_text. this also returns the entered text.
+    """
+    def __init__(self):
+        super(MultilineInputDialog, self).__init__()
+        self.answer = False
+
+    def fill(self, heading, label):
+        """
+        Fill the widget with elements
+        :param heading: (str) the heading of this widget
+        :param label: (str) the text to show above the input field
+        :return: -
+        """
+        self.setWindowTitle(heading)
+        self.layout = QtGui.QVBoxLayout()
+        self.setLayout(self.layout)
+
+        self.tl = QtGui.QLabel(label)
+        self.layout.addWidget(self.tl)
+        self.ti = QtGui.QPlainTextEdit()
+        self.layout.addWidget(self.ti)
+
+        # Advanced method to activate function call on Ctrl+Return (in this case the ok function aka press ok button)
+        # def ctrlEnter(event):
+        #     if event.key() == QtCore.Qt.Key_Return:
+        #         if event.modifiers() == QtCore.Qt.ControlModifier:
+        #             self.ok()
+        #     QtGui.QPlainTextEdit.keyPressEvent(self.ti, event)
+        # self.ti.keyPressEvent = ctrlEnter
+
+        self.blayout = QtGui.QHBoxLayout()
+        self.layout.addLayout(self.blayout)
+        self.okButton = QtGui.QPushButton(tr("Button", "OK"))
+        self.okButton.pressed.connect(self.ok)
+        self.okButton.setShortcut("Ctrl+Return")
+        self.cancelButton = QtGui.QPushButton(tr("Button", "Cancel"))
+        self.cancelButton.pressed.connect(self.cancel)
+        self.cancelButton.setShortcut("Esc")
+        self.blayout.addWidget(self.okButton)
+        self.blayout.addWidget(self.cancelButton)
+
+    def ok(self):
+        """
+        This gets executed when the ok button is pressed
+        """
+        self.answer = True
+        self.close()
+
+    def cancel(self):
+        """
+        This gets executed when the cancel button is pressed
+        """
+        self.answer = False
+        self.close()
+
+    def get_text(self, heading, label):
+        """
+        This function is the main entry point.
+        :param heading: (str) the heading of the widget
+        :param label: (str) the text to show above the input field
+        :return: (unicode) the entered text
+        """
+        self.fill(heading, label)
+        self.exec_()
+        return unicode(self.ti.toPlainText()), self.answer
+
+
+class IdGenerator():
+    """
+    Generate Unique Id for every subwindow
+    """
+    highest_id = 0
+    def genid(self):
+        """
+        Generate a new Id
+        :return:
+        """
+        self.highest_id += 1
+        return self.highest_id
+
+idg = IdGenerator()  # declare idgenerator instance to use globally
+
+
+_registered_possible_widgets = []
+_registered_possible_widget_functions = []
+
+
+def register_widget_creation_function(creation_func):
+    """
+    Register the function to create a certain widget
+    :param creation_func: (callable) function to call when the widget is to be created
+    :return: -
+    """
+    _registered_possible_widget_functions.append(creation_func)
+
+
+def register_widget(icon, text, target, shortcut=None):
+    """
+    Register a widget
+    :param icon: (QIcon) Icon to show on the toolbar button
+    :param text: (str) tooltip for the toolbar button
+    :param target: (callable) the function to create the widget
+    :param shortcut: (str) keyboard shortcut to open this widget
+    :return: -
+    """
+    _registered_possible_widgets.append([icon, text, target, shortcut])
+
+
+def get_registered_widgets():
+    """
+    Get the registered widgets
+    :return: (list) list of registered widgets
+    """
+    return _registered_possible_widgets
+
+
+def get_registered_widget_functions():
+    """
+    Get the functions that are registered
+    :return: (list) list of functions
+    """
+    return _registered_possible_widget_functions
+
+
+def error(code, text, severe=False):
+    """
+    Log an error using the logging module
+    :param code: the error code
+    :param text: the text to show
+    :param severe: if it is a severe error that has to quit the program
+    :return:
+    """
+    if isinstance(code, str) and code[0:2] == '0x':
+        cde = code
+    elif isinstance(code, str):
+        cde = '0x'+code
+    else:
+        cde = '0x'+format(code, '03x')
+    logging.critical('{code}: {text}'.format(code=cde, text=text))
+    if severe:
+        sys.exit(int(cde, 16))

+ 697 - 704
KCG/base/leftbar.py

@@ -1,704 +1,697 @@
-# -*- coding: utf-8 -*-
-from PyQt4 import QtGui, QtCore
-
-from . import kcgwidget as kcgw
-from .callbacks import callbacks
-from .plotWidget import PlotWidget
-from .backend.board import available_boards
-from .backend import io
-from . import storage
-from ..widgets import acquiresettings as acqs
-from .groupedelements import Elements
-from . import backendinterface as bif
-from ..widgets import singleread
-from . import log
-
-
-tr = kcgw.tr
-
-from .. import config
-
-LIVE = 1
-FILE = 2
-
-
-class BoardSpecificInformation(kcgw.KCGWidgets):
-    """
-    Specific Part for each board
-    """
-    def __init__(self, board_id):
-        """
-        This creates the information part for board with id board_id.
-        This is used to be able to create a variable number of board information areas.
-        :param board_id: the id of the board
-        """
-        super(BoardSpecificInformation, self).__init__()
-        self.temperature = self.createLabel(tr("Label", "Temp:"))
-
-        def update_temp():
-            '''update the temperature in the gui'''
-            if bif.bk_get_board_status(board_id, 'board_connected'):
-                self.temperature.setText("Temp: " + bif.bk_get_temperature(board_id) + " °C")
-        try:
-            update_temp()
-        except Exception:  # TODO: Find correct exception to catch
-            print("cannot get temperature")
-
-        self.timer = QtCore.QTimer()
-        self.timer.timeout.connect(update_temp)
-        self.timer.start(30000)
-
-        self.skipturns = self.createLabel(
-            tr("Label", "O_skip:") + " " + str(bif.bk_get_config(board_id, 'orbits_skip')),
-            tooltip=tr("Tooltip", "Skipped orbits\nKlick to open acquire settings"),
-            click=True,
-            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
-        )
-        bif.bk_get_board_config(board_id).observe(self.skipturns,
-                                                 lambda x: self.skipturns.setText(tr("Label", "O_skip:")+" "+str(x)),
-                                                 'orbits_skip')
-        self.orbitsobserved = self.createLabel(
-            tr("Label", "O_obs:") + " " + str(bif.bk_get_config(board_id, 'orbits_observe')),
-            tooltip=tr("Tooltip", "Number of observed Orbits\nKlick to open acquire settings"),
-            click=True,
-            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
-        )
-        bif.bk_get_board_config(board_id).observe(
-                self.orbitsobserved,
-                lambda x: self.orbitsobserved.setText(tr("Label", "O_obs:") + " " + str(x)), 'orbits_observe')
-        self.orbitscount = self.createLabel(
-            tr("Label", "Count:") + " " + str(bif.bk_get_config(board_id, 'acquisition_count')),
-            tooltip=tr("Tooltip", "Number of acquisitions\nKlick to open acquire settings"),
-            click=True,
-            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
-        )
-        bif.bk_get_board_config(board_id).observe(
-                self.orbitscount,
-                lambda x: self.orbitscount.setText(tr("Label", "Count:") + " " + str(x)), 'acquisition_count')
-        self.orbitswait = self.createLabel(
-            tr("Label", "T_wait:") + " " + str(bif.bk_get_config(board_id, 'orbits_wait_time')),
-            tooltip=tr("Tooltip", "Time in seconds to wait between acquisitions\nKlick to open acquire settings"),
-            click=True,
-            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
-        )
-        bif.bk_get_board_config(board_id).observe(
-                self.orbitswait,
-                lambda x: self.orbitswait.setText(tr("Label", "T_wait:") + " " + str(x)), 'orbits_wait_time')
-
-        self.boardSpecificLayout = QtGui.QVBoxLayout()
-        self.setLayout(self.boardSpecificLayout)
-        self.layout = QtGui.QGridLayout()
-        self.layout.addWidget(self.temperature, 0, 0)
-        self.layout.addWidget(self.skipturns, 1, 1)
-        self.layout.addWidget(self.orbitsobserved, 1, 0)
-        self.layout.addWidget(self.orbitscount, 2, 0)
-        self.layout.addWidget(self.orbitswait, 2, 1)
-        self.boardSpecificLayout.addLayout(self.layout)
-
-
-class AcquisitionAndInfo(kcgw.KCGWidgets):
-    """
-    Widget to show below the plot list.
-    Show certain information.
-    Provide Start Acquisition button and settings.
-    """
-    # consistency_updated = QtCore.pyqtSignal(bool)
-    # acquisition_started_signal = QtCore.pyqtSignal(int)
-    def __init__(self):
-        super(AcquisitionAndInfo, self).__init__()
-        self.layout = QtGui.QVBoxLayout()
-        self.layout.setContentsMargins(0, 0, 0, 0)
-        self.setLayout(self.layout)
-
-
-        if available_boards.multi_board:
-            self.scroll_area = QtGui.QScrollArea()
-            self.layout.addWidget(self.scroll_area)
-            self.information_box = kcgw.AccordionWidget()
-            for brd in available_boards.board_ids:
-                self.information_box.addItem(BoardSpecificInformation(brd),
-                                             available_boards.get_board_name_from_id(brd))
-            self.scroll_area.setWidget(self.information_box)
-            self.scroll_area.setWidgetResizable(True)
-            self.information_box.hideAll()
-            self.information_box.expandIndex(0)
-        else:
-            self.layout.addWidget(BoardSpecificInformation(available_boards.board_ids[0]))
-
-        # -----[ Constistency ]-----------
-        self.consistency = QtGui.QHBoxLayout()
-        self.layout.addLayout(self.consistency)
-        self.consistency_label = self.createLabel("Consistency:")
-        self.consistency_content = self.createLabel("●")
-        self.consistency_content.setStyleSheet("color: lightgrey;")
-        self.consistency.addWidget(self.consistency_label)
-        self.consistency.addWidget(self.consistency_content)
-
-        # -------[ acquire ]--------------
-        self.acquire = self.createButton(tr("Button", "Start Acquisition"),
-                                         icon=QtGui.QIcon(config.icon_path(config.startIcon)),
-                                         connect=self.toggle_acquisition)
-        if not available_boards.multi_board:
-            Elements.addItem('acquireTrigger_{}'.format(available_boards.board_ids[0]), self.acquire)
-
-        if available_boards.multi_board:
-            self.list_to_acquire = {
-                i: self.createCheckbox(
-                        str(i),
-                        tooltip=str(tr("Tooltip",
-                                       "Check this checkbox if you want to acquire with board {board}")
-                                    ). format(board=i),
-                        connect=self.update_acq_tickboxes) for i in available_boards}
-            self.list_of_active_boards = {i: False for i in available_boards}
-            self.acq_list_layout = QtGui.QHBoxLayout()
-            for b_id, cb in self.list_to_acquire.items():
-                self.acq_list_layout.addWidget(cb)
-                Elements.addItem('acquireTrigger_{}'.format(b_id), cb)
-                self.acquisition_stopped(b_id)
-
-        # -----[ log ]-------
-        self.log = self.createButton(tr("Button", "Log"),
-                                     icon=QtGui.QIcon(config.icon_path(config.logIcon)),
-                                     connect=lambda: log.log(additional="Manual Log"),
-                                     tooltip="Rightclick to Configure")
-        self.log_w_comment = self.createButton(tr("Button", "Log"),
-                                               icon=QtGui.QIcon(config.icon_path(config.logCommentIcon)),
-                                               connect=self.do_log_w_comment,
-                                               tooltip="Log with Comment\nRightclick to Configure")
-        self.log.contextMenuEvent = self.log_configure_context_menu
-        self.log_w_comment.contextMenuEvent = self.log_configure_context_menu
-
-        # self.layout.addLayout(self.consistency, 0, 0)  # TODO: für welches board? zum board???
-        if available_boards.multi_board:
-            self.layout.addLayout(self.acq_list_layout)
-        self.layout.addWidget(self.acquire)
-        self.logLayout = QtGui.QHBoxLayout()
-        self.logLayout.addWidget(self.log)
-        self.logLayout.addWidget(self.log_w_comment)
-        self.layout.addLayout(self.logLayout)
-
-        callbacks.add_callback('update_consistency', self.update_consistency)
-        callbacks.add_callback('acquisition_started', self.acquisition_started)
-        callbacks.add_callback('acquisition_stopped', self.acquisition_stopped)
-
-    def toggle_acquisition(self):
-        """
-        Turn acquisition on/off. This gets the information about which board to use by checking state of the checkboxes.
-        :return:
-        """
-        if not available_boards.multi_board:
-            bif.bk_acquire(available_boards.board_ids[0])
-            return
-        for b_id, cb in self.list_to_acquire.items():
-            if cb.isChecked():
-                bif.bk_acquire(b_id)
-        for cb in list(self.list_to_acquire.values()):
-            cb.setChecked(False)
-        self.update_acq_tickboxes()
-
-    def update_acq_tickboxes(self):
-        """
-        If at least one checkbox is checked, set all checkboxes with other state (acquiring or not)
-        to disabled. This prevents the user to start with one board and stop with the other simultaneously.
-        TODO: add option in settings to allow the now eliminated behaviour
-        """
-        active = False
-        inactive = False
-        for b_id, cb in self.list_to_acquire.items():
-            if cb.isChecked():  # search for first checked box
-                if self.list_of_active_boards[b_id] is True:  # if board is acquiring
-                    active = True
-                    break
-                else:
-                    inactive = True
-                    break
-        if active:  # if checked box is for acquiring boards (meaning to stop them)
-            for b_id, state in self.list_of_active_boards.items():
-                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)):
-                    self.list_to_acquire[b_id].setEnabled(state)  # set all active boards to enabled
-            self.acquire.setIcon(QtGui.QIcon(config.icon_path(config.stopIcon)))
-            self.acquire.setText(tr("Button", "Stop Acquisition"))
-        elif inactive:
-            for b_id, state in self.list_of_active_boards.items():
-                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)) or bif.bk_get_board_status(b_id, 'acquisition'):
-                    self.list_to_acquire[b_id].setEnabled(not state)  # set all active boards to not enabled
-            self.acquire.setIcon(QtGui.QIcon(config.icon_path(config.startIcon)))
-            self.acquire.setText(tr("Button", "Start Acquisition"))
-        else:  # if no board is selected
-            for b_id, cb in list(self.list_to_acquire.items()):
-                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)):
-                    cb.setEnabled(True)
-            self.acquire.setIcon(QtGui.QIcon(config.icon_path(config.startIcon)))
-            self.acquire.setText(tr("Button", "Start Acquisition"))
-
-    def acquisition_stopped(self, board_id):
-        """
-        This is called when a acquisition is stopped.
-        (has to be registered in Callbacks under 'acquisition_stopped'
-        :param board_id: id of the board
-        """
-        if not available_boards.multi_board:
-            for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
-                if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
-                    continue
-                elem.setIcon(QtGui.QIcon(config.icon_path(config.startIcon)))
-                elem.setText(tr("Button", "Start Acquisition"))
-            return
-        self.list_to_acquire[board_id].setStyleSheet('')
-        self.list_of_active_boards[board_id] = False
-        self.update_acq_tickboxes()
-
-    def acquisition_started(self, board_id):
-        """
-        This is called when a acquisition is started.
-        (has to be registered in Callbacks under 'acquisition_started'
-        :param board_id: id of the board
-        """
-        if not available_boards.multi_board:
-            for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
-                if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
-                    continue
-                elem.setIcon(QtGui.QIcon(config.icon_path(config.stopIcon)))
-                elem.setText(tr("Button", "Stop Acquisition"))
-            return
-        self.list_to_acquire[board_id].setStyleSheet("background-color: lightgreen;")
-        self.list_of_active_boards[board_id] = True
-        self.update_acq_tickboxes()
-
-    def do_log_w_comment(self):
-        """
-        Function to handle logging with comments
-        """
-        # text, ok = QtGui.QInputDialog.getText(self, tr("Heading", 'Log Comment'), tr("Label", 'Log Comment:'))
-        text, ok = kcgw.MultilineInputDialog().get_text(tr("Heading", "Log Comment"), tr("Label", "Log Comment:"))
-        if ok:
-            log.log(additional=text)
-
-    def log_configure_context_menu(self, event):
-        """
-        Function that creates the context menu for the log buttons
-        :param event: (QEvent) the event
-        :return: -
-        """
-        pos = event.globalPos()
-
-        def configure_log():
-            '''Open the configuration window for logs'''
-            c = log.ConfigureLog(self)
-            c.exec_()
-            del c
-        if pos is not None:
-            menu = QtGui.QMenu(self)
-            configure = menu.addAction(tr("Button", "Configure Log"))
-            configure.triggered.connect(configure_log)
-            menu.popup(pos)
-        event.accept()
-
-    def update_consistency(self, status):
-        """
-        Set consistency indicator
-        :param status: True if consistent, False if inconsistent, None is nothing
-        :return: -
-        """
-        if status is True:
-            self.consistency_content.setStyleSheet("color: green;")
-        elif status is False:
-            self.consistency_content.setStyleSheet("color: red;")
-        else:
-            self.consistency_content.setStyleSheet("color: lightgrey;")
-
-
-class LeftBar(kcgw.KCGWidgets):
-    """
-    Left bar in the main view of the gui.
-    Shows plot list and acquisition and info panel
-    """
-    # general notes:
-    # uid = unique_id
-    # usid = unique_sub_id
-    # upid = unique_parent_id
-    possiblePlots = ["Heatmap", "FFT", "Trains", "Combined", "Compare", "Follow"]
-
-    def __init__(self, parent):
-        super(LeftBar, self).__init__("LeftBar")
-        self.parent = parent
-
-        self.openData = {}
-        self.openDataNames = {}
-        self.openPlots = {}
-        self.openPlotWindows = {}
-        self.Data = {}
-
-        self.initUI()
-
-    def initUI(self):
-        """
-        Initialise the UI
-        """
-        self.layout = QtGui.QVBoxLayout()
-        self.layout.setContentsMargins(0, 0, 0, 0)
-
-
-        self.setMinimumWidth(230)
-
-        self.treeWidget = QtGui.QTreeWidget()
-        self.treeWidget.setObjectName("tree_view")
-        self.treeWidget.setColumnCount(2)
-        self.treeWidget.resizeColumnToContents(1)
-#        self.treeWidget.header().resizeSection(0, 110)
-        self.treeWidget.setHeaderHidden(True)
-        self.treeWidget.itemClicked.connect(self.plot_clicked)
-        self.treeWidget.contextMenuEvent = self.contextMenuEventListView
-        self.rootItem = self.treeWidget.invisibleRootItem()
-
-        self.infoAndAcquisitionWidget = AcquisitionAndInfo()
-        if config.integrate_single_read:
-            self.singleReadWidget = singleread.SingleReadWidget(0, self)
-
-        self.layout.addWidget(self.treeWidget)
-        if config.integrate_single_read:
-            self.layout.addWidget(self.singleReadWidget)
-        self.layout.addWidget(self.infoAndAcquisitionWidget)
-
-        self.setLayout(self.layout)
-
-    def _generate_menu(self, menu, item):
-        """
-        Generate the right click menu
-        :param menu: the menu to add the entries to
-        :param item: the item that was clicked on
-        """
-        heatmap = menu.addAction(tr("Button", "Heatmap"))
-        fft = menu.addAction(tr("Button", "FFT"))
-        trains = menu.addAction(tr("Button", "Trains"))
-        combined = menu.addAction(tr("Button", "Combined"))
-        compare = menu.addAction(tr("Button", "Compare"))
-
-        heatmap.triggered.connect(lambda: self.add_plot_node(
-                text=tr("Label", 'Heatmap'), unique_id=item.uid, d_type=0, datatype=item.datatype))
-        fft.triggered.connect(lambda: self.add_plot_node(
-                text=tr("Label", 'FFT'), unique_id=item.uid, d_type=1, datatype=item.datatype))
-        trains.triggered.connect(lambda: self.add_plot_node(
-                text=tr("Label", 'Trains'), unique_id=item.uid, d_type=2, datatype=item.datatype))
-        combined.triggered.connect(lambda: self.add_plot_node(
-                text=tr("Label", 'Combined'), unique_id=item.uid, d_type=3, datatype=item.datatype))
-        compare.triggered.connect(lambda: self.add_plot_node(
-                text=tr("Label", 'Combined'), unique_id=item.uid, d_type=4, datatype=item.datatype))
-
-    def contextMenuEventListView(self, event):
-        """
-        Gets called when right mouse button is clicked
-        :param event: the event that causes the call of this function
-        :return: -
-        """
-        pos = event.globalPos()
-        try:
-            item = self.treeWidget.selectedItems()[0]
-        except IndexError:
-            return
-        if pos is not None:
-            menu = QtGui.QMenu(self)
-            close = menu.addAction(tr("Button", "Close"))
-            if item.is_child:
-                close.triggered.connect(lambda: self.remove_plot(item.usid, silent=False, close_plot_window=True))
-            else:
-                close.triggered.connect(lambda: self.remove_plot_tree(item.uid))
-                self._generate_menu(menu, item)
-
-            menu.popup(pos)
-        event.accept()
-
-    def plot_clicked(self, item, i):
-        """
-        Function to handle when a plot item is clicked.
-        :param item: what item is clicked
-        :param i: what column
-        :return: -
-        """
-        if item.is_child:
-            self.openPlotWindows[item.usid].setFocus()
-        else:
-            if not item.ready:
-                return
-            if i == 1:
-                position = QtGui.QCursor.pos()
-                menu = QtGui.QMenu(self)
-
-                self._generate_menu(menu, item)
-
-                menu.popup(position)
-
-    def add_plot_tree(self, text=False, unique_id=None, datatype=None):
-        """
-        Add a top node to the plot list.
-        :param text: (str) text to be displayed in the plot list
-        :param unique_id: (int) unique id of this top node
-        :param datatype: (int) live or data from file
-        :return: -
-        """
-        if datatype == FILE:
-            file_name = QtGui.QFileDialog.getOpenFileName()
-            if file_name == "":
-                return
-            if file_name in iter(self.openDataNames.values()):  # is this to prevent double opening?
-                return
-            self.openDataNames[unique_id] = file_name
-        else:
-            if text in iter(self.openDataNames.values()):
-                return
-            self.openDataNames[unique_id] = QtCore.QString(text)  # Add a qstring for comparison
-        self.openData[unique_id] = QtGui.QTreeWidgetItem()
-        self.openData[unique_id].uid = unique_id
-        self.openData[unique_id].is_child = False
-        self.openData[unique_id].datatype = datatype
-        if datatype == FILE:
-            self.openData[unique_id].file_name = file_name
-        self.openData[unique_id].setText(0, "Live" if datatype == LIVE else file_name.split('/')[-1])
-        self.openData[unique_id].setToolTip(0, "Live" if datatype == LIVE else file_name.split('/')[-1])
-        self.openData[unique_id].setText(1, "Reading")
-        if datatype == LIVE:  # Insert LIVE on top
-            self.treeWidget.insertTopLevelItem(0, self.openData[unique_id])
-        else:
-            self.treeWidget.addTopLevelItem(self.openData[unique_id])
-        self.treeWidget.expandItem(self.openData[unique_id])
-        self.treeWidget.resizeColumnToContents(0)
-        self.openData[unique_id].ready = False  # indicates whether data was completely read or not
-        QtGui.qApp.processEvents()
-        try:
-            self.read_data(datatype, unique_id, file_name if datatype == FILE else None)
-        except Exception:
-            self.Data[unique_id] = None
-            self.remove_plot_tree(unique_id, silent=True)
-            QtGui.QMessageBox.critical(
-                    self,
-                    tr("Heading", "Error Reading Data"),
-                    tr("Dialog", "There was an error reading the datafile, make shure it is valid and try again."))
-        self.openData[unique_id].ready = True  # indicates whether data was completely read or not
-        self.openData[unique_id].setText(1, "+")
-        self.add_plot_node(d_type=0, unique_id=unique_id, datatype=datatype)
-
-    def read_data(self, d_type, uid, file_name):
-        """
-        Reads datafile
-        :param d_type: FILE or LIVE - type of datasource
-        :param uid: unique id for the treeelement
-        :param file_name: filename if type is FILE
-        :return: -
-        """
-        if d_type == FILE:
-            self.Data[uid] = io.read_from_file(file_name, header=storage.storage.header)
-        else:
-            self.Data[uid] = bif.livePlotData
-
-    def add_plot_node(self, board_id=None, text=False, unique_id=None, d_type=None, datatype=None):
-        """
-        Actually open a new plot window.
-        :param board_id: the id of the board to which the plot corresponds
-        :param text: (str) text that is displayed in the plot list
-        :param unique_id: (int) unique id of this plot window
-        :param d_type: (int) type of this plot window
-        :param datatype: (int) live or data from file
-        :return: -
-        """
-        if datatype == LIVE and board_id is None:  # get board_id for live plot from popup menu
-            # board_id = 0
-            if available_boards.multi_board:
-                # raise NotImplementedError("Dialog to ask for board is not implemented yet.")
-                position = QtGui.QCursor.pos()
-                menu = QtGui.QMenu(self)
-                for bid in available_boards:
-                    tmp = menu.addAction(available_boards.get_board_name_from_id(bid))
-                    tmp.triggered.connect(lambda _, b=bid: self.add_plot_node(board_id=b,
-                                                                           text=text,
-                                                                           unique_id=unique_id,
-                                                                           d_type=d_type,
-                                                                           datatype=datatype))
-                    # NOTE to the above: the lambda needs b=bid to capture the variable for the lambda
-                    # additionally the _ is needed because the call to the lambda includes an optional parameter
-                    # that is true or false, passed by pyqt
-                menu.popup(position)
-                return
-
-            else:
-                board_id = available_boards.board_ids[0]
-        # if not board_id:
-        #     TODO: Activate this (No Board Id Given)
-            # pass
-           # raise NoBoardId("No Board Id was given.")
-
-        unique_sub_id = kcgw.idg.genid()
-        nr = self.openData[unique_id].childCount()
-        if datatype == FILE:
-            file_name = self.openData[unique_id].file_name
-        else:
-            file_name = None
-        name = "Live" if not file_name else file_name.split(os.sep)[-1]
-
-        self.openPlotWindows[unique_sub_id] = PlotWidget(
-                board_id=board_id,
-                parent=self,
-                name=name,
-                unique_id=unique_sub_id,
-                type=d_type,
-                datatype=datatype,
-                prefix=nr,
-                fName=file_name,
-                data=self.Data[unique_id]
-        )
-        self.openPlotWindows[unique_sub_id].change_type_signal.connect(self.update_plot)
-        self.openPlotWindows[unique_sub_id].upid = unique_id
-        self.parent.area.newWidget(
-                self.openPlotWindows[unique_sub_id],
-                name=text,
-                unique_id=unique_sub_id,
-                widget_type=d_type
-        )
-        self.openPlots[unique_sub_id] = QtGui.QTreeWidgetItem()
-        if datatype == LIVE:
-            brd = " B: "+available_boards.get_board_name_from_id(board_id)
-        else:
-            brd = ""
-        if d_type == 4:  # Compare Plot
-            adc = "1+2"
-        else:
-            adc = "1"
-        self.openPlots[unique_sub_id].is_child = True
-        self.openPlots[unique_sub_id].usid = unique_sub_id
-        self.openPlots[unique_sub_id].nr = nr
-        self.update_plot(unique_sub_id, d_type, adc+brd)
-        self.openData[unique_id].addChild(self.openPlots[unique_sub_id])
-        self.treeWidget.resizeColumnToContents(0)
-
-    @QtCore.pyqtSlot()
-    def remove_plot(self, unique_id, silent=False, close_plot_window=False):
-        """
-        Removes a plot from the plot list.
-        :param unique_id: (int) the unique id of the plot to remove
-        :param silent: (bool) Ask to close the plot source node in the list if the last plot window is closed
-                        (if False the source node will not be closed)
-        :param close_plot_window: (bool) close the corresponding plot window
-        :return: -
-        """
-        parent = self.openData[self.openPlotWindows[unique_id].upid]
-        if not silent:
-            reply = QtGui.QMessageBox()
-            reply.setIcon(QtGui.QMessageBox.Question)
-            reply.setWindowTitle(tr("Heading", 'Close Plot'))
-            reply.setText(tr("Dialog", "Close this plot?"))
-            reply.addButton(QtGui.QMessageBox.Yes)
-            reply.addButton(QtGui.QMessageBox.No)
-            reply.setDefaultButton(QtGui.QMessageBox.No)
-
-            if parent.childCount() == 1:
-                reply.addButton("Close Plot Source", QtGui.QMessageBox.ResetRole)
-
-            reply.exec_()
-
-            if reply.buttonRole(reply.clickedButton()) == QtGui.QMessageBox.ResetRole:
-                self.remove_plot_tree(parent.uid, silent=True)
-                return True
-            elif reply.buttonRole(reply.clickedButton()) == QtGui.QMessageBox.NoRole:
-                return False
-        if close_plot_window:
-            self.openPlotWindows[unique_id].close_silent = True
-            self.openPlotWindows[unique_id].close()
-        parent.removeChild(self.openPlots[unique_id])
-        self.openPlotWindows[unique_id] = None
-        self.openPlots[unique_id] = None
-        del self.openPlotWindows[unique_id]
-        del self.openPlots[unique_id]
-        return True
-
-    def remove_plot_tree(self, unique_id, silent=False):
-        """
-        Remove the top node from the plot list
-        :param unique_id: (int) the id of the top node
-        :param silent: (bool) ask to close even if corresponding plot windows are open
-        :return: -
-        """
-        if self.openData[unique_id].childCount() != 0 and not silent:
-            reply = QtGui.QMessageBox.question(
-                    self, tr("Heading", 'Close Data Source'), tr(
-                            "Dialog", "There are open plot windows.\n"
-                            "Close this plot source and all corresponding plot windows?"),
-                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
-                    QtGui.QMessageBox.No)
-            if reply == QtGui.QMessageBox.No:
-                return
-        for plot in self.openData[unique_id].takeChildren():
-            self.openPlotWindows[plot.usid].close_silent = True
-            self.openPlotWindows[plot.usid].close()
-            self.openData[unique_id].removeChild(plot)
-        self.rootItem.removeChild(self.openData[unique_id])
-        del self.openData[unique_id]
-        del self.openDataNames[unique_id]
-        del self.Data[unique_id]
-
-    @QtCore.pyqtSlot(int, int, str)
-    def update_plot(self, uid, b_type, add):
-        """
-        Updates the plot list when the type of a plot window changes
-        :param uid: (int) unique id of the plot window to update
-        :param b_type: (int) type of that plot window
-        :param str add: the add, board and additional text to update
-        :return: -
-        """
-        text = str(self.openPlots[uid].nr) + "." + self.possiblePlots[b_type] + " - ADC " + str(add)
-        self.openPlots[uid].setText(0, text)
-        self.openPlots[uid].setToolTip(0, text)
-        self.treeWidget.resizeColumnToContents(0)
-
-    def handle_dialog(self, value, diag=None,):
-        """
-        Function that handles the return of the dialog that asks for live or data from file
-        :param value: (int) the value of the pressed button in the dialog
-        :param diag: (QDialog) the dialog that is to be closed (optional)
-        :return: -
-        """
-        if diag:
-            diag.close()
-        s = kcgw.idg.genid()
-        if value == 1:
-            if tr("Label", "Live") in iter(self.openDataNames.values()):
-                x = [bid for bid, obj in self.openData.items() if obj.datatype == LIVE][0]
-                self.add_plot_node(unique_id=x, d_type=0, datatype=LIVE)
-            else:
-                self.add_plot_tree(text=tr("Label", "Live"), unique_id=s, datatype=LIVE)
-        elif value == 2:
-            self.add_plot_tree(text=tr("Label", "File"), unique_id=s, datatype=FILE)
-        else:
-            pass
-
-    def add_plot(self, d_type=None):
-        """
-        Handles the creation of a plot.
-        Also shows the dialog that asks for the data source.
-        :param d_type: the type of the plot to add
-        :return: -
-        """
-        if d_type == LIVE or d_type == FILE:
-            self.handle_dialog(value=d_type)
-            return
-        ask = QtGui.QDialog()
-        window = self.parent.geometry()
-        ask_layout = QtGui.QVBoxLayout()
-        ask_layout.addWidget(QtGui.QLabel(tr("Dialog", "Open file from disk or read live data?")))
-
-        button_layout = QtGui.QHBoxLayout()
-        ask_button_live = QtGui.QPushButton(tr("Button", "Live"))
-        ask_button_file = QtGui.QPushButton(tr("Button", "Open File"))
-        ask_button_cancel = QtGui.QPushButton(tr("Button", "Cancel"))
-
-        ask_button_live.pressed.connect(lambda: self.handle_dialog(diag=ask, value=1))
-        ask_button_file.pressed.connect(lambda: self.handle_dialog(diag=ask, value=2))
-        ask_button_cancel.pressed.connect(lambda: self.handle_dialog(diag=ask, value=3))
-
-        button_layout.addWidget(ask_button_live)
-        button_layout.addWidget(ask_button_file)
-        button_layout.addWidget(ask_button_cancel)
-
-        ask_layout.addLayout(button_layout)
-        ask.setGeometry(window.width()/2.-100, window.height()/2.-50, 200, 100)
-
-        ask.setLayout(ask_layout)
-        ask.exec_()
+# -*- coding: utf-8 -*-
+from PyQt4 import QtGui, QtCore
+
+import kcgwidget as kcgw
+from callbacks import callbacks
+from plotWidget import PlotWidget
+from backend.board import available_boards
+from backend import io
+import storage
+from ..widgets import acquiresettings as acqs
+from groupedelements import Elements
+import backendinterface as bif
+import log
+
+
+tr = kcgw.tr
+
+from .. import config
+
+LIVE = 1
+FILE = 2
+
+
+class BoardSpecificInformation(kcgw.KCGWidgets):
+    """
+    Specific Part for each board
+    """
+    def __init__(self, board_id):
+        """
+        This creates the information part for board with id board_id.
+        This is used to be able to create a variable number of board information areas.
+        :param board_id: the id of the board
+        """
+        super(BoardSpecificInformation, self).__init__()
+        self.temperature = self.createLabel(tr("Label", "Temp:"))
+
+        def update_temp():
+            '''update the temperature in the gui'''
+            if bif.bk_get_board_status(board_id, 'board_connected'):
+                self.temperature.setText("Temp: " + bif.bk_get_temperature(board_id) + u" °C")
+        try:
+            update_temp()
+        except Exception:  # TODO: Find correct exception to catch
+            print ("cannot get temperature")
+
+        self.timer = QtCore.QTimer()
+        self.timer.timeout.connect(update_temp)
+        self.timer.start(30000)
+
+        self.skipturns = self.createLabel(
+            tr("Label", "O_skip:") + " " + str(bif.bk_get_config(board_id, 'orbits_skip')),
+            tooltip=tr("Tooltip", "Skipped orbits\nKlick to open acquire settings"),
+            click=True,
+            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
+        )
+        bif.bk_get_board_config(board_id).observe(self.skipturns,
+                                                 lambda x: self.skipturns.setText(tr("Label", "O_skip:")+" "+str(x)),
+                                                 'orbits_skip')
+        self.orbitsobserved = self.createLabel(
+            tr("Label", "O_obs:") + " " + str(bif.bk_get_config(board_id, 'orbits_observe')),
+            tooltip=tr("Tooltip", "Number of observed Orbits\nKlick to open acquire settings"),
+            click=True,
+            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
+        )
+        bif.bk_get_board_config(board_id).observe(
+                self.orbitsobserved,
+                lambda x: self.orbitsobserved.setText(tr("Label", "O_obs:") + " " + str(x)), 'orbits_observe')
+        self.orbitscount = self.createLabel(
+            tr("Label", "Count:") + " " + str(bif.bk_get_config(board_id, 'acquisition_count')),
+            tooltip=tr("Tooltip", "Number of acquisitions\nKlick to open acquire settings"),
+            click=True,
+            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
+        )
+        bif.bk_get_board_config(board_id).observe(
+                self.orbitscount,
+                lambda x: self.orbitscount.setText(tr("Label", "Count:") + " " + str(x)), 'acquisition_count')
+        self.orbitswait = self.createLabel(
+            tr("Label", "T_wait:") + " " + str(bif.bk_get_config(board_id, 'orbits_wait_time')),
+            tooltip=tr("Tooltip", "Time in seconds to wait between acquisitions\nKlick to open acquire settings"),
+            click=True,
+            connect=lambda: acqs.add_acquire_settings_widget(board_id=board_id)
+        )
+        bif.bk_get_board_config(board_id).observe(
+                self.orbitswait,
+                lambda x: self.orbitswait.setText(tr("Label", "T_wait:") + " " + str(x)), 'orbits_wait_time')
+
+        self.boardSpecificLayout = QtGui.QVBoxLayout()
+        self.setLayout(self.boardSpecificLayout)
+        self.layout = QtGui.QGridLayout()
+        self.layout.addWidget(self.temperature, 0, 0)
+        self.layout.addWidget(self.skipturns, 1, 1)
+        self.layout.addWidget(self.orbitsobserved, 1, 0)
+        self.layout.addWidget(self.orbitscount, 2, 0)
+        self.layout.addWidget(self.orbitswait, 2, 1)
+        self.boardSpecificLayout.addLayout(self.layout)
+
+
+class AcquisitionAndInfo(kcgw.KCGWidgets):
+    """
+    Widget to show below the plot list.
+    Show certain information.
+    Provide Start Acquisition button and settings.
+    """
+    # consistency_updated = QtCore.pyqtSignal(bool)
+    # acquisition_started_signal = QtCore.pyqtSignal(int)
+    def __init__(self):
+        super(AcquisitionAndInfo, self).__init__()
+        self.layout = QtGui.QVBoxLayout()
+        self.layout.setContentsMargins(0, 0, 0, 0)
+        self.setLayout(self.layout)
+
+
+        if available_boards.multi_board:
+            self.scroll_area = QtGui.QScrollArea()
+            self.layout.addWidget(self.scroll_area)
+            self.information_box = kcgw.AccordionWidget()
+            for brd in available_boards.board_ids:
+                self.information_box.addItem(BoardSpecificInformation(brd),
+                                             available_boards.get_board_name_from_id(brd))
+            self.scroll_area.setWidget(self.information_box)
+            self.scroll_area.setWidgetResizable(True)
+            self.information_box.hideAll()
+            self.information_box.expandIndex(0)
+        else:
+            self.layout.addWidget(BoardSpecificInformation(available_boards.board_ids[0]))
+
+        # -----[ Constistency ]-----------
+        self.consistency = QtGui.QHBoxLayout()
+        self.layout.addLayout(self.consistency)
+        self.consistency_label = self.createLabel("Consistency:")
+        self.consistency_content = self.createLabel(u"●")
+        self.consistency_content.setStyleSheet("color: lightgrey;")
+        self.consistency.addWidget(self.consistency_label)
+        self.consistency.addWidget(self.consistency_content)
+
+        # -------[ acquire ]--------------
+        self.acquire = self.createButton(tr("Button", "Start Acquisition"),
+                                         icon=QtGui.QIcon(config.install_path + config.startIcon),
+                                         connect=self.toggle_acquisition)
+        if not available_boards.multi_board:
+            Elements.addItem('acquireTrigger_{}'.format(available_boards.board_ids[0]), self.acquire)
+
+        if available_boards.multi_board:
+            self.list_to_acquire = {
+                i: self.createCheckbox(
+                        str(i),
+                        tooltip=str(tr("Tooltip",
+                                       "Check this checkbox if you want to acquire with board {board}")
+                                    ). format(board=i),
+                        connect=self.update_acq_tickboxes) for i in available_boards}
+            self.list_of_active_boards = {i: False for i in available_boards}
+            self.acq_list_layout = QtGui.QHBoxLayout()
+            for b_id, cb in self.list_to_acquire.iteritems():
+                self.acq_list_layout.addWidget(cb)
+                Elements.addItem('acquireTrigger_{}'.format(b_id), cb)
+                self.acquisition_stopped(b_id)
+
+        # -----[ log ]-------
+        self.log = self.createButton(tr("Button", "Log"),
+                                     icon=QtGui.QIcon(config.install_path + config.logIcon),
+                                     connect=lambda: log.log(additional="Manual Log"),
+                                     tooltip="Rightclick to Configure")
+        self.log_w_comment = self.createButton(tr("Button", "Log"),
+                                               icon=QtGui.QIcon(config.install_path + config.logCommentIcon),
+                                               connect=self.do_log_w_comment,
+                                               tooltip="Log with Comment\nRightclick to Configure")
+        self.log.contextMenuEvent = self.log_configure_context_menu
+        self.log_w_comment.contextMenuEvent = self.log_configure_context_menu
+
+        # self.layout.addLayout(self.consistency, 0, 0)  # TODO: für welches board? zum board???
+        if available_boards.multi_board:
+            self.layout.addLayout(self.acq_list_layout)
+        self.layout.addWidget(self.acquire)
+        self.logLayout = QtGui.QHBoxLayout()
+        self.logLayout.addWidget(self.log)
+        self.logLayout.addWidget(self.log_w_comment)
+        self.layout.addLayout(self.logLayout)
+
+        callbacks.add_callback('update_consistency', self.update_consistency)
+        callbacks.add_callback('acquisition_started', self.acquisition_started)
+        callbacks.add_callback('acquisition_stopped', self.acquisition_stopped)
+
+    def toggle_acquisition(self):
+        """
+        Turn acquisition on/off. This gets the information about which board to use by checking state of the checkboxes.
+        :return:
+        """
+        if not available_boards.multi_board:
+            bif.bk_acquire(available_boards.board_ids[0])
+            return
+        for b_id, cb in self.list_to_acquire.iteritems():
+            if cb.isChecked():
+                bif.bk_acquire(b_id)
+        for cb in self.list_to_acquire.values():
+            cb.setChecked(False)
+        self.update_acq_tickboxes()
+
+    def update_acq_tickboxes(self):
+        """
+        If at least one checkbox is checked, set all checkboxes with other state (acquiring or not)
+        to disabled. This prevents the user to start with one board and stop with the other simultaneously.
+        TODO: add option in settings to allow the now eliminated behaviour
+        """
+        active = False
+        inactive = False
+        for b_id, cb in self.list_to_acquire.iteritems():
+            if cb.isChecked():  # search for first checked box
+                if self.list_of_active_boards[b_id] is True:  # if board is acquiring
+                    active = True
+                    break
+                else:
+                    inactive = True
+                    break
+        if active:  # if checked box is for acquiring boards (meaning to stop them)
+            for b_id, state in self.list_of_active_boards.iteritems():
+                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)):
+                    self.list_to_acquire[b_id].setEnabled(state)  # set all active boards to enabled
+            self.acquire.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
+            self.acquire.setText(tr("Button", "Stop Acquisition"))
+        elif inactive:
+            for b_id, state in self.list_of_active_boards.iteritems():
+                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)) or bif.bk_get_board_status(b_id, 'acquisition'):
+                    self.list_to_acquire[b_id].setEnabled(not state)  # set all active boards to not enabled
+            self.acquire.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
+            self.acquire.setText(tr("Button", "Start Acquisition"))
+        else:  # if no board is selected
+            for b_id, cb in self.list_to_acquire.items():
+                if Elements.isEnabled('acquireTrigger_{}'.format(b_id)):
+                    cb.setEnabled(True)
+            self.acquire.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
+            self.acquire.setText(tr("Button", "Start Acquisition"))
+
+    def acquisition_stopped(self, board_id):
+        """
+        This is called when a acquisition is stopped.
+        (has to be registered in Callbacks under 'acquisition_stopped'
+        :param board_id: id of the board
+        """
+        if not available_boards.multi_board:
+            for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
+                if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
+                    continue
+                elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
+                elem.setText(tr("Button", "Start Acquisition"))
+            return
+        self.list_to_acquire[board_id].setStyleSheet('')
+        self.list_of_active_boards[board_id] = False
+        self.update_acq_tickboxes()
+
+    def acquisition_started(self, board_id):
+        """
+        This is called when a acquisition is started.
+        (has to be registered in Callbacks under 'acquisition_started'
+        :param board_id: id of the board
+        """
+        if not available_boards.multi_board:
+            for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
+                if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
+                    continue
+                elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
+                elem.setText(tr("Button", "Stop Acquisition"))
+            return
+        self.list_to_acquire[board_id].setStyleSheet("background-color: lightgreen;")
+        self.list_of_active_boards[board_id] = True
+        self.update_acq_tickboxes()
+
+    def do_log_w_comment(self):
+        """
+        Function to handle logging with comments
+        """
+        # text, ok = QtGui.QInputDialog.getText(self, tr("Heading", 'Log Comment'), tr("Label", 'Log Comment:'))
+        text, ok = kcgw.MultilineInputDialog().get_text(tr("Heading", "Log Comment"), tr("Label", "Log Comment:"))
+        if ok:
+            log.log(additional=text)
+
+    def log_configure_context_menu(self, event):
+        """
+        Function that creates the context menu for the log buttons
+        :param event: (QEvent) the event
+        :return: -
+        """
+        pos = event.globalPos()
+
+        def configure_log():
+            '''Open the configuration window for logs'''
+            c = log.ConfigureLog(self)
+            c.exec_()
+            del c
+        if pos is not None:
+            menu = QtGui.QMenu(self)
+            configure = menu.addAction(tr("Button", "Configure Log"))
+            configure.triggered.connect(configure_log)
+            menu.popup(pos)
+        event.accept()
+
+    def update_consistency(self, status):
+        """
+        Set consistency indicator
+        :param status: True if consistent, False if inconsistent, None is nothing
+        :return: -
+        """
+        if status is True:
+            self.consistency_content.setStyleSheet("color: green;")
+        elif status is False:
+            self.consistency_content.setStyleSheet("color: red;")
+        else:
+            self.consistency_content.setStyleSheet("color: lightgrey;")
+
+
+class LeftBar(kcgw.KCGWidgets):
+    """
+    Left bar in the main view of the gui.
+    Shows plot list and acquisition and info panel
+    """
+    # general notes:
+    # uid = unique_id
+    # usid = unique_sub_id
+    # upid = unique_parent_id
+    possiblePlots = ["Heatmap", "FFT", "Trains", "Combined", "Compare"]
+
+    def __init__(self, parent):
+        super(LeftBar, self).__init__("LeftBar")
+        self.parent = parent
+
+        self.openData = {}
+        self.openDataNames = {}
+        self.openPlots = {}
+        self.openPlotWindows = {}
+        self.Data = {}
+
+        self.initUI()
+
+    def initUI(self):
+        """
+        Initialise the UI
+        """
+        self.layout = QtGui.QVBoxLayout()
+        self.layout.setContentsMargins(0, 0, 5, 0)
+        self.setMinimumWidth(230)
+
+        self.treeWidget = QtGui.QTreeWidget()
+        self.treeWidget.setObjectName("tree_view")
+        self.treeWidget.setColumnCount(2)
+        self.treeWidget.resizeColumnToContents(1)
+#        self.treeWidget.header().resizeSection(0, 110)
+        self.treeWidget.setHeaderHidden(True)
+        self.treeWidget.itemClicked.connect(self.plot_clicked)
+        self.treeWidget.contextMenuEvent = self.contextMenuEventListView
+        self.rootItem = self.treeWidget.invisibleRootItem()
+
+        self.infoAndAcquisitionWidget = AcquisitionAndInfo()
+
+        self.layout.addWidget(self.treeWidget)
+        self.layout.addWidget(self.infoAndAcquisitionWidget)
+
+        self.setLayout(self.layout)
+
+    def _generate_menu(self, menu, item):
+        """
+        Generate the right click menu
+        :param menu: the menu to add the entries to
+        :param item: the item that was clicked on
+        """
+        heatmap = menu.addAction(tr("Button", "Heatmap"))
+        fft = menu.addAction(tr("Button", "FFT"))
+        trains = menu.addAction(tr("Button", "Trains"))
+        combined = menu.addAction(tr("Button", "Combined"))
+        compare = menu.addAction(tr("Button", "Compare"))
+
+        heatmap.triggered.connect(lambda: self.add_plot_node(
+                text=tr("Label", 'Heatmap'), unique_id=item.uid, d_type=0, datatype=item.datatype))
+        fft.triggered.connect(lambda: self.add_plot_node(
+                text=tr("Label", 'FFT'), unique_id=item.uid, d_type=1, datatype=item.datatype))
+        trains.triggered.connect(lambda: self.add_plot_node(
+                text=tr("Label", 'Trains'), unique_id=item.uid, d_type=2, datatype=item.datatype))
+        combined.triggered.connect(lambda: self.add_plot_node(
+                text=tr("Label", 'Combined'), unique_id=item.uid, d_type=3, datatype=item.datatype))
+        compare.triggered.connect(lambda: self.add_plot_node(
+                text=tr("Label", 'Combined'), unique_id=item.uid, d_type=4, datatype=item.datatype))
+
+    def contextMenuEventListView(self, event):
+        """
+        Gets called when right mouse button is clicked
+        :param event: the event that causes the call of this function
+        :return: -
+        """
+        pos = event.globalPos()
+        try:
+            item = self.treeWidget.selectedItems()[0]
+        except IndexError:
+            return
+        if pos is not None:
+            menu = QtGui.QMenu(self)
+            close = menu.addAction(tr("Button", "Close"))
+            if item.is_child:
+                close.triggered.connect(lambda: self.remove_plot(item.usid, silent=False, close_plot_window=True))
+            else:
+                close.triggered.connect(lambda: self.remove_plot_tree(item.uid))
+                self._generate_menu(menu, item)
+
+            menu.popup(pos)
+        event.accept()
+
+    def plot_clicked(self, item, i):
+        """
+        Function to handle when a plot item is clicked.
+        :param item: what item is clicked
+        :param i: what column
+        :return: -
+        """
+        if item.is_child:
+            self.openPlotWindows[item.usid].setFocus()
+        else:
+            if not item.ready:
+                return
+            if i == 1:
+                position = QtGui.QCursor.pos()
+                menu = QtGui.QMenu(self)
+
+                self._generate_menu(menu, item)
+
+                menu.popup(position)
+
+    def add_plot_tree(self, text=False, unique_id=None, datatype=None):
+        """
+        Add a top node to the plot list.
+        :param text: (str) text to be displayed in the plot list
+        :param unique_id: (int) unique id of this top node
+        :param datatype: (int) live or data from file
+        :return: -
+        """
+        if datatype == FILE:
+            file_name = QtGui.QFileDialog.getOpenFileName()
+            if file_name == "":
+                return
+            if file_name in self.openDataNames.itervalues():  # is this to prevent double opening?
+                return
+            self.openDataNames[unique_id] = file_name
+        else:
+            if text in self.openDataNames.itervalues():
+                return
+            self.openDataNames[unique_id] = QtCore.QString(text)  # Add a qstring for comparison
+        self.openData[unique_id] = QtGui.QTreeWidgetItem()
+        self.openData[unique_id].uid = unique_id
+        self.openData[unique_id].is_child = False
+        self.openData[unique_id].datatype = datatype
+        if datatype == FILE:
+            self.openData[unique_id].file_name = file_name
+        self.openData[unique_id].setText(0, "Live" if datatype == LIVE else file_name.split('/')[-1])
+        self.openData[unique_id].setToolTip(0, "Live" if datatype == LIVE else file_name.split('/')[-1])
+        self.openData[unique_id].setText(1, "Reading")
+        if datatype == LIVE:  # Insert LIVE on top
+            self.treeWidget.insertTopLevelItem(0, self.openData[unique_id])
+        else:
+            self.treeWidget.addTopLevelItem(self.openData[unique_id])
+        self.treeWidget.expandItem(self.openData[unique_id])
+        self.treeWidget.resizeColumnToContents(0)
+        self.openData[unique_id].ready = False  # indicates whether data was completely read or not
+        QtGui.qApp.processEvents()
+        try:
+            self.read_data(datatype, unique_id, file_name if datatype == FILE else None)
+        except Exception:
+            self.Data[unique_id] = None
+            self.remove_plot_tree(unique_id, silent=True)
+            QtGui.QMessageBox.critical(
+                    self,
+                    tr("Heading", "Error Reading Data"),
+                    tr("Dialog", "There was an error reading the datafile, make shure it is valid and try again."))
+        self.openData[unique_id].ready = True  # indicates whether data was completely read or not
+        self.openData[unique_id].setText(1, "+")
+        self.add_plot_node(d_type=0, unique_id=unique_id, datatype=datatype)
+
+    def read_data(self, d_type, uid, file_name):
+        """
+        Reads datafile
+        :param d_type: FILE or LIVE - type of datasource
+        :param uid: unique id for the treeelement
+        :param file_name: filename if type is FILE
+        :return: -
+        """
+        if d_type == FILE:
+            self.Data[uid] = io.read_from_file(file_name, header=storage.storage.header)
+        else:
+            self.Data[uid] = bif.livePlotData
+
+    def add_plot_node(self, board_id=None, text=False, unique_id=None, d_type=None, datatype=None):
+        """
+        Actually open a new plot window.
+        :param board_id: the id of the board to which the plot corresponds
+        :param text: (str) text that is displayed in the plot list
+        :param unique_id: (int) unique id of this plot window
+        :param d_type: (int) type of this plot window
+        :param datatype: (int) live or data from file
+        :return: -
+        """
+        if datatype == LIVE and board_id is None:  # get board_id for live plot from popup menu
+            # board_id = 0
+            if available_boards.multi_board:
+                # raise NotImplementedError("Dialog to ask for board is not implemented yet.")
+                position = QtGui.QCursor.pos()
+                menu = QtGui.QMenu(self)
+                for bid in available_boards:
+                    tmp = menu.addAction(available_boards.get_board_name_from_id(bid))
+                    tmp.triggered.connect(lambda _, b=bid: self.add_plot_node(board_id=b,
+                                                                           text=text,
+                                                                           unique_id=unique_id,
+                                                                           d_type=d_type,
+                                                                           datatype=datatype))
+                    # NOTE to the above: the lambda needs b=bid to capture the variable for the lambda
+                    # additionally the _ is needed because the call to the lambda includes an optional parameter
+                    # that is true or false, passed by pyqt
+                menu.popup(position)
+                return
+
+            else:
+                board_id = available_boards.board_ids[0]
+        # if not board_id:
+        #     TODO: Activate this (No Board Id Given)
+            # pass
+           # raise NoBoardId("No Board Id was given.")
+
+        unique_sub_id = kcgw.idg.genid()
+        nr = self.openData[unique_id].childCount()
+        if datatype == FILE:
+            file_name = self.openData[unique_id].file_name
+        else:
+            file_name = None
+        name = "Live" if not file_name else file_name.split('/')[-1]
+
+        self.openPlotWindows[unique_sub_id] = PlotWidget(
+                board_id=board_id,
+                parent=self,
+                name=name,
+                unique_id=unique_sub_id,
+                type=d_type,
+                datatype=datatype,
+                prefix=nr,
+                fName=file_name,
+                data=self.Data[unique_id]
+        )
+        self.openPlotWindows[unique_sub_id].change_type_signal.connect(self.update_plot)
+        self.openPlotWindows[unique_sub_id].upid = unique_id
+        self.parent.area.newWidget(
+                self.openPlotWindows[unique_sub_id],
+                name=text,
+                unique_id=unique_sub_id,
+                widget_type=d_type
+        )
+        self.openPlots[unique_sub_id] = QtGui.QTreeWidgetItem()
+        if datatype == LIVE:
+            brd = " B: "+available_boards.get_board_name_from_id(board_id)
+        else:
+            brd = ""
+        if d_type == 4:  # Compare Plot
+            adc = "1+2"
+        else:
+            adc = "1"
+        self.openPlots[unique_sub_id].is_child = True
+        self.openPlots[unique_sub_id].usid = unique_sub_id
+        self.openPlots[unique_sub_id].nr = nr
+        self.update_plot(unique_sub_id, d_type, adc+brd)
+        self.openData[unique_id].addChild(self.openPlots[unique_sub_id])
+        self.treeWidget.resizeColumnToContents(0)
+
+    @QtCore.pyqtSlot()
+    def remove_plot(self, unique_id, silent=False, close_plot_window=False):
+        """
+        Removes a plot from the plot list.
+        :param unique_id: (int) the unique id of the plot to remove
+        :param silent: (bool) Ask to close the plot source node in the list if the last plot window is closed
+                        (if False the source node will not be closed)
+        :param close_plot_window: (bool) close the corresponding plot window
+        :return: -
+        """
+        parent = self.openData[self.openPlotWindows[unique_id].upid]
+        if not silent:
+            reply = QtGui.QMessageBox()
+            reply.setIcon(QtGui.QMessageBox.Question)
+            reply.setWindowTitle(tr("Heading", 'Close Plot'))
+            reply.setText(tr("Dialog", "Close this plot?"))
+            reply.addButton(QtGui.QMessageBox.Yes)
+            reply.addButton(QtGui.QMessageBox.No)
+            reply.setDefaultButton(QtGui.QMessageBox.No)
+
+            if parent.childCount() == 1:
+                reply.addButton("Close Plot Source", QtGui.QMessageBox.ResetRole)
+
+            reply.exec_()
+
+            if reply.buttonRole(reply.clickedButton()) == QtGui.QMessageBox.ResetRole:
+                self.remove_plot_tree(parent.uid, silent=True)
+                return True
+            elif reply.buttonRole(reply.clickedButton()) == QtGui.QMessageBox.NoRole:
+                return False
+        if close_plot_window:
+            self.openPlotWindows[unique_id].close_silent = True
+            self.openPlotWindows[unique_id].close()
+        parent.removeChild(self.openPlots[unique_id])
+        self.openPlotWindows[unique_id] = None
+        self.openPlots[unique_id] = None
+        del self.openPlotWindows[unique_id]
+        del self.openPlots[unique_id]
+        return True
+
+    def remove_plot_tree(self, unique_id, silent=False):
+        """
+        Remove the top node from the plot list
+        :param unique_id: (int) the id of the top node
+        :param silent: (bool) ask to close even if corresponding plot windows are open
+        :return: -
+        """
+        if self.openData[unique_id].childCount() != 0 and not silent:
+            reply = QtGui.QMessageBox.question(
+                    self, tr("Heading", 'Close Data Source'), tr(
+                            "Dialog", "There are open plot windows.\n"
+                            "Close this plot source and all corresponding plot windows?"),
+                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
+                    QtGui.QMessageBox.No)
+            if reply == QtGui.QMessageBox.No:
+                return
+        for plot in self.openData[unique_id].takeChildren():
+            self.openPlotWindows[plot.usid].close_silent = True
+            self.openPlotWindows[plot.usid].close()
+            self.openData[unique_id].removeChild(plot)
+        self.rootItem.removeChild(self.openData[unique_id])
+        del self.openData[unique_id]
+        del self.openDataNames[unique_id]
+        del self.Data[unique_id]
+
+    @QtCore.pyqtSlot(int, int, str)
+    def update_plot(self, uid, b_type, add):
+        """
+        Updates the plot list when the type of a plot window changes
+        :param uid: (int) unique id of the plot window to update
+        :param b_type: (int) type of that plot window
+        :param str add: the add, board and additional text to update
+        :return: -
+        """
+        text = str(self.openPlots[uid].nr) + "." + self.possiblePlots[b_type] + " - ADC " + str(add)
+        self.openPlots[uid].setText(0, text)
+        self.openPlots[uid].setToolTip(0, text)
+        self.treeWidget.resizeColumnToContents(0)
+
+    def handle_dialog(self, value, diag=None,):
+        """
+        Function that handles the return of the dialog that asks for live or data from file
+        :param value: (int) the value of the pressed button in the dialog
+        :param diag: (QDialog) the dialog that is to be closed (optional)
+        :return: -
+        """
+        if diag:
+            diag.close()
+        s = kcgw.idg.genid()
+        if value == 1:
+            if tr("Label", "Live") in self.openDataNames.itervalues():
+                x = [bid for bid, obj in self.openData.iteritems() if obj.datatype == LIVE][0]
+                self.add_plot_node(unique_id=x, d_type=0, datatype=LIVE)
+            else:
+                self.add_plot_tree(text=tr("Label", "Live"), unique_id=s, datatype=LIVE)
+        elif value == 2:
+            self.add_plot_tree(text=tr("Label", "File"), unique_id=s, datatype=FILE)
+        else:
+            pass
+
+    def add_plot(self, d_type=None):
+        """
+        Handles the creation of a plot.
+        Also shows the dialog that asks for the data source.
+        :param d_type: the type of the plot to add
+        :return: -
+        """
+        if d_type == LIVE or d_type == FILE:
+            self.handle_dialog(value=d_type)
+            return
+        ask = QtGui.QDialog()
+        window = self.parent.geometry()
+        ask_layout = QtGui.QVBoxLayout()
+        ask_layout.addWidget(QtGui.QLabel(tr("Dialog", "Open file from disk or read live data?")))
+
+        button_layout = QtGui.QHBoxLayout()
+        ask_button_live = QtGui.QPushButton(tr("Button", "Live"))
+        ask_button_file = QtGui.QPushButton(tr("Button", "Open File"))
+        ask_button_cancel = QtGui.QPushButton(tr("Button", "Cancel"))
+
+        ask_button_live.pressed.connect(lambda: self.handle_dialog(diag=ask, value=1))
+        ask_button_file.pressed.connect(lambda: self.handle_dialog(diag=ask, value=2))
+        ask_button_cancel.pressed.connect(lambda: self.handle_dialog(diag=ask, value=3))
+
+        button_layout.addWidget(ask_button_live)
+        button_layout.addWidget(ask_button_file)
+        button_layout.addWidget(ask_button_cancel)
+
+        ask_layout.addLayout(button_layout)
+        ask.setGeometry(window.width()/2.-100, window.height()/2.-50, 200, 100)
+
+        ask.setLayout(ask_layout)
+        ask.exec_()

+ 360 - 333
KCG/base/log.py

@@ -1,333 +1,360 @@
-"""
-This is a custom logfile creation module
-"""
-
-import datetime
-import os
-from PyQt4 import QtGui, QtCore
-# from backend import board
-from .backend.board import available_boards
-from . import backendinterface as bif
-import numpy as np
-import codecs
-from .. import config
-from . import storage
-import logging
-
-
-
-# after = datetime.datetime.now()
-# if (after-before).total_seconds() > 3.0:
-#     no_epics = True
-#     logging.error("Epics is not accessible (Timeout)")
-
-# tr = kcgw.tr
-tr = lambda _, x: x  # log entries will not be translated
-
-
-class LogLevels:  # Keep every new value an integer. Every level with higher value will include those with lower level
-    """
-    Used Log Levels Container
-    """
-    NONE = -1
-    INFO = 0
-    DEBUG = 1
-
-TWO_COLUMNS = False
-BOARDID = "BOARD_ID"
-
-
-class MeasurementLogger(object):
-    """
-    Logfile creator class
-    It will automatically get the info needed to create a logfile entry from registered functions.
-    """
-    def __init__(self, filename=None, level=LogLevels.INFO, oneline=False):
-        """
-        Initialise the logfile creator
-        :param filename: (str) filename of the logfile THIS IS IGNORED AS OF NOW
-        :param level: (int) valid log level (see LogLevels)
-        :param oneline: (bool) whether to format the logfile entries in multilines or one line
-        :return: -
-        """
-        self.level = level
-        self.filename = filename
-        self.parameter_functions = []
-        self.oneline = oneline
-        self.dumper = None
-
-        self.predefined_parameters = [  # format: [logstring, [function (args,)]]
-            ["Number of Orbits", [bif.bk_get_config, (BOARDID, 'orbits_observe')]],
-            ["Number of Skipped Orbits", [bif.bk_get_config, (BOARDID, 'orbits_skip')]],
-            ["Number of Acquisitions", [bif.bk_get_config, (BOARDID, 'acquisition_count')]],
-            ["Time between Acquisitions", [bif.bk_get_config, (BOARDID, 'orbits_wait_time')]],
-            ["Pilot Bunch Simulator", [bif.bk_get_config, (BOARDID, 'pilot_bunch')]],
-            ["Header saved", [bif.bk_get_config, (BOARDID, 'header')]],
-            ["T/H Delay", [bif.bk_get_config, (BOARDID, 'th_delay')]],
-            ["ADC 1 Delay", [bif.bk_get_config, (BOARDID, 'chip_1_delay')]],
-            ["ADC 2 Delay", [bif.bk_get_config, (BOARDID, 'chip_2_delay')]],
-            ["ADC 3 Delay", [bif.bk_get_config, (BOARDID, 'chip_3_delay')]],
-            ["ADC 4 Delay", [bif.bk_get_config, (BOARDID, 'chip_4_delay')]]
-        ]
-        self.gui_values_number = len(self.predefined_parameters)
-
-    def __now(self):
-        """
-        Get the current time in %Y-%m-%d %H:%M:%S format
-        :return: the time
-        """
-        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-
-    def _logging_formatter(self, info):
-        """
-        Formatter for the logfile entries
-        Override this to implement custom formatters
-        """
-        if self.oneline:
-            return self.__now() + " " + ", ".join(info)
-        return self.__now() + "\n\t" + "\n\t".join(info)
-
-    def _log(self, board_id, stuff):
-        """
-        Write entries to logfile
-        This will also make shure the directory the logfile is located is available and if not creates it
-        :param stuff: (str) the entrie to write
-        :return: -
-        """
-        # if not self.filename:
-        #     filename = storage.storage.save_location + '/' + storage.storage.subdirname + "/Measurement.log"
-        # else:
-        #     filename = self.filename
-        filename = os.path.join(storage.storage.save_location, storage.storage.subdirname,"Measurement_board_" 
-                                + available_boards.get_board_name_from_id(board_id).replace(' ', '_') + ".log")
-        if not os.path.isdir(str(os.path.join(storage.storage.save_location, storage.storage.subdirname))):
-            os.makedirs(str(os.path.join(storage.storage.save_location, storage.storage.subdirname)))
-        f = codecs.open(filename, 'a', encoding='utf-8')
-        f.write(stuff+'\n\n')
-        f.close()
-
-    def register_parameter(self, logstring, func, args):
-        """
-        This function is used to register parameters for the logfile entries
-        :param logstring: (str) the string to describe the value in the logfile
-        :param func: (callable) the function used to get the value
-        :param args: arguments to pass to func upon gathering of information
-        :return: -
-        """
-        if not isinstance(args, tuple):
-            args = (args,)
-        self.parameter_functions.append([func, args, logstring])
-        # self.parameter_functions[logstring] = [func, args, logstring]
-
-    def reset_parameters(self):
-        """
-        Resets all parameters (unregisteres all parameters)
-        """
-        self.parameter_functions = []
-
-    def register_dumper(self, func):
-        """
-        Register a dumper that is used to dump a log of data into the logfile
-        :param func: (callable) Function that is used as dumper
-        :return: -
-        """
-        self.dumper = func
-
-    def dump(self, board_id):
-        """
-        Perform a dump (see register_dumper)
-        :return: -
-        """
-        self.do_log(board_id=board_id, c=self.__now() + " " + self.dumper())
-
-    def do_log(self, c=None, additional=None, board_id=None, level=LogLevels.INFO):
-        """
-        Perform a log. This creates a log entry in the logfile
-        :param c: (str) if this is not None this is the only text written to the logfile (in addition to the current time)
-        :param additional: (str) This text is written below the current time to customize log entries
-        :param board_id: the id of the board to log for, if this is not given, all boards are logged
-        :return: -
-        """
-        if level > self.level:
-            return
-        if c:
-            if board_id is not None:
-                self._log(board_id, c)
-            else:
-                for bid in available_boards:
-                    self._log(bid, c)
-        else:
-            def do_the_log(bid):
-                '''Actually perform the log'''
-                st = []
-                if additional:
-                    s = additional.split('\n')[0]
-                    for line in additional.split('\n')[1:]:
-                        s += "\n\t"+line
-                    st.append(s)
-                for par in self.parameter_functions:
-                    par[1] = list(par[1])
-                    for i, p in enumerate(par[1]):
-                        if p == BOARDID:
-                            par[1][i] = bid
-                    st.append(par[2] + ": " + str(par[0](*par[1])))
-                return st
-            if board_id is not None:
-                st = do_the_log(board_id)
-                self._log(board_id, self._logging_formatter(st))
-            else:
-                for bid in available_boards:
-                    st = do_the_log(bid)
-                    self._log(bid, self._logging_formatter(st))
-
-logger = None
-dump_general = False
-
-
-def log(c=None, additional=None, dump=False, board_id=None, level=LogLevels.INFO):
-    """
-    Execute logging if logger was registered
-    :param (str) c: if this is given c is the only entry that is created (additional is ignored)
-    :param (str) additional: the additional text that is written below the current time
-    :param (bool) dump: if this is true, a dump of every log value is performed
-    :param board_id: the board id to log for, if this is not given, all boards are logged
-    :return: -
-    """
-    global logger, dump_general
-    if logger:
-        if dump or dump_general:
-            logger.dump(board_id=board_id)
-        else:
-            logger.do_log(c, additional, board_id=board_id, level=level)
-    else:
-        print("No Logger registered")
-
-
-class ConfigureLog(QtGui.QDialog):
-    """
-    Class that is basically a QDialog to configure the logger
-    """
-    def __init__(self, parent=None):  # parameter parent does nothing when adding parent to the folowing __init__
-                                        # no entry in taskbar is made
-        super(ConfigureLog, self).__init__()
-
-        self.setWindowTitle("Configure Measurement Log Entries")
-        self.setWindowIcon(QtGui.QIcon(config.icon_path(config.guiIcon)))
-
-        self.sl = QtGui.QVBoxLayout()
-        self.setLayout(self.sl)
-        self.scrollArea = QtGui.QScrollArea()
-        if TWO_COLUMNS:
-            self.scrollArea.setMinimumSize(410, 300)
-        else:
-            self.scrollArea.setMinimumSize(250, 520)
-        self.sl.addWidget(self.scrollArea)
-        self.widget = QtGui.QWidget()
-
-        if TWO_COLUMNS:
-            self.layout = QtGui.QGridLayout()
-        else:
-            self.layout = QtGui.QVBoxLayout()
-        self.widget.setLayout(self.layout)
-
-        self.parameter = []
-
-        for par in logger.predefined_parameters:
-            self.parameter.append((QtGui.QCheckBox(par[0]), par[1]))
-
-        self.i = 0
-
-        def add(w):
-            '''add a widget to the layout. if w is 'sep' a seperator is added'''
-            if w == 'sep':
-                sep = QtGui.QFrame()
-                sep.setFrameShape(QtGui.QFrame.HLine)
-                w = sep
-            self.layout.addWidget(w, self.i//2, self.i%2)
-            self.i += 1
-        for i, (cb, _) in enumerate(self.parameter):
-            if TWO_COLUMNS:
-                if i == logger.gui_values_number:
-                    # sep1 = QtGui.QFrame()
-                    # sep1.setFrameShape(QtGui.QFrame.HLine)
-                    # sep2 = QtGui.QFrame()
-                    # sep2.setFrameShape(QtGui.QFrame.HLine)
-                    if logger.gui_values_number%2 == 1:
-                        self.i += 1
-                    # add(sep1)
-                    # add(sep2)
-                    add('sep')
-                    add('sep')
-                add(cb)
-
-            else:
-                if i == logger.gui_values_number:
-                    sep = QtGui.QFrame()
-                    sep.setFrameShape(QtGui.QFrame.HLine)
-                    self.layout.addWidget(sep)
-                self.layout.addWidget(cb)
-
-
-        self.all = QtGui.QCheckBox(tr("Label", "All of the above"))
-        self.all.clicked.connect(self.all_changed)
-
-        if TWO_COLUMNS:
-            if (len(logger.predefined_parameters)-1)%2 == 1:
-                self.i += 1
-            add('sep')
-            add('sep')
-            add(self.all)
-        else:
-            sepa = QtGui.QFrame()
-            sepa.setFrameShape(QtGui.QFrame.HLine)
-            self.layout.addWidget(sepa)
-            self.layout.addWidget(self.all)
-
-        self.buttonLayout = QtGui.QHBoxLayout()
-        self.sl.addLayout(self.buttonLayout)
-        self.ok = QtGui.QPushButton(tr("Button", "OK"))
-        self.cancel = QtGui.QPushButton(tr("Button", "Cancel"))
-        self.buttonLayout.addWidget(self.ok)
-        self.buttonLayout.addWidget(self.cancel)
-        self.ok.pressed.connect(self.do)
-        self.cancel.pressed.connect(self.close)
-
-        self.scrollArea.setWidget(self.widget)
-
-        self.initial_ticks()
-
-    def initial_ticks(self):
-        """
-        Set the initial ticks.
-        """
-        pf = np.array(logger.parameter_functions)
-        for cb, _ in self.parameter:
-            cb.setChecked(str(cb.text()) in pf[:, 2])
-
-    def all_changed(self):
-        """
-        This is called when the "all" checkbox is checked. It de/activates the other checkboxes
-        """
-        if self.all.isChecked():
-            for cb, _ in self.parameter:
-                cb.setEnabled(False)
-        else:
-             for cb, _ in self.parameter:
-                cb.setEnabled(True)
-
-    def do(self):
-        """
-        Change the logger instance to the activated log entries
-        :return:
-        """
-        global logger
-        logger.reset_parameters()
-        def nif(cb, f, args):
-            '''actually set the new parameters'''
-            if cb.isChecked() or self.all.isChecked():
-                logger.register_parameter(str(cb.text()), f, args)
-        for cb, par in self.parameter:
-            nif(cb, par[0], par[1])
-
-        self.close()
-
+"""
+This is a custom logfile creation module
+"""
+
+import datetime
+import os
+from PyQt4 import QtGui, QtCore
+# from backend import board
+from backend.board import available_boards
+import backendinterface as bif
+import numpy as np
+import codecs
+from .. import config
+import storage
+import logging
+
+epics_reachable = True
+try:  # try to import epics
+    import epics
+    no_epics = False
+
+    os.environ["EPICS_BASE"] = config.epics_base_path
+
+    try:  # if import was successful, try to find libca and test for connectivity with config.epics_test_pv
+        # sys.stderr = os.devnull  # epics.ca.find_libca() prints a useless None to stderr if libca is not found
+        epics.ca.find_libca()
+        # sys.stderr = sys.__stderr__
+        if epics.caget(config.epics_test_pv) == None:
+            no_epics = True
+            epics_reachable = False
+            logging.error("Epics is not accessible (possible Timeout)")
+
+    except epics.ca.ChannelAccessException as e:
+        if str(e) == "cannot find Epics CA DLL":
+            no_epics = True
+            logging.error("Epics CA DLL not found")
+
+
+except ImportError:
+    no_epics = True
+
+# after = datetime.datetime.now()
+# if (after-before).total_seconds() > 3.0:
+#     no_epics = True
+#     logging.error("Epics is not accessible (Timeout)")
+
+# tr = kcgw.tr
+tr = lambda _, x: x  # log entries will not be translated
+
+
+class LogLevels:  # Keep every new value an integer. Every level with higher value will include those with lower level
+    """
+    Used Log Levels Container
+    """
+    NONE = -1
+    INFO = 0
+    DEBUG = 1
+
+TWO_COLUMNS = False
+BOARDID = "BOARD_ID"
+
+
+class MeasurementLogger(object):
+    """
+    Logfile creator class
+    It will automatically get the info needed to create a logfile entry from registered functions.
+    """
+    def __init__(self, filename=None, level=LogLevels.INFO, oneline=False):
+        """
+        Initialise the logfile creator
+        :param filename: (str) filename of the logfile THIS IS IGNORED AS OF NOW
+        :param level: (int) valid log level (see LogLevels)
+        :param oneline: (bool) whether to format the logfile entries in multilines or one line
+        :return: -
+        """
+        self.level = level
+        self.filename = filename
+        self.parameter_functions = []
+        self.oneline = oneline
+        self.dumper = None
+
+        self.predefined_parameters = [  # format: [logstring, [function (args,)]]
+            ["Number of Orbits", [bif.bk_get_config, (BOARDID, 'orbits_observe')]],
+            ["Number of Skipped Orbits", [bif.bk_get_config, (BOARDID, 'orbits_skip')]],
+            ["Number of Acquisitions", [bif.bk_get_config, (BOARDID, 'acquisition_count')]],
+            ["Time between Acquisitions", [bif.bk_get_config, (BOARDID, 'orbits_wait_time')]],
+            ["Pilot Bunch Simulator", [bif.bk_get_config, (BOARDID, 'pilot_bunch')]],
+            ["Header saved", [bif.bk_get_config, (BOARDID, 'header')]],
+            ["T/H Delay", [bif.bk_get_config, (BOARDID, 'th_delay')]],
+            ["ADC 1 Delay", [bif.bk_get_config, (BOARDID, 'chip_1_delay')]],
+            ["ADC 2 Delay", [bif.bk_get_config, (BOARDID, 'chip_2_delay')]],
+            ["ADC 3 Delay", [bif.bk_get_config, (BOARDID, 'chip_3_delay')]],
+            ["ADC 4 Delay", [bif.bk_get_config, (BOARDID, 'chip_4_delay')]]
+        ]
+        self.gui_values_number = len(self.predefined_parameters)
+        if not no_epics:
+            for entr in config.epics_log_entry_pvs:
+                self.predefined_parameters.append([entr[0], [epics.caget, entr[1]]])
+
+    def __now(self):
+        """
+        Get the current time in %Y-%m-%d %H:%M:%S format
+        :return: the time
+        """
+        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+    def _logging_formatter(self, info):
+        """
+        Formatter for the logfile entries
+        Override this to implement custom formatters
+        """
+        if self.oneline:
+            return self.__now() + " " + ", ".join(info)
+        return self.__now() + "\n\t" + "\n\t".join(info)
+
+    def _log(self, board_id, stuff):
+        """
+        Write entries to logfile
+        This will also make shure the directory the logfile is located is available and if not creates it
+        :param stuff: (str) the entrie to write
+        :return: -
+        """
+        # if not self.filename:
+        #     filename = storage.storage.save_location + '/' + storage.storage.subdirname + "/Measurement.log"
+        # else:
+        #     filename = self.filename
+        filename = storage.storage.save_location + '/' + \
+            storage.storage.subdirname + '/' + "/Measurement_board_" + \
+            available_boards.get_board_name_from_id(board_id).replace(' ', '_') + ".log"
+        if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname)):
+            os.makedirs(str(storage.storage.save_location + '/' + storage.storage.subdirname))
+        f = codecs.open(filename, 'a', encoding='utf-8')
+        f.write(stuff+'\n\n')
+        f.close()
+
+    def register_parameter(self, logstring, func, args):
+        """
+        This function is used to register parameters for the logfile entries
+        :param logstring: (str) the string to describe the value in the logfile
+        :param func: (callable) the function used to get the value
+        :param args: arguments to pass to func upon gathering of information
+        :return: -
+        """
+        if not isinstance(args, tuple):
+            args = (args,)
+        self.parameter_functions.append([func, args, logstring])
+        # self.parameter_functions[logstring] = [func, args, logstring]
+
+    def reset_parameters(self):
+        """
+        Resets all parameters (unregisteres all parameters)
+        """
+        self.parameter_functions = []
+
+    def register_dumper(self, func):
+        """
+        Register a dumper that is used to dump a log of data into the logfile
+        :param func: (callable) Function that is used as dumper
+        :return: -
+        """
+        self.dumper = func
+
+    def dump(self, board_id):
+        """
+        Perform a dump (see register_dumper)
+        :return: -
+        """
+        self.do_log(board_id=board_id, c=self.__now() + " " + self.dumper())
+
+    def do_log(self, c=None, additional=None, board_id=None, level=LogLevels.INFO):
+        """
+        Perform a log. This creates a log entry in the logfile
+        :param c: (str) if this is not None this is the only text written to the logfile (in addition to the current time)
+        :param additional: (str) This text is written below the current time to customize log entries
+        :param board_id: the id of the board to log for, if this is not given, all boards are logged
+        :return: -
+        """
+        if level > self.level:
+            return
+        if c:
+            if board_id is not None:
+                self._log(board_id, c)
+            else:
+                for bid in available_boards:
+                    self._log(bid, c)
+        else:
+            def do_the_log(bid):
+                '''Actually perform the log'''
+                st = []
+                if additional:
+                    s = additional.split('\n')[0]
+                    for line in additional.split('\n')[1:]:
+                        s += "\n\t"+line
+                    st.append(s)
+                for par in self.parameter_functions:
+                    par[1] = list(par[1])
+                    for i, p in enumerate(par[1]):
+                        if p == BOARDID:
+                            par[1][i] = bid
+                    st.append(par[2] + ": " + str(par[0](*par[1])))
+                return st
+            if board_id is not None:
+                st = do_the_log(board_id)
+                self._log(board_id, self._logging_formatter(st))
+            else:
+                for bid in available_boards:
+                    st = do_the_log(bid)
+                    self._log(bid, self._logging_formatter(st))
+
+logger = None
+dump_general = False
+
+
+def log(c=None, additional=None, dump=False, board_id=None, level=LogLevels.INFO):
+    """
+    Execute logging if logger was registered
+    :param (str) c: if this is given c is the only entry that is created (additional is ignored)
+    :param (str) additional: the additional text that is written below the current time
+    :param (bool) dump: if this is true, a dump of every log value is performed
+    :param board_id: the board id to log for, if this is not given, all boards are logged
+    :return: -
+    """
+    global logger, dump_general
+    if logger:
+        if dump or dump_general:
+            logger.dump(board_id=board_id)
+        else:
+            logger.do_log(c, additional, board_id=board_id, level=level)
+    else:
+        print ("No Logger registered")
+
+
+class ConfigureLog(QtGui.QDialog):
+    """
+    Class that is basically a QDialog to configure the logger
+    """
+    def __init__(self, parent=None):  # parameter parent does nothing when adding parent to the folowing __init__
+                                        # no entry in taskbar is made
+        super(ConfigureLog, self).__init__()
+
+        self.setWindowTitle("Configure Measurement Log Entries")
+        self.setWindowIcon(QtGui.QIcon(config.install_path + config.guiIcon))
+
+        self.sl = QtGui.QVBoxLayout()
+        self.setLayout(self.sl)
+        self.scrollArea = QtGui.QScrollArea()
+        if TWO_COLUMNS:
+            self.scrollArea.setMinimumSize(410, 300)
+        else:
+            self.scrollArea.setMinimumSize(250, 520)
+        self.sl.addWidget(self.scrollArea)
+        self.widget = QtGui.QWidget()
+
+        if TWO_COLUMNS:
+            self.layout = QtGui.QGridLayout()
+        else:
+            self.layout = QtGui.QVBoxLayout()
+        self.widget.setLayout(self.layout)
+
+        self.parameter = []
+
+        for par in logger.predefined_parameters:
+            self.parameter.append((QtGui.QCheckBox(par[0]), par[1]))
+
+        self.i = 0
+
+        def add(w):
+            '''add a widget to the layout. if w is 'sep' a seperator is added'''
+            if w == 'sep':
+                sep = QtGui.QFrame()
+                sep.setFrameShape(QtGui.QFrame.HLine)
+                w = sep
+            self.layout.addWidget(w, self.i//2, self.i%2)
+            self.i += 1
+        for i, (cb, _) in enumerate(self.parameter):
+            if TWO_COLUMNS:
+                if i == logger.gui_values_number:
+                    # sep1 = QtGui.QFrame()
+                    # sep1.setFrameShape(QtGui.QFrame.HLine)
+                    # sep2 = QtGui.QFrame()
+                    # sep2.setFrameShape(QtGui.QFrame.HLine)
+                    if logger.gui_values_number%2 == 1:
+                        self.i += 1
+                    # add(sep1)
+                    # add(sep2)
+                    add('sep')
+                    add('sep')
+                add(cb)
+
+            else:
+                if i == logger.gui_values_number:
+                    sep = QtGui.QFrame()
+                    sep.setFrameShape(QtGui.QFrame.HLine)
+                    self.layout.addWidget(sep)
+                self.layout.addWidget(cb)
+
+
+        self.all = QtGui.QCheckBox(tr("Label", "All of the above"))
+        self.all.clicked.connect(self.all_changed)
+
+        if TWO_COLUMNS:
+            if (len(logger.predefined_parameters)-1)%2 == 1:
+                self.i += 1
+            add('sep')
+            add('sep')
+            add(self.all)
+        else:
+            sepa = QtGui.QFrame()
+            sepa.setFrameShape(QtGui.QFrame.HLine)
+            self.layout.addWidget(sepa)
+            self.layout.addWidget(self.all)
+
+        self.buttonLayout = QtGui.QHBoxLayout()
+        self.sl.addLayout(self.buttonLayout)
+        self.ok = QtGui.QPushButton(tr("Button", "OK"))
+        self.cancel = QtGui.QPushButton(tr("Button", "Cancel"))
+        self.buttonLayout.addWidget(self.ok)
+        self.buttonLayout.addWidget(self.cancel)
+        self.ok.pressed.connect(self.do)
+        self.cancel.pressed.connect(self.close)
+
+        self.scrollArea.setWidget(self.widget)
+
+        self.initial_ticks()
+
+    def initial_ticks(self):
+        """
+        Set the initial ticks.
+        """
+        pf = np.array(logger.parameter_functions)
+        for cb, _ in self.parameter:
+            cb.setChecked(str(cb.text()) in pf[:, 2])
+
+    def all_changed(self):
+        """
+        This is called when the "all" checkbox is checked. It de/activates the other checkboxes
+        """
+        if self.all.isChecked():
+            for cb, _ in self.parameter:
+                cb.setEnabled(False)
+        else:
+             for cb, _ in self.parameter:
+                cb.setEnabled(True)
+
+    def do(self):
+        """
+        Change the logger instance to the activated log entries
+        :return:
+        """
+        global logger
+        logger.reset_parameters()
+        def nif(cb, f, args):
+            '''actually set the new parameters'''
+            if cb.isChecked() or self.all.isChecked():
+                logger.register_parameter(str(cb.text()), f, args)
+        for cb, par in self.parameter:
+            nif(cb, par[0], par[1])
+
+        self.close()
+

+ 147 - 147
KCG/base/loghandler.py

@@ -1,147 +1,147 @@
-import logging
-from logging import handlers
-import os
-import sys
-from PyQt4 import QtGui, QtCore
-
-from .. import config
-
-"""
-This intends to be a QThread save logging handler with syntax highlighting
-"""
-
-
-class LogHandler(logging.Handler):
-    """
-    Handler Class to configure Logging
-    This also enables logging to the output field in the gui
-    """
-    def __init__(self, log_area):
-        logging.Handler.__init__(self)
-        self.text_area = log_area
-        self.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
-        self._normal_weight = self.text_area.fontWeight()
-        self._bold = 100
-
-    def emit(self, record):
-        """
-        Emit a log record
-        """
-        self.acquire()
-        self.text_area.append_signal.emit(record)
-        self.release()
-
-
-class Highlighter(QtGui.QSyntaxHighlighter):
-    """
-    Highlighter for the logging area
-    """
-    def __init__(self, parent, theme):
-        super(Highlighter, self).__init__(parent, )
-        self.parent = parent
-        self.highlightingRules = []
-
-        timestamp = QtGui.QTextCharFormat()
-        timestamp.setForeground(QtCore.Qt.blue)
-        timestamp.setFontWeight(QtGui.QFont.Bold)
-        self.timestamp_pattern = QtCore.QRegExp(r"^.{23}")
-        self.timestamp_pattern.setMinimal(True)
-        self.highlightingRules.append((self.timestamp_pattern, timestamp))
-
-        loglevel = QtGui.QTextCharFormat()
-        loglevel.setForeground(QtCore.Qt.black)
-        loglevel.setFontWeight(QtGui.QFont.Bold)
-        self.loglevel_pattern = QtCore.QRegExp(r":[A-Z\_]{3,13}:")
-        self.loglevel_pattern.setMinimal(True)
-        self.highlightingRules.append((self.loglevel_pattern, loglevel))
-
-        success = QtGui.QTextCharFormat()
-        success.setForeground(QtGui.QColor(30, 210, 0))
-        success.setFontWeight(QtGui.QFont.Bold)
-        pattern = QtCore.QRegExp(r".*(S|s)uccess.*")
-        self.highlightingRules.append((pattern, success))
-
-        error = QtGui.QTextCharFormat()
-        error.setBackground(QtGui.QColor(255, 50, 50))
-        self.error_pattern = QtCore.QRegExp("ERROR")
-        self.error_pattern.setMinimal(True)
-        self.highlightingRules.append((self.error_pattern, error))
-
-    def setKeywords(self, kw):
-        """
-        Set the keywords to check for
-        :param kw: the keywords
-        """
-        keyword = QtGui.QTextCharFormat()
-        keyword.setForeground(QtCore.Qt.darkBlue)
-        keyword.setFontWeight(QtGui.QFont.Bold)
-        keywords = QtCore.QStringList(kw)
-        for word in keywords:
-            pattern = QtCore.QRegExp("\\b" + word + "\\b")
-            self.highlightingRules.append((pattern, keyword))
-
-    def highlightBlock(self, text):
-        """
-        Highlight a block of text
-        :param text: the text to check in
-        """
-        for pattern, format in self.highlightingRules:
-            start = 0
-            if pattern not in [self.timestamp_pattern, self.error_pattern, self.loglevel_pattern]:
-                start = self.loglevel_pattern.indexIn(text)
-                start += self.loglevel_pattern.matchedLength()
-
-            index = pattern.indexIn(text, offset=start)
-
-            while index >= 0:
-                length = pattern.matchedLength()
-                pref_format = self.format(index)
-                pref_format.merge(format)
-                self.setFormat(index, length, pref_format)
-                index = text.indexOf(pattern, index + length)
-
-        self.setCurrentBlockState(0)
-
-
-class LogArea(QtGui.QTextEdit):
-    """
-    The log area for the KCG Gui
-    """
-    append_signal = QtCore.pyqtSignal(logging.LogRecord)
-
-    def __init__(self):
-        super(LogArea, self).__init__()
-        self.highlighter = Highlighter(self, 'Classic')
-        self.logHandler = None
-        self.streamLogger = logging.StreamHandler(stream=sys.stdout)
-        self.streamLogger.setLevel(config.log_level)
-        self.streamLogger.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
-        logging.getLogger().addHandler(self.streamLogger)
-        self.fileLogHandler = handlers.RotatingFileHandler(os.path.expanduser("~")+"/.kcg/kcg.log", maxBytes=10**7, backupCount=5)
-        self.fileLogHandler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
-        self.fileLogHandler.setLevel(config.log_level)
-        logging.getLogger().addHandler(self.fileLogHandler)
-        self.append_signal.connect(self.append)
-
-    def init_logging(self):
-        """
-        Initialize logging
-        """
-        self.logHandler = LogHandler(self)
-        self.logHandler.setLevel(config.log_level)
-        logging.getLogger().addHandler(self.logHandler)
-
-    def append(self, record):
-        """
-        Append to the logarea
-        :param record: the record to append
-        """
-        super(LogArea, self).append(self.logHandler.format(record))
-        self.ensureCursorVisible()
-
-    def setKeywords(self, kw):
-        """
-        Set the keywords for the highlighter
-        :param kw: the keywords to set
-        """
-        self.highlighter.setKeywords(kw)
+import logging
+from logging import handlers
+import os
+import sys
+from PyQt4 import QtGui, QtCore
+
+from .. import config
+
+"""
+This intends to be a QThread save logging handler with syntax highlighting
+"""
+
+
+class LogHandler(logging.Handler):
+    """
+    Handler Class to configure Logging
+    This also enables logging to the output field in the gui
+    """
+    def __init__(self, log_area):
+        logging.Handler.__init__(self)
+        self.text_area = log_area
+        self.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
+        self._normal_weight = self.text_area.fontWeight()
+        self._bold = 100
+
+    def emit(self, record):
+        """
+        Emit a log record
+        """
+        self.acquire()
+        self.text_area.append_signal.emit(record)
+        self.release()
+
+
+class Highlighter(QtGui.QSyntaxHighlighter):
+    """
+    Highlighter for the logging area
+    """
+    def __init__(self, parent, theme):
+        super(Highlighter, self).__init__(parent, )
+        self.parent = parent
+        self.highlightingRules = []
+
+        timestamp = QtGui.QTextCharFormat()
+        timestamp.setForeground(QtCore.Qt.blue)
+        timestamp.setFontWeight(QtGui.QFont.Bold)
+        self.timestamp_pattern = QtCore.QRegExp(r"^.{23}")
+        self.timestamp_pattern.setMinimal(True)
+        self.highlightingRules.append((self.timestamp_pattern, timestamp))
+
+        loglevel = QtGui.QTextCharFormat()
+        loglevel.setForeground(QtCore.Qt.black)
+        loglevel.setFontWeight(QtGui.QFont.Bold)
+        self.loglevel_pattern = QtCore.QRegExp(r":[A-Z\_]{3,13}:")
+        self.loglevel_pattern.setMinimal(True)
+        self.highlightingRules.append((self.loglevel_pattern, loglevel))
+
+        success = QtGui.QTextCharFormat()
+        success.setForeground(QtGui.QColor(30, 210, 0))
+        success.setFontWeight(QtGui.QFont.Bold)
+        pattern = QtCore.QRegExp(r".*(S|s)uccess.*")
+        self.highlightingRules.append((pattern, success))
+
+        error = QtGui.QTextCharFormat()
+        error.setBackground(QtGui.QColor(255, 50, 50))
+        self.error_pattern = QtCore.QRegExp("ERROR")
+        self.error_pattern.setMinimal(True)
+        self.highlightingRules.append((self.error_pattern, error))
+
+    def setKeywords(self, kw):
+        """
+        Set the keywords to check for
+        :param kw: the keywords
+        """
+        keyword = QtGui.QTextCharFormat()
+        keyword.setForeground(QtCore.Qt.darkBlue)
+        keyword.setFontWeight(QtGui.QFont.Bold)
+        keywords = QtCore.QStringList(kw)
+        for word in keywords:
+            pattern = QtCore.QRegExp("\\b" + word + "\\b")
+            self.highlightingRules.append((pattern, keyword))
+
+    def highlightBlock(self, text):
+        """
+        Highlight a block of text
+        :param text: the text to check in
+        """
+        for pattern, format in self.highlightingRules:
+            start = 0
+            if pattern not in [self.timestamp_pattern, self.error_pattern, self.loglevel_pattern]:
+                start = self.loglevel_pattern.indexIn(text)
+                start += self.loglevel_pattern.matchedLength()
+
+            index = pattern.indexIn(text, offset=start)
+
+            while index >= 0:
+                length = pattern.matchedLength()
+                pref_format = self.format(index)
+                pref_format.merge(format)
+                self.setFormat(index, length, pref_format)
+                index = text.indexOf(pattern, index + length)
+
+        self.setCurrentBlockState(0)
+
+
+class LogArea(QtGui.QTextEdit):
+    """
+    The log area for the KCG Gui
+    """
+    append_signal = QtCore.pyqtSignal(logging.LogRecord)
+
+    def __init__(self):
+        super(LogArea, self).__init__()
+        self.highlighter = Highlighter(self, 'Classic')
+        self.logHandler = None
+        self.streamLogger = logging.StreamHandler(stream=sys.stdout)
+        self.streamLogger.setLevel(config.log_level)
+        self.streamLogger.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
+        logging.getLogger().addHandler(self.streamLogger)
+        self.fileLogHandler = handlers.RotatingFileHandler(os.path.expanduser("~")+"/.kcg/kcg.log", maxBytes=10**7, backupCount=5)
+        self.fileLogHandler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
+        self.fileLogHandler.setLevel(config.log_level)
+        logging.getLogger().addHandler(self.fileLogHandler)
+        self.append_signal.connect(self.append)
+
+    def init_logging(self):
+        """
+        Initialize logging
+        """
+        self.logHandler = LogHandler(self)
+        self.logHandler.setLevel(config.log_level)
+        logging.getLogger().addHandler(self.logHandler)
+
+    def append(self, record):
+        """
+        Append to the logarea
+        :param record: the record to append
+        """
+        super(LogArea, self).append(self.logHandler.format(record))
+        self.ensureCursorVisible()
+
+    def setKeywords(self, kw):
+        """
+        Set the keywords for the highlighter
+        :param kw: the keywords to set
+        """
+        self.highlighter.setKeywords(kw)

+ 142 - 142
KCG/base/multiWidget.py

@@ -1,142 +1,142 @@
-"""
-This is the container widget for multiple subwindows
-"""
-from PyQt4 import QtGui, QtCore
-
-from . import kcgwidget as kcgw
-from .leftbar import LeftBar
-from . import leftbar
-from .globals import glob as global_objects
-from .. import config
-
-tr = kcgw.tr
-
-
-class WidgetTypeError(Exception):
-    """
-    Simple error that describes when a wrong window type gets added
-    """
-    pass
-
-
-class MDIArea(kcgw.KCGWidgets):
-    """
-    The MDI Area used by Multiwidget
-    """
-    def __init__(self, parent):
-        super(MDIArea, self).__init__("MDIArea")
-        self.parent = parent
-        self.layout = QtGui.QHBoxLayout()
-        self.layout.setContentsMargins(0, 0, 0, 0)
-        self.setLayout(self.layout)
-
-        self.widgets = {}
-        self.area = QtGui.QMdiArea()
-        self.area.setFrameStyle(self.area.StyledPanel | self.area.Sunken)
-        self.layout.addWidget(self.area)
-        brush = QtGui.QBrush(QtGui.QColor('white'))
-        self.area.setBackground(brush)
-
-    def newWidget(self, widget, name, unique_id, widget_type, minSize=False):
-        """
-        Add a new Widget to the MDIArea
-        :param widget: (subclass of QMdiSubWindow) The widget to show
-        :param name: (str) name of the window
-        :param unique_id: (int) unique id of the window
-        :param widget_type: (int) the type of this window
-        :param minSize: (bool) whether to shrink the window to minimum size upon creation
-        :return: -
-        """
-        if not isinstance(widget, kcgw.KCGWidgets):
-            raise WidgetTypeError("Newly Created Widget is of type " +
-                                  type(widget).__name__ +
-                                  ". Expected class derived of " +
-                                  kcgw.KCGWidgets.__name__ + ".")
-        subWidget = kcgw.KCGSubWidget(name=name, unique_id=unique_id, typ=widget_type, minSize=minSize)
-        subWidget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
-        widget.setParent(subWidget)
-
-        if minSize:
-            subWidget.resize(widget.minimumSizeHint()*1.2)
-
-
-        widget.closeSignal.connect(subWidget.close)
-        subWidget.setWidget(widget)
-        self.area.addSubWindow(subWidget)
-        subWidget.show()
-        self.widgets[unique_id] = widget
-
-class MultiWidget(QtGui.QWidget):
-    """
-    The Widget used as Multiwidget. This is the main View during operation with KCG.
-    """
-    def __init__(self):
-        super(MultiWidget, self).__init__()
-        self.layout = QtGui.QVBoxLayout()
-        self.setLayout(self.layout)
-
-        self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
-
-        self.leftBar = LeftBar(self)
-        self.area = MDIArea(self)
-        self.area.setObjectName("MDIArea")
-
-        self.toolbar = QtGui.QToolBar()
-        global_objects.set_global('area', self.area)
-
-        # --------[ ToolBar ]--------
-        self.new_live_action = QtGui.QAction(QtGui.QIcon(config.icon_path(config.newPlotLiveIcon)), tr("Button", "New Live Plot"), self)
-        self.new_live_action.setShortcut("Ctrl+L")
-        self.toolbar.addAction(self.new_live_action)
-        self.new_live_action.triggered.connect(lambda: self.leftBar.add_plot(leftbar.LIVE))
-        self.new_live_action.setToolTip(tr("Button", "New Live Plot") + "\n(Ctrl+L)")
-
-        self.new_data_action = QtGui.QAction(QtGui.QIcon(config.icon_path(config.newPlotDataIcon)), tr("Button", "New Data Plot"), self)
-        self.new_data_action.setShortcuts(["Ctrl+D", "Ctrl+O"])
-        self.toolbar.addAction(self.new_data_action)
-        self.new_data_action.triggered.connect(lambda: self.leftBar.add_plot(leftbar.FILE))
-        self.new_data_action.setToolTip(tr("Button", "New Data Plot") + "\n(Ctrl+D/O)")
-
-        self.layout.addWidget(self.toolbar)
-        # ------[ End Tool Bar ]-----
-
-        self.splitter.addWidget(self.leftBar)
-        self.splitter.addWidget(self.area)
-        self.layout.addWidget(self.splitter)
-
-        self.evaluate_registered_widgets()
-        self.evaluate_registered_widget_functions()
-
-    def addToolbarButton(self, icon, text, target, shortcut=None):
-        """
-        Add a toolbar button.
-        :param icon: (QIcon) The icon to show on the toolbar button
-        :param text: (str) tooltip for this button
-        :param target: (callable) The function to call upon press on button
-        :param shortcut: (str) Keyboard shortcut to call target
-        :return: -
-        """
-        na = QtGui.QAction(icon, text, self)
-        if shortcut:
-            na.setShortcut(shortcut)
-            na.setToolTip(text + "\n("+shortcut+")")
-        self.toolbar.addAction(na)
-        na.triggered.connect(target)
-
-    def evaluate_registered_widgets(self):
-        """
-        Evaluate all the registered widgets and add a toolbar button for those
-        :return:
-        """
-        for f in kcgw.get_registered_widgets():
-            self.addToolbarButton(*f)
-
-    def evaluate_registered_widget_functions(self):
-        """
-        Evaluate the functions that are registered
-        Those are in general functions that have to be called after widgets are created
-        :return:
-        """
-        for f in kcgw.get_registered_widget_functions():
-            f()
-
+"""
+This is the container widget for multiple subwindows
+"""
+from PyQt4 import QtGui, QtCore
+
+import kcgwidget as kcgw
+from leftbar import LeftBar
+import leftbar
+from globals import glob as global_objects
+from .. import config
+
+tr = kcgw.tr
+
+
+class WidgetTypeError(Exception):
+    """
+    Simple error that describes when a wrong window type gets added
+    """
+    pass
+
+
+class MDIArea(kcgw.KCGWidgets):
+    """
+    The MDI Area used by Multiwidget
+    """
+    def __init__(self, parent):
+        super(MDIArea, self).__init__("MDIArea")
+        self.parent = parent
+        self.layout = QtGui.QHBoxLayout()
+        self.layout.setContentsMargins(0, 0, 0, 0)
+        self.setLayout(self.layout)
+
+        self.widgets = {}
+        self.area = QtGui.QMdiArea()
+        self.area.setFrameStyle(self.area.StyledPanel | self.area.Sunken)
+        self.layout.addWidget(self.area)
+        brush = QtGui.QBrush(QtGui.QColor('white'))
+        self.area.setBackground(brush)
+
+    def newWidget(self, widget, name, unique_id, widget_type, minSize=False):
+        """
+        Add a new Widget to the MDIArea
+        :param widget: (subclass of QMdiSubWindow) The widget to show
+        :param name: (str) name of the window
+        :param unique_id: (int) unique id of the window
+        :param widget_type: (int) the type of this window
+        :param minSize: (bool) whether to shrink the window to minimum size upon creation
+        :return: -
+        """
+        if not isinstance(widget, kcgw.KCGWidgets):
+            raise WidgetTypeError("Newly Created Widget is of type " +
+                                  type(widget).__name__ +
+                                  ". Expected class derived of " +
+                                  kcgw.KCGWidgets.__name__ + ".")
+        subWidget = kcgw.KCGSubWidget(name=name, unique_id=unique_id, typ=widget_type, minSize=minSize)
+        subWidget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+        widget.setParent(subWidget)
+
+        if minSize:
+            subWidget.resize(widget.minimumSizeHint()*1.2)
+
+
+        widget.closeSignal.connect(subWidget.close)
+        subWidget.setWidget(widget)
+        self.area.addSubWindow(subWidget)
+        subWidget.show()
+        self.widgets[unique_id] = widget
+
+class MultiWidget(QtGui.QWidget):
+    """
+    The Widget used as Multiwidget. This is the main View during operation with KCG.
+    """
+    def __init__(self):
+        super(MultiWidget, self).__init__()
+        self.layout = QtGui.QVBoxLayout()
+        self.setLayout(self.layout)
+
+        self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
+
+        self.leftBar = LeftBar(self)
+        self.area = MDIArea(self)
+        self.area.setObjectName("MDIArea")
+
+        self.toolbar = QtGui.QToolBar()
+        global_objects.set_global('area', self.area)
+
+        # --------[ ToolBar ]--------
+        self.new_live_action = QtGui.QAction(QtGui.QIcon(config.install_path + config.newPlotLiveIcon), tr("Button", "New Live Plot"), self)
+        self.new_live_action.setShortcut("Ctrl+L")
+        self.toolbar.addAction(self.new_live_action)
+        self.new_live_action.triggered.connect(lambda: self.leftBar.add_plot(leftbar.LIVE))
+        self.new_live_action.setToolTip(tr("Button", "New Live Plot") + "\n(Ctrl+L)")
+
+        self.new_data_action = QtGui.QAction(QtGui.QIcon(config.install_path + config.newPlotDataIcon), tr("Button", "New Data Plot"), self)
+        self.new_data_action.setShortcuts(["Ctrl+D", "Ctrl+O"])
+        self.toolbar.addAction(self.new_data_action)
+        self.new_data_action.triggered.connect(lambda: self.leftBar.add_plot(leftbar.FILE))
+        self.new_data_action.setToolTip(tr("Button", "New Data Plot") + "\n(Ctrl+D/O)")
+
+        self.layout.addWidget(self.toolbar)
+        # ------[ End Tool Bar ]-----
+
+        self.splitter.addWidget(self.leftBar)
+        self.splitter.addWidget(self.area)
+        self.layout.addWidget(self.splitter)
+
+        self.evaluate_registered_widgets()
+        self.evaluate_registered_widget_functions()
+
+    def addToolbarButton(self, icon, text, target, shortcut=None):
+        """
+        Add a toolbar button.
+        :param icon: (QIcon) The icon to show on the toolbar button
+        :param text: (str) tooltip for this button
+        :param target: (callable) The function to call upon press on button
+        :param shortcut: (str) Keyboard shortcut to call target
+        :return: -
+        """
+        na = QtGui.QAction(icon, text, self)
+        if shortcut:
+            na.setShortcut(shortcut)
+            na.setToolTip(text + "\n("+shortcut+")")
+        self.toolbar.addAction(na)
+        na.triggered.connect(target)
+
+    def evaluate_registered_widgets(self):
+        """
+        Evaluate all the registered widgets and add a toolbar button for those
+        :return:
+        """
+        for f in kcgw.get_registered_widgets():
+            self.addToolbarButton(*f)
+
+    def evaluate_registered_widget_functions(self):
+        """
+        Evaluate the functions that are registered
+        Those are in general functions that have to be called after widgets are created
+        :return:
+        """
+        for f in kcgw.get_registered_widget_functions():
+            f()
+

+ 200 - 212
KCG/base/multipage.py

@@ -1,212 +1,200 @@
-from PyQt4 import QtGui, QtCore
-from .groupedelements import Elements, MenuItems
-from . import kcgwidget as kcgw
-from .. import config
-
-class RightSwitch(kcgw.ClickableSVG):
-    """
-    Buttons to change the Page in a MultiPage
-    """
-    def __init__(self, pagesWidget, width=10, height=20, wwidth=None, hidden=False):
-        """
-        Initialise a RightSwitch
-        :param pagesWidget: (MultiPage) The MultiPage widget to switch pages upon press
-        :param width: (int) width of the icon shown on the switch
-        :param height: (int) height of the icon shown on the switch
-        :param wwidth: (int) Width of the switch
-        :param hidden: (bool) whether this switch is shown or hidden
-        :return: -
-        """
-        super(RightSwitch, self).__init__(config.icon_path("chevron-right.svg"), width, height, wwidth)
-        self.setObjectName("right_switch")
-        if hidden:
-            self.hide()
-        self.clicked.connect(lambda: pagesWidget.setCurrentIndex(pagesWidget.currentIndex()+1))
-
-
-class LeftSwitch(kcgw.ClickableSVG):
-    """
-    Buttons to change the Page in a MultiPage Widget
-    """
-    def __init__(self, pagesWidget, width=10, height=20, wwidth=None, hidden=False):
-        """
-        Initialise a LeftSwitch
-        :param pagesWidget: (MultiPage) The MultiPage widget to switch pages upon press
-        :param width: (int) width of the icon shown on the switch
-        :param height: (int) height of the icon shown on the switch
-        :param wwidth: (int) Width of the switch
-        :param hidden: (bool) whether this switch is shown or hidden
-        :return: -
-        """
-        super(LeftSwitch, self).__init__(config.icon_path("chevron-left.svg"), width, height, wwidth)
-        self.setObjectName("left_switch")
-        if hidden:
-            self.hide()
-        self.clicked.connect(lambda: pagesWidget.setCurrentIndex(pagesWidget.currentIndex()-1))
-
-
-class LeftRightSwitch(QtGui.QWidget):
-    """
-    Small Buttons to change the Page in a MultiPage Widget
-    """
-    def __init__(self, pagesWidget):
-        """
-        Initialise a combination of left and right switch
-        :param pagesWidget: (MultiPage) The multipage widget instance to change pages on
-        """
-        super(LeftRightSwitch, self).__init__()
-        self.right = RightSwitch(pagesWidget, width=10, height=10)
-        self.right.setObjectName("leftright")
-        self.left = LeftSwitch(pagesWidget, width=10, height=10)
-        self.left.setObjectName("leftright")
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.layout.addWidget(self.left)
-        self.layout.addWidget(self.right)
-
-    def disable_left(self):
-        """
-        Disable the switch to the page left of the current one
-        :return: -
-        """
-        # self.left.setStyleSheet("border-radius: 4px; background-color: lightgrey;")
-        self.left.setStyleSheet("#leftright:hover { background-color: none;}")
-        self.left.changeSvg(config.icon_path("chevron-left-grey.svg"))
-
-    def enable_left(self):
-        """
-        Enable the switch to the page left of the current one
-        :return: -
-        """
-        self.left.setStyleSheet("")
-        self.left.changeSvg(config.icon_path("chevron-left.svg"))
-
-    def disable_right(self):
-        """
-        Disable the switch to the page right of the current one
-        :return: -
-        """
-        # self.right.setStyleSheet("border-radius: 4px; background-color: lightgrey;")
-        self.right.setStyleSheet("#leftright:hover { background-color: none;}")
-        self.right.changeSvg(config.icon_path("chevron-right-grey.svg"))
-
-    def enable_right(self):
-        """
-        Enable the switch to the page right of the current one
-        :return: -
-        """
-        self.right.setStyleSheet("")
-        self.right.changeSvg(config.icon_path("chevron-right.svg"))
-
-
-# 888b     d888        888888   d8b8888888b.
-# 8888b   d8888        888888   Y8P888   Y88b
-# 88888b.d88888        888888      888    888
-# 888Y88888P888888  888888888888888888   d88P 8888b.  .d88b.  .d88b.
-# 888 Y888P 888888  888888888   8888888888P"     "88bd88P"88bd8P  Y8b
-# 888  Y8P  888888  888888888   888888       .d888888888  88888888888
-# 888   "   888Y88b 888888Y88b. 888888       888  888Y88b 888Y8b.
-# 888       888 "Y88888888 "Y888888888       "Y888888 "Y88888 "Y8888
-#                                                         888
-#                                                    Y8b d88P
-#                                                     "Y88P"
-
-class MultiPage(QtGui.QStackedWidget):
-    """
-    Implementation of a Paginated View Widget
-    """
-    def __init__(self, parent=None):
-        super(MultiPage, self).__init__(parent)
-        self.pages = []
-        self.numOfPages = -1
-        self.leftright = LeftRightSwitch(self)
-        self.leftright.hide()
-        self.setCurrentIndex(0)
-        self.leftShortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Left"), self, self.left)
-        self.rightShortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Right"), self, self.right)
-        self.lSwitches = []
-        self.rSwitches = []
-
-    def addPage(self, NewPage, name=None, set_to_first=True):
-        """
-        Add a page (a Widget) to the MultiPage Widget
-        :param NewPage: widget to add as new page
-        :param name: name of that page (e.g. to show in the status bar)
-        :param bool set_to_first: Set the current page to first page
-        :return: -
-        """
-        self.leftright.show()
-        self.numOfPages += 1
-        self.pages.append(QtGui.QWidget())
-        self.pages[-1].widget = NewPage
-        self.rSwitches.append(RightSwitch(self, wwidth=20, hidden=True))
-        self.lSwitches.append(LeftSwitch(self, wwidth=20, hidden=True))
-        NewPage.index = len(self.pages)-1
-        self.pages[-1].name = name
-        self.pages[-1].layout = QtGui.QHBoxLayout()
-        self.pages[-1].setLayout(self.pages[-1].layout)
-        if len(self.pages) == 1:
-            self.pages[-1].layout.addWidget(NewPage)
-        if len(self.pages) > 1:
-            self.rSwitches[-2].show()
-            self.pages[-2].layout.addWidget(self.rSwitches[-2])
-            self.lSwitches[-1].show()
-            self.pages[-1].layout.addWidget(self.lSwitches[-1])
-            self.pages[-1].layout.addWidget(NewPage)
-        self.addWidget(self.pages[-1])
-        if set_to_first:
-            self.setCurrentIndex(0)
-        else:
-            name = self.pages[self.currentIndex()].name+" " if self.pages[self.currentIndex()].name else " "
-            self.window().pageIndicator.setText(name+"| "+str(self.currentIndex()+1)+"/"+str(self.numOfPages+1))
-
-
-    def removePage(self, page):
-        """
-        Removes a page from the pages widget and adjusts switches accordingly
-        :param page: what page to remove
-        :return: -
-        """
-        if self.numOfPages == 1:
-            raise IndexError("Not enough pages left")
-        self.numOfPages -=1
-        idx = page.index
-        self.removeWidget(self.pages[idx])
-        del self.pages[idx]
-        del self.lSwitches[idx]
-        del self.rSwitches[idx]
-        self.pages[-1].layout.removeWidget(self.rSwitches[-1])
-        self.rSwitches[-1].hide()
-        name = self.pages[self.currentIndex()].name+" " if self.pages[self.currentIndex()].name else " "
-        self.window().pageIndicator.setText(name+"| "+str(self.currentIndex()+1)+"/"+str(self.numOfPages+1))
-
-    def left(self):
-        self.setCurrentIndex(self.currentIndex()-1)
-
-    def right(self):
-        self.setCurrentIndex(self.currentIndex()+1)
-
-    def setCurrentIndex(self, p_int):
-        """
-        Set te current Index of the MultiPage Widget (e.g. set the current page)
-        :param p_int: (int) what page
-        :return: -
-        """
-        if self.currentIndex() >= 0 and p_int <= self.numOfPages:
-            MenuItems.setEnabled(self.pages[self.currentIndex()].name.strip(), False)
-        if p_int > self.numOfPages or p_int < 0:
-            return
-        if p_int <= 0:
-            self.leftright.disable_left()
-        else:
-            self.leftright.enable_left()
-        if p_int >= self.numOfPages:
-            self.leftright.disable_right()
-        else:
-            self.leftright.enable_right()
-        name = self.pages[p_int].name+" " if self.pages[p_int].name else " "
-        self.window().pageIndicator.setText(name+"| "+str(p_int+1)+"/"+str(self.numOfPages+1))
-        super(MultiPage, self).setCurrentIndex(p_int)
-        MenuItems.setEnabled(name.strip(), True)
-        if getattr(self.pages[self.currentIndex()].widget, 'pages_update_function', None) is not None:
-            self.pages[self.currentIndex()].widget.pages_update_function()
+from PyQt4 import QtGui, QtCore
+from groupedelements import Elements, MenuItems
+import kcgwidget as kcgw
+from .. import config
+
+class RightSwitch(kcgw.ClickableSVG):
+    """
+    Buttons to change the Page in a MultiPage
+    """
+    def __init__(self, pagesWidget, width=10, height=20, wwidth=None, hidden=False):
+        """
+        Initialise a RightSwitch
+        :param pagesWidget: (MultiPage) The MultiPage widget to switch pages upon press
+        :param width: (int) width of the icon shown on the switch
+        :param height: (int) height of the icon shown on the switch
+        :param wwidth: (int) Width of the switch
+        :param hidden: (bool) whether this switch is shown or hidden
+        :return: -
+        """
+        super(RightSwitch, self).__init__(config.install_path+"icons/chevron-right.svg", width, height, wwidth)
+        self.setObjectName("right_switch")
+        if hidden:
+            self.hide()
+        self.clicked.connect(lambda: pagesWidget.setCurrentIndex(pagesWidget.currentIndex()+1))
+
+
+class LeftSwitch(kcgw.ClickableSVG):
+    """
+    Buttons to change the Page in a MultiPage Widget
+    """
+    def __init__(self, pagesWidget, width=10, height=20, wwidth=None, hidden=False):
+        """
+        Initialise a LeftSwitch
+        :param pagesWidget: (MultiPage) The MultiPage widget to switch pages upon press
+        :param width: (int) width of the icon shown on the switch
+        :param height: (int) height of the icon shown on the switch
+        :param wwidth: (int) Width of the switch
+        :param hidden: (bool) whether this switch is shown or hidden
+        :return: -
+        """
+        super(LeftSwitch, self).__init__(config.install_path+"icons/chevron-left.svg", width, height, wwidth)
+        self.setObjectName("left_switch")
+        if hidden:
+            self.hide()
+        self.clicked.connect(lambda: pagesWidget.setCurrentIndex(pagesWidget.currentIndex()-1))
+
+
+class LeftRightSwitch(QtGui.QWidget):
+    """
+    Small Buttons to change the Page in a MultiPage Widget
+    """
+    def __init__(self, pagesWidget):
+        """
+        Initialise a combination of left and right switch
+        :param pagesWidget: (MultiPage) The multipage widget instance to change pages on
+        """
+        super(LeftRightSwitch, self).__init__()
+        self.right = RightSwitch(pagesWidget, width=10, height=10)
+        self.right.setObjectName("leftright")
+        self.left = LeftSwitch(pagesWidget, width=10, height=10)
+        self.left.setObjectName("leftright")
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.layout.addWidget(self.left)
+        self.layout.addWidget(self.right)
+
+    def disable_left(self):
+        """
+        Disable the switch to the page left of the current one
+        :return: -
+        """
+        # self.left.setStyleSheet("border-radius: 4px; background-color: lightgrey;")
+        self.left.setStyleSheet("#leftright:hover { background-color: none;}")
+        self.left.changeSvg(config.install_path+"icons/chevron-left-grey.svg")
+
+    def enable_left(self):
+        """
+        Enable the switch to the page left of the current one
+        :return: -
+        """
+        self.left.setStyleSheet("")
+        self.left.changeSvg(config.install_path+"icons/chevron-left.svg")
+
+    def disable_right(self):
+        """
+        Disable the switch to the page right of the current one
+        :return: -
+        """
+        # self.right.setStyleSheet("border-radius: 4px; background-color: lightgrey;")
+        self.right.setStyleSheet("#leftright:hover { background-color: none;}")
+        self.right.changeSvg(config.install_path+"icons/chevron-right-grey.svg")
+
+    def enable_right(self):
+        """
+        Enable the switch to the page right of the current one
+        :return: -
+        """
+        self.right.setStyleSheet("")
+        self.right.changeSvg(config.install_path+"icons/chevron-right.svg")
+
+
+class MultiPage(QtGui.QStackedWidget):
+    """
+    Implementation of a Paginated View Widget
+    """
+    def __init__(self, parent=None):
+        super(MultiPage, self).__init__(parent)
+        self.pages = []
+        self.numOfPages = -1
+        self.leftright = LeftRightSwitch(self)
+        self.leftright.hide()
+        self.setCurrentIndex(0)
+        self.leftShortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Left"), self, self.left)
+        self.rightShortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Right"), self, self.right)
+        self.lSwitches = []
+        self.rSwitches = []
+
+    def addPage(self, NewPage, name=None, set_to_first=True):
+        """
+        Add a page (a Widget) to the MultiPage Widget
+        :param NewPage: widget to add as new page
+        :param name: name of that page (e.g. to show in the status bar)
+        :param bool set_to_first: Set the current page to first page
+        :return: -
+        """
+        self.leftright.show()
+        self.numOfPages += 1
+        self.pages.append(QtGui.QWidget())
+        self.pages[-1].widget = NewPage
+        self.rSwitches.append(RightSwitch(self, wwidth=20, hidden=True))
+        self.lSwitches.append(LeftSwitch(self, wwidth=20, hidden=True))
+        NewPage.index = len(self.pages)-1
+        self.pages[-1].name = name
+        self.pages[-1].layout = QtGui.QHBoxLayout()
+        self.pages[-1].setLayout(self.pages[-1].layout)
+        if len(self.pages) == 1:
+            self.pages[-1].layout.addWidget(NewPage)
+        if len(self.pages) > 1:
+            self.rSwitches[-2].show()
+            self.pages[-2].layout.addWidget(self.rSwitches[-2])
+            self.lSwitches[-1].show()
+            self.pages[-1].layout.addWidget(self.lSwitches[-1])
+            self.pages[-1].layout.addWidget(NewPage)
+        self.addWidget(self.pages[-1])
+        if set_to_first:
+            self.setCurrentIndex(0)
+        else:
+            name = self.pages[self.currentIndex()].name+" " if self.pages[self.currentIndex()].name else " "
+            self.window().pageIndicator.setText(name+"| "+str(self.currentIndex()+1)+"/"+str(self.numOfPages+1))
+
+
+    def removePage(self, page):
+        """
+        Removes a page from the pages widget and adjusts switches accordingly
+        :param page: what page to remove
+        :return: -
+        """
+        if self.numOfPages == 1:
+            raise IndexError("Not enough pages left")
+        self.numOfPages -=1
+        idx = page.index
+        self.removeWidget(self.pages[idx])
+        del self.pages[idx]
+        del self.lSwitches[idx]
+        del self.rSwitches[idx]
+        self.pages[-1].layout.removeWidget(self.rSwitches[-1])
+        self.rSwitches[-1].hide()
+        name = self.pages[self.currentIndex()].name+" " if self.pages[self.currentIndex()].name else " "
+        self.window().pageIndicator.setText(name+"| "+str(self.currentIndex()+1)+"/"+str(self.numOfPages+1))
+
+    def left(self):
+        self.setCurrentIndex(self.currentIndex()-1)
+
+    def right(self):
+        self.setCurrentIndex(self.currentIndex()+1)
+
+    def setCurrentIndex(self, p_int):
+        """
+        Set te current Index of the MultiPage Widget (e.g. set the current page)
+        :param p_int: (int) what page
+        :return: -
+        """
+        if self.currentIndex() >= 0 and p_int <= self.numOfPages:
+            MenuItems.setEnabled(self.pages[self.currentIndex()].name.strip(), False)
+        if p_int > self.numOfPages or p_int < 0:
+            return
+        if p_int <= 0:
+            self.leftright.disable_left()
+        else:
+            self.leftright.enable_left()
+        if p_int >= self.numOfPages:
+            self.leftright.disable_right()
+        else:
+            self.leftright.enable_right()
+        name = self.pages[p_int].name+" " if self.pages[p_int].name else " "
+        self.window().pageIndicator.setText(name+"| "+str(p_int+1)+"/"+str(self.numOfPages+1))
+        super(MultiPage, self).setCurrentIndex(p_int)
+        MenuItems.setEnabled(name.strip(), True)
+        if getattr(self.pages[self.currentIndex()].widget, 'pages_update_function', None) is not None:
+            self.pages[self.currentIndex()].widget.pages_update_function()

+ 885 - 940
KCG/base/plotWidget.py

@@ -1,940 +1,885 @@
-"""
-This Module implements the Plot Windows used in KCG
-"""
-import pyqtgraph as pg
-try:  # on some versions of pyqtgraph useOpenGl is no valid option
-    pg.setConfigOption('useOpenGl', True)
-except:
-    pass
-pg.setConfigOption('background', 'w')
-pg.setConfigOption('foreground', 'k')
-import numpy as np
-from PyQt4 import QtCore, QtGui
-from .groupedelements import live_plot_windows
-from .backend import io
-from .backend import board
-from .backend.board import available_boards
-from . import kcgwidget as kcgw
-from .. import config
-
-tr = kcgw.tr
-
-LIVE = 1
-FILE = 2
-ERROR = 99
-NO_DATA = 98
-
-
-class Enum():
-    """
-    Simple Enum Class (as this is not supported by clean python)
-    """
-    def __init__(self, *args):
-        self.idx = 0
-        for i in args:
-            setattr(self, i, self.idx)
-            self.idx += 1
-
-plotList = [tr("Label", "Heatmap"), tr("Label", "FFT"), tr("Label", "Trains"), tr("Label", "Combined"), tr("Label", "Compare"), tr("Label", "Follow")]
-PlotType = Enum(*[str(i) for i in plotList])
-
-# gradient = {'mode': 'rgb',
-#             'ticks': [(0.0, (0.37281, 0.11883, 3.53583, 255)),
-#                       (0.0390625, (4.9401150000000005, 3.858915, 22.635585, 255)),
-#                       (0.078125, (15.6417, 9.330449999999999, 45.29871, 255)),
-#                       (0.1171875, (29.74728, 12.13137, 69.44185499999999, 255)),
-#                       (0.15625, (46.774395000000005, 10.283895, 90.51760499999999, 255)),
-#                       (0.1953125, (64.1631, 9.614775, 102.86139, 255)),
-#                       (0.234375, (80.65191, 13.63995, 108.40458, 255)),
-#                       (0.2734375, (96.64525499999999, 19.444515, 110.343345, 255)),
-#                       (0.3125, (112.507785, 25.33119, 110.05646999999999, 255)),
-#                       (0.3515625, (128.390715, 31.001625, 107.95578, 255)),
-#                       (0.390625, (144.29277, 36.609585, 104.10579, 255)),
-#                       (0.4296875, (160.100985, 42.476625, 98.50038, 255)),
-#                       (0.46875, (175.606515, 49.020945, 91.188765, 255)),
-#                       (0.5078125, (190.517385, 56.70639, 82.32827999999999, 255)),
-#                       (0.546875, (204.477105, 65.96187, 72.190245, 255)),
-#                       (0.5859375, (217.10292, 77.07629999999999, 61.10718, 255)),
-#                       (0.625, (228.047775, 90.11674500000001, 49.36392, 255)),
-#                       (0.6640625, (237.05922, 104.927145, 37.068585, 255)),
-#                       (0.703125, (243.99726, 121.21578, 24.147225, 255)),
-#                       (0.7421875, (248.797635, 138.66849, 11.122589999999999, 255)),
-#                       (0.78125, (251.41776000000002, 157.01625, 6.52596, 255)),
-#                       (0.8203125, (251.80332, 176.04333, 20.397450000000003, 255)),
-#                       (0.859375, (249.90816, 195.543435, 42.420015, 255)),
-#                       (0.8984375, (245.92047, 215.18124, 69.714705, 255)),
-#                       (0.9375, (241.63647, 233.936745, 104.719575, 255)),
-#                       (0.9765625, (245.26206, 248.86062, 145.840875, 255)),
-#                       (0.99609375, (252.03231, 254.58282, 164.45562, 255))]}
-
-gradient = {'mode': 'rgb',
-            'ticks': [(0.0, (0.37281, 0.11883, 3.53583, 255)),
-                      (0.1171875, (29.74728, 12.13137, 69.44185499999999, 255)),
-                      (0.234375, (80.65191, 13.63995, 108.40458, 255)),
-                      (0.3515625, (128.390715, 31.001625, 107.95578, 255)),
-                      (0.46875, (175.606515, 49.020945, 91.188765, 255)),
-                      (0.5859375, (217.10292, 77.07629999999999, 61.10718, 255)),
-                      (0.703125, (243.99726, 121.21578, 24.147225, 255)),
-                      (0.8203125, (251.80332, 176.04333, 20.397450000000003, 255)),
-                      (0.9375, (241.63647, 233.936745, 104.719575, 255)),
-                      (0.99609375, (252.03231, 254.58282, 164.45562, 255))]}
-
-
-class CustomGradientEditorItem(pg.GradientEditorItem):
-    """
-    A Gradient Editor Item to insert a perception linear gradient
-    """
-    def __init__(self, **kwargs):
-        pg.GradientEditorItem.__init__(self, **kwargs)
-        self.customGradients = {}
-
-    def addGrad(self, name, grad_dic):
-        """
-        Add a gradient to the list of gradients in the gui
-        :param name: the name of the gradient
-        :param grad_dic: the dictionary containing the gradient data
-        """
-        if name not in self.customGradients:
-            self.customGradients[name] = grad_dic
-            px = QtGui.QPixmap(100, 15)
-            p = QtGui.QPainter(px)
-            self.restoreState(grad_dic)
-            length_backup = self.length
-            self.length = 100
-            grad = self.getGradient()
-            self.length = length_backup
-            brush = QtGui.QBrush(grad)
-            p.fillRect(QtCore.QRect(0, 0, 100, 15), brush)
-            p.end()
-            label = QtGui.QLabel()
-            label.setPixmap(px)
-            label.setContentsMargins(1, 1, 1, 1)
-            act = QtGui.QWidgetAction(self)
-            act.setDefaultWidget(label)
-            act.triggered.connect(self.contextMenuClicked)
-            act.name = name
-            if len(self.customGradients) > 1:
-                self.menu.insertAction(self.menu.actions()[0], act)
-            else:
-                sep = self.menu.insertSeparator(self.menu.actions()[0])
-                self.menu.insertAction(sep, act)
-
-        self.restoreState(grad_dic)
-
-    def restoreState(self, state):
-        """
-        Reimplemented of pyqtgraph.GradientEditorItem.restoreState to work with our custom perception linear gradient.
-        :param state: the state to restore to
-        """
-        self.setColorMode(state['mode'])
-        for t in list(self.ticks.keys()):
-            self.removeTick(t, finish=False)
-        self.bottomTick = [None, 10]
-        self.topmostTick = [None, -1]
-        for t in state['ticks']:
-            c = QtGui.QColor(*t[1])
-            tick = self.addTick(t[0], c, finish=False)
-            if t[0] < self.bottomTick[1]:
-                self.bottomTick = [tick, t[0]]
-            if t[0] > self.topmostTick[1]:
-                self.topmostTick = [tick, t[0]]
-        self.tickPosInPercent = self.ticks.copy()
-        for t in self.ticks:
-            if t is self.topmostTick[0] or t is self.bottomTick[0]:
-                continue
-            t.hide()
-            t.movable = False
-        # self.bottomTick[1] *= self.length
-        # self.topmostTick[1] *= self.length
-        self.updateGradient()
-        self.sigGradientChangeFinished.emit(self)
-
-    def loadPreset(self, name):
-        """
-        Reimplemented of pyqtgraph.GradientEditorItem.loadPreset to work with our custom perception linear gradient.
-        :param name: the name of the preset to load
-        """
-        if name in self.customGradients:
-            self.restoreState(self.customGradients[name])
-        else:
-            super(CustomGradientEditorItem, self).loadPreset(name)
-
-    def tickMoved(self, tick, pos):
-        """
-        Reimplemented of pyqtgraph.GradientEditorItem.tickMoved to work with our custom perception linear gradient,
-        which has a lot of steps which would create a lot of ticks in the gradient legend. This removes all but 2
-        ticks and aligns all the internal ticks accordingly.
-        :param tick: the tick to move
-        :param pos: the position to move to
-        """
-        if tick is self.bottomTick[0]:
-            # self.bottomTick[1] = pos.x()
-            pg.TickSliderItem.tickMoved(self, tick, pos)
-        elif tick is self.topmostTick[0]:
-            # self.topmostTick[1] = pos.x()
-            pg.TickSliderItem.tickMoved(self, tick, pos)
-        if tick in [self.bottomTick[0], self.topmostTick[0]]:  # if bottom or topmost tick was moved
-            if pos.x() < self.length and pos.x() > 0:  # if tick is not at top or bottom
-                newUnit = (self.topmostTick[0].pos().x() - self.bottomTick[0].pos().x())  # create new virtual unit length
-                for t in self.ticks:  # for every tick
-                    if t not in [self.bottomTick[0], self.topmostTick[0]]:  # if tick is not bottom or topmost tick
-                        pos = t.pos()
-                        new_x = self.bottomTick[0].pos().x() + self.tickPosInPercent[t] * newUnit
-                        pos.setX(new_x)
-                        t.setPos(pos)
-                        pg.TickSliderItem.tickMoved(self, t, pos)
-        self.updateGradient()
-
-
-#  .d8888b.                         888                                                       .d8888b.         888
-# d88P  Y88b                        888                                                      d88P  Y88b        888
-# Y88b.                             888                                                      888    888        888
-#  "Y888b.  88888b.  .d88b.  .d8888b888888888d888 .d88b.  .d88b. 888d888 8888b. 88888b.d88b. 888        .d88b. 888 .d88b. 888d888
-#     "Y88b.888 "88bd8P  Y8bd88P"   888   888P"  d88""88bd88P"88b888P"      "88b888 "888 "88b888       d88""88b888d88""88b888P"
-#       "888888  88888888888888     888   888    888  888888  888888    .d888888888  888  888888    888888  888888888  888888
-# Y88b  d88P888 d88PY8b.    Y88b.   Y88b. 888    Y88..88PY88b 888888    888  888888  888  888Y88b  d88PY88..88P888Y88..88P888
-#  "Y8888P" 88888P"  "Y8888  "Y8888P "Y888888     "Y88P"  "Y88888888    "Y888888888  888  888 "Y8888P"  "Y88P" 888 "Y88P" 888
-#           888                                               888
-#           888                                          Y8b d88P
-#           888                                           "Y88P"
-#
-class SpectrogramColorLegendItem(pg.GraphicsWidget):
-    """
-    The Item used as Legend for Heatmap and FFT Plot
-    """
-    gradientChanged = QtCore.pyqtSignal()
-    def __init__(self, img=None):
-        """
-        Initialise the Legend
-        :param img: (ImageItem) the image item this is the legend for
-        :return: -
-        """
-        super(SpectrogramColorLegendItem, self).__init__()
-        self.layout = QtGui.QGraphicsGridLayout()
-        self.setLayout(self.layout)
-        self.img = img
-        # self.gei = pg.GradientEditorItem(orientation='right')
-        self.gei = CustomGradientEditorItem(orientation='right', allowAdd=False)
-        self.legend_axis = pg.AxisItem(orientation='left')
-        # self.addItem(self.legend_axis)
-        self.layout.addItem(self.legend_axis, 0, 0, alignment=QtCore.Qt.AlignVCenter)
-        # self.addItem(self.gei)
-        self.layout.addItem(self.gei, 0, 1)
-        self.gei.sigGradientChanged.connect(self.gradient_changed)
-        # self.gei.loadPreset('spectrum')
-        self.gei.addGrad('inferno', gradient)
-        # self.setFixedWidth(80)
-        self.image_changed()
-
-    def gradient_changed(self):
-        """
-        Proxy function for the gradient_cnahged event of the GradientEditorItem
-        :return:
-        """
-        self.gradientChanged.emit()
-
-    def set_image(self, img):
-        """
-        Set the Image this is the legend for (only needed if not already done upon initialisation)
-        :param img: (ImageItem) The image item to use
-        :return: -
-        """
-        self.img = img
-        self.image_changed(True)
-
-    def update_axis(self):
-        """
-        Update the axis of this legend
-        :return: -
-        """
-        if self.img.getLevels():
-            # min, max = self.img.getLevels()
-            min, max = (np.min(self.img.image), np.max(self.img.image))
-            self.legend_axis.setRange(min, max)
-
-    def reset_gradient(self):
-        """
-        Reset the gradient to the preset "spectrum"
-        :return: -
-        """
-        # self.gei.loadPreset('spectrum')
-        self.gei.loadPreset('inferno')
-        # self.gei.restoreState(gradient)
-
-    def image_changed(self, reset_gradient=True):
-        """
-        Call this to adjust the legend when the image has changed
-        :param reset_gradient: (bool) whether to reset the gradient or not
-        :return:
-        """
-        if self.img:
-            self.update_axis()
-            if reset_gradient:
-                self.reset_gradient()
-
-    def getLookupTable(self, nPts, alpha=None):
-        """
-        Get the look up table of the imageitem
-        :param nPts: (int) number of points the lookup table is defined on
-        :param alpha: ??
-        :return: -
-        """
-        return self.gei.getLookupTable(nPts, alpha=alpha)
-
-    def resizeEvent(self, event):
-        """
-        Handle resizing of the window
-        """
-        self.legend_axis.setHeight(self.gei.length)
-
-#  .d8888b.         888     8888888b. 888        888   888       888d8b     888                888
-# d88P  Y88b        888     888   Y88b888        888   888   o   888Y8P     888                888
-# Y88b.             888     888    888888        888   888  d8b  888        888                888
-#  "Y888b.  888  88888888b. 888   d88P888 .d88b. 888888888 d888b 888888 .d88888 .d88b.  .d88b. 888888
-#     "Y88b.888  888888 "88b8888888P" 888d88""88b888   888d88888b888888d88" 888d88P"88bd8P  Y8b888
-#       "888888  888888  888888       888888  888888   88888P Y88888888888  888888  88888888888888
-# Y88b  d88PY88b 888888 d88P888       888Y88..88PY88b. 8888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.
-#  "Y8888P"  "Y8888888888P" 888       888 "Y88P"  "Y888888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888
-#                                                                                   888
-#                                                                              Y8b d88P
-#                                                                               "Y88P"
-#
-class SubPlotWidget(pg.GraphicsLayoutWidget):
-    """
-    The Widget actually containing the plots and images
-    """
-    def __init__(self, dType=FILE):
-        super(SubPlotWidget, self).__init__()
-
-        self.dType = dType
-
-        self._type_changed = False
-        # self.setStyleSheet("border: 5px solid black; margin: 10px;")
-        self.plotType = 1
-        self.plotItem = pg.PlotItem()
-        self.img = pg.ImageItem()
-        self.plotItem2 = pg.PlotItem()
-        self.img2 = pg.ImageItem()
-        self.plotItem2.addItem(self.img2)
-        self.setFrameStyle(self.StyledPanel | self.Sunken)
-
-        self.gradient_legend = SpectrogramColorLegendItem(self.img)
-        def changelut():
-            """
-            Handle changing of the lookup table
-            """
-            self.img.setLookupTable(self.gradient_legend.getLookupTable(512))
-            self.img2.setLookupTable(self.gradient_legend.getLookupTable(512))
-        self.gradient_legend.gradientChanged.connect(changelut)
-
-        self.addItem(self.plotItem)
-        self.plotItem.addItem(self.img)
-        self.plotItemPlotScatter = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(50, 50, 255), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(50, 50, 255), downsample=200)
-        self.plotItemPlotScatter1 = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(255, 50, 50), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(255, 50, 50), downsample=200)
-        # self.plotItemPlotScatter2 = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(50, 255, 50), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(50, 255, 50), downsample=200)
-        self.plotItemPlot = pg.PlotDataItem()  # pen="#000000")
-        self.plotItem.addItem(self.plotItemPlot)
-        self.plotItem.addItem(self.plotItemPlotScatter)
-        self.plotItem.addItem(self.plotItemPlotScatter1)
-        # self.plotItem.addItem(self.plotItemPlotScatter2)
-
-        self.plotItem.vb.origAutoRange = self.plotItem.vb.autoRange
-
-        self.error_label = pg.LabelItem("Error during plot.", color=(200, 50, 50))
-        self.error_label.hide()
-        self.error_label.scale(1, -0.6)
-        self.error_label.translate(0, -30)
-        self.plotItem.addItem(self.error_label)
-
-        self.no_data_label = pg.LabelItem("No data to plot.", color=(200, 50, 50))
-        self.no_data_label.hide()
-        self.no_data_label.scale(1, -0.6)
-        self.no_data_label.translate(0, -30)
-        self.plotItem.addItem(self.no_data_label)
-
-    def _enableCustomAutoRange(self, data):
-        """
-        Enable custom auto range in this plot
-        :param data: the data to autorange
-        :return:
-        """
-        def newAutoRange(*args, **kwargs):
-            ''' function to handle the new autorange '''
-            if len(data) == 0:
-                return
-            bounds = [np.min(data), np.max(data)]
-            self.plotItem.vb.setRange(xRange=[0, len(data)],
-                yRange=[bounds[0]-0.1*(bounds[1]-bounds[0])-1, bounds[1]+0.1*(bounds[1]-bounds[0])+1], update=True)
-            self.plotItem.update()
-        self.plotItem.vb.autoRange = newAutoRange
-        self.plotItem.autoBtn.clicked.disconnect()
-        self.plotItem.autoBtn.clicked.connect(newAutoRange)
-
-    def _disableCustomAutoRange(self):
-        """
-        Disable the custom autorange and reset it to default
-        :return: -
-        """
-        self.plotItem.vb.autoRange = self.plotItem.vb.origAutoRange
-        self.plotItem.autoBtn.clicked.disconnect()
-        self.plotItem.autoBtn.clicked.connect(self.plotItem.autoBtnClicked)
-
-    @QtCore.pyqtSlot(np.ndarray, tuple, tuple)
-    def plot(self, data, xvalueborders=None, yvalueborders=None, autorange=True):
-        """
-        Plot Data. The plot type depends on the type property
-        :param data: (dataset.DataSet) data to plot
-        :param xvalueborders: (touple) the borders for the xvalues
-        :param yvalueborders: (touple) the borders for the yvalues
-        :param autorange: (bool) whether to perform a autorange or not
-        :return: -
-        """
-        self.error_label.hide()
-        self.no_data_label.hide()
-        self.plotItemPlot.clear()
-        self.plotItemPlotScatter.clear()
-        self.plotItemPlotScatter1.clear()
-        # self.plotItemPlotScatter2.clear()
-        self.plotItem.resetTransform()
-        self.plotItem.getAxis('bottom').setScale()
-        self.img.show()
-        if self._type_changed:
-            self.plotItem.autoBtnClicked()
-        try:
-            self.removeItem(self.plotItem2)
-        except Exception as e:
-            if "Could not determine index of item" in str(e):
-                pass
-            else:
-                raise
-
-        if self.plotType == PlotType.FFT or self.plotType == PlotType.Heatmap:
-            self.addItem(self.gradient_legend)
-            self.gradient_legend.show()
-
-            self.img.setImage(data, scale=[1, 1/1000.])
-            self.img.resetTransform()
-            self.img.setAutoDownsample(True)
-            if autorange:
-                self.gradient_legend.image_changed()
-
-            if xvalueborders:
-                self.plotItem.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data.shape[0]))
-                self.img.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
-
-            else:
-                self.plotItem.getAxis('bottom').setScale()
-
-            if yvalueborders:
-                self.img.translate(0, yvalueborders[0])
-                self.plotItem.getAxis('left').setScale(yvalueborders[1]/float(data.shape[1]))
-
-            self._disableCustomAutoRange()
-            self.plotItem.setClipToView(True)
-
-        if self.plotType == PlotType.Compare:
-            self.addItem(self.plotItem2)
-            self.plotItem2.vb.linkView(self.plotItem2.vb.XAxis, self.plotItem.vb)
-            self.plotItem2.vb.linkView(self.plotItem2.vb.YAxis, self.plotItem.vb)
-            self.addItem(self.gradient_legend)
-            self.gradient_legend.show()
-
-            self.img.setImage(data[0], scale=[1, 1/1000.])
-            self.img.show()
-            self.img.resetTransform()
-            self.img2.setImage(data[1], scale=[1, 1/1000.])
-            self.img2.show()
-            self.img2.resetTransform()
-            self.img.setAutoDownsample(True)
-            self.img2.setAutoDownsample(True)
-            if autorange:
-                self.gradient_legend.image_changed()
-                pass
-
-            if xvalueborders:
-                self.plotItem.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data[0].shape[0]))
-                self.plotItem2.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data[1].shape[0]))
-                self.img.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
-                self.img2.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
-
-            else:
-                self.plotItem.getAxis('bottom').setScale()
-                self.plotItem2.getAxis('bottom').setScale()
-
-            if yvalueborders:
-                self.img.translate(0, yvalueborders[0])
-                self.img2.translate(0, yvalueborders[0])
-                self.plotItem.getAxis('left').setScale(yvalueborders[1]/float(data[0].shape[1]))
-                self.plotItem2.getAxis('left').setScale(yvalueborders[1]/float(data[1].shape[1]))
-
-            self._disableCustomAutoRange()
-            self.plotItem.setClipToView(True)
-
-        if self.plotType == PlotType.Trains or self.plotType == PlotType.Follow:
-            self.img.clear()
-            self.plotItemPlot.show()
-            self.gradient_legend.hide()
-            try:
-                self.removeItem(self.gradient_legend)
-                pass
-            except Exception as e:
-                if not "Could not determine index of item" in str(e):
-                    raise
-            self.plotItemPlotScatter.clear()
-            self.plotItemPlotScatter1.clear()
-            self.plotItemPlot.setData(data)
-
-            self._enableCustomAutoRange(data)
-
-        if self.plotType == PlotType.Combined:
-            self.img.clear()
-            self.plotItemPlotScatter.show()
-            self.plotItemPlotScatter1.show()
-            self.gradient_legend.hide()
-            try:
-                self.removeItem(self.gradient_legend)
-                pass
-            except Exception as e:
-                if not "Could not determine index of item" in str(e):
-                    raise
-            self.plotItemPlot.clear()
-            self.plotItemPlotScatter.setData(data[0].transpose())
-            self.plotItemPlotScatter1.setData(data[1].transpose())
-
-            self._enableCustomAutoRange(data[0][1])
-
-            self.plotItem.setClipToView(False)  # NOTE: otherwise only a very small portion of data is visible :wonder:
-
-        if self.plotType == ERROR or self.plotType == NO_DATA:
-            self.img.hide()
-            self.gradient_legend.hide()
-            self.img2.hide()
-            self.plotItemPlot.hide()
-            self.plotItemPlotScatter.hide()
-            self.plotItemPlotScatter1.hide()
-            if self.plotType == ERROR:
-                self.error_label.show()
-            else:
-                self.no_data_label.show()
-
-        if autorange:
-            self.plotItem.getViewBox().autoRange()
-            self.plotItem.getViewBox().autoRange()
-
-        self.labelAxes()
-        if self.plotType in [PlotType.FFT, PlotType.Heatmap, PlotType.Compare]:
-            self.gradient_legend.update_axis()
-
-    def labelAxes(self):
-        """
-        Add Labels to the axis depending on the self.plotType property.
-        :return: -
-        """
-        if self.plotType == PlotType.Heatmap:
-            self.plotItem.setLabel('bottom', 'Turn', '')
-            self.plotItem.setLabel('left', 'Bunch Position', '')
-        elif self.plotType == PlotType.FFT:
-            self.plotItem.setLabel('left', 'Bunch Position', '')
-            self.plotItem.setLabel('bottom', 'Frequency', 'Hz')
-        elif self.plotType == PlotType.Trains or self.plotType == PlotType.Follow:
-            self.plotItem.setLabel('left', '', '')
-            self.plotItem.setLabel('bottom', 'Sample Point', '')
-        elif self.plotType == PlotType.Combined:
-            self.plotItem.setLabel('left', '', '')
-            self.plotItem.setLabel('bottom', '', '')
-        if self.plotType == PlotType.Compare:
-            self.plotItem.setLabel('bottom', 'Turn', '')
-            self.plotItem.setLabel('left', 'Bunch Position', '')
-            self.plotItem2.setLabel('bottom', 'Turn', '')
-            self.plotItem2.setLabel('left', 'Bunch Position', '')
-
-    def changeType(self, type):
-        """
-        Change the plot Type
-        :param type: (int) the new type
-        :return: -
-        """
-        if type != self.plotType:
-            self._type_changed = True
-        self.plotType = type
-        # if type in [PlotType.FFT, PlotType.Compare, PlotType.Heatmap]:  # this will generate an error
-        #     self.gradient_legend.image_changed()
-
-# 8888888b. 888        888   888       888d8b     888                888
-# 888   Y88b888        888   888   o   888Y8P     888                888
-# 888    888888        888   888  d8b  888        888                888
-# 888   d88P888 .d88b. 888888888 d888b 888888 .d88888 .d88b.  .d88b. 888888
-# 8888888P" 888d88""88b888   888d88888b888888d88" 888d88P"88bd8P  Y8b888
-# 888       888888  888888   88888P Y88888888888  888888  88888888888888
-# 888       888Y88..88PY88b. 8888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.
-# 888       888 "Y88P"  "Y888888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888
-#                                                         888
-#                                                    Y8b d88P
-#                                                     "Y88P"
-#
-class PlotWidget(kcgw.KCGWidgets):
-    """
-    The container Class holding various buttons and controls and the actual plots as SubPlotWidgets instance
-    """
-    close_signal = QtCore.pyqtSignal()
-    change_type_signal = QtCore.pyqtSignal(int, int, str)
-
-    def __init__(self, board_id, parent=None, name=None, unique_id=None, type=None, datatype=None, prefix=None, fName=None, data=None):
-        """
-        Initialise the Plot Widgt
-        :param parent: (QWidget) the parent widget
-        :param name: (str) name of this widget
-        :param unique_id: (int) unique id of this widget
-        :param type: (int) type of this widget
-        :param datatype: (int) datatype (LIVE or FILE)
-        :param prefix: (str) prefix for text in the listview in LeftBar
-        :param fName: (str) the filename (this is only used if datatype is FILE)
-        :param data: (dataset.DataSet) the data to be plotted
-        :return: -
-        """
-        super(PlotWidget, self).__init__()
-
-        self.board_id = board_id
-
-        if name != None:
-            self.theName = name
-        if unique_id != None:
-            self.theId = unique_id
-        if type != None:
-            self.theType = type
-            self._old_type = type
-        if datatype != None:
-            self.theDataType = datatype
-        if prefix != None:
-            self.thePrefix = prefix
-        if self.theDataType == FILE:
-            self.fName = fName
-        if self.theDataType == FILE:
-            self.data = data
-        else:
-            self.data = None
-
-        self.close_silent = False
-        self.parent = parent
-        self.adc = 1
-        self.secadc = 2
-        self._single_adc_checked = False
-        self.initUI()
-        self.changePlotType(self.theType)  # initially mark the correct button
-        self.setWindowTitle(self.theName + " - " + plotList[type] + " - " + str(self.thePrefix))
-        if self.theDataType == FILE:
-            self.plot(type)
-        else:
-            live_plot_windows.addWindow(board_id, self)
-            if board.get_board_status(board_id).last_file is not None:
-                self.data = io.read_from_file(board.get_board_status(board_id).last_file)
-                self.plot(type)
-
-    def initUI(self):
-        """
-        Initialise the UI
-        :return: -
-        """
-        self.plot_widget = SubPlotWidget(dType=self.theDataType)
-        self.layout = QtGui.QVBoxLayout()
-        self.plot_buttons_layout = QtGui.QHBoxLayout()
-
-        self.heatmap_button  = self.createButton(text=tr("Button", "Heatmap"),  connect=lambda: self.plot(type=PlotType.Heatmap))
-        self.fft_button      = self.createButton(text=tr("Button", "FFT"),      connect=lambda: self.plot(type=PlotType.FFT))
-        self.trains_button   = self.createButton(text=tr("Button", "Trains"),   connect=lambda: self.plot(type=PlotType.Trains))
-        self.combined_button = self.createButton(text=tr("Button", "Combined"), connect=lambda: self.plot(type=PlotType.Combined))
-        self.compare_button  = self.createButton(text=tr("Button", "Compare"),  connect=lambda: self.plot(type=PlotType.Compare))
-        self.follow_button   = self.createButton(text=tr("Button", "Follow"),   connect=lambda: self.plot(type=PlotType.Follow))
-        self.type_buttons    = {PlotType.Heatmap:self.heatmap_button, PlotType.FFT:self.fft_button,
-                                PlotType.Trains:self.trains_button,   PlotType.Combined:self.combined_button,
-                                PlotType.Compare:self.compare_button, PlotType.Follow:self.follow_button}
-        self.defaultButtonStyleSheet = self.heatmap_button.styleSheet()
-
-        self.plot_buttons_layout.addWidget(self.heatmap_button)
-        self.plot_buttons_layout.addWidget(self.fft_button)
-        self.plot_buttons_layout.addWidget(self.trains_button)
-        self.plot_buttons_layout.addWidget(self.combined_button)
-        self.plot_buttons_layout.addWidget(self.compare_button)
-        self.plot_buttons_layout.addWidget(self.follow_button)
-
-
-        self.adc1 = self.createCheckbox(text="ADC 1", connect=self.change_adc)
-        self.adc1.setChecked(True)
-        self.adc2 = self.createCheckbox(text="ADC 2", connect=self.change_adc)
-        self.adc3 = self.createCheckbox(text="ADC 3", connect=self.change_adc)
-        self.adc4 = self.createCheckbox(text="ADC 4", connect=self.change_adc)  
-
-        self.adc_checkbox_layout = QtGui.QHBoxLayout()
-        self.adc_checkbox_layout.addWidget(self.adc1)
-        self.adc_checkbox_layout.addWidget(self.adc2)
-        self.adc_checkbox_layout.addWidget(self.adc3)
-        self.adc_checkbox_layout.addWidget(self.adc4)
-
-        self.groupWidget = QtGui.QWidget()
-        self.groupWidget.setLayout(self.adc_checkbox_layout)
-        self.group = QtGui.QButtonGroup()
-        self.group.addButton(self.adc1, 1)
-        self.group.addButton(self.adc2, 2)
-        self.group.addButton(self.adc3, 3)
-        self.group.addButton(self.adc4, 4)
-        self.group.setExclusive(True)
-
-        self.compare_heading_left = self.createLabel("ADC 1")
-        self.compare_heading_left.setAlignment(QtCore.Qt.AlignCenter)
-        self.compare_heading_right = self.createLabel("ADC 2")
-        self.compare_heading_right.setAlignment(QtCore.Qt.AlignCenter)
-        self.compare_heading = QtGui.QWidget()
-        self.compare_heading_layout = QtGui.QHBoxLayout()
-        self.compare_heading_layout.addWidget(self.compare_heading_left)
-        self.compare_heading_layout.addWidget(self.compare_heading_right)
-        self.compare_heading.setLayout(self.compare_heading_layout)
-        self.compare_heading.hide()
-        self.adc1Compare = self.createCheckbox(text="ADC 1", connect=lambda: self.change_adc_compare(who=1))
-        self.adc2Compare = self.createCheckbox(text="ADC 2", connect=lambda: self.change_adc_compare(who=2))
-        self.adc3Compare = self.createCheckbox(text="ADC 3", connect=lambda: self.change_adc_compare(who=3))
-        self.adc4Compare = self.createCheckbox(text="ADC 4", connect=lambda: self.change_adc_compare(who=4))
-        self.adc1Compare.setChecked(True)
-        self.adc2Compare.setChecked(True)
-
-        self.adc_checkbox_compare_layout = QtGui.QHBoxLayout()
-        self.adc_checkbox_compare_layout.addWidget(self.adc1Compare)
-        self.adc_checkbox_compare_layout.addWidget(self.adc2Compare)
-        self.adc_checkbox_compare_layout.addWidget(self.adc3Compare)
-        self.adc_checkbox_compare_layout.addWidget(self.adc4Compare)
-
-        self.groupWidgetCompare = QtGui.QWidget()
-        self.groupWidgetCompare.setLayout(self.adc_checkbox_compare_layout)
-        self.groupCompare = QtGui.QButtonGroup()
-        self.groupCompare.setExclusive(False)
-        self.groupCompare.addButton(self.adc1Compare, 1)
-        self.groupCompare.addButton(self.adc2Compare, 2)
-        self.groupCompare.addButton(self.adc3Compare, 3)
-        self.groupCompare.addButton(self.adc4Compare, 4)
-        self.groupWidgetCompare.hide()
-
-        self.from_to_layout = QtGui.QHBoxLayout()
-        self.bucket_part = QtGui.QWidget()
-        self.bucket_layout = QtGui.QHBoxLayout()
-        self.bucket_layout.addWidget(self.createLabel(tr("Label", "Bucket:")))
-        self.bucket_spinbox = self.createSpinbox(0, config.bunches_per_turn-1, interval=1, connect=lambda: self.plot(self.theType))
-        self.bucket_layout.addWidget(self.bucket_spinbox)
-        self.bucket_part.setLayout(self.bucket_layout)
-        self.from_spinbox = self.createSpinbox(0, 100000000, interval=100, connect=lambda: self.plot(self.theType))
-        self.to_spinbox = self.createSpinbox(0, 100000000, start_value=1000, interval=100, connect=lambda: self.plot(self.theType))
-        self.from_to_layout.addStretch()
-        self.from_to_layout.addWidget(self.bucket_part)
-        self.from_to_layout.addWidget(self.createLabel(tr("Label", "From:")))
-        self.from_to_layout.addWidget(self.from_spinbox)
-        self.from_to_layout.addWidget(self.createLabel(tr("Label", "To:")))
-        self.from_to_layout.addWidget(self.to_spinbox)
-
-        self.layout.addLayout(self.plot_buttons_layout)
-        self.layout.addWidget(self.compare_heading)
-        self.layout.addWidget(self.plot_widget)
-        self.layout.addWidget(self.groupWidget)
-        self.layout.addWidget(self.groupWidgetCompare)
-        self.layout.addLayout(self.from_to_layout)
-        self.setLayout(self.layout)
-
-    def change_adc(self):
-        """
-        Change the adc for which data is plotted
-        :return: -
-        """
-        self.adc = self.group.checkedId()
-        self.change_identifier_text()
-        self.plot(self.theType)
-
-    def change_adc_compare(self, who):
-        """
-        Change the adcs displayed in a compare plot
-        :return:
-        """
-        if not self._single_adc_checked:
-            self.adc1Compare.setChecked(False)
-            self.adc2Compare.setChecked(False)
-            self.adc3Compare.setChecked(False)
-            self.adc4Compare.setChecked(False)
-            getattr(self, 'adc'+str(who)+'Compare').setChecked(True)
-            self._single_adc_checked = True
-        else:
-            checked = [i.isChecked() for i in self.groupCompare.buttons()]
-            self._single_adc_checked = False
-            self.adc    = np.where(np.array(checked) == True)[0][0] + 1  # +1 because adcs are 1 based and indices 0
-            self.secadc = np.where(np.array(checked) == True)[0][1] + 1  # +1 because adcs are 1 based and indices 0
-            self.compare_heading_left.setText("ADC "+str(self.adc))
-            self.compare_heading_right.setText("ADC "+str(self.secadc))
-            self.change_identifier_text()
-            if self.theType in [ERROR, NO_DATA]:
-                self.plot(self._old_type)
-            else:
-                self.plot(self.theType)
-
-    def disable_buttons(self, b_bool):
-        """
-        Disable the buttons on this widget
-        This is not used at the moment
-        :param b_bool: (bool) disable(False) or enable(True)
-        :return: -
-        """
-        self.heatmap_button.setDisabled(b_bool)
-        self.fft_button.setDisabled(b_bool)
-        self.trains_button.setDisabled(b_bool)
-        self.combined_button.setDisabled(b_bool)
-
-    def changePlotType(self, type):
-        """
-        Change the plot type to the given type.
-        :param type: the new type
-        """
-        if type not in [ERROR, NO_DATA]:
-            self._old_type = type
-        self.theType = type
-        self.plot_widget.changeType(type)
-        for btype, button in self.type_buttons.items():
-            style = ""
-            if btype == type:
-                style += "QPushButton {background-color:lightgreen;}"
-            else:
-                style += self.defaultButtonStyleSheet
-            style += "QPushButton:focus{background-color: lightgrey!important; border-color: lightblue;}"  # Note: this does not work
-            button.setStyleSheet(style)
-        if self.theType == PlotType.Follow:
-            self.bucket_part.show()
-        else:
-            self.bucket_part.hide()
-    def plot(self, type=None):
-        """
-        Wrapper function to call the correct plot function depending on type
-        :param type: (int) the plot type
-        :return: -
-        """
-        try:
-            if type == None:
-                if self.theType in [ERROR, NO_DATA]:
-                    type = self._old_type
-                else:
-                    type = self.theType
-            self.do_plot(type)
-        except Exception:
-            if self.data is None or len(self.data.array) == 0:
-                self.changePlotType(NO_DATA)
-            else:
-                self.changePlotType(ERROR)
-            self.plot_widget.plot(None)
-
-    def plot_live(self, type=None, data=None):
-        """
-        Function to call when livedata is to be plotted
-        :param type: (int) plot type
-        :param data: (dataset.DataSet) data to plot
-        :return: -
-        """
-        self.data = data
-        try:
-            if type == None:
-                if self.theType in [ERROR, NO_DATA]:
-                    type = self._old_type
-                else:
-                    type = self.theType
-            if type == PlotType.Trains or type == PlotType.Combined or type == PlotType.Follow:
-                self.do_plot(type, autorange=True)
-            else:
-                self.do_plot(type, autorange=False)
-        except Exception:
-            if len(self.data.array) == 0:
-                self.changePlotType(NO_DATA)
-            else:
-                self.changePlotType(ERROR)
-            self.plot_widget.plot(None)
-
-    def do_plot(self, type, autorange=True):
-        """
-        Actually perform a plot (this calls SubPlotWidget.plot)
-        :param type: (int) plot type
-        :param autorange: (bool) whether to perform a autorange upon plot
-        :return: -
-        """
-        if self.theType != type:
-            if type == PlotType.Combined:
-                self.groupWidget.hide()
-            elif self.theType == PlotType.Combined:
-                self.groupWidget.show()
-
-            if type == PlotType.Compare:
-                self.groupWidget.hide()
-                self.groupWidgetCompare.show()
-                self.compare_heading.show()
-            else:
-                self.groupWidget.show()
-                self.groupWidgetCompare.hide()
-                self.compare_heading.hide()
-
-            self.changePlotType(type)
-            self.setWindowTitle(self.theName + " - " + plotList[type] + " - " + str(self.thePrefix))
-            self.change_identifier_text()
-            if self.theType == PlotType.Compare:
-                self.change_identifier_text()
-
-        if self.theType == PlotType.Compare:
-            self.groupWidget.hide()
-            self.groupWidgetCompare.show()
-            self.compare_heading.show()
-        else:
-            self.groupWidgetCompare.hide()
-            self.compare_heading.hide()
-
-        f = self.from_spinbox.value()
-        t = self.to_spinbox.value() if self.to_spinbox.value() > f else f+1
-        b = self.bucket_spinbox.value()
-        if type == PlotType.FFT:
-            self.plot_widget.plot(np.abs(self.data.fft(adc=self.adc, frm=f, to=t+1, drop_first_bin=True)).transpose(),
-                                  autorange=autorange,
-                                  xvalueborders=[self.data.fft_freq_dist(), self.data.fft_max_freq()])
-        if type == PlotType.Heatmap:
-            self.plot_widget.plot(self.data.heatmap(adc=self.adc, frm=f, to=t).transpose(), autorange=autorange)
-        if type == PlotType.Compare:
-            self.plot_widget.plot([self.data.heatmap(adc=self.adc, frm=f, to=t).transpose(),self.data.heatmap(adc=self.secadc, frm=f, to=t).transpose()], autorange=autorange)
-        if type == PlotType.Trains:
-            self.plot_widget.plot(self.data.train(adc=self.adc, frm=f, to=t), autorange=autorange)
-        if type == PlotType.Follow:
-            self.plot_widget.plot(self.data.follow(adc=self.adc, frm=f, to=t, bunch=b), autorange=autorange)
-        if type == PlotType.Combined:
-            self.plot_widget.plot(self.data.combined(frm=f, to=t), autorange=autorange)
-
-    def change_identifier_text(self):
-        """
-        Change the text that identifies the plot in the left bar
-        :return:
-        """
-        if self.theType is PlotType.Compare:
-            the_text = str(self.adc)+"+"+str(self.secadc)
-        else:
-            the_text = str(self.adc)
-        if self.theType in [ERROR, NO_DATA]:
-            type = self._old_type
-        else:
-            type = self.theType
-        if self.theDataType is LIVE:
-            self.change_type_signal.emit(self.theId, type,
-                                         the_text+" B: "+available_boards.get_board_name_from_id(self.board_id))
-        else:
-            self.change_type_signal.emit(self.theId, type, the_text)
-
-    def closeEvent(self, event):
-        """
-        Event Handler to handle the event of closing this window
-        :param event: QEvent
-        :return: -
-        """
-        if not self.close_silent:
-            if not self.parent.remove_plot(self.theId, silent=self.close_silent):
-                event.ignore()
-                return
-        del self.data
-        del self.plot_widget
-        super(PlotWidget, self).closeEvent(event)
-        if self.theDataType == LIVE:
-            live_plot_windows.removeWindow(self.board_id, self)
-        self.close_signal.emit()
-        del self
+"""
+This Module implements the Plot Windows used in KCG
+"""
+import pyqtgraph as pg
+try:  # some versions of pyqtgraph do not support this option
+    pg.setConfigOption('useOpenGl', True)
+except:
+    pass
+pg.setConfigOption('background', 'w')
+pg.setConfigOption('foreground', 'k')
+import numpy as np
+from PyQt4 import QtCore, QtGui
+from groupedelements import live_plot_windows
+from backend import io
+from backend import board
+from backend.board import available_boards
+import kcgwidget as kcgw
+
+tr = kcgw.tr
+
+LIVE = 1
+FILE = 2
+ERROR = 99
+NO_DATA = 98
+
+
+class Enum():
+    """
+    Simple Enum Class (as this is not supported by clean python)
+    """
+    def __init__(self, *args):
+        self.idx = 0
+        for i in args:
+            setattr(self, i, self.idx)
+            self.idx += 1
+
+plotList = [tr("Label", "Heatmap"), tr("Label", "FFT"), tr("Label", "Trains"), tr("Label", "Combined"), tr("Label", "Compare")]
+PlotType = Enum(*[str(i) for i in plotList])
+
+# gradient = {'mode': 'rgb',
+#             'ticks': [(0.0, (0.37281, 0.11883, 3.53583, 255)),
+#                       (0.0390625, (4.9401150000000005, 3.858915, 22.635585, 255)),
+#                       (0.078125, (15.6417, 9.330449999999999, 45.29871, 255)),
+#                       (0.1171875, (29.74728, 12.13137, 69.44185499999999, 255)),
+#                       (0.15625, (46.774395000000005, 10.283895, 90.51760499999999, 255)),
+#                       (0.1953125, (64.1631, 9.614775, 102.86139, 255)),
+#                       (0.234375, (80.65191, 13.63995, 108.40458, 255)),
+#                       (0.2734375, (96.64525499999999, 19.444515, 110.343345, 255)),
+#                       (0.3125, (112.507785, 25.33119, 110.05646999999999, 255)),
+#                       (0.3515625, (128.390715, 31.001625, 107.95578, 255)),
+#                       (0.390625, (144.29277, 36.609585, 104.10579, 255)),
+#                       (0.4296875, (160.100985, 42.476625, 98.50038, 255)),
+#                       (0.46875, (175.606515, 49.020945, 91.188765, 255)),
+#                       (0.5078125, (190.517385, 56.70639, 82.32827999999999, 255)),
+#                       (0.546875, (204.477105, 65.96187, 72.190245, 255)),
+#                       (0.5859375, (217.10292, 77.07629999999999, 61.10718, 255)),
+#                       (0.625, (228.047775, 90.11674500000001, 49.36392, 255)),
+#                       (0.6640625, (237.05922, 104.927145, 37.068585, 255)),
+#                       (0.703125, (243.99726, 121.21578, 24.147225, 255)),
+#                       (0.7421875, (248.797635, 138.66849, 11.122589999999999, 255)),
+#                       (0.78125, (251.41776000000002, 157.01625, 6.52596, 255)),
+#                       (0.8203125, (251.80332, 176.04333, 20.397450000000003, 255)),
+#                       (0.859375, (249.90816, 195.543435, 42.420015, 255)),
+#                       (0.8984375, (245.92047, 215.18124, 69.714705, 255)),
+#                       (0.9375, (241.63647, 233.936745, 104.719575, 255)),
+#                       (0.9765625, (245.26206, 248.86062, 145.840875, 255)),
+#                       (0.99609375, (252.03231, 254.58282, 164.45562, 255))]}
+
+gradient = {'mode': 'rgb',
+            'ticks': [(0.0, (0.37281, 0.11883, 3.53583, 255)),
+                      (0.1171875, (29.74728, 12.13137, 69.44185499999999, 255)),
+                      (0.234375, (80.65191, 13.63995, 108.40458, 255)),
+                      (0.3515625, (128.390715, 31.001625, 107.95578, 255)),
+                      (0.46875, (175.606515, 49.020945, 91.188765, 255)),
+                      (0.5859375, (217.10292, 77.07629999999999, 61.10718, 255)),
+                      (0.703125, (243.99726, 121.21578, 24.147225, 255)),
+                      (0.8203125, (251.80332, 176.04333, 20.397450000000003, 255)),
+                      (0.9375, (241.63647, 233.936745, 104.719575, 255)),
+                      (0.99609375, (252.03231, 254.58282, 164.45562, 255))]}
+
+
+class CustomGradientEditorItem(pg.GradientEditorItem):
+    """
+    A Gradient Editor Item to insert a perception linear gradient
+    """
+    def __init__(self, **kwargs):
+        pg.GradientEditorItem.__init__(self, **kwargs)
+        self.customGradients = {}
+
+    def addGrad(self, name, grad_dic):
+        """
+        Add a gradient to the list of gradients in the gui
+        :param name: the name of the gradient
+        :param grad_dic: the dictionary containing the gradient data
+        """
+        if name not in self.customGradients:
+            self.customGradients[name] = grad_dic
+            px = QtGui.QPixmap(100, 15)
+            p = QtGui.QPainter(px)
+            self.restoreState(grad_dic)
+            length_backup = self.length
+            self.length = 100
+            grad = self.getGradient()
+            self.length = length_backup
+            brush = QtGui.QBrush(grad)
+            p.fillRect(QtCore.QRect(0, 0, 100, 15), brush)
+            p.end()
+            label = QtGui.QLabel()
+            label.setPixmap(px)
+            label.setContentsMargins(1, 1, 1, 1)
+            act = QtGui.QWidgetAction(self)
+            act.setDefaultWidget(label)
+            act.triggered.connect(self.contextMenuClicked)
+            act.name = name
+            if len(self.customGradients) > 1:
+                self.menu.insertAction(self.menu.actions()[0], act)
+            else:
+                sep = self.menu.insertSeparator(self.menu.actions()[0])
+                self.menu.insertAction(sep, act)
+
+        self.restoreState(grad_dic)
+
+    def restoreState(self, state):
+        """
+        Reimplemented of pyqtgraph.GradientEditorItem.restoreState to work with our custom perception linear gradient.
+        :param state: the state to restore to
+        """
+        self.setColorMode(state['mode'])
+        for t in list(self.ticks.keys()):
+            self.removeTick(t, finish=False)
+        self.bottomTick = [None, 10]
+        self.topmostTick = [None, -1]
+        for t in state['ticks']:
+            c = QtGui.QColor(*t[1])
+            tick = self.addTick(t[0], c, finish=False)
+            if t[0] < self.bottomTick[1]:
+                self.bottomTick = [tick, t[0]]
+            if t[0] > self.topmostTick[1]:
+                self.topmostTick = [tick, t[0]]
+        self.tickPosInPercent = self.ticks.copy()
+        for t in self.ticks:
+            if t is self.topmostTick[0] or t is self.bottomTick[0]:
+                continue
+            t.hide()
+            t.movable = False
+        # self.bottomTick[1] *= self.length
+        # self.topmostTick[1] *= self.length
+        self.updateGradient()
+        self.sigGradientChangeFinished.emit(self)
+
+    def loadPreset(self, name):
+        """
+        Reimplemented of pyqtgraph.GradientEditorItem.loadPreset to work with our custom perception linear gradient.
+        :param name: the name of the preset to load
+        """
+        if name in self.customGradients:
+            self.restoreState(self.customGradients[name])
+        else:
+            super(CustomGradientEditorItem, self).loadPreset(name)
+
+    def tickMoved(self, tick, pos):
+        """
+        Reimplemented of pyqtgraph.GradientEditorItem.tickMoved to work with our custom perception linear gradient,
+        which has a lot of steps which would create a lot of ticks in the gradient legend. This removes all but 2
+        ticks and aligns all the internal ticks accordingly.
+        :param tick: the tick to move
+        :param pos: the position to move to
+        """
+        if tick is self.bottomTick[0]:
+            # self.bottomTick[1] = pos.x()
+            pg.TickSliderItem.tickMoved(self, tick, pos)
+        elif tick is self.topmostTick[0]:
+            # self.topmostTick[1] = pos.x()
+            pg.TickSliderItem.tickMoved(self, tick, pos)
+        if tick in [self.bottomTick[0], self.topmostTick[0]]:  # if bottom or topmost tick was moved
+            if pos.x() < self.length and pos.x() > 0:  # if tick is not at top or bottom
+                newUnit = (self.topmostTick[0].pos().x() - self.bottomTick[0].pos().x())  # create new virtual unit length
+                for t in self.ticks:  # for every tick
+                    if t not in [self.bottomTick[0], self.topmostTick[0]]:  # if tick is not bottom or topmost tick
+                        pos = t.pos()
+                        new_x = self.bottomTick[0].pos().x() + self.tickPosInPercent[t] * newUnit
+                        pos.setX(new_x)
+                        t.setPos(pos)
+                        pg.TickSliderItem.tickMoved(self, t, pos)
+        self.updateGradient()
+
+
+class SpectrogramColorLegendItem(pg.GraphicsWidget):
+    """
+    The Item used as Legend for Heatmap and FFT Plot
+    """
+    gradientChanged = QtCore.pyqtSignal()
+    def __init__(self, img=None):
+        """
+        Initialise the Legend
+        :param img: (ImageItem) the image item this is the legend for
+        :return: -
+        """
+        super(SpectrogramColorLegendItem, self).__init__()
+        self.layout = QtGui.QGraphicsGridLayout()
+        self.setLayout(self.layout)
+        self.img = img
+        # self.gei = pg.GradientEditorItem(orientation='right')
+        self.gei = CustomGradientEditorItem(orientation='right', allowAdd=False)
+        self.legend_axis = pg.AxisItem(orientation='left')
+        # self.addItem(self.legend_axis)
+        self.layout.addItem(self.legend_axis, 0, 0, alignment=QtCore.Qt.AlignVCenter)
+        # self.addItem(self.gei)
+        self.layout.addItem(self.gei, 0, 1)
+        self.gei.sigGradientChanged.connect(self.gradient_changed)
+        # self.gei.loadPreset('spectrum')
+        self.gei.addGrad('inferno', gradient)
+        # self.setFixedWidth(80)
+        self.image_changed()
+
+    def gradient_changed(self):
+        """
+        Proxy function for the gradient_cnahged event of the GradientEditorItem
+        :return:
+        """
+        self.gradientChanged.emit()
+
+    def set_image(self, img):
+        """
+        Set the Image this is the legend for (only needed if not already done upon initialisation)
+        :param img: (ImageItem) The image item to use
+        :return: -
+        """
+        self.img = img
+        self.image_changed(True)
+
+    def update_axis(self):
+        """
+        Update the axis of this legend
+        :return: -
+        """
+        if self.img.getLevels():
+            # min, max = self.img.getLevels()
+            min, max = (np.min(self.img.image), np.max(self.img.image))
+            self.legend_axis.setRange(min, max)
+
+    def reset_gradient(self):
+        """
+        Reset the gradient to the preset "spectrum"
+        :return: -
+        """
+        # self.gei.loadPreset('spectrum')
+        self.gei.loadPreset('inferno')
+        # self.gei.restoreState(gradient)
+
+    def image_changed(self, reset_gradient=True):
+        """
+        Call this to adjust the legend when the image has changed
+        :param reset_gradient: (bool) whether to reset the gradient or not
+        :return:
+        """
+        if self.img:
+            self.update_axis()
+            if reset_gradient:
+                self.reset_gradient()
+
+    def getLookupTable(self, nPts, alpha=None):
+        """
+        Get the look up table of the imageitem
+        :param nPts: (int) number of points the lookup table is defined on
+        :param alpha: ??
+        :return: -
+        """
+        return self.gei.getLookupTable(nPts, alpha=alpha)
+
+    def resizeEvent(self, event):
+        """
+        Handle resizing of the window
+        """
+        self.legend_axis.setHeight(self.gei.length)
+
+
+class SubPlotWidget(pg.GraphicsLayoutWidget):
+    """
+    The Widget actually containing the plots and images
+    """
+    def __init__(self, dType=FILE):
+        super(SubPlotWidget, self).__init__()
+
+        self.dType = dType
+
+        self._type_changed = False
+        # self.setStyleSheet("border: 5px solid black; margin: 10px;")
+        self.plotType = 1
+        self.plotItem = pg.PlotItem()
+        self.img = pg.ImageItem()
+        self.plotItem2 = pg.PlotItem()
+        self.img2 = pg.ImageItem()
+        self.plotItem2.addItem(self.img2)
+        self.setFrameStyle(self.StyledPanel | self.Sunken)
+
+        self.gradient_legend = SpectrogramColorLegendItem(self.img)
+        def changelut():
+            """
+            Handle changing of the lookup table
+            """
+            self.img.setLookupTable(self.gradient_legend.getLookupTable(512))
+            self.img2.setLookupTable(self.gradient_legend.getLookupTable(512))
+        self.gradient_legend.gradientChanged.connect(changelut)
+
+        self.addItem(self.plotItem)
+        self.plotItem.addItem(self.img)
+        self.plotItemPlotScatter = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(50, 50, 255), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(50, 50, 255), downsample=200)
+        self.plotItemPlotScatter1 = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(255, 50, 50), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(255, 50, 50), downsample=200)
+        # self.plotItemPlotScatter2 = pg.PlotDataItem(pen=None, symbolPen=pg.mkPen(50, 255, 50), symbolSize=3, pxMode=True, symbolBrush=pg.mkBrush(50, 255, 50), downsample=200)
+        self.plotItemPlot = pg.PlotDataItem()  # pen="#000000")
+        self.plotItem.addItem(self.plotItemPlot)
+        self.plotItem.addItem(self.plotItemPlotScatter)
+        self.plotItem.addItem(self.plotItemPlotScatter1)
+        # self.plotItem.addItem(self.plotItemPlotScatter2)
+
+        self.plotItem.vb.origAutoRange = self.plotItem.vb.autoRange
+
+        self.error_label = pg.LabelItem("Error during plot.", color=(200, 50, 50))
+        self.error_label.hide()
+        self.error_label.scale(1, -0.6)
+        self.error_label.translate(0, -30)
+        self.plotItem.addItem(self.error_label)
+
+        self.no_data_label = pg.LabelItem("No data to plot.", color=(200, 50, 50))
+        self.no_data_label.hide()
+        self.no_data_label.scale(1, -0.6)
+        self.no_data_label.translate(0, -30)
+        self.plotItem.addItem(self.no_data_label)
+
+    def _enableCustomAutoRange(self, data):
+        """
+        Enable custom auto range in this plot
+        :param data: the data to autorange
+        :return:
+        """
+        def newAutoRange(*args, **kwargs):
+            ''' function to handle the new autorange '''
+            bounds = [np.min(data), np.max(data)]
+            self.plotItem.vb.setRange(xRange=[0, len(data)],
+                yRange=[bounds[0]-0.1*(bounds[1]-bounds[0])-1, bounds[1]+0.1*(bounds[1]-bounds[0])+1], update=True)
+            self.plotItem.update()
+        self.plotItem.vb.autoRange = newAutoRange
+        self.plotItem.autoBtn.clicked.disconnect()
+        self.plotItem.autoBtn.clicked.connect(newAutoRange)
+
+    def _disableCustomAutoRange(self):
+        """
+        Disable the custom autorange and reset it to default
+        :return: -
+        """
+        self.plotItem.vb.autoRange = self.plotItem.vb.origAutoRange
+        self.plotItem.autoBtn.clicked.disconnect()
+        self.plotItem.autoBtn.clicked.connect(self.plotItem.autoBtnClicked)
+
+    @QtCore.pyqtSlot(np.ndarray, tuple, tuple)
+    def plot(self, data, xvalueborders=None, yvalueborders=None, autorange=True):
+        """
+        Plot Data. The plot type depends on the type property
+        :param data: (dataset.DataSet) data to plot
+        :param xvalueborders: (touple) the borders for the xvalues
+        :param yvalueborders: (touple) the borders for the yvalues
+        :param autorange: (bool) whether to perform a autorange or not
+        :return: -
+        """
+        self.error_label.hide()
+        self.no_data_label.hide()
+        self.plotItemPlot.clear()
+        self.plotItemPlotScatter.clear()
+        self.plotItemPlotScatter1.clear()
+        # self.plotItemPlotScatter2.clear()
+        self.plotItem.resetTransform()
+        self.plotItem.getAxis('bottom').setScale()
+        self.img.show()
+        if self._type_changed:
+            self.plotItem.autoBtnClicked()
+        try:
+            self.removeItem(self.plotItem2)
+        except Exception as e:
+            if "Could not determine index of item" in str(e):
+                pass
+            else:
+                raise
+
+        if self.plotType == PlotType.FFT or self.plotType == PlotType.Heatmap:
+            self.addItem(self.gradient_legend)
+            self.gradient_legend.show()
+
+            self.img.setImage(data, scale=[1, 1/1000.])
+            self.img.resetTransform()
+            self.img.setAutoDownsample(True)
+            if autorange:
+                self.gradient_legend.image_changed()
+
+            if xvalueborders:
+                self.plotItem.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data.shape[0]))
+                self.img.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
+
+            else:
+                self.plotItem.getAxis('bottom').setScale()
+
+            if yvalueborders:
+                self.img.translate(0, yvalueborders[0])
+                self.plotItem.getAxis('left').setScale(yvalueborders[1]/float(data.shape[1]))
+
+            self._disableCustomAutoRange()
+            self.plotItem.setClipToView(True)
+
+        if self.plotType == PlotType.Compare:
+            self.addItem(self.plotItem2)
+            self.plotItem2.vb.linkView(self.plotItem2.vb.XAxis, self.plotItem.vb)
+            self.plotItem2.vb.linkView(self.plotItem2.vb.YAxis, self.plotItem.vb)
+            self.addItem(self.gradient_legend)
+            self.gradient_legend.show()
+
+            self.img.setImage(data[0], scale=[1, 1/1000.])
+            self.img.show()
+            self.img.resetTransform()
+            self.img2.setImage(data[1], scale=[1, 1/1000.])
+            self.img2.show()
+            self.img2.resetTransform()
+            self.img.setAutoDownsample(True)
+            self.img2.setAutoDownsample(True)
+            if autorange:
+                self.gradient_legend.image_changed()
+                pass
+
+            if xvalueborders:
+                self.plotItem.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data[0].shape[0]))
+                self.plotItem2.getAxis('bottom').setScale((xvalueborders[1]-xvalueborders[0])/float(data[1].shape[0]))
+                self.img.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
+                self.img2.translate(xvalueborders[0]/self.plotItem.getAxis('bottom').scale, 0)
+
+            else:
+                self.plotItem.getAxis('bottom').setScale()
+                self.plotItem2.getAxis('bottom').setScale()
+
+            if yvalueborders:
+                self.img.translate(0, yvalueborders[0])
+                self.img2.translate(0, yvalueborders[0])
+                self.plotItem.getAxis('left').setScale(yvalueborders[1]/float(data[0].shape[1]))
+                self.plotItem2.getAxis('left').setScale(yvalueborders[1]/float(data[1].shape[1]))
+
+            self._disableCustomAutoRange()
+            self.plotItem.setClipToView(True)
+
+        if self.plotType == PlotType.Trains:
+            self.img.clear()
+            self.plotItemPlot.show()
+            self.gradient_legend.hide()
+            try:
+                self.removeItem(self.gradient_legend)
+                pass
+            except Exception, e:
+                if not "Could not determine index of item" in str(e):
+                    raise
+            self.plotItemPlotScatter.clear()
+            self.plotItemPlotScatter1.clear()
+            self.plotItemPlot.setData(data)
+
+            self._enableCustomAutoRange(data)
+
+        if self.plotType == PlotType.Combined:
+            self.img.clear()
+            self.plotItemPlotScatter.show()
+            self.plotItemPlotScatter1.show()
+            self.gradient_legend.hide()
+            try:
+                self.removeItem(self.gradient_legend)
+                pass
+            except Exception, e:
+                if not "Could not determine index of item" in str(e):
+                    raise
+            self.plotItemPlot.clear()
+            self.plotItemPlotScatter.setData(data[0].transpose())
+            self.plotItemPlotScatter1.setData(data[1].transpose())
+
+            self._enableCustomAutoRange(data[0][1])
+
+            self.plotItem.setClipToView(False)  # NOTE: otherwise only a very small portion of data is visible :wonder:
+
+        if self.plotType == ERROR or self.plotType == NO_DATA:
+            self.img.hide()
+            self.gradient_legend.hide()
+            self.img2.hide()
+            self.plotItemPlot.hide()
+            self.plotItemPlotScatter.hide()
+            self.plotItemPlotScatter1.hide()
+            if self.plotType == ERROR:
+                self.error_label.show()
+            else:
+                self.no_data_label.show()
+
+        if autorange:
+            self.plotItem.getViewBox().autoRange()
+            self.plotItem.getViewBox().autoRange()
+
+        self.labelAxes()
+        if self.plotType in [PlotType.FFT, PlotType.Heatmap, PlotType.Compare]:
+            self.gradient_legend.update_axis()
+
+    def labelAxes(self):
+        """
+        Add Labels to the axis depending on the self.plotType property.
+        :return: -
+        """
+        if self.plotType == PlotType.Heatmap:
+            self.plotItem.setLabel('bottom', 'Turn', '')
+            self.plotItem.setLabel('left', 'Bunch Position', '')
+        elif self.plotType == PlotType.FFT:
+            self.plotItem.setLabel('left', 'Bunch Position', '')
+            self.plotItem.setLabel('bottom', 'Frequency', 'Hz')
+        elif self.plotType == PlotType.Trains:
+            self.plotItem.setLabel('left', '', '')
+            self.plotItem.setLabel('bottom', 'Sample Point', '')
+        elif self.plotType == PlotType.Combined:
+            self.plotItem.setLabel('left', '', '')
+            self.plotItem.setLabel('bottom', '', '')
+        if self.plotType == PlotType.Compare:
+            self.plotItem.setLabel('bottom', 'Turn', '')
+            self.plotItem.setLabel('left', 'Bunch Position', '')
+            self.plotItem2.setLabel('bottom', 'Turn', '')
+            self.plotItem2.setLabel('left', 'Bunch Position', '')
+
+    def changeType(self, type):
+        """
+        Change the plot Type
+        :param type: (int) the new type
+        :return: -
+        """
+        if type != self.plotType:
+            self._type_changed = True
+        self.plotType = type
+        # if type in [PlotType.FFT, PlotType.Compare, PlotType.Heatmap]:  # this will generate an error
+        #     self.gradient_legend.image_changed()
+
+class PlotWidget(kcgw.KCGWidgets):
+    """
+    The container Class holding various buttons and controls and the actual plots as SubPlotWidgets instance
+    """
+    close_signal = QtCore.pyqtSignal()
+    change_type_signal = QtCore.pyqtSignal(int, int, str)
+
+    def __init__(self, board_id, parent=None, name=None, unique_id=None, type=None, datatype=None, prefix=None, fName=None, data=None):
+        """
+        Initialise the Plot Widgt
+        :param parent: (QWidget) the parent widget
+        :param name: (str) name of this widget
+        :param unique_id: (int) unique id of this widget
+        :param type: (int) type of this widget
+        :param datatype: (int) datatype (LIVE or FILE)
+        :param prefix: (str) prefix for text in the listview in LeftBar
+        :param fName: (str) the filename (this is only used if datatype is FILE)
+        :param data: (dataset.DataSet) the data to be plotted
+        :return: -
+        """
+        super(PlotWidget, self).__init__()
+
+        self.board_id = board_id
+
+        if name != None:
+            self.theName = name
+        if unique_id != None:
+            self.theId = unique_id
+        if type != None:
+            self.theType = type
+            self._old_type = type
+        if datatype != None:
+            self.theDataType = datatype
+        if prefix != None:
+            self.thePrefix = prefix
+        if self.theDataType == FILE:
+            self.fName = fName
+        if self.theDataType == FILE:
+            self.data = data
+        else:
+            self.data = None
+
+        self.close_silent = False
+        self.parent = parent
+        self.adc = 1
+        self.secadc = 2
+        self._single_adc_checked = False
+        self.initUI()
+        self.changePlotType(self.theType)  # initially mark the correct button
+        self.setWindowTitle(self.theName + " - " + plotList[type] + " - " + str(self.thePrefix))
+        if self.theDataType == FILE:
+            self.plot(type)
+        else:
+            live_plot_windows.addWindow(board_id, self)
+            if board.get_board_status(board_id).last_file is not None:
+                self.data = io.read_from_file(board.get_board_status(board_id).last_file)
+                self.plot(type)
+
+    def initUI(self):
+        """
+        Initialise the UI
+        :return: -
+        """
+        self.plot_widget = SubPlotWidget(dType=self.theDataType)
+        self.layout = QtGui.QVBoxLayout()
+        self.plot_buttons_layout = QtGui.QHBoxLayout()
+
+        self.heatmap_button = self.createButton(text=tr("Button", "Heatmap"), connect=lambda: self.plot(type=PlotType.Heatmap))
+        self.fft_button = self.createButton(text=tr("Button", "FFT"), connect=lambda: self.plot(type=PlotType.FFT))
+        self.trains_button = self.createButton(text=tr("Button", "Trains"), connect=lambda: self.plot(type=PlotType.Trains))
+        self.combined_button = self.createButton(text=tr("Button", "Combined"), connect=lambda: self.plot(type=PlotType.Combined))
+        self.compare_button = self.createButton(text=tr("Button", "Compare"), connect=lambda: self.plot(type=PlotType.Compare))
+        self.type_buttons = {PlotType.Heatmap:self.heatmap_button, PlotType.FFT:self.fft_button,
+                             PlotType.Trains:self.trains_button, PlotType.Combined:self.combined_button,
+                             PlotType.Compare:self.compare_button}
+        self.defaultButtonStyleSheet = self.heatmap_button.styleSheet()
+
+        self.adc1 = self.createCheckbox(text="ADC 1", connect=self.change_adc)
+        self.adc1.setChecked(True)
+        self.adc2 = self.createCheckbox(text="ADC 2", connect=self.change_adc)
+        self.adc3 = self.createCheckbox(text="ADC 3", connect=self.change_adc)
+        self.adc4 = self.createCheckbox(text="ADC 4", connect=self.change_adc)
+
+        self.plot_buttons_layout.addWidget(self.heatmap_button)
+        self.plot_buttons_layout.addWidget(self.fft_button)
+        self.plot_buttons_layout.addWidget(self.trains_button)
+        self.plot_buttons_layout.addWidget(self.combined_button)
+        self.plot_buttons_layout.addWidget(self.compare_button)
+
+        self.adc_checkbox_layout = QtGui.QHBoxLayout()
+        self.adc_checkbox_layout.addWidget(self.adc1)
+        self.adc_checkbox_layout.addWidget(self.adc2)
+        self.adc_checkbox_layout.addWidget(self.adc3)
+        self.adc_checkbox_layout.addWidget(self.adc4)
+
+        self.groupWidget = QtGui.QWidget()
+        self.groupWidget.setLayout(self.adc_checkbox_layout)
+        self.group = QtGui.QButtonGroup()
+        self.group.addButton(self.adc1, 1)
+        self.group.addButton(self.adc2, 2)
+        self.group.addButton(self.adc3, 3)
+        self.group.addButton(self.adc4, 4)
+        self.group.setExclusive(True)
+
+        self.compare_heading_left = self.createLabel("ADC 1")
+        self.compare_heading_left.setAlignment(QtCore.Qt.AlignCenter)
+        self.compare_heading_right = self.createLabel("ADC 2")
+        self.compare_heading_right.setAlignment(QtCore.Qt.AlignCenter)
+        self.compare_heading = QtGui.QWidget()
+        self.compare_heading_layout = QtGui.QHBoxLayout()
+        self.compare_heading_layout.addWidget(self.compare_heading_left)
+        self.compare_heading_layout.addWidget(self.compare_heading_right)
+        self.compare_heading.setLayout(self.compare_heading_layout)
+        self.compare_heading.hide()
+        self.adc1Compare = self.createCheckbox(text="ADC 1", connect=lambda: self.change_adc_compare(who=1))
+        self.adc2Compare = self.createCheckbox(text="ADC 2", connect=lambda: self.change_adc_compare(who=2))
+        self.adc3Compare = self.createCheckbox(text="ADC 3", connect=lambda: self.change_adc_compare(who=3))
+        self.adc4Compare = self.createCheckbox(text="ADC 4", connect=lambda: self.change_adc_compare(who=4))
+        self.adc1Compare.setChecked(True)
+        self.adc2Compare.setChecked(True)
+
+        self.adc_checkbox_compare_layout = QtGui.QHBoxLayout()
+        self.adc_checkbox_compare_layout.addWidget(self.adc1Compare)
+        self.adc_checkbox_compare_layout.addWidget(self.adc2Compare)
+        self.adc_checkbox_compare_layout.addWidget(self.adc3Compare)
+        self.adc_checkbox_compare_layout.addWidget(self.adc4Compare)
+
+        self.groupWidgetCompare = QtGui.QWidget()
+        self.groupWidgetCompare.setLayout(self.adc_checkbox_compare_layout)
+        self.groupCompare = QtGui.QButtonGroup()
+        self.groupCompare.setExclusive(False)
+        self.groupCompare.addButton(self.adc1Compare, 1)
+        self.groupCompare.addButton(self.adc2Compare, 2)
+        self.groupCompare.addButton(self.adc3Compare, 3)
+        self.groupCompare.addButton(self.adc4Compare, 4)
+        self.groupWidgetCompare.hide()
+
+        self.from_to_layout = QtGui.QHBoxLayout()
+        self.from_spinbox = self.createSpinbox(0, 100000000, interval=100, connect=lambda: self.plot(self.theType))
+        self.to_spinbox = self.createSpinbox(0, 100000000, start_value=1000, interval=100, connect=lambda: self.plot(self.theType))
+        self.from_to_layout.addStretch()
+        self.from_to_layout.addWidget(self.createLabel(tr("Label", "From:")))
+        self.from_to_layout.addWidget(self.from_spinbox)
+        self.from_to_layout.addWidget(self.createLabel(tr("Label", "To:")))
+        self.from_to_layout.addWidget(self.to_spinbox)
+
+        self.layout.addLayout(self.plot_buttons_layout)
+        self.layout.addWidget(self.compare_heading)
+        self.layout.addWidget(self.plot_widget)
+        self.layout.addWidget(self.groupWidget)
+        self.layout.addWidget(self.groupWidgetCompare)
+        self.layout.addLayout(self.from_to_layout)
+        self.setLayout(self.layout)
+
+    def change_adc(self):
+        """
+        Change the adc for which data is plotted
+        :return: -
+        """
+        self.adc = self.group.checkedId()
+        self.change_identifier_text()
+        self.plot(self.theType)
+
+    def change_adc_compare(self, who):
+        """
+        Change the adcs displayed in a compare plot
+        :return:
+        """
+        if not self._single_adc_checked:
+            self.adc1Compare.setChecked(False)
+            self.adc2Compare.setChecked(False)
+            self.adc3Compare.setChecked(False)
+            self.adc4Compare.setChecked(False)
+            getattr(self, 'adc'+str(who)+'Compare').setChecked(True)
+            self._single_adc_checked = True
+        else:
+            checked = [i.isChecked() for i in self.groupCompare.buttons()]
+            self._single_adc_checked = False
+            self.adc = np.where(np.array(checked) == True)[0][0] + 1  # +1 because adcs are 1 based and indices 0
+            self.secadc = np.where(np.array(checked) == True)[0][1] + 1  # +1 because adcs are 1 based and indices 0
+            self.compare_heading_left.setText("ADC "+str(self.adc))
+            self.compare_heading_right.setText("ADC "+str(self.secadc))
+            self.change_identifier_text()
+            if self.theType in [ERROR, NO_DATA]:
+                self.plot(self._old_type)
+            else:
+                self.plot(self.theType)
+
+    def disable_buttons(self, b_bool):
+        """
+        Disable the buttons on this widget
+        This is not used at the moment
+        :param b_bool: (bool) disable(False) or enable(True)
+        :return: -
+        """
+        self.heatmap_button.setDisabled(b_bool)
+        self.fft_button.setDisabled(b_bool)
+        self.trains_button.setDisabled(b_bool)
+        self.combined_button.setDisabled(b_bool)
+
+    def changePlotType(self, type):
+        """
+        Change the plot type to the given type.
+        :param type: the new type
+        """
+        if type not in [ERROR, NO_DATA]:
+            self._old_type = type
+        self.theType = type
+        self.plot_widget.changeType(type)
+        for btype, button in self.type_buttons.iteritems():
+            style = ""
+            if btype == type:
+                style += "QPushButton {background-color:lightgreen;}"
+            else:
+                style += self.defaultButtonStyleSheet
+            style += "QPushButton:focus{background-color: lightgrey!important; border-color: lightblue;}"  # Note: this does not work
+            button.setStyleSheet(style)
+    def plot(self, type=None):
+        """
+        Wrapper function to call the correct plot function depending on type
+        :param type: (int) the plot type
+        :return: -
+        """
+        try:
+            if type == None:
+                if self.theType in [ERROR, NO_DATA]:
+                    type = self._old_type
+                else:
+                    type = self.theType
+            self.do_plot(type)
+        except Exception:
+            if self.data is None or len(self.data.array) == 0:
+                self.changePlotType(NO_DATA)
+            else:
+                self.changePlotType(ERROR)
+            self.plot_widget.plot(None)
+
+    def plot_live(self, type=None, data=None):
+        """
+        Function to call when livedata is to be plotted
+        :param type: (int) plot type
+        :param data: (dataset.DataSet) data to plot
+        :return: -
+        """
+        self.data = data
+        try:
+            if type == None:
+                if self.theType in [ERROR, NO_DATA]:
+                    type = self._old_type
+                else:
+                    type = self.theType
+            if type == PlotType.Trains or type == PlotType.Combined:
+                self.do_plot(type, autorange=True)
+            else:
+                self.do_plot(type, autorange=False)
+        except Exception:
+            if len(self.data.array) == 0:
+                self.changePlotType(NO_DATA)
+            else:
+                self.changePlotType(ERROR)
+            self.plot_widget.plot(None)
+
+    def do_plot(self, type, autorange=True):
+        """
+        Actually perform a plot (this calls SubPlotWidget.plot)
+        :param type: (int) plot type
+        :param autorange: (bool) whether to perform a autorange upon plot
+        :return: -
+        """
+        if self.theType != type:
+            if type == PlotType.Combined:
+                self.groupWidget.hide()
+            elif self.theType == PlotType.Combined:
+                self.groupWidget.show()
+
+            if type == PlotType.Compare:
+                self.groupWidget.hide()
+                self.groupWidgetCompare.show()
+                self.compare_heading.show()
+            else:
+                self.groupWidget.show()
+                self.groupWidgetCompare.hide()
+                self.compare_heading.hide()
+
+            self.changePlotType(type)
+            self.setWindowTitle(self.theName + " - " + plotList[type] + " - " + str(self.thePrefix))
+            self.change_identifier_text()
+            if self.theType == PlotType.Compare:
+                self.change_identifier_text()
+
+        if self.theType == PlotType.Compare:
+            self.groupWidget.hide()
+            self.groupWidgetCompare.show()
+            self.compare_heading.show()
+        else:
+            self.groupWidgetCompare.hide()
+            self.compare_heading.hide()
+
+        f = self.from_spinbox.value()
+        t = self.to_spinbox.value() if self.to_spinbox.value() > f else f+1
+        if type == PlotType.FFT:
+            self.plot_widget.plot(np.abs(self.data.fft(adc=self.adc, frm=f, to=t+1, drop_first_bin=True)).transpose(),
+                                  autorange=autorange,
+                                  xvalueborders=[self.data.fft_freq_dist(), self.data.fft_max_freq()])
+        if type == PlotType.Heatmap:
+            self.plot_widget.plot(self.data.heatmap(adc=self.adc, frm=f, to=t).transpose(), autorange=autorange)
+        if type == PlotType.Compare:
+            self.plot_widget.plot([self.data.heatmap(adc=self.adc, frm=f, to=t).transpose(),self.data.heatmap(adc=self.secadc, frm=f, to=t).transpose()], autorange=autorange)
+        if type == PlotType.Trains:
+            self.plot_widget.plot(self.data.train(adc=self.adc, frm=f, to=t), autorange=autorange)
+        if type == PlotType.Combined:
+            self.plot_widget.plot(self.data.combined(frm=f, to=t), autorange=autorange)
+
+    def change_identifier_text(self):
+        """
+        Change the text that identifies the plot in the left bar
+        :return:
+        """
+        if self.theType is PlotType.Compare:
+            the_text = str(self.adc)+"+"+str(self.secadc)
+        else:
+            the_text = str(self.adc)
+        if self.theType in [ERROR, NO_DATA]:
+            type = self._old_type
+        else:
+            type = self.theType
+        if self.theDataType is LIVE:
+            self.change_type_signal.emit(self.theId, type,
+                                         the_text+" B: "+available_boards.get_board_name_from_id(self.board_id))
+        else:
+            self.change_type_signal.emit(self.theId, type, the_text)
+
+    def closeEvent(self, event):
+        """
+        Event Handler to handle the event of closing this window
+        :param event: QEvent
+        :return: -
+        """
+        if not self.close_silent:
+            if not self.parent.remove_plot(self.theId, silent=self.close_silent):
+                event.ignore()
+                return
+        del self.data
+        del self.plot_widget
+        super(PlotWidget, self).closeEvent(event)
+        if self.theDataType == LIVE:
+            live_plot_windows.removeWindow(self.board_id, self)
+        self.close_signal.emit()
+        del self

+ 155 - 158
KCG/base/settings.py

@@ -1,158 +1,155 @@
-from PyQt4 import QtGui, QtCore
-
-from . import kcgwidget as kcgw
-from .. import config
-
-tr = kcgw.tr
-
-class Settings(kcgw.KCGWidgets):
-    """
-    Settings Window
-    """
-    changed = QtCore.pyqtSignal(list)
-    def __init__(self, storage_handler):
-        """
-        Initialise the Settings Window
-        :param storage_handler: the container class used to store settings
-        :return: -
-        """
-        super(Settings, self).__init__()
-        self.storage_handler = storage_handler
-        self.local_storage = []
-        self.langlist = ["de_DE", "en_GB"]
-
-        # --------[ Instance Objects ]-----------
-        self.layout = QtGui.QVBoxLayout()
-        self.grid = QtGui.QGridLayout()
-
-        self.headerTick = self.build_new_setting(QtGui.QCheckBox(tr("Button", "Header")), 'header')
-        self.subDirName = self.build_new_setting(self.createInput(self.storage_handler.subdirname, width=320), 'subdirname')
-        self.dirName = self.build_new_setting(self.createInput(self.storage_handler.save_location, width=320), 'save_location')
-        self.timescan_offset_correction = self.build_new_setting(self.createInput(self.storage_handler.offset_correction, width=320), 'offset_correction')
-        self.language = self.build_new_setting(QtGui.QComboBox(), 'language')
-        self.advanced_control = self.build_new_setting(self.createCheckbox(tr("Label", "Enable Advanced Table View")), 'advanced_control')
-        for lang in self.langlist:
-            self.language.addItem(lang)
-        self.local_storage.append(self.headerTick)
-        self.applyButton = self.createButton(tr("Button", "Apply"), connect=self._apply)
-        self.okButton = self.createButton(tr("Button", "OK"), connect=self._ok)
-        self.cancelButton = self.createButton(tr("Button", "Cancel"), connect=self._cancel)
-        # ------[ End Instance Objects ]--------
-
-        # ------[ Apply Layout and read Settings ] ---------
-        self.initUI()
-        self._setSettings()
-        # ------[ End Apply... ]----------------------------
-
-        self.show()
-
-    def initUI(self):
-        """
-        Initialise the UI
-        """
-        self.setLayout(self.layout)
-        self.layout.addLayout(self.grid)
-
-        self.headerTick.setToolTip(tr("Tooltip", "Save header in output file")) # TODO: better tooltip
-        self.applyButton.setToolTip(tr("Tooltip", "Save settings"))
-        self.okButton.setToolTip(tr("Tooltip", "Save settings and close settings-window"))
-        self.cancelButton.setToolTip(tr("Tooltip", "Discard changes and close settings-window"))
-        # self.grid.addWidget(self.createLabel("Settings"), 0, 0)
-        self.grid.addWidget(self.headerTick, 0, 0)
-        self.grid.addWidget(self.createLabel(tr("Label", "Subdirectory Name:")), 1, 0)
-        self.grid.addWidget(self.subDirName, 1, 1)
-        self.grid.addWidget(self.createLabel(tr("Label", "Save location:")), 2, 0)
-        self.grid.addWidget(self.dirName, 2, 1)
-        self.grid.addWidget(self.createLabel(tr("Label", "Timescan Offset Correction:")), 3, 0)
-        self.grid.addWidget(self.timescan_offset_correction, 3, 1)
-        self.grid.addWidget(self.createLabel(tr("Label", "Language")), 4, 0)
-        self.grid.addWidget(self.language, 4, 1)
-        self.grid.addWidget(self.advanced_control, 5, 0)
-        self.hbox = QtGui.QHBoxLayout()
-        self.layout.addLayout(self.hbox)
-        self.hbox.addStretch(1)
-        self.hbox.addWidget(self.okButton)
-        self.hbox.addWidget(self.applyButton)
-        self.hbox.addWidget(self.cancelButton)
-        self.setWindowTitle(tr("Heading", "KCG - Settings"))
-        self.setWindowIcon(QtGui.QIcon(config.icon_path(config.guiIcon)))
-
-    def build_new_setting(self, handle, value):
-        """
-        Build a new setting.
-        This will add the setting to the local_storage.
-        :param handle: the handle of the new setting (input field, checkbox etc)
-        :param value: (str) key for this setting
-        :return: the handle :)
-        """
-        self.local_storage.append(handle)
-        self.local_storage[-1].setting_name = value
-        return handle
-
-    def _read_setting_from_object(self, object):
-        """
-        Get the value from a settings object
-        :param object: the object to read the value from
-        :return:
-        """
-        if type(object).__name__ == 'QCheckBox':
-            return object.isChecked()
-        if isinstance(object, QtGui.QLineEdit):
-            return str(object.text())
-        if isinstance(object, QtGui.QComboBox):
-            return object.currentText()
-
-    def _setSettings(self):
-        """
-        Set the values in the settings window to the current values
-        :return:
-        """
-        self.headerTick.setChecked(self.storage_handler.header)
-        self.subDirName.setText(self.storage_handler.subdirname)
-        self.dirName.setText(self.storage_handler.save_location)
-        self.language.setCurrentIndex(self.langlist.index(self.storage_handler.language))
-        self.advanced_control.setChecked(self.storage_handler.advanced_control)
-
-    def _apply(self):
-        """
-        Called when the apply button is pressed. It emits the changed signal if anything was changed
-        """
-        hasChanged = False
-        changedSettings = []
-        for object in self.local_storage:
-            if self.storage_handler.__getattr__(object.setting_name) != self._read_setting_from_object(object):
-                hasChanged = True
-                self.storage_handler.__setattr__(object.setting_name, self._read_setting_from_object(object))
-                changedSettings.append(object.setting_name)
-        if hasChanged:
-            self.changed.emit(changedSettings)
-
-    def _ok(self):
-        """
-        Handler for the ok button. It calls _apply and close
-        :return:
-        """
-        self._apply()
-        self.close()
-
-    def _cancel(self):
-        """
-        Handler for the cancel button. It calls close() and discards changed values
-        :return:
-        """
-        self.close()
-
-    def show(self):
-        """
-        Shows the window and sets the current values (calls _setSettings)
-        :return:
-        """
-        self._setSettings()
-        super(Settings, self).show()
-
-    def keyPressEvent(self, QKeyEvent):
-        """
-        Reimplementation of the keyPressEvent of QWidget to close the window on ESC press.
-        """
-        if QKeyEvent.key() == QtCore.Qt.Key_Escape:
-            self.close()
+from PyQt4 import QtGui, QtCore
+
+import kcgwidget as kcgw
+from .. import config
+
+tr = kcgw.tr
+
+class Settings(kcgw.KCGWidgets):
+    """
+    Settings Window
+    """
+    changed = QtCore.pyqtSignal(list)
+    def __init__(self, storage_handler):
+        """
+        Initialise the Settings Window
+        :param storage_handler: the container class used to store settings
+        :return: -
+        """
+        super(Settings, self).__init__()
+        self.storage_handler = storage_handler
+        self.local_storage = []
+        self.langlist = ["de_DE", "en_GB"]
+
+        # --------[ Instance Objects ]-----------
+        self.layout = QtGui.QVBoxLayout()
+        self.grid = QtGui.QGridLayout()
+
+        self.headerTick = self.build_new_setting(QtGui.QCheckBox(tr("Button", "Header")), 'header')
+        self.subDirName = self.build_new_setting(self.createInput(self.storage_handler.subdirname, width=320), 'subdirname')
+        self.dirName = self.build_new_setting(self.createInput(self.storage_handler.save_location, width=320), 'save_location')
+        self.language = self.build_new_setting(QtGui.QComboBox(), 'language')
+        self.advanced_control = self.build_new_setting(self.createCheckbox(tr("Label", "Enable Advanced Table View")), 'advanced_control')
+        for lang in self.langlist:
+            self.language.addItem(lang)
+        self.local_storage.append(self.headerTick)
+        self.applyButton = self.createButton(tr("Button", "Apply"), connect=self._apply)
+        self.okButton = self.createButton(tr("Button", "OK"), connect=self._ok)
+        self.cancelButton = self.createButton(tr("Button", "Cancel"), connect=self._cancel)
+        # ------[ End Instance Objects ]--------
+
+        # ------[ Apply Layout and read Settings ] ---------
+        self.initUI()
+        self._setSettings()
+        # ------[ End Apply... ]----------------------------
+
+        self.show()
+
+    def initUI(self):
+        """
+        Initialise the UI
+        """
+        self.setLayout(self.layout)
+        self.layout.addLayout(self.grid)
+
+        self.headerTick.setToolTip(tr("Tooltip", "Save header in output file")) # TODO: better tooltip
+        self.applyButton.setToolTip(tr("Tooltip", "Save settings"))
+        self.okButton.setToolTip(tr("Tooltip", "Save settings and close settings-window"))
+        self.cancelButton.setToolTip(tr("Tooltip", "Discard changes and close settings-window"))
+        # self.grid.addWidget(self.createLabel("Settings"), 0, 0)
+        self.grid.addWidget(self.headerTick, 0, 0)
+        self.grid.addWidget(self.createLabel(tr("Label", "Subdirectory Name:")), 1, 0)
+        self.grid.addWidget(self.subDirName, 1, 1)
+        self.grid.addWidget(self.createLabel(tr("Label", "Save location:")), 2, 0)
+        self.grid.addWidget(self.dirName, 2, 1)
+        self.grid.addWidget(self.createLabel(tr("Label", "Language")), 3, 0)
+        self.grid.addWidget(self.language, 3, 1)
+        self.grid.addWidget(self.advanced_control, 4, 0)
+        self.hbox = QtGui.QHBoxLayout()
+        self.layout.addLayout(self.hbox)
+        self.hbox.addStretch(1)
+        self.hbox.addWidget(self.okButton)
+        self.hbox.addWidget(self.applyButton)
+        self.hbox.addWidget(self.cancelButton)
+        self.setWindowTitle(tr("Heading", "KCG - Settings"))
+        self.setWindowIcon(QtGui.QIcon(config.install_path + config.guiIcon))
+
+    def build_new_setting(self, handle, value):
+        """
+        Build a new setting.
+        This will add the setting to the local_storage.
+        :param handle: the handle of the new setting (input field, checkbox etc)
+        :param value: (str) key for this setting
+        :return: the handle :)
+        """
+        self.local_storage.append(handle)
+        self.local_storage[-1].setting_name = value
+        return handle
+
+    def _read_setting_from_object(self, object):
+        """
+        Get the value from a settings object
+        :param object: the object to read the value from
+        :return:
+        """
+        if type(object).__name__ == 'QCheckBox':
+            return object.isChecked()
+        if isinstance(object, QtGui.QLineEdit):
+            return str(object.text())
+        if isinstance(object, QtGui.QComboBox):
+            return object.currentText()
+
+    def _setSettings(self):
+        """
+        Set the values in the settings window to the current values
+        :return:
+        """
+        self.headerTick.setChecked(self.storage_handler.header)
+        self.subDirName.setText(self.storage_handler.subdirname)
+        self.dirName.setText(self.storage_handler.save_location)
+        self.language.setCurrentIndex(self.langlist.index(self.storage_handler.language))
+        self.advanced_control.setChecked(self.storage_handler.advanced_control)
+
+    def _apply(self):
+        """
+        Called when the apply button is pressed. It emits the changed signal if anything was changed
+        """
+        hasChanged = False
+        changedSettings = []
+        for object in self.local_storage:
+            if self.storage_handler.__getattr__(object.setting_name) != self._read_setting_from_object(object):
+                hasChanged = True
+                self.storage_handler.__setattr__(object.setting_name, self._read_setting_from_object(object))
+                changedSettings.append(object.setting_name)
+        if hasChanged:
+            self.changed.emit(changedSettings)
+
+    def _ok(self):
+        """
+        Handler for the ok button. It calls _apply and close
+        :return:
+        """
+        self._apply()
+        self.close()
+
+    def _cancel(self):
+        """
+        Handler for the cancel button. It calls close() and discards changed values
+        :return:
+        """
+        self.close()
+
+    def show(self):
+        """
+        Shows the window and sets the current values (calls _setSettings)
+        :return:
+        """
+        self._setSettings()
+        super(Settings, self).show()
+
+    def keyPressEvent(self, QKeyEvent):
+        """
+        Reimplementation of the keyPressEvent of QWidget to close the window on ESC press.
+        """
+        if QKeyEvent.key() == QtCore.Qt.Key_Escape:
+            self.close()

+ 178 - 178
KCG/base/storage.py

@@ -1,178 +1,178 @@
-from PyQt4 import QtCore
-
-
-class StorageError(Exception):
-    """
-    Simple Storage Error Base Class
-    """
-    pass
-
-
-class Storage(object):
-    """
-    Simple Class to store Values and Throw an error if a value is retrieved that has not been stored before.
-    """
-    def __init__(self, default=None):
-        self._storage = {}
-        if default:
-            self._storage = default
-
-    def setdefault(self, key, object):
-        """
-        Call setdefault on the underlying dictionary and return the result
-        :param key: the key to call setdefault with
-        :param object: the object to call setdefault with
-        :return: the return value of setdefault of the dictionary
-        """
-        return self._storage.setdefault(key, object)
-
-    def __setattr__(self, key, value):
-        if key == '_storage':
-            super(Storage, self).__setattr__(key, value)
-        else:
-            self._storage[key] = value
-
-    def __getattr__(self, item):
-        if item == '_storage':
-            return super(Storage, self).__getattr__(item)
-        if item in self._storage:
-            return self._storage[item]
-        else:
-            raise StorageError("'"+item+"' Not saved in Storage")
-
-storage = Storage()  # Container Object to use storage globally
-
-_board_specific_storages = {}
-
-
-def get_board_specific_storage(board_id):
-    """
-    Get the storage instance for a specific board
-    :param board_id: the board to get the storage for
-    :return: the instance of the storage for the given board
-    """
-    if board_id in _board_specific_storages:
-        return _board_specific_storages[board_id]
-    else:
-        _board_specific_storages[board_id] = Storage()
-        return _board_specific_storages[board_id]
-
-
-class ThreadStorage(object):
-    """
-    Wrapper for QThreads
-    """
-    #FixMe: I am a workaround, because before the acquisition thread was not used and the acquisition accidentally run in the main thread
-    class TThread(QtCore.QThread):
-        def run(self):
-            self.m()
-
-        def set_run_method(self, m):
-            self.m=m
-
-    def __init__(self):
-        self._q_thread = None
-        self._threaded_object = None
-        self.running = False
-
-    def register(self, threaded_object):
-        """
-        Register a class object to run in a QThread
-        :param threaded_object: the class object
-        :return:
-        """
-        del self._q_thread  # start over
-        self._q_thread = ThreadStorage.TThread()
-        #self._q_thread.started.connect(self.__start)
-        self._threaded_object = threaded_object
-
-    def is_registered(self):
-        """
-        Check if a class for this thread is registered.
-        :return:
-        """
-        return not (self._threaded_object is None)
-
-    def __start(self):
-        """
-        This will be registered to the QThread started signal and will call the actual start_method
-        :return:
-        """
-        self._start_method()
-
-    def start(self, method_to_run_in_thread):
-        """
-        Move the class object to a QThread, connect the QThread.started signal to the given method of the
-        class object
-        :param method_to_run_in_thread: the method to start when starting the QThread
-        :return:
-        """
-        self._threaded_object.moveToThread(self._q_thread)
-        if isinstance(method_to_run_in_thread, str):
-            self._start_method = getattr(self._threaded_object, method_to_run_in_thread)
-            # self._q_thread.started.connect(getattr(self._threaded_object, method_to_run_in_thread))
-        else:
-            self._start_method = method_to_run_in_thread
-            # self._q_thread.started.connect(method_to_run_in_thread)
-        self._q_thread.set_run_method(self._start_method)
-        self._q_thread.start()
-        self.running = True
-
-    def quit(self):
-        """
-        Quit the thread class (if it has a method to quit)
-        :return:
-        """
-        if hasattr(self._threaded_object, 'quit'):
-            self._threaded_object.quit()
-
-    def stop(self):
-        """
-        Call quit and wait to the underlying QThread
-        :return:
-        """
-        self._q_thread.quit()
-        self._q_thread.wait()
-        self.running = False
-
-    def __getattr__(self, item):
-        """
-        Get Attributes of the registered class object
-        :param item: the item to get from the class object
-        :return: the attribute of the class object
-        """
-        if not item.startswith('obj_'):
-            raise AttributeError("If this was a call to an object of the threaded object, the call has to start with"
-                                 "obj_.")
-        if self._threaded_object is None:
-            raise AttributeError("This Thread object does not have a threaded object. Register one first")
-
-        return getattr(self._threaded_object, item[4:])
-
-    def connect(self, signal, slot):
-        """
-        Connect signals of the class object to the given slots
-        :param signal: the signal
-        :param slot: the slot
-        :return:
-        """
-        if isinstance(signal, str):
-            getattr(self._threaded_object, signal).connect(slot)
-        else:
-            signal.connect(slot)
-
-    def disconnect(self, *args):
-        """
-        Disconnect the given signals
-        :param args: the signals to disconnect
-        :return:
-        """
-        for signal in args:
-            getattr(self._threaded_object, signal).disconnect()
-
-    def init(self, *args, **kwargs):
-        """
-        call the threaded objects init method (not __init__) with the passed arguments
-        :return:
-        """
-        self._threaded_object.init(*args, **kwargs)
+from PyQt4 import QtCore
+
+
+class StorageError(Exception):
+    """
+    Simple Storage Error Base Class
+    """
+    pass
+
+
+class Storage(object):
+    """
+    Simple Class to store Values and Throw an error if a value is retrieved that has not been stored before.
+    """
+    def __init__(self, default=None):
+        self._storage = {}
+        if default:
+            self._storage = default
+
+    def setdefault(self, key, object):
+        """
+        Call setdefault on the underlying dictionary and return the result
+        :param key: the key to call setdefault with
+        :param object: the object to call setdefault with
+        :return: the return value of setdefault of the dictionary
+        """
+        return self._storage.setdefault(key, object)
+
+    def __setattr__(self, key, value):
+        if key == '_storage':
+            super(Storage, self).__setattr__(key, value)
+        else:
+            self._storage[key] = value
+
+    def __getattr__(self, item):
+        if item == '_storage':
+            return super(Storage, self).__getattr__(item)
+        if item in self._storage:
+            return self._storage[item]
+        else:
+            raise StorageError("'"+item+"' Not saved in Storage")
+
+storage = Storage()  # Container Object to use storage globally
+
+_board_specific_storages = {}
+
+
+def get_board_specific_storage(board_id):
+    """
+    Get the storage instance for a specific board
+    :param board_id: the board to get the storage for
+    :return: the instance of the storage for the given board
+    """
+    if board_id in _board_specific_storages:
+        return _board_specific_storages[board_id]
+    else:
+        _board_specific_storages[board_id] = Storage()
+        return _board_specific_storages[board_id]
+
+
+class ThreadStorage(object):
+    """
+    Wrapper for QThreads
+    """
+    #FixMe: I am a workaround, because before the acquisition thread was not used and the acquisition accidentally run in the main thread
+    class TThread(QtCore.QThread):
+        def run(self):
+            self.m()
+
+        def set_run_method(self, m):
+            self.m=m
+
+    def __init__(self):
+        self._q_thread = None
+        self._threaded_object = None
+        self.running = False
+
+    def register(self, threaded_object):
+        """
+        Register a class object to run in a QThread
+        :param threaded_object: the class object
+        :return:
+        """
+        del self._q_thread  # start over
+        self._q_thread = ThreadStorage.TThread()
+        #self._q_thread.started.connect(self.__start)
+        self._threaded_object = threaded_object
+
+    def is_registered(self):
+        """
+        Check if a class for this thread is registered.
+        :return:
+        """
+        return not (self._threaded_object is None)
+
+    def __start(self):
+        """
+        This will be registered to the QThread started signal and will call the actual start_method
+        :return:
+        """
+        self._start_method()
+
+    def start(self, method_to_run_in_thread):
+        """
+        Move the class object to a QThread, connect the QThread.started signal to the given method of the
+        class object
+        :param method_to_run_in_thread: the method to start when starting the QThread
+        :return:
+        """
+        self._threaded_object.moveToThread(self._q_thread)
+        if isinstance(method_to_run_in_thread, str):
+            self._start_method = getattr(self._threaded_object, method_to_run_in_thread)
+            # self._q_thread.started.connect(getattr(self._threaded_object, method_to_run_in_thread))
+        else:
+            self._start_method = method_to_run_in_thread
+            # self._q_thread.started.connect(method_to_run_in_thread)
+        self._q_thread.set_run_method(self._start_method)
+        self._q_thread.start()
+        self.running = True
+
+    def quit(self):
+        """
+        Quit the thread class (if it has a method to quit)
+        :return:
+        """
+        if hasattr(self._threaded_object, 'quit'):
+            self._threaded_object.quit()
+
+    def stop(self):
+        """
+        Call quit and wait to the underlying QThread
+        :return:
+        """
+        self._q_thread.quit()
+        self._q_thread.wait()
+        self.running = False
+
+    def __getattr__(self, item):
+        """
+        Get Attributes of the registered class object
+        :param item: the item to get from the class object
+        :return: the attribute of the class object
+        """
+        if not item.startswith('obj_'):
+            raise AttributeError("If this was a call to an object of the threaded object, the call has to start with"
+                                 "obj_.")
+        if self._threaded_object is None:
+            raise AttributeError("This Thread object does not have a threaded object. Register one first")
+
+        return getattr(self._threaded_object, item[4:])
+
+    def connect(self, signal, slot):
+        """
+        Connect signals of the class object to the given slots
+        :param signal: the signal
+        :param slot: the slot
+        :return:
+        """
+        if isinstance(signal, str):
+            getattr(self._threaded_object, signal).connect(slot)
+        else:
+            signal.connect(slot)
+
+    def disconnect(self, *args):
+        """
+        Disconnect the given signals
+        :param args: the signals to disconnect
+        :return:
+        """
+        for signal in args:
+            getattr(self._threaded_object, signal).disconnect()
+
+    def init(self, *args, **kwargs):
+        """
+        call the threaded objects init method (not __init__) with the passed arguments
+        :return:
+        """
+        self._threaded_object.init(*args, **kwargs)

+ 29 - 18
KCG/default_config.cfg → KCG/config.cfg

@@ -37,6 +37,7 @@ default_save_location = "pwd"
 # {timestamp} unix timestamp without msec
 # {user} the current logged in user
 # {sessionname} Ask for session name at startup
+
 # {ask} always ask for a foldername
 #default_subdirectory_name = "{user}_{dateGd}-{timel}_{sessionname}"
 default_subdirectory_name = "{sessionname}"
@@ -47,10 +48,25 @@ force_ask = False
 # Show advanced table view per default? (boolean value)
 show_advanced_control = False
 
-# Integrate the single read functionality into the left bar above the general acquisition widget
-integrate_single_read = False
-
 [Logging]
+# These are PVs that will be possible to insert into log files
+# This variable is to be a list consisting of touples of two entries,
+# the first ist the Text that describes the value and the second is the EPICS PV that
+# holds that value
+epics_log_entry_pvs = [
+    ("Beam Energy (GeV)", "A:SR:BeamInfo:01:Energy"),
+    ("Beam Current (mA)", "A:SR:BeamInfo:01:Current"),
+    ("Beam Lifetime (s)", "A:SR:BeamInfo:01:Lifetime"),
+    ("Beam Lifetime (H:M:S)", "A:SR:BeamInfo:01:Lifetime:String"),
+    ("Injection Rate (mA/s)", "A:SR:BeamInfo:01:InjRate"),
+    ("Injection Rate (mA/5s)", "A:SR:BeamInfo:01:InjRate:5s"),
+    ("Fill Number", "A:SR:OperationStatus:01:FillNumber")
+    ]
+# This pv is used to determine if epics pvs are accessible
+epics_test_pv = "A:SR:BeamInfo:01:Current"
+# Path to your epics base installation
+epics_base_path = "/opt/epics/base/"
+
 # List of Entries that are default to save in Log
 # Possible Values are:
 # "Number of Orbits"
@@ -77,19 +93,17 @@ default_log_entries = [
     "Beam Current (mA)"
     ]
 
-
 [Misc]
-# defined relativ to icons folder
-newPlotLiveIcon= "graph.svg"
-newPlotDataIcon= "folder.svg"
-timingIcon = "clock.svg"
-singleReadIcon = "project.svg"
-acquireSettingsIcon = "wrench.svg"
-startIcon = "media-play-black.svg"
-stopIcon = "media-stop.svg"
-logIcon = "clipboard.svg"
-logCommentIcon = "clipboard_comm.svg"
-guiIcon = "KCG_Logo_r.png"
+newPlotLiveIcon= "icons/graph.svg"
+newPlotDataIcon= "icons/folder.svg"
+timingIcon = "icons/clock.svg"
+singleReadIcon = "icons/project.svg"
+acquireSettingsIcon = "icons/wrench.svg"
+startIcon = "icons/media-play-black.svg"
+stopIcon = "icons/media-stop.svg"
+logIcon = "icons/clipboard.svg"
+logCommentIcon = "icons/clipboard_comm.svg"
+guiIcon = "icons/KCG_Logo_r.png"
 # style = 'blue'
 style = 'default'
 # board_detection_method has to be one of 'dev', 'dummy', 'list'
@@ -107,6 +121,3 @@ device_list = []
 device_names = {'test0': 'ich_bin_toll', 'test1': 'ne'}
 # num_dummy_boards is the number of dummy boards to create
 num_dummy_boards = 5
-
-# Datafile whith Timescandata for 50ohm termination
-fifty_ohm_timescan_datafile = "Put 'fifty_ohm_timescan_datafile = path_to_50_ohm_termination_timescanfile' into ~/.kcg/config.cfg"

+ 15 - 23
KCG/config.py

@@ -2,20 +2,12 @@
 Configuration Module.
 This reads the configuration file and conveniently makes the settings available to KCG
 """
-import configparser
+import ConfigParser
 import ast
 import os
 import sys
 import logging
 
-install_path = ''
-
-def icon_path(iconName):
-    return os.path.join(install_path,"icons",iconName) 
-
-def style_path(styleName):
-    return os.path.join(install_path,"style",styleName)
-
 
 class NoValueException(Exception):
     """
@@ -78,7 +70,7 @@ class ConfSection(object):
                     continue
                 except Exception:
                     if self._log_level <= logging.ERROR:
-                        print("Error in parsing commandline configuration using configuration in config file.")
+                        print ("Error in parsing commandline configuration using configuration in config file.")
             if self._conf_obj.has_option(self._section, conf):
                 try:
                     globals()[conf] = leval(self._conf_obj.get(self._section, conf))
@@ -86,7 +78,7 @@ class ConfSection(object):
                     error = True
             elif self._def_conf_obj is not None:
                 if self._log_level <= logging.DEBUG:
-                    print("Using default configuration value for " + conf)
+                    print ("Using default configuration value for " + conf)
                 globals()[conf] = leval(self._def_conf_obj.get(self._section, conf))
             else:
                 error = True
@@ -126,7 +118,7 @@ class Configuration(object):
                 section = self._parsed_args.setdefault(sec, {})
                 section[sets.strip()] = setv.strip()
             except:
-                print("Format of commandline configuration is wrong. Using config file version")
+                print ("Format of commandline configuration is wrong. Using config file version")
 
     def read(self):
         """
@@ -134,20 +126,20 @@ class Configuration(object):
         user config file does not contain all values) and adds them as parameters to this module
         :return:
         """
-        config = configparser.ConfigParser()
+        config = ConfigParser.ConfigParser()
         config.optionxform = str
-        config.read(os.path.join(os.path.expanduser("~"),".kcg","config.cfg"))
-        defaultConfig = configparser.ConfigParser()
+        config.read(os.path.expanduser("~")+"/.kcg/config.cfg")
+        defaultConfig = ConfigParser.ConfigParser()
         defaultConfig.optionxform = str
-        defaultConfig.read(os.path.join(os.path.dirname(__file__), "default_config.cfg"))
+        defaultConfig.read(os.path.join(os.path.dirname(__file__), "config.cfg"))
 
         Machine_conf = ["bunches_per_turn", "save_header", "tRev"]
-        Ui_conf = ["language", "default_save_location", "default_subdirectory_name", "force_ask", "show_advanced_control", "integrate_single_read", "use_epics"]
-        Logging_conf = ["default_log_entries"]
+        Ui_conf = ["language", "default_save_location", "default_subdirectory_name", "force_ask", "show_advanced_control"]
+        Logging_conf = ["epics_test_pv", "epics_base_path", "epics_log_entry_pvs", "default_log_entries"]
         Misc_conf = ['newPlotLiveIcon', 'newPlotDataIcon', 'timingIcon', 'singleReadIcon',
                      'acquireSettingsIcon', 'startIcon', 'stopIcon', 'logIcon', 'logCommentIcon', 'guiIcon', 'style',
                      'board_detection_method', 'device_list', 'device_names',
-                     'num_dummy_boards', 'fifty_ohm_timescan_datafile']
+                     'num_dummy_boards']
 
         try:
             machine_c = ConfSection('Machine', Machine_conf, config, log_level=self._log_level)
@@ -174,16 +166,16 @@ class Configuration(object):
             #     if not conf in globals().keys():
             #         globals()[conf] = leval(val)
 
-        except (configparser.NoOptionError, configparser.NoSectionError) as e:
+        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as e:
             self.error = True
-            print("There was an error parsing configuration: " + str(e))
+            print ("There was an error parsing configuration: " + str(e))
 
     def setup(self):
         """
         Check for the user config file and if not exists calls doSetup
         :return:
         """
-        if not os.path.isfile(os.path.join(os.path.expanduser("~"),".kcg","config.cfg")):
+        if not os.path.isfile(os.path.expanduser("~")+"/.kcg/config.cfg"):
             self.doSetup()
         else:
             self.read()
@@ -193,7 +185,7 @@ class Configuration(object):
         Shows the initial config dialog
         :return:
         """
-        from .widgets.initialconfig import ConfigSetup
+        from widgets.initialconfig import ConfigSetup
         from PyQt4 import QtGui, QtCore
         if not rerun:
             setupConfigApp = QtGui.QApplication([])

BIN
KCG/icons/open-trash-can.png


+ 0 - 1
KCG/kcg.pro

@@ -4,7 +4,6 @@ SOURCES += ./base/kcg.py \
 ./widgets/example_widget.py \
 ./widgets/singleread.py \
 ./widgets/acquiresettings.py \
-./widgets/epics_widget.py \
 ./kcg.py \
 ./base/groupedelements.py \
 ./base/multiWidget.py \

+ 151 - 153
KCG/kcg.py

@@ -1,153 +1,151 @@
-#!/usr/bin/python
-"""
-This is the main program for KCG
-It imports all modules and starts the Gui
-"""
-from PyQt4 import QtGui, QtCore
-import sys
-import os
-import argparse as ap
-import logging
-from logging import handlers
-from . import config
-
-# -------[ Register Logger here to enable logging before anything of this app is performed ]---------------
-logging.getLogger().setLevel(0)
-if not os.path.isdir(os.path.join(os.path.expanduser("~"),".kcg")):
-    os.makedirs(os.path.join(os.path.expanduser("~"),".kcg"))
-fileLogHandler = handlers.RotatingFileHandler(os.path.join(os.path.expanduser("~"),".kcg","kcg.log.full"), maxBytes=10**7, backupCount=5)
-fileLogHandler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
-fileLogHandler.setLevel(0)
-logging.getLogger().addHandler(fileLogHandler)
-logging.info("==========[Start Application]===========")
-
-import KCG.base.kcgwidget as kcgw
-import KCG.config as config
-from KCG.config import Configuration
-
-
-logging.addLevelName(logging.INFO-1, 'VINFO')
-
-translator = QtCore.QTranslator()
-kcgw.translator = translator
-# kcgw.tr = translator.translate
-kcgw.tr = QtCore.QCoreApplication.translate
-
-config.install_path = os.path.dirname(config.__file__)
-
-
-try:  # Try to use Erax for exception logging
-    sys.path.append(os.path.join(os.path.expanduser('~'),"Documents","PythonProjects"))
-    from erax import Erax
-
-    exception_log_handler = Erax('https://psraspi.no-ip.biz/p/erax/insert/78e55a9524a191f7628f82a20bcaa167:kcg',
-                                 no_epl_errors=True, cert='/tmp/raspi.pem')
-    exception_log_handler.install()
-except ImportError:
-    pass
-
-
-def print_version(verbose=False):
-    """
-    Print the version of the current used KCG instance
-    :param verbose: print verbose?
-    """
-    # print "KCG - KAPTURE Control Gui"
-    # print "=" * 30
-    print("KCG", end=' ')
-    with open(os.path.join(config.install_path,'VERSION'), 'r') as v:
-        print(v.read().strip())
-    if verbose:
-        print("=" * 30)
-        print("Using Python:")
-        print(sys.version)
-        print("=" * 30)
-        print("From: " + config.install_path)
-
-
-def inject_setting(section, setting, value, args):
-    """
-    Inject a setting from the command line
-    :param section: the section to inject to
-    :param setting: the setting to inject
-    :param value: the value of this setting
-    :param args: the argparse parsed instance
-    """
-    args.config += section + '->' + setting + '=' + str(value) + ';'
-
-
-def log_type(level):
-    """
-    Method to validate and cast the log level
-    :param level: the level to validate and cast
-    :return: a valid logging level
-    """
-    try:
-        return int(level)
-    except ValueError:
-        try:
-            return logging._checkLevel(level)
-        except ValueError:
-            raise ap.ArgumentTypeError("No valid log level.")
-
-
-def run():
-    """
-    Main Function, gets called when GUI is started
-    :return:
-    """
-    app = QtGui.QApplication(sys.argv)
-    app.installTranslator(translator)
-    app.processEvents()
-
-    # pixmap = QtGui.QPixmap(config.install_path+"icons/KCG_Logo.png").scaled(400, 400)
-    # splash_screen = QtGui.QSplashScreen(pixmap)
-    # splash_screen.setMask(pixmap.mask())
-    # splash_screen.show()
-    # splash_screen.update()
-    # splash_screen.showMessage("Loading KCG", QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, QtCore.Qt.red)
-    # splash_screen.update()
-    # app.processEvents()
-
-    parser = ap.ArgumentParser("KCG - KAPTURE Control Gui")
-    parser.add_argument('--config', type=str, default='', help='Override Configuration file settings.'
-                        'Format: "Section->setting=content;Section->setting2=content;Section2->setting3=content" etc.')
-    parser.add_argument('--version', action='store_true', help="Print Version and exit")
-    parser.add_argument('--vversion', action='store_true', help="Print Version verbose and exit")
-    parser.add_argument('--fpga-detection', action='store_true', help="If Present, use 'dev' detection mode (detect"
-                                                                      "boards by using /dev/fpga# files.)")
-    parser.add_argument('--log', type=log_type, default=config.logging.INFO, help="Set the log level")
-    parser.add_argument('--testing', action='store_true', default=False,
-                        help="start KCG in testing version. DO NOT USE THIS IN PRODUCTION.")
-    args = parser.parse_args()
-    if args.version or args.vversion:
-        print_version(args.vversion)
-        sys.exit()
-
-    kcgw.testing = args.testing
-
-    if args.fpga_detection:
-        inject_setting('Misc', 'board_detection_method', '"dev"', args)
-
-    # logger = logging.getLogger()
-    # logger.setLevel(args.log)
-    # logging.logger = logger
-    def vinfo(content):
-        '''log with level VINFO'''
-        logging.log(logging.getLevelName('VINFO'), content)
-    logging.vinfo = vinfo
-
-    conf = Configuration(args.config, log_level=args.log)
-    conf.setup()
-    while conf.error:
-        conf.doSetup()
-    if args.testing:
-        config.default_subdirectory_name = 't'
-        config.board_detection_method = 'dummy'
-
-    import KCG.base.kcg as kcg
-
-    gui = kcg.Gui()
-    # splash_screen.finish(gui)
-    gui.show()
-    sys.exit(app.exec_())
+#!/usr/bin/python
+"""
+This is the main program for KCG
+It imports all modules and starts the Gui
+"""
+from PyQt4 import QtGui, QtCore
+import sys
+import os
+import argparse as ap
+import logging
+from logging import handlers
+
+# -------[ Register Logger here to enable logging before anything of this app is performed ]---------------
+logging.getLogger().setLevel(0)
+if not os.path.exists(os.path.expanduser("~")+"/.kcg"):
+    os.makedirs(os.path.expanduser("~")+"/.kcg")
+fileLogHandler = handlers.RotatingFileHandler(os.path.expanduser("~")+"/.kcg/kcg.log.full", maxBytes=10**7, backupCount=5)
+fileLogHandler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
+fileLogHandler.setLevel(0)
+logging.getLogger().addHandler(fileLogHandler)
+logging.info("==========[Start Application]===========")
+
+import base.kcgwidget as kcgw
+import config
+from config import Configuration
+
+logging.addLevelName(logging.INFO-1, 'VINFO')
+
+translator = QtCore.QTranslator()
+kcgw.translator = translator
+# kcgw.tr = translator.translate
+kcgw.tr = QtCore.QCoreApplication.translate
+
+config.install_path = os.path.dirname(config.__file__) + "/"
+
+
+try:  # Try to use Erax for exception logging
+    sys.path.append(os.path.expanduser('~/Documents/PythonProjects/'))
+    from erax import Erax
+
+    exception_log_handler = Erax('https://psraspi.no-ip.biz/p/erax/insert/78e55a9524a191f7628f82a20bcaa167:kcg',
+                                 no_epl_errors=True, cert='/tmp/raspi.pem')
+    exception_log_handler.install()
+except ImportError:
+    pass
+
+
+def print_version(verbose=False):
+    """
+    Print the version of the current used KCG instance
+    :param verbose: print verbose?
+    """
+    # print "KCG - KAPTURE Control Gui"
+    # print "=" * 30
+    print ("KCG",)
+    with open(config.install_path+'VERSION', 'r') as v:
+        print (v.read().strip())
+    if verbose:
+        print ("=" * 30)
+        print ("Using Python:")
+        print (sys.version)
+        print ("=" * 30)
+        print ("From: " + config.install_path)
+
+
+def inject_setting(section, setting, value, args):
+    """
+    Inject a setting from the command line
+    :param section: the section to inject to
+    :param setting: the setting to inject
+    :param value: the value of this setting
+    :param args: the argparse parsed instance
+    """
+    args.config += section + '->' + setting + '=' + str(value) + ';'
+
+
+def log_type(level):
+    """
+    Method to validate and cast the log level
+    :param level: the level to validate and cast
+    :return: a valid logging level
+    """
+    try:
+        return int(level)
+    except ValueError:
+        try:
+            return logging._checkLevel(level)
+        except ValueError:
+            raise ap.ArgumentTypeError("No valid log level.")
+
+
+def run():
+    """
+    Main Function, gets called when GUI is started
+    :return:
+    """
+    # pixmap = QtGui.QPixmap(config.install_path+"icons/KCG_Logo.png").scaled(400, 400)
+    # splash_screen = QtGui.QSplashScreen(pixmap)
+    # splash_screen.setMask(pixmap.mask())
+    # splash_screen.show()
+    # splash_screen.update()
+    # splash_screen.showMessage("Loading KCG", QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, QtCore.Qt.red)
+    # splash_screen.update()
+    # app.processEvents()
+
+    parser = ap.ArgumentParser("KCG - KAPTURE Control Gui")
+    parser.add_argument('--config', type=str, default='', help='Override Configuration file settings.'
+                        'Format: "Section->setting=content;Section->setting2=content;Section2->setting3=content" etc.')
+    parser.add_argument('--version', action='store_true', help="Print Version and exit")
+    parser.add_argument('--vversion', action='store_true', help="Print Version verbose and exit")
+    parser.add_argument('--fpga-detection', action='store_true', help="If Present, use 'dev' detection mode (detect"
+                                                                      "boards by using /dev/fpga# files.)")
+    parser.add_argument('--log', type=log_type, default=config.logging.INFO, help="Set the log level")
+    parser.add_argument('--testing', action='store_true', default=False,
+                        help="start KCG in testing version. DO NOT USE THIS IN PRODUCTION.")
+    args = parser.parse_args()
+    if args.version or args.vversion:
+        print_version(args.vversion)
+        sys.exit()
+
+    kcgw.testing = args.testing
+
+    if args.fpga_detection:
+        inject_setting('Misc', 'board_detection_method', '"dev"', args)
+
+    # logger = logging.getLogger()
+    # logger.setLevel(args.log)
+    # logging.logger = logger
+    def vinfo(content):
+        '''log with level VINFO'''
+        logging.log(logging.getLevelName('VINFO'), content)
+    logging.vinfo = vinfo
+
+    conf = Configuration(args.config, log_level=args.log)
+    conf.setup()
+    while conf.error:
+        conf.doSetup()
+    if args.testing:
+        config.default_subdirectory_name = 't'
+
+    import base.kcg as kcg
+
+    app = QtGui.QApplication(sys.argv)
+    app.installTranslator(translator)
+    app.processEvents()
+
+
+    gui = kcg.Gui()
+    # splash_screen.finish(gui)
+    gui.show()
+    sys.exit(app.exec_())

+ 1 - 1
KCG/widgets/__init__.py

@@ -1 +1 @@
-__all__ = ['acquiresettings', 'singleread', 'timingWidget', 'epics_widget'] #, 'example_widget']
+__all__ = ['acquiresettings', 'singleread', 'timingWidget'] #, 'example_widget']

+ 486 - 493
KCG/widgets/acquiresettings.py

@@ -1,493 +1,486 @@
-"""
-This Module Is the Acquiresettings subWindow.
-"""
-from PyQt4 import QtGui, QtCore
-from collections import OrderedDict
-
-from ..base import kcgwidget as kcgw
-from ..base.backend import board
-from ..base.backend.board import available_boards
-from ..base import backendinterface as bif
-from ..base.groupedelements import Elements
-from .. import config
-from ..base.globals import glob as global_objects
-tr = kcgw.tr
-
-__widget_id__ = None
-
-
-class AcquireSettingsTab(kcgw.KCGWidgets):
-    """
-    A single tab in the acquire settings window
-    """
-    available_for_same_as = OrderedDict()
-
-    def __init__(self, board_id, same_as_widgets, parent=None):
-        super(AcquireSettingsTab, self).__init__()
-        self.available_for_same_as[board_id] = True
-        self.widgets = None  # IMPORTANT: THIS WIDGET NEEDS A LIST OF ALL THE OTHER WIDGETS
-        self.same_as_widgets = same_as_widgets
-        self.parent = parent
-        self.board_id = board_id
-        self.layout = QtGui.QGridLayout()
-        self.outerLayout = QtGui.QVBoxLayout()
-        self.outerLayout.addLayout(self.layout)
-        self.outerLayout.addStretch(1)
-        self.setLayout(self.outerLayout)
-
-        self.sameAsLabel = self.createLabel("Same as")
-        self.sameAsCombo = QtGui.QComboBox()
-        self.sameAsTick = self.createCheckbox("", connect=self.same_as)
-        self.sameAs = QtGui.QHBoxLayout()
-        self.sameAs.addWidget(self.sameAsCombo)
-        self.sameAs.addWidget(self.sameAsTick)
-        if available_boards.multi_board:
-            self.layout.addWidget(self.sameAsLabel, 0, 0)
-            self.layout.addLayout(self.sameAs, 0, 1)
-        else:
-            self.sameAsLabel.hide()
-
-        # ---------[ Create Labels and corresponding Fields ]---------
-        self.numOfOrbitsLabel       = self.createLabel(tr("Label", "Number of turns to observe"))
-        self.numOfSkipOrbitsLabel   = self.createLabel(tr("Label", "Number of turns to skip"))
-        self.numOfOrbitsSpinbox     = self.createSpinbox(1, 10000000, connect=self.on_number_of_orbits_changed)
-        self.numOfSkipOrbitsSpinbox = self.createSpinbox(0, 100, connect=self.on_number_of_skipped_orbits_changed)
-        self.fileSizeLabel          = self.createLabel(tr("Label", "Resulting file size"))
-        self.fileSizeOutLabel       = self.createLabel(tr("Label", "??"))
-        self.countLabel             = self.createLabel(tr("Label", "Number of acquisitions"))
-        self.countSpinbox           = self.createSpinbox(1, 10000000, start_value=10, connect=self.on_count_changed)
-        self.waitLabel              = self.createLabel(tr("Label", "Wait(s)"))
-        self.waitSpinbox            = self.createSpinbox(0, 60, start_value=15, connect=self.on_wait_changed)
-        self.simulatePilotBunch     = self.createCheckbox(tr("Button", "Simulate Pilot Bunch"),
-                                                      connect=self.on_simulate_pilot_bunch_changed)
-        self.buildSpectrogrammTickbox = self.createCheckbox(tr("Button", "Build Spectograms"),
-                                                            connect=self.on_build_spectrograms_changed)
-
-        # ----------[ Trigger part ]--------------------
-        self.trigger_skip_label   = self.createLabel(tr("Label", "Number of trigger signals to skip"))
-        self.trigger_skip_signals = self.createSpinbox(0, 1000, 1, start_value=0,
-                                                       connect=self.on_trigger_skip_signals_changed)
-        self.timeout_label = self.createLabel(tr("Label", "Timeout (s)"))
-        self.timeout       = self.createSpinbox(0, 10000000, start_value=12, connect=self.on_trigger_timeout_changed)
-        self.method_label  = self.createLabel(tr("Label", "Method"))
-        self.method = kcgw.Switch("1", "2")
-        self.method.clicked.connect(self.on_trigger_method_changed)
-        self.trigger_skip_label.hide()
-        self.trigger_skip_signals.hide()
-        self.timeout_label.hide()
-        self.timeout.hide()
-        self.method_label.hide()
-        self.method.hide()
-
-        self.trigger_tick = self.createCheckbox(tr("Label", "Use External Trigger"), connect=self.use_external_trigger)
-
-        # -------[ register observers ]------
-        board.get_board_config(board_id).observe(self.numOfOrbitsSpinbox,
-                                                 lambda x: self.set_value_silent(self.numOfOrbitsSpinbox, x),
-                                                 'orbits_observe')
-        board.get_board_config(board_id).observe(self.numOfSkipOrbitsSpinbox,
-                                                 lambda x: self.set_value_silent(self.numOfSkipOrbitsSpinbox, x),
-                                                 'orbits_skip')
-        board.get_board_config(board_id).observe(self.countSpinbox,
-                                                 lambda x: self.set_value_silent(self.countSpinbox, x),
-                                                 'acquisition_count')
-        board.get_board_config(board_id).observe(self.waitSpinbox,
-                                                 lambda x: self.set_value_silent(self.waitSpinbox, x),
-                                                 'orbits_wait_time')
-        board.get_board_config(board_id).observe(self.simulatePilotBunch,
-                                                 lambda x: self.tick_silent(self.simulatePilotBunch, x),
-                                                 'pilot_bunch')
-        board.get_board_config(board_id).observe(self.buildSpectrogrammTickbox,
-                                                 lambda x: self.tick_silent(self.buildSpectrogrammTickbox, x),
-                                                 'build_spectrograms')
-        board.get_board_config(board_id).observe(self.trigger_skip_signals,
-                                                 lambda x: self.set_value_silent(self.trigger_skip_signals, x),
-                                                 'trigger_skip')
-        board.get_board_config(board_id).observe(self.timeout,
-                                                 lambda x: self.set_value_silent(self.timeout, x), 'trigger_timeout')
-        board.get_board_config(board_id).observe(self.trigger_tick, self.update_external_trigger, 'use_trigger')
-        # -------[ register elements ]-------
-        Elements.addItem(["acquire", "no_board", "acquire_"+str(board_id)],
-                         [
-                             self.numOfOrbitsSpinbox,
-                             self.numOfSkipOrbitsSpinbox,
-                             self.countSpinbox,
-                             self.waitSpinbox,
-                             self.buildSpectrogrammTickbox,
-                             self.simulatePilotBunch,
-                             self.trigger_skip_signals,
-                             self.timeout,
-                             self.method,
-                             self.trigger_tick
-                         ])
-
-        # -------[ Add to grid ]-------------
-        self.layout.addWidget(self.numOfOrbitsLabel, 1, 0)
-        self.layout.addWidget(self.numOfOrbitsSpinbox, 1, 1)
-        self.layout.addWidget(self.numOfSkipOrbitsLabel, 2, 0)
-        self.layout.addWidget(self.numOfSkipOrbitsSpinbox, 2, 1)
-        self.layout.addWidget(self.fileSizeLabel, 3,0)
-        self.layout.addWidget(self.fileSizeOutLabel, 3,1)
-        line = QtGui.QFrame()
-        line.setFrameShape(QtGui.QFrame.HLine)
-        self.layout.addWidget(line, 4, 0, 1, 2)
-        self.layout.addWidget(self.countLabel, 5, 0)
-        self.layout.addWidget(self.countSpinbox, 5, 1)
-        self.layout.addWidget(self.waitLabel, 6, 0)
-        self.layout.addWidget(self.waitSpinbox, 6, 1)
-        self.layout.addWidget(self.simulatePilotBunch, 11, 0)
-        self.layout.addWidget(self.buildSpectrogrammTickbox, 11, 1)
-        self.layout.addWidget(self.trigger_tick, 9, 0)
-        line = QtGui.QFrame()
-        line.setFrameShape(QtGui.QFrame.HLine)
-        self.layout.addWidget(line, 10, 0, 1, 2)
-
-    def build_same_as_list(self):
-        """
-        Build the list to show in the same-as drop down menu
-        :return:
-        """
-        for wid_id, wid in self.widgets.items():
-            wid.sameAsCombo.clear()
-            for key, value in self.available_for_same_as.items():
-                if value:
-                    if key is not wid_id:
-                        wid.sameAsCombo.addItem(available_boards.get_board_name_from_id(key))
-
-    def same_as(self):
-        """
-        Toggle SameAs functionality
-        :return: -
-        """
-        # This only does the ui
-        bif._bif_enable_wait_cursor()
-        QtGui.qApp.processEvents()
-        c_t = available_boards.get_board_id_from_name(str(self.sameAsCombo.currentText()))
-        if self.sameAsTick.isChecked():
-            self.available_for_same_as[self.board_id] = False
-            self.sameAsCombo.setEnabled(False)
-            board.get_board_config(c_t).observe_all(
-                board.get_board_config(self.board_id).update)
-            self.widgets[c_t].sameAsTick.setEnabled(False)
-            self.widgets[c_t].sameAsCombo.setEnabled(False)
-            Elements.setEnabled('acquire_{}'.format(self.board_id), False)
-            board.get_board_config(c_t).notify_all_observers()
-
-        else:
-            self.available_for_same_as[self.board_id] = True
-            self.sameAsCombo.setEnabled(True)
-            board.get_board_config(c_t).unobserve_all_observer(
-                board.get_board_config(self.board_id).update)
-            self.widgets[c_t].sameAsTick.setEnabled(True)
-            self.widgets[c_t].sameAsCombo.setEnabled(True)
-            Elements.setEnabled('acquire_{}'.format(self.board_id), True)
-        # actual changes on board
-        if c_t in self.same_as_widgets:
-            self.same_as_widgets[c_t].append(self.board_id)
-        else:
-            self.same_as_widgets[c_t] = [self.board_id, ]
-
-        self.build_same_as_list()
-        bif._bif_disable_wait_cursor()
-
-    def update_external_trigger(self, state):
-        """
-        Update the use external trigger tick
-        :param state: the state to set the tick to
-        """
-        self.tick_silent(self.trigger_tick, state)
-        self.use_external_trigger(state, update_config=False)
-
-    def use_external_trigger(self, state, update_config=True):
-        """
-        Set use external trigger to state
-        :param state: the state to set to
-        :param update_config: true to update the board config
-        """
-        if state:
-            if update_config:
-                board.get_board_config(self.board_id).update('use_trigger', True)
-            self.layout.removeWidget(self.waitLabel)
-            self.layout.removeWidget(self.waitSpinbox)
-            self.waitLabel.hide()
-            self.waitSpinbox.hide()
-
-            self.layout.addWidget(self.timeout_label, 6, 0)
-            self.layout.addWidget(self.timeout, 6, 1)
-            self.layout.addWidget(self.trigger_skip_label, 7, 0)
-            self.layout.addWidget(self.trigger_skip_signals, 7, 1)
-            self.layout.addWidget(self.method_label, 8, 0)
-            self.layout.addWidget(self.method, 8, 1)
-            self.timeout_label.show()
-            self.timeout.show()
-            self.trigger_skip_label.show()
-            self.trigger_skip_signals.show()
-            self.method_label.show()
-            self.method.show()
-        else:
-            if update_config:
-                board.get_board_config(self.board_id).update('use_trigger', False)
-            self.layout.addWidget(self.waitLabel, 6, 0)
-            self.layout.addWidget(self.waitSpinbox, 6, 1)
-            self.waitLabel.show()
-            self.waitSpinbox.show()
-
-            self.layout.removeWidget(self.timeout_label)
-            self.layout.removeWidget(self.timeout)
-            self.layout.removeWidget(self.trigger_skip_label)
-            self.layout.removeWidget(self.trigger_skip_signals)
-            self.layout.removeWidget(self.method_label)
-            self.layout.removeWidget(self.method)
-            self.timeout_label.hide()
-            self.timeout.hide()
-            self.trigger_skip_label.hide()
-            self.trigger_skip_signals.hide()
-            self.method_label.hide()
-            self.method.hide()
-        QtCore.QCoreApplication.processEvents()
-        try:
-            self.parent.parent().adjustSize()
-        except AttributeError:  # when widget is opened with external trigger activated parent.parent() is None
-            pass
-
-    def set_value_silent(self, element, value):
-        """
-        Set a Value silent without notifying observers
-        :param element: what element
-        :param value: set to what value
-        :return: -
-        """
-        element.blockSignals(True)
-        element.setValue(value)
-        element.blockSignals(False)
-
-    def tick_silent(self, element, state):
-        """
-        Tick a tickbox without notifying observers
-        :param element: what tickbox
-        :param state: (bool) to what state
-        :return: -
-        """
-        element.blockSignals(True)
-        element.setChecked(state)
-        element.blockSignals(False)
-
-    def set_values(self):
-        """
-        Set Initial Values when creating this Window.
-        This will read the appropriate Values from the base.backend.board.BoardConfiguration instance used in the gui.
-        :return: -
-        """
-        # bk_get_config has to be done for each widget seperately (get dientifier by widget.board_id)
-        self.set_value_silent(self.numOfOrbitsSpinbox, bif.bk_get_config(self.board_id, 'orbits_observe'))
-        self.set_value_silent(self.numOfSkipOrbitsSpinbox, bif.bk_get_config(self.board_id, 'orbits_skip'))
-        self.set_value_silent(self.countSpinbox, bif.bk_get_config(self.board_id, 'acquisition_count'))
-        self.set_value_silent(self.waitSpinbox, bif.bk_get_config(self.board_id, 'orbits_wait_time'))
-        self.tick_silent(self.buildSpectrogrammTickbox, bif.bk_get_config(self.board_id, 'build_spectrograms'))
-        self.tick_silent(self.simulatePilotBunch, bif.bk_get_config(self.board_id, 'pilot_bunch'))
-        self.update_external_trigger(bif.bk_get_config(self.board_id, 'use_trigger'))
-
-    # -----------------[ Value Change Handlers ]---------------------------
-    def on_number_of_orbits_changed(self):
-        """
-        Handle the change of the number of orbits
-        """
-        bif.bk_change_num_of_orbits(self.board_id, self.numOfOrbitsSpinbox.value())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_num_of_orbits(wid.board_id, wid.numOfOrbitsSpinbox.value())
-
-    def on_number_of_skipped_orbits_changed(self):
-        """
-        Handle the change of the number of skipped orbits
-        """
-        bif.bk_change_num_of_skipped_orbits(self.board_id, self.numOfSkipOrbitsSpinbox.value())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_num_of_skipped_orbits(wid.board_id, wid.numOfSkipOrbitsSpinbox.value())
-
-    def on_count_changed(self):
-        """
-        Handle the change of the count value
-        """
-        bif.bk_change_count(self.board_id, self.countSpinbox.value())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_count(wid.board_id, wid.countSpinbox.value())
-
-    def on_wait_changed(self):
-        """
-        Handle the change of the wait time
-        """
-        bif.bk_change_wait(self.board_id, self.waitSpinbox.value())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_wait(wid.board_id, wid.waitSpinbox.value())
-
-    def on_build_spectrograms_changed(self):
-        """
-        Handle the change of the build_spectrograms checkbox
-        """
-        bif.bk_change_build_spectrograms(self.board_id, self.buildSpectrogrammTickbox.checkState())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_build_spectrograms(wid.board_id, wid.buildSpectrogrammTickbox.checkState())
-
-    def on_simulate_pilot_bunch_changed(self):
-        """
-        Handle the change of the simulate_pilot_bunch checkbox
-        """
-        bif.bk_change_pilot_bunch(self.board_id, self.simulatePilotBunch.checkState())
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_change_pilot_bunch(wid.board_id, wid.simulatePilotBunch.checkState())
-
-    def on_trigger_skip_signals_changed(self, value):
-        """
-        Handle change of trigger skip signal spinbox
-        :param int value: the value to set trigger_skip to (the number of trigger signals to skip between acquisitions
-        """
-        bif.bk_update_config(self.board_id, 'trigger_skip', value)
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_update_config(wid.board_id, 'trigger_skip', value)
-
-    def on_trigger_method_changed(self):
-        """
-        Handle change of trigger method.
-        """
-        bif.bk_update_config(self.board_id, 'trigger_method', 2 if self.method.state() else 1)
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_update_config(wid.board_id, 'trigger_method', 2 if self.method.satate() else 1)
-
-    def on_trigger_timeout_changed(self, value):
-        """
-        Handle change of trigger timeout spinbox
-        :param int value: the value to set the trigger_timout to
-        """
-        bif.bk_update_config(self.board_id, 'trigger_timeout', value)
-        for b_id in self.same_as_widgets[self.board_id]:
-            wid = self.widgets[b_id]
-            bif.bk_update_config(wid.board_id, 'trigger_timeout', value)
-
-    def closeEvent(self, event):
-        """
-        Event handler to handle the event of closing this window and gracefully delete resources and such
-        :param event: the event to handle (gets passed to by PyQt)
-        """
-        board.get_board_config(self.board_id).unobserve(self.numOfOrbitsSpinbox, 'orbits_observe')
-        board.get_board_config(self.board_id).unobserve(self.numOfSkipOrbitsSpinbox, 'orbits_skip')
-        board.get_board_config(self.board_id).unobserve(self.countSpinbox, 'acquisition_count')
-        board.get_board_config(self.board_id).unobserve(self.waitSpinbox, 'orbits_wait_time')
-        board.get_board_config(self.board_id).unobserve(self.simulatePilotBunch, 'pilot_bunch')
-        board.get_board_config(self.board_id).unobserve(self.buildSpectrogrammTickbox, 'build_spectrograms')
-        board.get_board_config(self.board_id).unobserve(self.trigger_skip_signals, 'trigger_skip')
-        board.get_board_config(self.board_id).unobserve(self.timeout, 'trigger_timeout')
-        board.get_board_config(self.board_id).unobserve(self.trigger_tick, 'use_trigger')
-        Elements.removeItem(["acquire_{}".format(self.board_id), "no_board_{}".format(self.board_id)],
-                            [
-                                 self.numOfOrbitsSpinbox,
-                                 self.numOfSkipOrbitsSpinbox,
-                                 self.countSpinbox,
-                                 self.waitSpinbox,
-                                 self.buildSpectrogrammTickbox,
-                                 self.simulatePilotBunch,
-                                 self.trigger_skip_signals,
-                                 self.timeout,
-                                 self.method,
-                                 self.trigger_tick
-                             ]
-                            )
-
-
-class AcquireSettings(kcgw.KCGWidgets):
-    """
-    This is the actual Acquire Settings Window.
-    """
-    def __init__(self, unique_id, parent=None):
-        """
-        Initialise the acquire settings window
-        :param unique_id: the id for thiw widget
-        :param parent: parent object
-        :return: -
-        """
-        super(AcquireSettings, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.setWindowTitle(tr("Heading", "Acquire Settings"))
-        self.same_as_widgets = {i: [] for i in available_boards}
-        self.widgets = OrderedDict({i: AcquireSettingsTab(i, self.same_as_widgets, self) for i in available_boards})
-        if available_boards.multi_board:
-            self.tabs = QtGui.QTabWidget()
-            self.layout.addWidget(self.tabs)
-
-            for wid in list(self.widgets.values()):
-                self.tabs.addTab(wid, available_boards.get_board_name_from_id(wid.board_id))
-                wid.sameAsCombo.addItems([available_boards.get_board_name_from_id(i) for i in available_boards if i != wid.board_id])
-                wid.widgets = self.widgets
-        else:
-            self.single_widget = list(self.widgets.values())[0]
-            self.layout.addWidget(self.single_widget)
-        for wid in list(self.widgets.values()):
-            wid.set_values()
-
-    def show_tab(self, board_id):
-        """
-        Show the tab for the given board
-        :param board_id: the id of the board to show
-        """
-        if not available_boards.multi_board:
-            return
-        self.tabs.setCurrentIndex(list(self.widgets.keys()).index(board_id))
-
-    def closeEvent(self, event):
-        global __widget_id__
-        for wid in list(self.widgets.values()):
-            wid.closeEvent(event)
-        __widget_id__ = None
-        del self.par.widgets[self.id]
-        super(AcquireSettings, self).closeEvent(event)
-
-
-def add_acquire_settings_widget(board_id=None):
-    """
-    Add this widget to the gui.
-    This function will actually open the subwindow.
-    :return: -
-    """
-    global __widget_id__
-    if __widget_id__:
-        global_objects.get_global('area').widgets[__widget_id__].setFocus()
-        if board_id is not None:
-            global_objects.get_global('area').widgets[__widget_id__].show_tab(board_id)
-    else:
-        nid = kcgw.idg.genid()
-        __widget_id__ = nid
-        w = AcquireSettings(nid, global_objects.get_global('area'))
-        global_objects.get_global('area').newWidget(w, "Acquire", nid, widget_type=4, minSize=True)  # TODO: proper type
-        if board_id is not None:
-            global_objects.get_global('area').widgets[__widget_id__].show_tab(board_id)
-
-
-def setValueSilent(key, value):
-    """
-    Set values without notifying observers
-    :param key: the key
-    :param value: the value to set
-    :return: -
-    """
-    if not __widget_id__:
-        return
-    global_objects.get_global('area').widgets[__widget_id__].blockSignals(True)
-    if key == "orbits_observe":
-        global_objects.get_global('area').widgets[__widget_id__].numOfOrbitsSpinbox.setValue(value)
-    if key == "orbits_skip":
-        global_objects.get_global('area').widgets[__widget_id__].numOfSkipOrbitsSpinbox.setValue(value)
-    global_objects.get_global('area').widgets[__widget_id__].blockSignals(False)
-
-
-kcgw.register_widget(QtGui.QIcon(config.icon_path(config.acquireSettingsIcon)), tr("Heading", "Acquire Settings"), add_acquire_settings_widget, "Ctrl+A")
+"""
+This Module Is the Acquiresettings subWindow.
+"""
+from PyQt4 import QtGui, QtCore
+from collections import OrderedDict
+
+from ..base import kcgwidget as kcgw
+from ..base.backend import board
+from ..base.backend.board import available_boards
+from ..base import backendinterface as bif
+from ..base.groupedelements import Elements
+from .. import config
+from ..base.globals import glob as global_objects
+tr = kcgw.tr
+
+__widget_id__ = None
+
+
+class AcquireSettingsTab(kcgw.KCGWidgets):
+    """
+    A single tab in the acquire settings window
+    """
+    available_for_same_as = OrderedDict()
+
+    def __init__(self, board_id, same_as_widgets, parent=None):
+        super(AcquireSettingsTab, self).__init__()
+        self.available_for_same_as[board_id] = True
+        self.widgets = None  # IMPORTANT: THIS WIDGET NEEDS A LIST OF ALL THE OTHER WIDGETS
+        self.same_as_widgets = same_as_widgets
+        self.parent = parent
+        self.board_id = board_id
+        self.layout = QtGui.QGridLayout()
+        self.outerLayout = QtGui.QVBoxLayout()
+        self.outerLayout.addLayout(self.layout)
+        self.outerLayout.addStretch(1)
+        self.setLayout(self.outerLayout)
+
+        self.sameAsLabel = self.createLabel("Same as")
+        self.sameAsCombo = QtGui.QComboBox()
+        self.sameAsTick = self.createCheckbox("", connect=self.same_as)
+        self.sameAs = QtGui.QHBoxLayout()
+        self.sameAs.addWidget(self.sameAsCombo)
+        self.sameAs.addWidget(self.sameAsTick)
+        if available_boards.multi_board:
+            self.layout.addWidget(self.sameAsLabel, 0, 0)
+            self.layout.addLayout(self.sameAs, 0, 1)
+        else:
+            self.sameAsLabel.hide()
+
+        # ---------[ Create Labels and corresponding Fields ]---------
+        self.numOfOrbitsLabel = self.createLabel(tr("Label", "Number of orbits to observe"))
+        self.numOfSkipOrbitsLabel = self.createLabel(tr("Label", "Number of orbits to skip"))
+        self.numOfOrbitsSpinbox = self.createSpinbox(1, 10000000, connect=self.on_number_of_orbits_changed)
+        self.numOfSkipOrbitsSpinbox = self.createSpinbox(0, 100, connect=self.on_number_of_skipped_orbits_changed)
+        self.countLabel = self.createLabel(tr("Label", "Number of acquisitions"))
+        self.countSpinbox = self.createSpinbox(1, 10000000, start_value=10, connect=self.on_count_changed)
+        self.waitLabel = self.createLabel(tr("Label", "Wait(s)"))
+        self.waitSpinbox = self.createSpinbox(0, 60, start_value=15, connect=self.on_wait_changed)
+        self.simulatePilotBunch = self.createCheckbox(tr("Button", "Simulate Pilot Bunch"),
+                                                      connect=self.on_simulate_pilot_bunch_changed)
+        self.buildSpectrogrammTickbox = self.createCheckbox(tr("Button", "Build Spectograms"),
+                                                            connect=self.on_build_spectrograms_changed)
+
+        # ----------[ Trigger part ]--------------------
+        self.trigger_skip_label = self.createLabel(tr("Label", "Number of trigger signals to skip"))
+        self.trigger_skip_signals = self.createSpinbox(0, 1000, 1, start_value=0,
+                                                       connect=self.on_trigger_skip_signals_changed)
+        self.timeout_label = self.createLabel(tr("Label", "Timeout (s)"))
+        self.timeout = self.createSpinbox(0, 10000000, start_value=12, connect=self.on_trigger_timeout_changed)
+        self.method_label = self.createLabel(tr("Label", "Method"))
+        self.method = kcgw.Switch("1", "2")
+        self.method.clicked.connect(self.on_trigger_method_changed)
+        self.trigger_skip_label.hide()
+        self.trigger_skip_signals.hide()
+        self.timeout_label.hide()
+        self.timeout.hide()
+        self.method_label.hide()
+        self.method.hide()
+
+        self.trigger_tick = self.createCheckbox(tr("Label", "Use External Trigger"), connect=self.use_external_trigger)
+
+        # -------[ register observers ]------
+        board.get_board_config(board_id).observe(self.numOfOrbitsSpinbox,
+                                                 lambda x: self.set_value_silent(self.numOfOrbitsSpinbox, x),
+                                                 'orbits_observe')
+        board.get_board_config(board_id).observe(self.numOfSkipOrbitsSpinbox,
+                                                 lambda x: self.set_value_silent(self.numOfSkipOrbitsSpinbox, x),
+                                                 'orbits_skip')
+        board.get_board_config(board_id).observe(self.countSpinbox,
+                                                 lambda x: self.set_value_silent(self.countSpinbox, x),
+                                                 'acquisition_count')
+        board.get_board_config(board_id).observe(self.waitSpinbox,
+                                                 lambda x: self.set_value_silent(self.waitSpinbox, x),
+                                                 'orbits_wait_time')
+        board.get_board_config(board_id).observe(self.simulatePilotBunch,
+                                                 lambda x: self.tick_silent(self.simulatePilotBunch, x),
+                                                 'pilot_bunch')
+        board.get_board_config(board_id).observe(self.buildSpectrogrammTickbox,
+                                                 lambda x: self.tick_silent(self.buildSpectrogrammTickbox, x),
+                                                 'build_spectrograms')
+        board.get_board_config(board_id).observe(self.trigger_skip_signals,
+                                                 lambda x: self.set_value_silent(self.trigger_skip_signals, x),
+                                                 'trigger_skip')
+        board.get_board_config(board_id).observe(self.timeout,
+                                                 lambda x: self.set_value_silent(self.timeout, x), 'trigger_timeout')
+        board.get_board_config(board_id).observe(self.trigger_tick, self.update_external_trigger, 'use_trigger')
+        # -------[ register elements ]-------
+        Elements.addItem(["acquire", "no_board", "acquire_"+str(board_id)],
+                         [
+                             self.numOfOrbitsSpinbox,
+                             self.numOfSkipOrbitsSpinbox,
+                             self.countSpinbox,
+                             self.waitSpinbox,
+                             self.buildSpectrogrammTickbox,
+                             self.simulatePilotBunch,
+                             self.trigger_skip_signals,
+                             self.timeout,
+                             self.method,
+                             self.trigger_tick
+                         ])
+
+        # -------[ Add to grid ]-------------
+        self.layout.addWidget(self.numOfOrbitsLabel, 1, 0)
+        self.layout.addWidget(self.numOfOrbitsSpinbox, 1, 1)
+        self.layout.addWidget(self.numOfSkipOrbitsLabel, 2, 0)
+        self.layout.addWidget(self.numOfSkipOrbitsSpinbox, 2, 1)
+        self.layout.addWidget(self.countLabel, 3, 0)
+        self.layout.addWidget(self.countSpinbox, 3, 1)
+        self.layout.addWidget(self.waitLabel, 4, 0)
+        self.layout.addWidget(self.waitSpinbox, 4, 1)
+        self.layout.addWidget(self.simulatePilotBunch, 9, 0)
+        self.layout.addWidget(self.buildSpectrogrammTickbox, 9, 1)
+        self.layout.addWidget(self.trigger_tick, 7, 0)
+        line = QtGui.QFrame()
+        line.setFrameShape(QtGui.QFrame.HLine)
+        self.layout.addWidget(line, 8, 0, 1, 2)
+
+    def build_same_as_list(self):
+        """
+        Build the list to show in the same-as drop down menu
+        :return:
+        """
+        for wid_id, wid in self.widgets.iteritems():
+            wid.sameAsCombo.clear()
+            for key, value in self.available_for_same_as.iteritems():
+                if value:
+                    if key is not wid_id:
+                        wid.sameAsCombo.addItem(available_boards.get_board_name_from_id(key))
+
+    def same_as(self):
+        """
+        Toggle SameAs functionality
+        :return: -
+        """
+        # This only does the ui
+        bif._bif_enable_wait_cursor()
+        QtGui.qApp.processEvents()
+        c_t = available_boards.get_board_id_from_name(str(self.sameAsCombo.currentText()))
+        if self.sameAsTick.isChecked():
+            self.available_for_same_as[self.board_id] = False
+            self.sameAsCombo.setEnabled(False)
+            board.get_board_config(c_t).observe_all(
+                board.get_board_config(self.board_id).update)
+            self.widgets[c_t].sameAsTick.setEnabled(False)
+            self.widgets[c_t].sameAsCombo.setEnabled(False)
+            Elements.setEnabled('acquire_{}'.format(self.board_id), False)
+            board.get_board_config(c_t).notify_all_observers()
+
+        else:
+            self.available_for_same_as[self.board_id] = True
+            self.sameAsCombo.setEnabled(True)
+            board.get_board_config(c_t).unobserve_all_observer(
+                board.get_board_config(self.board_id).update)
+            self.widgets[c_t].sameAsTick.setEnabled(True)
+            self.widgets[c_t].sameAsCombo.setEnabled(True)
+            Elements.setEnabled('acquire_{}'.format(self.board_id), True)
+        # actual changes on board
+        if c_t in self.same_as_widgets:
+            self.same_as_widgets[c_t].append(self.board_id)
+        else:
+            self.same_as_widgets[c_t] = [self.board_id, ]
+
+        self.build_same_as_list()
+        bif._bif_disable_wait_cursor()
+
+    def update_external_trigger(self, state):
+        """
+        Update the use external trigger tick
+        :param state: the state to set the tick to
+        """
+        self.tick_silent(self.trigger_tick, state)
+        self.use_external_trigger(state, update_config=False)
+
+    def use_external_trigger(self, state, update_config=True):
+        """
+        Set use external trigger to state
+        :param state: the state to set to
+        :param update_config: true to update the board config
+        """
+        if state:
+            if update_config:
+                board.get_board_config(self.board_id).update('use_trigger', True)
+            self.layout.removeWidget(self.waitLabel)
+            self.layout.removeWidget(self.waitSpinbox)
+            self.waitLabel.hide()
+            self.waitSpinbox.hide()
+
+            self.layout.addWidget(self.timeout_label, 4, 0)
+            self.layout.addWidget(self.timeout, 4, 1)
+            self.layout.addWidget(self.trigger_skip_label, 5, 0)
+            self.layout.addWidget(self.trigger_skip_signals, 5, 1)
+            self.layout.addWidget(self.method_label, 6, 0)
+            self.layout.addWidget(self.method, 6, 1)
+            self.timeout_label.show()
+            self.timeout.show()
+            self.trigger_skip_label.show()
+            self.trigger_skip_signals.show()
+            self.method_label.show()
+            self.method.show()
+        else:
+            if update_config:
+                board.get_board_config(self.board_id).update('use_trigger', False)
+            self.layout.addWidget(self.waitLabel, 4, 0)
+            self.layout.addWidget(self.waitSpinbox, 4, 1)
+            self.waitLabel.show()
+            self.waitSpinbox.show()
+
+            self.layout.removeWidget(self.timeout_label)
+            self.layout.removeWidget(self.timeout)
+            self.layout.removeWidget(self.trigger_skip_label)
+            self.layout.removeWidget(self.trigger_skip_signals)
+            self.layout.removeWidget(self.method_label)
+            self.layout.removeWidget(self.method)
+            self.timeout_label.hide()
+            self.timeout.hide()
+            self.trigger_skip_label.hide()
+            self.trigger_skip_signals.hide()
+            self.method_label.hide()
+            self.method.hide()
+        QtCore.QCoreApplication.processEvents()
+        try:
+            self.parent.parent().adjustSize()
+        except AttributeError:  # when widget is opened with external trigger activated parent.parent() is None
+            pass
+
+    def set_value_silent(self, element, value):
+        """
+        Set a Value silent without notifying observers
+        :param element: what element
+        :param value: set to what value
+        :return: -
+        """
+        element.blockSignals(True)
+        element.setValue(value)
+        element.blockSignals(False)
+
+    def tick_silent(self, element, state):
+        """
+        Tick a tickbox without notifying observers
+        :param element: what tickbox
+        :param state: (bool) to what state
+        :return: -
+        """
+        element.blockSignals(True)
+        element.setChecked(state)
+        element.blockSignals(False)
+
+    def set_values(self):
+        """
+        Set Initial Values when creating this Window.
+        This will read the appropriate Values from the base.backend.board.BoardConfiguration instance used in the gui.
+        :return: -
+        """
+        # bk_get_config has to be done for each widget seperately (get dientifier by widget.board_id)
+        self.set_value_silent(self.numOfOrbitsSpinbox, bif.bk_get_config(self.board_id, 'orbits_observe'))
+        self.set_value_silent(self.numOfSkipOrbitsSpinbox, bif.bk_get_config(self.board_id, 'orbits_skip'))
+        self.set_value_silent(self.countSpinbox, bif.bk_get_config(self.board_id, 'acquisition_count'))
+        self.set_value_silent(self.waitSpinbox, bif.bk_get_config(self.board_id, 'orbits_wait_time'))
+        self.tick_silent(self.buildSpectrogrammTickbox, bif.bk_get_config(self.board_id, 'build_spectrograms'))
+        self.tick_silent(self.simulatePilotBunch, bif.bk_get_config(self.board_id, 'pilot_bunch'))
+        self.update_external_trigger(bif.bk_get_config(self.board_id, 'use_trigger'))
+
+    # -----------------[ Value Change Handlers ]---------------------------
+    def on_number_of_orbits_changed(self):
+        """
+        Handle the change of the number of orbits
+        """
+        bif.bk_change_num_of_orbits(self.board_id, self.numOfOrbitsSpinbox.value())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_num_of_orbits(wid.board_id, wid.numOfOrbitsSpinbox.value())
+
+    def on_number_of_skipped_orbits_changed(self):
+        """
+        Handle the change of the number of skipped orbits
+        """
+        bif.bk_change_num_of_skipped_orbits(self.board_id, self.numOfSkipOrbitsSpinbox.value())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_num_of_skipped_orbits(wid.board_id, wid.numOfSkipOrbitsSpinbox.value())
+
+    def on_count_changed(self):
+        """
+        Handle the change of the count value
+        """
+        bif.bk_change_count(self.board_id, self.countSpinbox.value())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_count(wid.board_id, wid.countSpinbox.value())
+
+    def on_wait_changed(self):
+        """
+        Handle the change of the wait time
+        """
+        bif.bk_change_wait(self.board_id, self.waitSpinbox.value())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_wait(wid.board_id, wid.waitSpinbox.value())
+
+    def on_build_spectrograms_changed(self):
+        """
+        Handle the change of the build_spectrograms checkbox
+        """
+        bif.bk_change_build_spectrograms(self.board_id, self.buildSpectrogrammTickbox.checkState())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_build_spectrograms(wid.board_id, wid.buildSpectrogrammTickbox.checkState())
+
+    def on_simulate_pilot_bunch_changed(self):
+        """
+        Handle the change of the simulate_pilot_bunch checkbox
+        """
+        bif.bk_change_pilot_bunch(self.board_id, self.simulatePilotBunch.checkState())
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_change_pilot_bunch(wid.board_id, wid.simulatePilotBunch.checkState())
+
+    def on_trigger_skip_signals_changed(self, value):
+        """
+        Handle change of trigger skip signal spinbox
+        :param int value: the value to set trigger_skip to (the number of trigger signals to skip between acquisitions
+        """
+        bif.bk_update_config(self.board_id, 'trigger_skip', value)
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_update_config(wid.board_id, 'trigger_skip', value)
+
+    def on_trigger_method_changed(self):
+        """
+        Handle change of trigger method.
+        """
+        bif.bk_update_config(self.board_id, 'trigger_method', 2 if self.method.state() else 1)
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_update_config(wid.board_id, 'trigger_method', 2 if self.method.satate() else 1)
+
+    def on_trigger_timeout_changed(self, value):
+        """
+        Handle change of trigger timeout spinbox
+        :param int value: the value to set the trigger_timout to
+        """
+        bif.bk_update_config(self.board_id, 'trigger_timeout', value)
+        for b_id in self.same_as_widgets[self.board_id]:
+            wid = self.widgets[b_id]
+            bif.bk_update_config(wid.board_id, 'trigger_timeout', value)
+
+    def closeEvent(self, event):
+        """
+        Event handler to handle the event of closing this window and gracefully delete resources and such
+        :param event: the event to handle (gets passed to by PyQt)
+        """
+        board.get_board_config(self.board_id).unobserve(self.numOfOrbitsSpinbox, 'orbits_observe')
+        board.get_board_config(self.board_id).unobserve(self.numOfSkipOrbitsSpinbox, 'orbits_skip')
+        board.get_board_config(self.board_id).unobserve(self.countSpinbox, 'acquisition_count')
+        board.get_board_config(self.board_id).unobserve(self.waitSpinbox, 'orbits_wait_time')
+        board.get_board_config(self.board_id).unobserve(self.simulatePilotBunch, 'pilot_bunch')
+        board.get_board_config(self.board_id).unobserve(self.buildSpectrogrammTickbox, 'build_spectrograms')
+        board.get_board_config(self.board_id).unobserve(self.trigger_skip_signals, 'trigger_skip')
+        board.get_board_config(self.board_id).unobserve(self.timeout, 'trigger_timeout')
+        board.get_board_config(self.board_id).unobserve(self.trigger_tick, 'use_trigger')
+        Elements.removeItem(["acquire_{}".format(self.board_id), "no_board_{}".format(self.board_id)],
+                            [
+                                 self.numOfOrbitsSpinbox,
+                                 self.numOfSkipOrbitsSpinbox,
+                                 self.countSpinbox,
+                                 self.waitSpinbox,
+                                 self.buildSpectrogrammTickbox,
+                                 self.simulatePilotBunch,
+                                 self.trigger_skip_signals,
+                                 self.timeout,
+                                 self.method,
+                                 self.trigger_tick
+                             ]
+                            )
+
+
+class AcquireSettings(kcgw.KCGWidgets):
+    """
+    This is the actual Acquire Settings Window.
+    """
+    def __init__(self, unique_id, parent=None):
+        """
+        Initialise the acquire settings window
+        :param unique_id: the id for thiw widget
+        :param parent: parent object
+        :return: -
+        """
+        super(AcquireSettings, self).__init__()
+
+        self.id = unique_id
+        self.par = parent
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.setWindowTitle(tr("Heading", "Acquire Settings"))
+        self.same_as_widgets = {i: [] for i in available_boards}
+        self.widgets = OrderedDict({i: AcquireSettingsTab(i, self.same_as_widgets, self) for i in available_boards})
+        if available_boards.multi_board:
+            self.tabs = QtGui.QTabWidget()
+            self.layout.addWidget(self.tabs)
+
+            for wid in self.widgets.values():
+                self.tabs.addTab(wid, available_boards.get_board_name_from_id(wid.board_id))
+                wid.sameAsCombo.addItems([available_boards.get_board_name_from_id(i) for i in available_boards if i != wid.board_id])
+                wid.widgets = self.widgets
+        else:
+            self.single_widget = self.widgets.values()[0]
+            self.layout.addWidget(self.single_widget)
+        for wid in self.widgets.values():
+            wid.set_values()
+
+    def show_tab(self, board_id):
+        """
+        Show the tab for the given board
+        :param board_id: the id of the board to show
+        """
+        if not available_boards.multi_board:
+            return
+        self.tabs.setCurrentIndex(self.widgets.keys().index(board_id))
+
+    def closeEvent(self, event):
+        global __widget_id__
+        for wid in self.widgets.values():
+            wid.closeEvent(event)
+        __widget_id__ = None
+        del self.par.widgets[self.id]
+        super(AcquireSettings, self).closeEvent(event)
+
+
+def add_acquire_settings_widget(board_id=None):
+    """
+    Add this widget to the gui.
+    This function will actually open the subwindow.
+    :return: -
+    """
+    global __widget_id__
+    if __widget_id__:
+        global_objects.get_global('area').widgets[__widget_id__].setFocus()
+        if board_id is not None:
+            global_objects.get_global('area').widgets[__widget_id__].show_tab(board_id)
+    else:
+        nid = kcgw.idg.genid()
+        __widget_id__ = nid
+        w = AcquireSettings(nid, global_objects.get_global('area'))
+        global_objects.get_global('area').newWidget(w, "Acquire", nid, widget_type=4, minSize=True)  # TODO: proper type
+        if board_id is not None:
+            global_objects.get_global('area').widgets[__widget_id__].show_tab(board_id)
+
+
+def setValueSilent(key, value):
+    """
+    Set values without notifying observers
+    :param key: the key
+    :param value: the value to set
+    :return: -
+    """
+    if not __widget_id__:
+        return
+    global_objects.get_global('area').widgets[__widget_id__].blockSignals(True)
+    if key == "orbits_observe":
+        global_objects.get_global('area').widgets[__widget_id__].numOfOrbitsSpinbox.setValue(value)
+    if key == "orbits_skip":
+        global_objects.get_global('area').widgets[__widget_id__].numOfSkipOrbitsSpinbox.setValue(value)
+    global_objects.get_global('area').widgets[__widget_id__].blockSignals(False)
+
+
+kcgw.register_widget(QtGui.QIcon(config.install_path + config.acquireSettingsIcon), tr("Heading", "Acquire Settings"), add_acquire_settings_widget, "Ctrl+A")

+ 0 - 388
KCG/widgets/epics_widget.py

@@ -1,388 +0,0 @@
-from PyQt4 import QtGui
-
-
-from ..base import kcgwidget as kcgw
-from ..base.globals import glob as global_objects
-from ..base import log
-from .. import config
-import configparser
-import os
-import logging
-from functools import partial
-import numpy as np
-
-__widget_id__ = None
-
-try:  # try to import epics
-    import epics
-    no_epics = False
-except ImportError:
-    no_epics = True
-    logging.error("Python Epics not found")
-
-def caget(pv):
-    if no_epics: return -np.inf
-    try:
-        return epics.caget(pv)
-    except:
-        logging.error("Epics PV is not accessible (possible Timeout)")
-        return -np.inf
-
-class EpicsConfig(object):
-    """
-    need one instance at app startup
-    is done by kcgGUI via
-    from ..widgets import epics_widget
-        epics_widget.epicsConfig = epics_widget.EpicsConfig()
-    """
-    def __init__(self):
-
-        self.loadConfig(False)
-        
-        os.environ["EPICS_BASE"] = self.getKey('epics_base_path') # config.epics_base_path
-        try:  # if import was successful, try to find libca and test for connectivity with config.epics_test_pv
-            # sys.stderr = os.devnull  # epics.ca.find_libca() prints a useless None to stderr if libca is not found
-            epics.ca.find_libca()
-            # sys.stderr = sys.__stderr__
-            if epics.caget(self.getKey('epics_test_pv')) == None:
-                no_epics = True
-                epics_reachable = False
-                logging.error("Epics is not accessible (possible Timeout)")
-
-        except epics.ca.ChannelAccessException as e:
-            if str(e) == "cannot find Epics CA DLL":
-                no_epics = True
-                logging.error("Epics CA DLL not found")
-
-
-        self.setLogger()
-
-    def setLogger(self):
-        if no_epics:
-            return
-        # first remove all epics entrys
-        params = log.logger.predefined_parameters
-        log.logger.predefined_parameters = []
-        for item in params:
-            if item[1][0] != epics.caget:
-                log.logger.predefined_parameters.append(item)
-        params = log.logger.parameter_functions
-        log.logger.reset_parameters()
-        for item in params:
-            if item[0] != epics.caget:
-                log.logger.register_parameter(item[2], item[0], item[1])
-        # add epics entrys
-        for entry in self.getKey('epics_log_entry_pvs'):#config.epics_log_entry_pvs:
-            log.logger.predefined_parameters.append([entry[0], [epics.caget, entry[1]]])
-            if entry[2] == "True":
-                log.logger.register_parameter(entry[0], epics.caget, entry[1])
-
-
-    def loadConfig(self, updateLogger=True):
-
-        epics_key = ["epics_log_entry_pvs", "epics_test_pv", "epics_base_path"]
-
-        self.parser = configparser.ConfigParser()
-        self.parser.optionxform = str
-
-        if not os.path.isfile(os.path.join(os.path.expanduser("~"),".kcg","epics.cfg")):
-            with open(os.path.join(os.path.expanduser("~"),".kcg","epics.cfg"), 'w') as f:
-                self.parser['EPICS'] = {'epics_log_entry_pvs':'[("Beam Energy (GeV)", "A:SR:BeamInfo:01:Energy","False"),("Beam Current (mA)", "A:SR:BeamInfo:01:Current","True")]',
-                                    'epics_test_pv':'"A:SR:BeamInfo:01:Current"',
-                                    'epics_base_path':'"/opt/epics/base/"'
-                                    }
-                self.parser.write(f)
-         #       print("new Epics config")
-        
-        self.parser.read(os.path.join(os.path.expanduser("~"),".kcg","epics.cfg"))
-        #print("epics config loaded")
-        if updateLogger: self.setLogger()  
-
-
-    def saveConfig(self):
-        with open(os.path.join(os.path.expanduser("~"),".kcg","epics.cfg"), 'w+') as f:
-            self.parser.write(f)
-        self.setLogger()
-
-
-    def getKey(self, key):
-        return config.leval(self.parser['EPICS'][key])
-
-    def getPVList(self):
-        return self.getKey('epics_log_entry_pvs')
-
-    def setPVList(self, pvList):
-        self.parser['EPICS']['epics_log_entry_pvs'] = self.generatePVKey(pvList)
-
-    def addPV(self, name, pv, default, monitor=False):
-        dat = self.getKey('epics_log_entry_pvs')
-        dat.append((name,pv,default,monitor))
-        self.parser['EPICS']['epics_log_entry_pvs'] = self.generatePVKey(dat)
-
-    def removePV(self, index):
-        n = int(index)
-        pvList = []
-        for i, item in enumerate(self.getPVList()):
-            if n != i: pvList.append((item[0], item[1], item[2], item[3]))
-        self.setPVList(pvList)
-
-        
-
-
-    def generatePVKey(self, pvList):
-        out = "[\n"
-        i=0
-        for item in pvList:
-            if i: out = out + ",\n"
-            out = out + "('" + str(item[0]) + "', '" + str(item[1]) + "', '" + str(item[2]) + "', '" + str(item[3]) +"')"
-            i = i+1
-
-        out = out + "\n]"
-        return out
-    
-
-
-
-
-
-
-
-
-
-# 8888888888        d8b                888       888d8b     888                888
-# 888               Y8P                888   o   888Y8P     888                888
-# 888                                  888  d8b  888        888                888
-# 8888888   88888b. 888 .d8888b.d8888b 888 d888b 888888 .d88888 .d88b.  .d88b. 888888
-# 888       888 "88b888d88P"   88K     888d88888b888888d88" 888d88P"88bd8P  Y8b888
-# 888       888  888888888     "Y8888b.88888P Y88888888888  888888  88888888888888
-# 888       888 d88P888Y88b.        X888888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.
-# 888888888888888P" 888 "Y8888P 88888P'888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888
-#           888                                                     888
-#           888                                                Y8b d88P
-#           888                                                 "Y88P"
-#
-
-class EpicsWidget(kcgw.KCGWidgets):
-    def __init__(self, unique_id, parent):
-        super(EpicsWidget, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-        self.monitorPVList = {}
-
-        self.layout = QtGui.QVBoxLayout() #.QGridLayout()
-        self.setLayout(self.layout)
-        self.editInnerGroupBox = QtGui.QGroupBox("")
-        self.editInnerGroupBoxLayout = QtGui.QGridLayout()
-        self.editScrollArea = QtGui.QScrollArea()
-
-        self.pvInputList = []
-        self.editInnerGroupBoxLayout.addWidget(self.createLabel("Name"), 0,0)
-        self.editInnerGroupBoxLayout.addWidget(self.createLabel("EPICS PV"), 0,1)
-        self.editInnerGroupBoxLayout.addWidget(self.createLabel("in Log"), 0,2)
-        self.editInnerGroupBoxLayout.addWidget(self.createLabel("Monitoring"), 0,3)
-        self.editInnerGroupBoxLayout.addWidget(self.createLabel("Value"), 0,4)
-        self.generateList(False)
-       
-        self.editInnerGroupBox.setLayout(self.editInnerGroupBoxLayout)
-        self.editScrollArea.setWidget(self.editInnerGroupBox)
-        self.editOuterGroupBox = QtGui.QGroupBox("Epics PV List")
-        self.editOuterGroupBox.clicked.connect(self.toggleEdit)
-        self.editOuterGroupBoxLayout = QtGui.QGridLayout()
-        self.editOuterGroupBoxLayout.addWidget(self.editScrollArea,0,0,1,5)
-
-        self.readValues()
-
-        self.button1 = self.createButton("refresh values",  connect=self.readValues)
-        self.button2 = self.createButton("apply changes",   connect=self.apply)
-        self.button3 = self.createButton("restore changes", connect=self.restore)
-        self.button4 = self.createButton("apply + save",  connect=self.save)
-        self.button5 = self.createButton("load", connect=self.load)
-
-        self.editOuterGroupBoxLayout.addWidget(self.button1, 1,0)
-        self.editOuterGroupBoxLayout.addWidget(self.button2, 1,1)
-        self.editOuterGroupBoxLayout.addWidget(self.button3, 1,2)
-        self.editOuterGroupBoxLayout.addWidget(self.button4, 1,3)
-        self.editOuterGroupBoxLayout.addWidget(self.button5, 1,4)
-
-        self.editOuterGroupBox.setLayout(self.editOuterGroupBoxLayout)
-        
-
-        self.monitorLabel = self.createLabel("Monitor\n")
-        self.layout.addWidget(self.monitorLabel)#,0,0)
-        self.layout.addWidget(self.createCheckbox("Open PV List", connect=self.toggleEdit))#, 1,0)
-        self.layout.addWidget(self.editOuterGroupBox)#,2,0)
-
-        self.pvList = []
-        self.editHide = False
-        self.toggleEdit()
-       
-        self.addMonitor()
-
-       # self.resize(self.monitorLabel.frameGeometry().width()+50, self.monitorLabel.frameGeometry().height()+10)
-        self.layout.addStretch(1)
-        self.setWindowTitle("Epics Widget")
-
-
-
-    def generateList(self, updateMonitor=True):
-        if updateMonitor:
-            self.removeMonitor()
-
-        if len(self.pvInputList):
-            for item in self.pvInputList:
-                for q in item:
-                    self.editInnerGroupBoxLayout.removeWidget(q)
-                    q.deleteLater()
-            self.pvInputList = []
-            for q in self.pvInputAdd:
-                self.editInnerGroupBoxLayout.removeWidget(q)
-                q.deleteLater()
-
-        
-        i = 0
-        for item in epicsConfig.getKey('epics_log_entry_pvs'): #config.epics_log_entry_pvs:
-            self.pvInputList.append([self.createInput(item[0]), self.createInput(item[1],width=300), self.createSwitch(item[2]=="True"), 
-                                    self.createSwitch(item[3]=="True"), self.createLabel("value"), 
-                                    self.createLabel(image=QtGui.QPixmap(config.icon_path('open-trash-can.png')), click=True, connect=partial(self.remove,i))
-                                    ])
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][0], i+1, 0)
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][1], i+1, 1)
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][2], i+1, 2)
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][3], i+1, 3)
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][4], i+1, 4)
-            self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][5], i+1, 5)
-            i = i+1
-
-
-        self.pvInputAdd = [ self.createInput('name?'), self.createInput("?",width=300), 
-                            self.createSwitch(True), self.createSwitch(False),self.createButton("add",connect=self.add)]
-        self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[0], i+1, 0)
-        self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[1], i+1, 1)
-        self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[2], i+1, 2)
-        self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[3], i+1, 3)
-        self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[4], i+1, 4)
-
-        if updateMonitor:
-            self.addMonitor()
-
-        
-
-    def closeEvent(self, event):
-        global __widget_id__
-        __widget_id__ = None
-        self.removeMonitor()
-        del self.par.widgets[self.id]
-
-    def toggleEdit(self):
-        if self.editHide:            
-        #   self.resize(800, 800)
-            self.editOuterGroupBox.show()
-        else:
-         #   self.resize(self.monitorLabel.frameGeometry().width()+10, self.monitorLabel.frameGeometry().height()+50)
-            self.editOuterGroupBox.hide()
-
-        self.editHide = not self.editHide
-        #self.update()
-        
-
-    def readValues(self):
-        for item in self.pvInputList:
-            try:
-                val = str(epics.caget(str(item[1].text())))
-            except:
-                val = "ERROR"
-
-            try:
-                val = "{0:.5f}".format(float(val))
-            except:
-                pass
-
-            item[4].setText(val)
-
-
-    def add(self):
-        epicsConfig.addPV(self.pvInputAdd[0].text(), self.pvInputAdd[1].text(), self.pvInputAdd[2].state, self.pvInputAdd[3].state)
-        self.generateList()
-        self.readValues()
-
-    def remove(self, n):
-        epicsConfig.removePV(n)
-        self.generateList()
-        self.readValues()
-        
-    def restore(self):
-        self.generateList()
-        self.readValues()
-
-    def load(self):
-        epicsConfig.loadConfig()
-        self.generateList()
-        self.readValues()
-
-    def save(self):
-        self.apply()
-        epicsConfig.saveConfig()
-
-    def apply(self):
-        pvList = []
-        for item in self.pvInputList:
-            pvList.append((item[0].text(), item[1].text(), item[2].state, item[3].state))
-        epicsConfig.setPVList(pvList)
-        self.generateList()
-        self.readValues()
-
-
-
-    def monitorPV(self, pvname=None, value=None, char_value=None, **kw):
-        self.monitorPVList[str(pvname)] = [str(pvname), char_value]
-        self.updateMonitor()
-
-    def updateMonitor(self):
-        string = "Monitor\n"
-        for item in self.monitorPVList:
-            string += str(self.monitorPVList[item][0]) + " = " + str(self.monitorPVList[item][1]) + "\n"
-        self.monitorLabel.setText(string)
-
-    def addMonitor(self):
-        if len(self.pvList):
-            self.removeMonitor()
-        for item in epicsConfig.getKey('epics_log_entry_pvs'):
-            if item[3] == 'True': 
-                try:
-                    if epics.caget(item[1]) != None:
-                        self.pvList.append(item[1])
-                        epics.camonitor(item[1], callback=self.monitorPV)
-                        self.monitorPV(item[1], char_value=epics.caget(item[1]))
-                except:
-                    print('add Failed', item[1])
-                    pass
-
-    def removeMonitor(self):
-        for item in self.pvList:
-            try:
-                epics.camonitor_clear(item)
-            except:
-                print('remove Failed', item)
-                pass
-
-        self.pvList = []
-        self.monitorPVList = {}
-
-
-
-
-
-
-
-def addEpicsWidget():
-    global __widget_id__
-    if __widget_id__:
-        global_objects.get_global('area').widgets[__widget_id__].setFocus()
-    else:
-        nid = kcgw.idg.genid()
-        __widget_id__ = nid
-        w = EpicsWidget(nid, global_objects.get_global('area'))
-        global_objects.get_global('area').newWidget(w, "Epics Widget", nid, widget_type=4)
-
-kcgw.register_widget(QtGui.QIcon(config.icon_path("sproject.svg")), "Epics Widget", addEpicsWidget, "Ctrl+e")

+ 47 - 47
KCG/widgets/example_widget.py

@@ -1,47 +1,47 @@
-from PyQt4 import QtGui
-
-
-from ..base import kcgwidget as kcgw
-from ..base.globals import glob as global_objects
-
-__widget_id__ = None
-
-
-class exampleWidget(kcgw.KCGWidgets):
-    def __init__(self, unique_id, parent):
-        super(exampleWidget, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-
-        self.button1 = self.createButton("Button 1", connect=self.pressed)
-        self.button2 = self.createButton("Button 2", connect=self.pressed)
-
-        self.layout.addWidget(self.button1)
-        self.layout.addWidget(self.button2)
-
-        self.setWindowTitle("Example Widget")
-
-    def pressed(self):
-        print('Pressed')
-
-    def closeEvent(self, event):
-        global __widget_id__
-        __widget_id__ = None
-        del self.par.widgets[self.id]
-
-
-def addExampleWidget():
-    global __widget_id__
-    if __widget_id__:
-        global_objects.get_global('area').widgets[__widget_id__].setFocus()
-    else:
-        nid = kcgw.idg.genid()
-        __widget_id__ = nid
-        w = exampleWidget(nid, global_objects.get_global('area'))
-        global_objects.get_global('area').newWidget(w, "Example Widget", nid, widget_type=4)
-
-kcgw.register_widget(QtGui.QIcon(config.icon_path("sproject.svg")), "Example Widget", addExampleWidget, "Ctrl+e")
+from PyQt4 import QtGui
+
+
+from ..base import kcgwidget as kcgw
+from ..base.globals import glob as global_objects
+
+__widget_id__ = None
+
+
+class exampleWidget(kcgw.KCGWidgets):
+    def __init__(self, unique_id, parent):
+        super(exampleWidget, self).__init__()
+
+        self.id = unique_id
+        self.par = parent
+
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+
+        self.button1 = self.createButton("Button 1", connect=self.pressed)
+        self.button2 = self.createButton("Button 2", connect=self.pressed)
+
+        self.layout.addWidget(self.button1)
+        self.layout.addWidget(self.button2)
+
+        self.setWindowTitle("Example Widget")
+
+    def pressed(self):
+        print ('Pressed')
+
+    def closeEvent(self, event):
+        global __widget_id__
+        __widget_id__ = None
+        del self.par.widgets[self.id]
+
+
+def addExampleWidget():
+    global __widget_id__
+    if __widget_id__:
+        global_objects.get_global('area').widgets[__widget_id__].setFocus()
+    else:
+        nid = kcgw.idg.genid()
+        __widget_id__ = nid
+        w = exampleWidget(nid, global_objects.get_global('area'))
+        global_objects.get_global('area').newWidget(w, "Example Widget", nid, widget_type=4)
+
+kcgw.register_widget(QtGui.QIcon("icons/project.svg"), "Example Widget", addExampleWidget, "Ctrl+e")

+ 348 - 363
KCG/widgets/initialconfig.py

@@ -1,363 +1,348 @@
-"""
-This defines the window for initial configuration
-"""
-from PyQt4 import QtGui, QtCore
-from ..base import kcgwidget as kcgw
-import os
-from .. import config
-try:
-    from pprint import pformat
-except ImportError:
-    def pformat(string, width=None):
-        return string
-
-#  .d8888b.                  .d888d8b        8888888888        888
-# d88P  Y88b                d88P" Y8P        888               888
-# 888    888                888              888               888
-# 888        .d88b. 88888b. 888888888 .d88b. 8888888   88888b. 888888888d888888  888
-# 888       d88""88b888 "88b888   888d88P"88b888       888 "88b888   888P"  888  888
-# 888    888888  888888  888888   888888  888888       888  888888   888    888  888
-# Y88b  d88PY88..88P888  888888   888Y88b 888888       888  888Y88b. 888    Y88b 888
-#  "Y8888P"  "Y88P" 888  888888   888 "Y888888888888888888  888 "Y888888     "Y88888
-#                                         888                                    888
-#                                    Y8b d88P                               Y8b d88P
-#                                     "Y88P"                                 "Y88P"
-
-class ConfigEntry(kcgw.KCGWidgets):
-    """
-    Container for a single entry in the init config window
-    """
-    def __init__(self, comm='', conf='', type=str, multiline=False):
-        """
-        Initialise an entry
-        :param comm: comment
-        :param conf: configuration option
-        :param type: data type for this option
-        :param multiline: if multiline editfield or not
-        :return: -
-        """
-        super(ConfigEntry, self).__init__()
-        self.layout = QtGui.QVBoxLayout()
-        self.entryLayout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.toggle = self.createButton("?", connect=self.toggle)
-        self.toggle.setFixedWidth(20)
-
-        if len(comm.split("\n")) <= 4:
-            self.comment = self.createLabel(comm)
-            self.comment.setStyleSheet("color: grey;")
-            self.comment.setAlignment(QtCore.Qt.AlignRight)
-
-        else:
-            self.commentText = self.createLabel(comm)
-            self.commentText.setStyleSheet("color: grey;")
-            self.commentText.setAlignment(QtCore.Qt.AlignRight)
-
-            self.commentWidget = QtGui.QWidget()
-            self.comment = QtGui.QScrollArea()
-            self.comment.setAlignment(QtCore.Qt.AlignRight)
-            self.commlayout = QtGui.QVBoxLayout()
-            self.commentWidget.setLayout(self.commlayout)
-            self.commlayout.addWidget(self.commentText, alignment=QtCore.Qt.AlignRight)
-            self.comment.setWidget(self.commentWidget)
-            self.comment.text = lambda: self.commentText.text()
-
-        self.config = self.createLabel(conf)
-        self.config.setFixedWidth(200)
-        self.multiline = multiline
-        self.type = type
-        if not multiline:
-            self.value = self.createInput(width=300)
-        else:
-            self.value = QtGui.QTextEdit()
-            self.value.setFixedWidth(500)
-        try:
-            self.value.setText(pformat(getattr(config, conf), width=60, indent=4))
-        except AttributeError:
-            pass
-        self.layout.addWidget(self.comment)
-        self.layout.addLayout(self.entryLayout)
-        self.entryLayout.addWidget(self.config)
-        self.entryLayout.addWidget(self.value)
-        self.entryLayout.addWidget(self.toggle)
-        self.comment.hide()
-
-    def toggle(self):
-        """
-        Toggle the visibility of the comment
-        :return:
-        """
-        self.comment.setHidden(not self.comment.isHidden())
-
-    def set_comment(self, comm):
-        """
-        Set the comment
-        :param comm:
-        :return:
-        """
-        self.comment.setText(comm)
-
-    def set_config(self, conf):
-        """
-        Set the config option
-        :param conf:
-        :return:
-        """
-        self.config.setText(conf)
-
-    def validate(self, mark=False):
-        """
-        Validate if entered value is valid
-        :param mark: mark false entered values?
-        :return: -
-        """
-        if not self.value.isEnabled():
-            return True
-        if self.multiline:
-            try:
-                v, e = self.san(str(self.value.toPlainText()))
-                if e:
-                    self.value.setText(v)
-                if type(config.leval(v)) == self.type:
-                    self.value.setStyleSheet("background-color: #FFFFFF;")
-                    return True
-            except (ValueError, config.NoValueException, SyntaxError):
-                pass
-        else:
-            try:
-                v, e = self.san(str(self.value.text()))
-                if e:
-                    self.value.setText(v)
-                if type(config.leval(v)) == self.type:
-                    self.value.setStyleSheet("background-color: #FFFFFF;")
-                    return True
-            except (ValueError, config.NoValueException, SyntaxError):
-                pass
-        if mark:
-            self.value.setStyleSheet("background-color: #FFAAAA;")
-        return False
-
-    def san(self, val):
-        """
-        Sanitise string entries (adding " or ' and converting mixed ones)
-        :param val: the value to sanitise
-        :return: sanitised value and errors
-        """
-        print(val)
-        err = False
-        if self.type == str:
-
-            val.replace('"', '\'')
-            if len(val) == 0:
-                return "", False
-            if val[0] == "'":
-                if not val[-1] == "'":
-                    val += "'"
-                    err = True
-            elif val[-1] == "'":
-                val = "'" + val
-                err = True
-            else:
-                val = '"' + val + '"'
-                err = False
-        return val, err
-
-    def __str__(self):
-        """
-        Convert this entry to a string that can be saved to a configuration file
-        :return:
-        """
-        comment = ''
-        for line in str(self.comment.text()).split('\n'):
-            comment += "# " + line + '\n'
-        if self.value.isEnabled():
-            if self.multiline:
-                val = self.value.toPlainText()
-            else:
-                val = self.value.text()
-        else:
-            val = "'\"disabled\"'"
-
-        val, e = self.san(val)
-
-        return str(comment + self.config.text() + " = " + str(val))
-
-
-#  .d8888b.                  .d888d8b         .d8888b.         888
-# d88P  Y88b                d88P" Y8P        d88P  Y88b        888
-# 888    888                888              Y88b.             888
-# 888        .d88b. 88888b. 888888888 .d88b.  "Y888b.   .d88b. 888888888  88888888b.
-# 888       d88""88b888 "88b888   888d88P"88b    "Y88b.d8P  Y8b888   888  888888 "88b
-# 888    888888  888888  888888   888888  888      "88888888888888   888  888888  888
-# Y88b  d88PY88..88P888  888888   888Y88b 888Y88b  d88PY8b.    Y88b. Y88b 888888 d88P
-#  "Y8888P"  "Y88P" 888  888888   888 "Y88888 "Y8888P"  "Y8888  "Y888 "Y8888888888P"
-#                                         888                                888
-#                                    Y8b d88P                                888
-#                                     "Y88P"                                 888
-
-class ConfigSetup(kcgw.KCGWidgets):
-    """
-    This is the Config window
-    """
-    success_signal = QtCore.pyqtSignal()
-    machine_comments = ["Bunches per turn of accelerator (integer value)",
-                        "Save headerinformation in file (bool value[True/False])",
-                        "Revolution time (double value in s eg 3.6825476109875985e-07)"
-                        ]
-    ui_comments = ['NOTE: This value will be overwritten when the language is changed in the gui-settings\n'
-                        'possible languages:\n'
-                        '"en_GB" - English\n'
-                        '"de_DE" - German',
-                   'default_save_location: use "pwd" for current working\n'
-                        'directory KCG will always save in a subdirectory to\n'
-                        'this given path and save files in this directory',
-                   'default_subdirectory_name_format: this is the\n'
-                        'naming scheme for the subdirectory in which the\n'
-                        'files are saved. Format of this string:\n'
-                        '"{tag1}text{tag2}text" etc.\n'
-                        'possible tags:\n'
-                        '{dateG} will produce e.g. 04.01.2015\n'
-                        '{dateGd} will produce e.g. 04_01_2015\n'
-                        '{dateA} will pdoduce e.g. 01-04-2015\n'
-                        '{times} will produce e.g. 14_04\n'
-                        '{timel} will produce e.g. 14_04_12\n'
-                        '{d} the Day in 2 digit format\n'
-                        '{m} the Month in 2 digit format\n'
-                        '{y} the Year in 4 digit Format\n'
-                        '{H} the Hour in 2 digit format\n'
-                        '{M} the minute in 2 digit format\n'
-                        '{S} the seconds in 2 digit format\n'
-                        '{timestamp} unix timestamp without msec\n'
-                        '{user} the current logged in user\n'
-                        '{sessionname} Ask for session name at startup\n'
-                        '{ask} always ask for a foldername',
-                   'reask on cancel in dialog or use {user}_{dateGd}-{timel} as default when cancel is pressed?',
-                   'Show advanced table view per default? (boolean value)'
-                ]
-    logging_comments = ['These are PVs that will be possible to insert into log files\n'
-                            'This variable is to be a list consisting of touples of two entries,\n'
-                            'the first ist the Text that describes the value and the second is the EPICS PV that\n'
-                            'holds that value',
-                       'This pv is used to determine if epics pvs are accessible' ,
-                       'Path to your epics base installation',
-                       'List of Entries that are default to save in Log\n'
-                           'Possible Values are:\n'
-                           '"Number of Turns" \n'
-                           '"Number of Skipped Turns" \n'
-                           '"Number of Acquisitions" \n'
-                           '"Time between Acquisitions" \n'
-                           '"Pilot Bunch Simulator" \n'
-                           '"Header saved" \n'
-                           '"T/H Delay" \n'
-                           '"ADC 1 Delay" \n'
-                           '"ADC 2 Delay" \n'
-                           '"ADC 3 Delay" \n'
-                           '"ADC 4 Delay" \n'
-                           'All of the description text entries in epics_log_entry_pvs, see above \n'
-                           'NOTE: These entries have to match the aforementioned strings exactly'
-                    ]
-    machine_configs = ['bunches_per_turn',
-                       'save_header',
-                       'tRev']
-    ui_configs = ['language',
-                  'default_save_location',
-                  'default_subdirectory_name',
-                  'force_ask',
-                  'show_advanced_control']
-    logging_configs = ['epics_log_entry_pvs',
-                       'epics_test_pv',
-                       'epics_base_path',
-                       'default_log_entries']
-    machine_types = [int, bool, float]
-    ui_types = [str, str, str, bool, bool]
-    logging_types = [list, str, str, list]
-
-    def __init__(self, restart=False):
-        """
-        Initialise this config window
-        :return:
-        """
-        super(ConfigSetup, self).__init__()
-        self.restart = restart
-        self.configs = []
-        self.result = False
-        self.resize(800, 600)
-        self.mainlayout = QtGui.QVBoxLayout()
-        # self.textEdit = QtGui.QTextEdit()
-        # self.layout.addWidget(self.textEdit)
-        # with open(os.path.dirname(__file__)+'/../config.cfg', 'r') as f:
-        #     self.textEdit.setText(f.read())
-        self.setLayout(self.mainlayout)
-        self.scrollWidget = QtGui.QScrollArea()
-        self.heading = self.createLabel("Initial Configuration Setup")
-        self.heading.setStyleSheet("font-size:25pt;")
-        self.mainlayout.addWidget(self.heading)
-        self.mainlayout.addWidget(self.scrollWidget)
-        add = " and restart" if self.restart else ""
-        self.saveButton = self.createButton("save"+add, connect=self.save)
-        self.mainlayout.addWidget(self.saveButton)
-
-        self.wid = QtGui.QWidget()
-        self.layout = QtGui.QVBoxLayout()
-        self.wid.setLayout(self.layout)
-
-        self.machine_label = self.createLabel("Machine")
-        self.machine_label.setStyleSheet("font-size: 20pt;")
-        self.layout.addWidget(self.machine_label)
-        self.configs.append("\n[Machine]")
-        for comm, conf, type in zip(self.machine_comments, self.machine_configs, self.machine_types):
-            self.configs.append(ConfigEntry(comm, conf, type))
-            self.layout.addWidget(self.configs[-1])
-
-
-        self.ui_label = self.createLabel("Ui")
-        self.ui_label.setStyleSheet("font-size: 20pt;")
-        self.layout.addWidget(self.ui_label)
-        self.configs.append("\n[Ui]")
-        for comm, conf, type in zip(self.ui_comments, self.ui_configs, self.ui_types):
-            self.configs.append(ConfigEntry(comm, conf, type))
-            self.layout.addWidget(self.configs[-1])
-
-        self.logging_label = self.createLabel("Logging")
-        self.logging_label.setStyleSheet("font-size:20pt;")
-        self.layout.addWidget(self.logging_label)
-        
-        self.configs.append("\n[Logging]")
-        self.logging_entries = []
-        for comm, conf, type in zip(self.logging_comments, self.logging_configs, self.logging_types):
-            if conf == 'epics_log_entry_pvs' or conf == 'default_log_entries':
-                self.configs.append(ConfigEntry(comm, conf, type, multiline=True))
-            else:
-                self.configs.append(ConfigEntry(comm, conf, type))
-            self.layout.addWidget(self.configs[-1])
-            self.logging_entries.append(self.configs[-1])
-
-        self.scrollWidget.setWidget(self.wid)
-       
-    def save(self):
-        """
-        Save the configuration entered in the config window to a configuration file
-        namely: os.path.expanduser("~")+"/.kcg/config.cfg"
-        This also makes shure the entered information is valid to some level
-        :return:
-        """
-        if not os.path.isdir(os.path.join(os.path.expanduser("~"),".kcg")):
-            os.mkdir(os.path.join(os.path.expanduser("~"),".kcg"))
-
-        abort = False
-        for item in self.configs:
-            if type(item) == str:
-                continue
-            if not item.validate(mark=True):
-                abort = True
-        if abort:
-            return
-
-        with open(os.path.join(os.path.expanduser("~"),'.kcg','config.cfg', 'w+')) as f:
-            for item in self.configs:
-                f.write(str(item)+'\n')
-        self.result = True
-        self.success_signal.emit()
-        self.close()
-
-
+"""
+This defines the window for initial configuration
+"""
+from PyQt4 import QtGui, QtCore
+from ..base import kcgwidget as kcgw
+import os
+from .. import config
+try:
+    from pprint import pformat
+except ImportError:
+    def pformat(string, width=None):
+        return string
+
+class ConfigEntry(kcgw.KCGWidgets):
+    """
+    Container for a single entry in the init config window
+    """
+    def __init__(self, comm='', conf='', type=str, multiline=False):
+        """
+        Initialise an entry
+        :param comm: comment
+        :param conf: configuration option
+        :param type: data type for this option
+        :param multiline: if multiline editfield or not
+        :return: -
+        """
+        super(ConfigEntry, self).__init__()
+        self.layout = QtGui.QVBoxLayout()
+        self.entryLayout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.toggle = self.createButton("?", connect=self.toggle)
+        self.toggle.setFixedWidth(20)
+
+        if len(comm.split("\n")) <= 4:
+            self.comment = self.createLabel(comm)
+            self.comment.setStyleSheet("color: grey;")
+            self.comment.setAlignment(QtCore.Qt.AlignRight)
+
+        else:
+            self.commentText = self.createLabel(comm)
+            self.commentText.setStyleSheet("color: grey;")
+            self.commentText.setAlignment(QtCore.Qt.AlignRight)
+
+            self.commentWidget = QtGui.QWidget()
+            self.comment = QtGui.QScrollArea()
+            self.comment.setAlignment(QtCore.Qt.AlignRight)
+            self.commlayout = QtGui.QVBoxLayout()
+            self.commentWidget.setLayout(self.commlayout)
+            self.commlayout.addWidget(self.commentText, alignment=QtCore.Qt.AlignRight)
+            self.comment.setWidget(self.commentWidget)
+            self.comment.text = lambda: self.commentText.text()
+
+        self.config = self.createLabel(conf)
+        self.config.setFixedWidth(200)
+        self.multiline = multiline
+        self.type = type
+        if not multiline:
+            self.value = self.createInput(width=300)
+        else:
+            self.value = QtGui.QTextEdit()
+            self.value.setFixedWidth(500)
+        try:
+            self.value.setText(pformat(getattr(config, conf), width=60, indent=4))
+        except AttributeError:
+            pass
+        self.layout.addWidget(self.comment)
+        self.layout.addLayout(self.entryLayout)
+        self.entryLayout.addWidget(self.config)
+        self.entryLayout.addWidget(self.value)
+        self.entryLayout.addWidget(self.toggle)
+        self.comment.hide()
+
+    def toggle(self):
+        """
+        Toggle the visibility of the comment
+        :return:
+        """
+        self.comment.setHidden(not self.comment.isHidden())
+
+    def set_comment(self, comm):
+        """
+        Set the comment
+        :param comm:
+        :return:
+        """
+        self.comment.setText(comm)
+
+    def set_config(self, conf):
+        """
+        Set the config option
+        :param conf:
+        :return:
+        """
+        self.config.setText(conf)
+
+    def validate(self, mark=False):
+        """
+        Validate if entered value is valid
+        :param mark: mark false entered values?
+        :return: -
+        """
+        if not self.value.isEnabled():
+            return True
+        if self.multiline:
+            try:
+                v, e = self.san(str(self.value.toPlainText()))
+                if e:
+                    self.value.setText(v)
+                if type(config.leval(v)) == self.type:
+                    self.value.setStyleSheet("background-color: #FFFFFF;")
+                    return True
+            except (ValueError, config.NoValueException, SyntaxError):
+                pass
+        else:
+            try:
+                v, e = self.san(str(self.value.text()))
+                if e:
+                    self.value.setText(v)
+                if type(config.leval(v)) == self.type:
+                    self.value.setStyleSheet("background-color: #FFFFFF;")
+                    return True
+            except (ValueError, config.NoValueException, SyntaxError):
+                pass
+        if mark:
+            self.value.setStyleSheet("background-color: #FFAAAA;")
+        return False
+
+    def san(self, val):
+        """
+        Sanitise string entries (adding " or ' and converting mixed ones)
+        :param val: the value to sanitise
+        :return: sanitised value and errors
+        """
+        err = False
+        if self.type == str:
+            val.replace('"', '\'')
+            if len(val) == 0:
+                return "", False
+            if val[0] == "'":
+                if not val[-1] == "'":
+                    val += "'"
+                    err = True
+            elif val[-1] == "'":
+                val = "'" + val
+                err = True
+            else:
+                val = '"' + val + '"'
+                err = False
+        return val, err
+
+    def __str__(self):
+        """
+        Convert this entry to a string that can be saved to a configuration file
+        :return:
+        """
+        comment = ''
+        for line in str(self.comment.text()).split('\n'):
+            comment += "# " + line + '\n'
+        if self.value.isEnabled():
+            if self.multiline:
+                val = self.value.toPlainText()
+            else:
+                val = self.value.text()
+        else:
+            val = "'\"disabled\"'"
+
+        val, e = self.san(val)
+
+        return str(comment + self.config.text() + " = " + str(val))
+
+
+class ConfigSetup(kcgw.KCGWidgets):
+    """
+    This is the Config window
+    """
+    success_signal = QtCore.pyqtSignal()
+    machine_comments = ["Bunches per turn of accelerator (integer value)",
+                        "Save headerinformation in file (bool value)",
+                        "Revolution time (double value)"
+                        ]
+    ui_comments = ['NOTE: This value will be overwritten when the language is changed in the gui-settings\n'
+                        'possible languages:\n'
+                        '"en_GB" - English\n'
+                        '"de_DE" - German',
+                   'default_save_location: use "pwd" for current working\n'
+                        'directory KCG will always save in a subdirectory to\n'
+                        'this given path and save files in this directory',
+                   'default_subdirectory_name_format: this is the\n'
+                        'naming scheme for the subdirectory in which the\n'
+                        'files are saved. Format of this string:\n'
+                        '"{tag1}text{tag2}text" etc.\n'
+                        'possible tags:\n'
+                        '{dateG} will produce e.g. 04.01.2015\n'
+                        '{dateGd} will produce e.g. 04_01_2015\n'
+                        '{dateA} will pdoduce e.g. 01-04-2015\n'
+                        '{times} will produce e.g. 14_04\n'
+                        '{timel} will produce e.g. 14_04_12\n'
+                        '{d} the Day in 2 digit format\n'
+                        '{m} the Month in 2 digit format\n'
+                        '{y} the Year in 4 digit Format\n'
+                        '{H} the Hour in 2 digit format\n'
+                        '{M} the minute in 2 digit format\n'
+                        '{S} the seconds in 2 digit format\n'
+                        '{timestamp} unix timestamp without msec\n'
+                        '{user} the current logged in user\n'
+                        '{sessionname} Ask for session name at startup\n'
+                        '{ask} always ask for a foldername',
+                   'reask on cancel in dialog or use {user}_{dateGd}-{timel} as default when cancel is pressed?',
+                   'Show advanced table view per default? (boolean value)'
+                ]
+    logging_comments = ['These are PVs that will be possible to insert into log files\n'
+                            'This variable is to be a list consisting of touples of two entries,\n'
+                            'the first ist the Text that describes the value and the second is the EPICS PV that\n'
+                            'holds that value',
+                       'This pv is used to determine if epics pvs are accessible' ,
+                       'Path to your epics base installation',
+                       'List of Entries that are default to save in Log\n'
+                           'Possible Values are:\n'
+                           '"Number of Orbits" \n'
+                           '"Number of Skipped Orbits" \n'
+                           '"Number of Acquisitions" \n'
+                           '"Time between Acquisitions" \n'
+                           '"Pilot Bunch Simulator" \n'
+                           '"Header saved" \n'
+                           '"T/H Delay" \n'
+                           '"ADC 1 Delay" \n'
+                           '"ADC 2 Delay" \n'
+                           '"ADC 3 Delay" \n'
+                           '"ADC 4 Delay" \n'
+                           'All of the description text entries in epics_log_entry_pvs, see above \n'
+                           'NOTE: These entries have to match the aforementioned strings exactly'
+                    ]
+    machine_configs = ['bunches_per_turn',
+                       'save_header',
+                       'tRev']
+    ui_configs = ['language',
+                  'default_save_location',
+                  'default_subdirectory_name',
+                  'force_ask',
+                  'show_advanced_control']
+    logging_configs = ['epics_log_entry_pvs',
+                       'epics_test_pv',
+                       'epics_base_path',
+                       'default_log_entries']
+    machine_types = [int, bool, float]
+    ui_types = [str, str, str, bool, bool]
+    logging_types = [list, str, str, list]
+
+    def __init__(self, restart=False):
+        """
+        Initialise this config window
+        :return:
+        """
+        super(ConfigSetup, self).__init__()
+        self.restart = restart
+        self.configs = []
+        self.result = False
+        self.resize(800, 600)
+        self.mainlayout = QtGui.QVBoxLayout()
+        # self.textEdit = QtGui.QTextEdit()
+        # self.layout.addWidget(self.textEdit)
+        # with open(os.path.dirname(__file__)+'/../config.cfg', 'r') as f:
+        #     self.textEdit.setText(f.read())
+        self.setLayout(self.mainlayout)
+        self.scrollWidget = QtGui.QScrollArea()
+        self.heading = self.createLabel("Initial Configuration Setup")
+        self.heading.setStyleSheet("font-size:25pt;")
+        self.mainlayout.addWidget(self.heading)
+        self.mainlayout.addWidget(self.scrollWidget)
+        add = " and restart" if self.restart else ""
+        self.saveButton = self.createButton("save"+add, connect=self.save)
+        self.mainlayout.addWidget(self.saveButton)
+
+        self.wid = QtGui.QWidget()
+        self.layout = QtGui.QVBoxLayout()
+        self.wid.setLayout(self.layout)
+
+        self.machine_label = self.createLabel("Machine")
+        self.machine_label.setStyleSheet("font-size: 20pt;")
+        self.layout.addWidget(self.machine_label)
+        self.configs.append("\n[Machine]")
+        for comm, conf, type in zip(self.machine_comments, self.machine_configs, self.machine_types):
+            self.configs.append(ConfigEntry(comm, conf, type))
+            self.layout.addWidget(self.configs[-1])
+
+
+        self.ui_label = self.createLabel("Ui")
+        self.ui_label.setStyleSheet("font-size: 20pt;")
+        self.layout.addWidget(self.ui_label)
+        self.configs.append("\n[Ui]")
+        for comm, conf, type in zip(self.ui_comments, self.ui_configs, self.ui_types):
+            self.configs.append(ConfigEntry(comm, conf, type))
+            self.layout.addWidget(self.configs[-1])
+
+        self.logging_label = self.createLabel("Logging")
+        self.logging_label.setStyleSheet("font-size:20pt;")
+        self.layout.addWidget(self.logging_label)
+        self.disable_logging = self.createCheckbox("Disable Epics", connect=self.disable_epics)
+        self.layout.addWidget(self.disable_logging)
+        
+        self.configs.append("\n[Logging]")
+        self.logging_entries = []
+        for comm, conf, type in zip(self.logging_comments, self.logging_configs, self.logging_types):
+            if conf == 'epics_log_entry_pvs' or conf == 'default_log_entries':
+                self.configs.append(ConfigEntry(comm, conf, type, multiline=True))
+            else:
+                self.configs.append(ConfigEntry(comm, conf, type))
+            self.layout.addWidget(self.configs[-1])
+            self.logging_entries.append(self.configs[-1])
+
+        self.scrollWidget.setWidget(self.wid)
+        try:
+            import epics
+        except ImportError:
+            self.disable_epics()
+            self.disable_logging.setChecked(True)
+
+    def disable_epics(self, state=True):
+        for le in self.logging_entries:
+            le.value.setEnabled(not state)
+
+    def save(self):
+        """
+        Save the configuration entered in the config window to a configuration file
+        namely: os.path.expanduser("~")+"/.kcg/config.cfg"
+        This also makes shure the entered information is valid to some level
+        :return:
+        """
+        if not os.path.isdir(os.path.expanduser("~")+"/.kcg"):
+            os.mkdir(os.path.expanduser("~")+"/.kcg")
+
+        abort = False
+        for item in self.configs:
+            if type(item) == str:
+                continue
+            if not item.validate(mark=True):
+                abort = True
+        if abort:
+            return
+
+        with open(os.path.expanduser("~")+'/.kcg/config.cfg', 'w+') as f:
+            for item in self.configs:
+                f.write(str(item)+'\n')
+        self.result = True
+        self.success_signal.emit()
+        self.close()
+
+

+ 178 - 178
KCG/widgets/singleread.py

@@ -1,178 +1,178 @@
-# -*- coding: utf-8 -*-
-"""
-This Module is the SingleRead window.
-"""
-from PyQt4 import QtGui
-
-from ..base import kcgwidget as kcgw
-from ..base import backendinterface as bif
-from ..base.groupedelements import Elements
-from ..base.backend.board import available_boards
-from ..base.storage import storage
-from ..base.globals import glob as global_objects
-from .. import config
-
-
-tr = kcgw.tr
-
-__widget_id__ = None
-
-
-class SingleReadWidget(kcgw.KCGWidgets):
-    """
-    The actual single read window.
-    """
-    def __init__(self, unique_id, parent=None):
-        """
-        Initialise the single read widget
-        :param unique_id: the id for this widget
-        :param parent: parent object
-        :return: -
-        """
-        super(SingleReadWidget, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-        self.layout = QtGui.QGridLayout()
-        self.outerLayout = QtGui.QVBoxLayout()
-
-        self.setLayout(self.outerLayout)
-
-        # ---------[ Create Labels and corresponding Fields ]---------
-        self.single_read_button = self.createButton(tr("Button", "Single Read"), connect=self.on_single_read)
-        self.interval_spinbox = self.createSpinbox(0, 100000, start_value=1000)
-        self.interval_spinbox.valueChanged.connect(self.set_interval)
-
-        self.board_ticks = {board_id: self.createCheckbox(str(board_id), checked=True if board_id==0 else False) for board_id in available_boards}
-        self.board_ticks_continuous_read = {
-            board_id: self.createCheckbox(str(board_id),
-                                          connect=lambda state, b_id=board_id:
-                                          self.on_continuous_read(state, board_id=b_id))
-            for board_id in available_boards
-            }
-
-        for board_id, tick in list(self.board_ticks_continuous_read.items()):
-            Elements.addItem('acquireTrigger_{}'.format(board_id), tick)
-            Elements.addItem(["acquire_{}".format(board_id), "no_board_{}".format(board_id)], tick)
-            Elements.addItem('continuous_read_{}'.format(board_id), tick)
-        for board_id, tick in self.board_ticks.items():
-            Elements.addItem('acquireTrigger_{}'.format(board_id), tick)
-            Elements.addItem(["acquire_{}".format(board_id), "no_board_{}".format(board_id)], tick)
-
-        # --------[ Fill Grid ]----------------
-        self.layout.addWidget(self.single_read_button, 0, 1)
-        # self.layout.addWidget(self.continuous_read_button, 0, 1)
-        self.layout.addWidget(self.createLabel(tr("Label", "Interval:") + " (ms)"), 1, 0)
-        self.layout.addWidget(self.interval_spinbox, 1, 1)
-        self.setWindowTitle(tr("Heading", "Single and Continuous Read"))
-
-        self.tickLayout = QtGui.QHBoxLayout()
-        self.tickLayout.addWidget(self.createLabel(
-                tr("Label", "Boards"), tooltip=tr("Tooltip", "Check the board you want to select for single read"))
-        )
-        self.tickLayout.addStretch(1)
-        for tick in list(self.board_ticks.values()):
-            self.tickLayout.addWidget(tick)
-        self.tickLayoutContinuousRead = QtGui.QHBoxLayout()
-        self.tickLayoutContinuousRead.addWidget(self.createLabel(
-                tr("Label", "Continuous Read"),
-                tooltip=tr("Tooltip",
-                           "Tick the board you want to continuously read."
-                           "\nThe selected boards will continue reading until the checkbox is unticked.")
-                )
-        )
-        self.tickLayoutContinuousRead.addStretch(1)
-        for tick in list(self.board_ticks_continuous_read.values()):
-            self.tickLayoutContinuousRead.addWidget(tick)
-        self.outerLayout.addLayout(self.tickLayoutContinuousRead)
-        self.outerLayout.addLayout(self.tickLayout)
-        self.outerLayout.addLayout(self.layout)
-#        self.outerLayout.addStretch(1)
-
-        self.set_interval()
-
-    def on_single_read(self):
-        """
-        Perform a single read on all boards which ticks are set
-        :return:
-        """
-        for board_id, tick in self.board_ticks.items():
-            if tick.isChecked() and tick.isEnabled():
-                bif.bk_single_read(board_id)
-
-    def on_continuous_read(self, state, board_id):
-        """
-        Toggle function to toggle continuous read when the corresponding button is pressed.
-        :param bool state: True to activate and False to deactivate continuous read for board_id
-        :param int? board_id: the id of the board to take action with
-        :return: -
-        """
-        # if self.board_ticks_continuous_read[id].isChecked():
-        self.set_interval()
-        if state:
-            # self.continuous_read_button.setText(tr("Button", "Stop Continuous Read"))
-            # self.continuous_read[id] = True
-            self.board_ticks_continuous_read[board_id].setStyleSheet("border-color: green; border-width: 3px;")
-        else:
-            self.board_ticks_continuous_read[board_id].setStyleSheet("")
-        bif.bk_continuous_read(board_id)  # interval=self.interval_spinbox.value())
-
-    def set_interval(self):
-        """
-        Set the interval between reads
-        """
-        storage.continuous_interval = self.interval_spinbox.value()
-
-    def closeEvent(self, event):
-        """
-        Event handler to handle the event of closing this window and gracefully delete ids and such
-        :param event: the event to process (gets passed by PyQt)
-        """
-        global __widget_id__
-        __widget_id__ = None
-        del self.par.widgets[self.id]
-        Elements.removeItem(['no_board', 'acquire'],
-                            [
-                                self.single_read_button,
-                                # self.continuous_read_button
-                            ])
-        for board_id, tick in list(self.board_ticks_continuous_read.items()):
-            Elements.removeItem(
-                [
-                    "continuous_read_{}".format(board_id),
-                    "acquireTrigger_{}".format(board_id),
-                    "acquire_{}".format(board_id),
-                    "no_board_{}".format(board_id)
-                ], tick)
-
-        for board_id, tick in self.board_ticks.items():
-            Elements.removeItem(
-                [
-                    "acquireTrigger_{}".format(board_id),
-                    "acquire_{}".format(board_id),
-                    "no_board_{}".format(board_id)
-                ], tick)
-
-        super(SingleReadWidget, self).closeEvent(event)
-
-
-def add_single_read_widget():
-    """
-    Add this widget to the gui.
-    This function will actually open the subwindow.
-    :return: -
-    """
-    global __widget_id__
-    if __widget_id__:
-        global_objects.get_global('area').widgets[__widget_id__].setFocus()
-    else:
-        nid = kcgw.idg.genid()
-        __widget_id__ = nid
-        w = SingleReadWidget(nid, global_objects.get_global('area'))
-        global_objects.get_global('area').newWidget(w, "Single and Continuous Read", nid, widget_type=4, minSize=True)  # TODO: proper type
-
-kcgw.register_widget(QtGui.QIcon(config.icon_path(config.singleReadIcon)),
-                     tr("Heading", "Single Read"),
-                     add_single_read_widget,
-                     "Ctrl+i"
-                     )
+# -*- coding: utf-8 -*-
+"""
+This Module is the SingleRead window.
+"""
+from PyQt4 import QtGui
+
+from ..base import kcgwidget as kcgw
+from ..base import backendinterface as bif
+from ..base.groupedelements import Elements
+from ..base.backend.board import available_boards
+from ..base.storage import storage
+from ..base.globals import glob as global_objects
+from .. import config
+
+
+tr = kcgw.tr
+
+__widget_id__ = None
+
+
+class SingleReadWidget(kcgw.KCGWidgets):
+    """
+    The actual single read window.
+    """
+    def __init__(self, unique_id, parent=None):
+        """
+        Initialise the single read widget
+        :param unique_id: the id for this widget
+        :param parent: parent object
+        :return: -
+        """
+        super(SingleReadWidget, self).__init__()
+
+        self.id = unique_id
+        self.par = parent
+        self.layout = QtGui.QGridLayout()
+        self.outerLayout = QtGui.QVBoxLayout()
+
+        self.setLayout(self.outerLayout)
+
+        # ---------[ Create Labels and corresponding Fields ]---------
+        self.single_read_button = self.createButton(tr("Button", "Single Read"), connect=self.on_single_read)
+        self.interval_spinbox = self.createSpinbox(0, 100000, start_value=1000)
+        self.interval_spinbox.valueChanged.connect(self.set_interval)
+
+        self.board_ticks = {board_id: self.createCheckbox(str(board_id), checked=True if board_id==0 else False) for board_id in available_boards}
+        self.board_ticks_continuous_read = {
+            board_id: self.createCheckbox(str(board_id),
+                                          connect=lambda state, b_id=board_id:
+                                          self.on_continuous_read(state, board_id=b_id))
+            for board_id in available_boards
+            }
+
+        for board_id, tick in self.board_ticks_continuous_read.items():
+            Elements.addItem('acquireTrigger_{}'.format(board_id), tick)
+            Elements.addItem(["acquire_{}".format(board_id), "no_board_{}".format(board_id)], tick)
+            Elements.addItem('continuous_read_{}'.format(board_id), tick)
+        for board_id, tick in self.board_ticks.iteritems():
+            Elements.addItem('acquireTrigger_{}'.format(board_id), tick)
+            Elements.addItem(["acquire_{}".format(board_id), "no_board_{}".format(board_id)], tick)
+
+        # --------[ Fill Grid ]----------------
+        self.layout.addWidget(self.single_read_button, 0, 1)
+        # self.layout.addWidget(self.continuous_read_button, 0, 1)
+        self.layout.addWidget(self.createLabel(tr("Label", "Interval:") + " (ms)"), 1, 0)
+        self.layout.addWidget(self.interval_spinbox, 1, 1)
+        self.setWindowTitle(tr("Heading", "Single and Continuous Read"))
+
+        self.tickLayout = QtGui.QHBoxLayout()
+        self.tickLayout.addWidget(self.createLabel(
+                tr("Label", "Boards"), tooltip=tr("Tooltip", "Check the board you want to select for single read"))
+        )
+        self.tickLayout.addStretch(1)
+        for tick in self.board_ticks.values():
+            self.tickLayout.addWidget(tick)
+        self.tickLayoutContinuousRead = QtGui.QHBoxLayout()
+        self.tickLayoutContinuousRead.addWidget(self.createLabel(
+                tr("Label", "Continuous Read"),
+                tooltip=tr("Tooltip",
+                           "Tick the board you want to continuously read."
+                           "\nThe selected boards will continue reading until the checkbox is unticked.")
+                )
+        )
+        self.tickLayoutContinuousRead.addStretch(1)
+        for tick in self.board_ticks_continuous_read.values():
+            self.tickLayoutContinuousRead.addWidget(tick)
+        self.outerLayout.addLayout(self.tickLayoutContinuousRead)
+        self.outerLayout.addLayout(self.tickLayout)
+        self.outerLayout.addLayout(self.layout)
+        self.outerLayout.addStretch(1)
+
+        self.set_interval()
+
+    def on_single_read(self):
+        """
+        Perform a single read on all boards which ticks are set
+        :return:
+        """
+        for board_id, tick in self.board_ticks.iteritems():
+            if tick.isChecked() and tick.isEnabled():
+                bif.bk_single_read(board_id)
+
+    def on_continuous_read(self, state, board_id):
+        """
+        Toggle function to toggle continuous read when the corresponding button is pressed.
+        :param bool state: True to activate and False to deactivate continuous read for board_id
+        :param int? board_id: the id of the board to take action with
+        :return: -
+        """
+        # if self.board_ticks_continuous_read[id].isChecked():
+        self.set_interval()
+        if state:
+            # self.continuous_read_button.setText(tr("Button", "Stop Continuous Read"))
+            # self.continuous_read[id] = True
+            self.board_ticks_continuous_read[board_id].setStyleSheet("border-color: green; border-width: 3px;")
+        else:
+            self.board_ticks_continuous_read[board_id].setStyleSheet("")
+        bif.bk_continuous_read(board_id)  # interval=self.interval_spinbox.value())
+
+    def set_interval(self):
+        """
+        Set the interval between reads
+        """
+        storage.continuous_interval = self.interval_spinbox.value()
+
+    def closeEvent(self, event):
+        """
+        Event handler to handle the event of closing this window and gracefully delete ids and such
+        :param event: the event to process (gets passed by PyQt)
+        """
+        global __widget_id__
+        __widget_id__ = None
+        del self.par.widgets[self.id]
+        Elements.removeItem(['no_board', 'acquire'],
+                            [
+                                self.single_read_button,
+                                # self.continuous_read_button
+                            ])
+        for board_id, tick in self.board_ticks_continuous_read.items():
+            Elements.removeItem(
+                [
+                    "continuous_read_{}".format(board_id),
+                    "acquireTrigger_{}".format(board_id),
+                    "acquire_{}".format(board_id),
+                    "no_board_{}".format(board_id)
+                ], tick)
+
+        for board_id, tick in self.board_ticks.iteritems():
+            Elements.removeItem(
+                [
+                    "acquireTrigger_{}".format(board_id),
+                    "acquire_{}".format(board_id),
+                    "no_board_{}".format(board_id)
+                ], tick)
+
+        super(SingleReadWidget, self).closeEvent(event)
+
+
+def add_single_read_widget():
+    """
+    Add this widget to the gui.
+    This function will actually open the subwindow.
+    :return: -
+    """
+    global __widget_id__
+    if __widget_id__:
+        global_objects.get_global('area').widgets[__widget_id__].setFocus()
+    else:
+        nid = kcgw.idg.genid()
+        __widget_id__ = nid
+        w = SingleReadWidget(nid, global_objects.get_global('area'))
+        global_objects.get_global('area').newWidget(w, "Single and Continuous Read", nid, widget_type=4, minSize=True)  # TODO: proper type
+
+kcgw.register_widget(QtGui.QIcon(config.install_path + config.singleReadIcon),
+                     tr("Heading", "Single Read"),
+                     add_single_read_widget,
+                     "Ctrl+i"
+                     )

+ 778 - 885
KCG/widgets/timingWidget.py

@@ -1,885 +1,778 @@
-"""
-This Module Is the Timingsettings subWindow.
-"""
-import logging
-
-from PyQt4 import QtGui, QtCore
-import pyqtgraph as pg
-import numpy as np
-
-from ..base import kcgwidget as kcgw
-from ..base.backend import board
-from ..base.backend.board import available_boards
-from ..base import backendinterface as bif
-from ..base.groupedelements import Elements
-from ..base.globals import glob as global_objects
-from .. import config
-from ..base import storage
-
-tr = kcgw.tr
-
-__widget_id__ = None
-__timing_plot_widget_id__ = {}
-__timing_plot_widget__ = None
-
-BUNCHES_PER_TURN = config.bunches_per_turn
-
-##################################################################################
-class timingPlotWidget(kcgw.KCGWidgets):
-    """
-    The Timescan result plot subwindow.
-    """
-    def __init__(self, unique_id, board_id, parent=None):
-        """
-        Initialises the timing plot window
-        :param unique_id: the id for this window
-        :param parent: parent object
-        :return: -
-        """
-        super(timingPlotWidget, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-        self.board_id = board_id
-
-        # ------[ Variable declaration ]------------
-        self.plot_type = "colour"  # The Plot Type for this window (Changeable in this window)
-        self.xrange = (None, None)
-        self.yrange = (None, None)
-        self.x = None  # Number of Ticks in the x-direction     This is set by the gui programmatically
-        self.y = None  # Number of Ticks in the y-direction     This is set by the gui programmatically
-        self.data = None  # Data to plot
-        self.levels = None  # Levels for colors
-
-        self.inputsSet = False  # If Inputs (Timing) are set
-
-        # -------[ Create Plot Elements ]-----------
-        self.adc1_plot_widget = pg.PlotWidget(title="ADC 1")
-        self.adc2_plot_widget = pg.PlotWidget(title="ADC 2")
-        self.adc3_plot_widget = pg.PlotWidget(title="ADC 3")
-        self.adc4_plot_widget = pg.PlotWidget(title="ADC 4")
-
-        # -------[ Create structure ]----------
-        self.outerLayout = QtGui.QVBoxLayout()  # Outermost layout of this window
-        self.setLayout(self.outerLayout)
-        self.headerLayout = QtGui.QHBoxLayout()  # Layout for the header (including the switch to change plots)
-        self.outerLayout.addLayout(self.headerLayout)
-        self.layout = QtGui.QGridLayout()  # Main Layout to hold the most elements in this window
-
-        # ------[ Create Some Elements ]-----------
-        self.position_label = self.createLabel("")
-        self.position_label.setAlignment(QtCore.Qt.AlignCenter)
-        self.plot_type_switcher = self.createSwitch(connect=self.switch)
-        self.zero_correction_switcher = self.createSwitch(connect=self.zero_correction)
-        self.uses_zero_correction = False
-        self.zero_correction_values = None
-
-        # ------[ Add Elements to various layouts ]--------------
-        self.headerLayout.addWidget(self.position_label)
-        self.headerLayout.addWidget(self.createLabel("Colour Plot"))
-        self.headerLayout.addWidget(self.plot_type_switcher)
-        self.headerLayout.addWidget(self.createLabel("Line Plot"))
-        t = QtGui.QFrame()
-        t.setFrameShape(QtGui.QFrame.VLine)
-        t.setFrameShadow(QtGui.QFrame.Sunken)
-        self.headerLayout.addWidget(t)
-        self.headerLayout.addWidget(self.createLabel("RawData"))
-        self.headerLayout.addWidget(self.zero_correction_switcher)
-        self.headerLayout.addWidget(self.createLabel("Corrected"))
-        self.headerLayout.setAlignment(QtCore.Qt.AlignRight)
-        self.layout.addWidget(self.adc1_plot_widget, 0, 0)
-        self.layout.addWidget(self.adc2_plot_widget, 0, 1)
-        self.layout.addWidget(self.adc3_plot_widget, 1, 0)
-        self.layout.addWidget(self.adc4_plot_widget, 1, 1)
-        self.outerLayout.addLayout(self.layout)
-        self.setWindowTitle(tr("Heading", "Timescan Results")+" "+available_boards.get_board_name_from_id(board_id))
-
-        # Initially show the colour plot
-        self.colour_plot()
-
-    def switch(self):
-        """
-        Switch Plot types
-        :return: -
-        """
-        if self.plot_type == 'colour':
-            self.remove_colour_plot()
-            self.line_plot()
-            self.plot()
-        else:
-            self.adc1_plot_widget.plotItem.clearPlots()
-            self.adc2_plot_widget.plotItem.clearPlots()
-            self.adc3_plot_widget.plotItem.clearPlots()
-            self.adc4_plot_widget.plotItem.clearPlots()
-            self.colour_plot()
-            self.plot()
-
-##################################################################################
-    def zero_correction(self):
-        """
-        Toggle Zero (50Ohm) Correction
-        """
-    # Note: when selecting a slice the upper bound has to be included since that's how the timescan works. Python array slicing does not include the upper bound => +1
-        if self.uses_zero_correction:
-            self.uses_zero_correction = False
-            self.data += self.zero_correction_values[:, self.xrange[0]:self.xrange[1]+1, self.yrange[0]:self.yrange[1]+1]
-
-        else:
-            self.uses_zero_correction = True
-            if self.zero_correction_values is None:
-                # Adjust for closed measurement of 0 (Measured using a 50 ohm impedance
-                # This will read the complete file into memory. Could be optimized to read only necessary values
-#                f = file(config.fifty_ohm_timescan_datafile)
-                f = file(storage.storage.offset_correction)
-                c_adc = -1  # -1 because first adc will have #ADC0 and this will increment c_adc
-                self.zero_correction_values = np.zeros((4, 32, 16))  # we know there are 4 adcs, 16 coarse delays and 32 fine delays
-                all_check = np.zeros((4, 32, 16), dtype=np.bool)
-                for line in f:
-                    line = line.strip()
-                    if line == "": continue  # skip empty lines
-                    if line.startswith("#"):
-                        c_adc += 1  # c_adc = int(line.split("_")[-1])
-                        continue
-                    coarse, fine, value = line.split(";")
-                    self.zero_correction_values[c_adc, int(fine), int(coarse)] = value
-                    all_check[c_adc, int(fine), int(coarse)] = True
-                if False in all_check:
-                    logging.warn("Read 50Ohm timescan for offset correction does not include all delay values")
-
-            self.data -= self.zero_correction_values[:, self.xrange[0]:self.xrange[1]+1, self.yrange[0]:self.yrange[1]+1]
-
-        maxima = np.zeros((4, 3))
-        for adc, a in enumerate(self.data):
-            f, c = np.unravel_index(np.argmax(a), a.shape)
-            maxima[adc] = [a[f, c], c+self.yrange[0], f+self.xrange[0]]
-
-        self.plot(maxima=maxima)
-
-##################################################################################
-    def colour_plot(self):
-        """
-        Initialize Color Plot
-        :return: -
-        """
-        self.plot_type = "colour"
-        self.adc1 = pg.ImageItem()
-        self.adc2 = pg.ImageItem()
-        self.adc3 = pg.ImageItem()
-        self.adc4 = pg.ImageItem()
-
-        self.adc1.mouseClickEvent = lambda x: self.click("ADC 1", x)
-        self.adc2.mouseClickEvent = lambda x: self.click("ADC 2", x)
-        self.adc3.mouseClickEvent = lambda x: self.click("ADC 3", x)
-        self.adc4.mouseClickEvent = lambda x: self.click("ADC 4", x)
-
-        def xtickStrings(values, scale, spacing):
-            """
-            Generate the strings for ticks
-            """
-            return [str(i + self.xrange[0]) for i in range(int(values[0]), int(values[-1]),int(spacing))]
-
-        def ytickStrings(values, scale, spacing):
-            """
-            Generate the strings for ticks
-            """
-            return [str(int(i) + self.yrange[0]) for i in values]
-
-        # ----------[ Configure and add color plots for ADC1 ]----------
-
-        for i in range(1, 5):
-            getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
-            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Coarse delay"))
-            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Fine delay"))
-            getattr(self, "adc{}_plot_widget".format(i)).plotItem.addItem(getattr(self, "adc"+str(i)))
-            bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
-            lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
-
-            bax.tickSpacing = self.spacing
-            bax.tickStrings = xtickStrings
-            lax.setTickSpacing(levels=[(1, 1.5),])
-            lax.tickStrings = ytickStrings
-
-
-        pos = np.array([0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1])
-        color = np.array([[0, 0, 255, 255], [0, 255, 255, 255], [0, 255, 0, 255], [130, 255, 0, 255], [255, 255, 0, 255], [255, 180, 0, 255], [255, 100, 0, 255], [255, 0, 0, 255]])
-        cmap = pg.ColorMap(pos, color)
-        lut = cmap.getLookupTable(0.0, 1.0, 256)
-        self.adc1.setLookupTable(lut)
-        self.adc2.setLookupTable(lut)
-        self.adc3.setLookupTable(lut)
-        self.adc4.setLookupTable(lut)
-
-##################################################################################
-    def remove_colour_plot(self):
-        """
-        Remove the color plot elements
-        :return: -
-        """
-        self.adc1_plot_widget.plotItem.removeItem(self.adc1)
-        self.adc2_plot_widget.plotItem.removeItem(self.adc2)
-        self.adc3_plot_widget.plotItem.removeItem(self.adc3)
-        self.adc4_plot_widget.plotItem.removeItem(self.adc4)
-
-##################################################################################
-    def line_plot_axis_strings(self, values, scale, spacing):
-        """
-        Plot to format the strings for the line plot
-        This is used to override the default tickStrings method of pyqtgraph.AxisItem
-        :param values: See pyqtgraph.AxisItem.tickStrings
-        :param scale: See pyqtgraph.AxisItem.tickStrings
-        :param spacing: See pyqtgraph.AxisItem.tickStrings
-        :return: See pyqtgraph.AxisItem.tickStrings
-        """
-        coarses = [self.yrange[0] + i//(self.x) for i in values]
-        fines   = [self.xrange[0] + i%(self.x) for i in values]
-        return [str(int(c))+"\n"+str(int(f)) for c, f in zip(coarses, fines)]
-
-##################################################################################
-    def spacing(self, minVal, maxVal, size):
-        """
-        Calculate spacing between ticks
-        This is used to override the default tickSpacing method of pyqtgraph.AxisItem
-        :param minVal: See pyqtgraph.AxisItem.tickSpacing
-        :param maxVal: See pyqtgraph.AxisItem.tickSpacing
-        :param size: See pyqtgraph.AxisItem.tickSpacing
-        :return: See pyqtgraph.AxisItem.tickSpacing
-        """
-        if maxVal - minVal < 10.:
-            return [(1, 0),]
-        else:
-            return [(round((maxVal-minVal)/10.), 0),]
-
-##################################################################################
-    def y_axis_strings(self, values, scale, spacing):
-        """
-        Sets the strings for the y axis
-        :param values:
-        :param scale:
-        :param spacing:
-        :return:
-        """
-        if np.mean(values) > 3:
-            return [str(int(v)) for v in values]
-        return [str(v) for v in values]
-
-##################################################################################
-    def line_plot(self):
-        """
-        Initialise the Line Plot
-        :return: -
-        """
-        self.plot_type = "line"
-
-        for i in range(1, 5):
-            getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
-            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Intensity"))
-            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Coarse over Fine"))
-            bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
-            lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
-
-            bax.setTickSpacing()
-            bax.tickSpacing = self.spacing
-            bax.tickStrings = self.line_plot_axis_strings
-            lax.setTickSpacing()
-            lax.tickStrings = self.y_axis_strings
-
-
-##################################################################################
-    def setInputs(self, coarse_input, fine_inputs):
-        """
-        Register the Inputfields for timing in the timingsettings window
-        :param coarse_input: (QSpinBox) Spinbox for coarse timing settings
-        :param fine_inputs: (list) List of (QSpinBox)es for fine timing settings
-        :return: -
-        """
-        self.c_input = coarse_input
-        self.adc1_f_input = fine_inputs[0]
-        self.adc2_f_input = fine_inputs[1]
-        self.adc3_f_input = fine_inputs[2]
-        self.adc4_f_input = fine_inputs[3]
-        self.inputsSet = True
-
-##################################################################################
-    def click(self, adc, event):
-        """
-        Handler for the click event in color plots
-        :param adc: (str) The ADC plot that was clicked on
-        :param event: (QEvent) the event
-        :return: -
-        """
-        event.accept()
-        pos = event.pos()
-        self.position_label.setText(adc + " - " + str(int(pos.x())+self.xrange[0]) + ":"+str(int(pos.y())+self.yrange[0]))
-        if not self.inputsSet:
-            return
-
-        self.c_input.setValue(int(pos.y())+self.yrange[0])
-        if adc == "ADC 1":
-            self.adc1_f_input.setValue(int(pos.x())+self.xrange[0])
-        elif adc == "ADC 2":
-            self.adc2_f_input.setValue(int(pos.x())+self.xrange[0])
-        elif adc == "ADC 3":
-            self.adc3_f_input.setValue(int(pos.x())+self.xrange[0])
-        elif adc == "ADC 4":
-            self.adc4_f_input.setValue(int(pos.x())+self.xrange[0])
-
-##################################################################################
-    def plot(self, data=None, levels=None, ranges=None, newTitle=None, maxima=None):
-        """
-        Plot Data
-        :param data: (numpy 4d array) data to plot
-        :param levels: (tuple) Min and Max Values for color plot
-        :param ranges: (list) From to range of coarse (y) and fine (x) delay scan
-        :param newTitle: (str) The title for the window
-        :param maxima: (list) Maximum for each adc
-        :return: -
-        """
-#        if self.data is not None and data is not None: # keine ahnung
-#            return
-        if self.data is None and data is None:
-            return
-        if data is not None:
-            self.data = data # if called with no data a replot is performed (if data is set previously)
-        if levels is not None:
-            self.levels = levels
-        if ranges is not None:
-            self.yrange = ranges[0:2]
-            self.xrange = ranges[2:4]
-
-        self.x = self.data.shape[1]
-        self.y = self.data.shape[2]
-        if self.plot_type == 'colour':
-            # self.colour_plot()
-            self.adc1.setImage(self.data[0])
-            self.adc2.setImage(self.data[1])
-            self.adc3.setImage(self.data[2])
-            self.adc4.setImage(self.data[3])
-            if self.levels:
-                self.adc1.setLevels(self.levels)
-                self.adc2.setLevels(self.levels)
-                self.adc3.setLevels(self.levels)
-                self.adc4.setLevels(self.levels)
-            if newTitle:
-                self.setWindowTitle(tr("Heading", "Timescan Result:") + " " + newTitle + " " +
-                                    available_boards.get_board_name_from_id(self.board_id))
-
-        if maxima is not None:
-            self.adc1_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 1: Maximum C:{c} F:{f}")).format(c=int(maxima[0, 1]), f=int(maxima[0, 2])))
-            self.adc2_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 2: Maximum C:{c} F:{f}")).format(c=int(maxima[1, 1]), f=int(maxima[1, 2])))
-            self.adc3_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 3: Maximum C:{c} F:{f}")).format(c=int(maxima[2, 1]), f=int(maxima[2, 2])))
-            self.adc4_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 4: Maximum C:{c} F:{f}")).format(c=int(maxima[3, 1]), f=int(maxima[3, 2])))
-        if self.plot_type == 'line':
-            # self.line_plot()
-            def reshape(data):
-                ''' simply reshape the data'''
-                return np.reshape(data, data.shape[0]*data.shape[1], order='F')
-            self.adc1_plot_widget.plotItem.clear()
-            self.adc2_plot_widget.plotItem.clear()
-            self.adc3_plot_widget.plotItem.clear()
-            self.adc4_plot_widget.plotItem.clear()
-
-            self.adc1_plot_widget.plotItem.plot(reshape(self.data[0]))
-            self.adc2_plot_widget.plotItem.plot(reshape(self.data[1]))
-            self.adc3_plot_widget.plotItem.plot(reshape(self.data[2]))
-            self.adc4_plot_widget.plotItem.plot(reshape(self.data[3]))
-
-        # ---[ Show this window and set focus to it ]------
-        self.parent().show()
-        self.show()
-        self.setFocus()
-
-##################################################################################
-    def closeEvent(self, event):
-        """
-        Event handler for closing this window
-        """
-        global __timing_plot_widget_id__
-        __timing_plot_widget_id__ = {}
-        del self.par.widgets[self.id]
-
-
-# 88888888888d8b             d8b                8888888b.                888
-#     888    Y8P             Y8P                888   Y88b               888
-#     888                                       888    888               888
-#     888    88888888b.d88b. 88888888b.  .d88b. 888   d88P 8888b. 888d888888888
-#     888    888888 "888 "88b888888 "88bd88P"88b8888888P"     "88b888P"  888
-#     888    888888  888  888888888  888888  888888       .d888888888    888
-#     888    888888  888  888888888  888Y88b 888888       888  888888    Y88b.
-#     888    888888  888  888888888  888 "Y88888888       "Y888888888     "Y888
-#                                            888
-#                                       Y8b d88P
-#                                        "Y88P"
-
-class timingPart(kcgw.KCGWidgets):
-    """
-    The actual timing settings window
-    """
-    def __init__(self, board_id, parent=None):
-        """
-        Initialise the timing settings window
-        :param unique_id:
-        :param parent:
-        :return:
-        """
-        super(timingPart, self).__init__()
-
-        if __timing_plot_widget__:
-            self.plotWidget = __timing_plot_widget__
-        self.parent = parent
-        self.board_id = board_id
-        self.layout = QtGui.QGridLayout()
-        self.outerLayout = QtGui.QVBoxLayout()
-        self.outerLayout.addLayout(self.layout)
-        self.setLayout(self.outerLayout)
-        self.time_scan_enabled = False
-
-        # --------[ Create TimeScan part ]----------
-        # --------[ Create Elements ]-------------
-        self.coarse_scan_range_label = self.createLabel(tr("Label", "Coarse scan"))
-        self.fine_scan_range_label = self.createLabel(tr("Label", "Fine scan"))
-        self.from_label = self.createLabel(tr("Label", "From"))
-        self.to_label = self.createLabel(tr("Label", "To"))
-        self.coarse_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=0)
-        self.coarse_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=bif.bk_get_config(board_id, 'th_delay_max'))
-        self.fine_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=0)
-        self.fine_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=bif.bk_get_config(board_id, 'chip_delay_max'))
-
-        self.average_one_bunch_label = self.createLabel(tr("Label", "Average bunch number"))
-        self.average_all_bunches_checkbox = self.createCheckbox(tr("Button", "Average all bunches"), tr("Tooltip", "Average over signal from all bunches for timescan"),
-                                                         checked=True,connect=self.showAverageOneBunch)
-        self.average_one_bunch_spinbox = self.createSpinbox(0, BUNCHES_PER_TURN-1, start_value=0)
-        self.average_one_bunch_spinbox.setEnabled(False)
-
-        self.average_threshold_label = self.createLabel(tr("Label", "Averaging Threshold"))
-        self.average_threshold_checkbox = self.createCheckbox(tr("Button", "Use Threshold"), tr("Tooltip", "Average over signal from all bunches that are above/below the specified threshold for timescan"),
-                                                         checked=False,connect=self.considerThreshold)
-        self.average_threshold_spinbox = self.createSpinbox(0, 2048, start_value=0)
-        self.average_threshold_spinbox.setEnabled(False)
-
-        self.orbits_observe_spinbox = self.createSpinbox(1, 10000000, start_value=100)
-        self.orbits_skip_spinbox = self.createSpinbox(0, 100, start_value=2)
-
-        self.time_scan_progressbar = QtGui.QProgressBar()
-        self.time_scan_button = self.createButton(tr("Button", "Start time scan"), connect=self.time_scan)
-        Elements.addButton("start_time_scan_{}".format(board_id), self.time_scan_button)
-        self.timeScan = QtGui.QWidget()
-
-        # --------[ Create and Fill Timescan layouts ]----------------
-        self.timeScanVLayout = QtGui.QVBoxLayout()
-        self.timeScanLayout = QtGui.QGridLayout()
-        self.timeScanVLayout.addWidget(self.time_scan_progressbar)
-        self.timeScanVLayout.addLayout(self.timeScanLayout)
-        self.timeScan.setLayout(self.timeScanVLayout)
-        self.timeScanLayout.addWidget(self.from_label, 0, 1)
-        self.timeScanLayout.addWidget(self.to_label, 0, 2)
-        self.timeScanLayout.addWidget(self.coarse_scan_range_label, 1, 0)
-        self.timeScanLayout.addWidget(self.coarse_scan_min_spinbox, 1, 1)
-        self.timeScanLayout.addWidget(self.coarse_scan_max_spinbox, 1, 2)
-        self.timeScanLayout.addWidget(self.fine_scan_range_label, 2, 0)
-        self.timeScanLayout.addWidget(self.fine_scan_min_spinbox, 2, 1)
-        self.timeScanLayout.addWidget(self.fine_scan_max_spinbox, 2, 2)
-        self.timeScanLayout.addWidget(self.createLabel("Orbits to Observe"), 3, 1)
-        self.timeScanLayout.addWidget(self.orbits_observe_spinbox, 3, 2)
-        self.timeScanLayout.addWidget(self.createLabel("Orbits to Skip"), 4, 1)
-        self.timeScanLayout.addWidget(self.orbits_skip_spinbox, 4, 2)
-        self.timeScanLayout.addWidget(self.average_all_bunches_checkbox, 5, 0)
-        self.timeScanLayout.addWidget(self.average_one_bunch_label, 5, 1)
-        self.timeScanLayout.addWidget(self.average_one_bunch_spinbox, 5, 2)
-        self.timeScanLayout.addWidget(self.average_threshold_checkbox, 6, 0)
-        self.timeScanLayout.addWidget(self.average_threshold_label, 6, 1)
-        self.timeScanLayout.addWidget(self.average_threshold_spinbox, 6, 2)
-        self.timeScanLayout.addWidget(self.time_scan_button, 7, 2)
-        self.setTabOrder(self.fine_scan_max_spinbox, self.time_scan_button)
-
-        #  --------[ End Time Scan Part ]-------------
-
-        #  --------[ Create Labels and corresponding Fields ]---------
-
-        def update_delay(which, spinbox):
-            '''update the delays on the board'''
-            board.get_board_config(board_id).update(which, getattr(self, spinbox).value())
-            board.get_board_config(board_id).set_delay(self.coarseInput.value())
-        self.thdelayLabel = self.createLabel(tr("Label", "T/H Delay"))
-        self.adc1thdelayLabel = self.createLabel(tr("Label", "2. ADC1 T/H Delay"))
-        self.coarseLabel = self.createLabel(tr("Label", "Coarse Delay"))
-        self.coarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("th_delay", "coarseInput"))
-        self.adc1CoarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("adc_1_delay_individual", "adc1CoarseInput"))
-        self.adc1CoarseInput.setEnabled(False)
-        self.adc1CoarseInputSwitch = self.createCheckbox(tr("Label", "Enable second ADC1 T/H Delay"), connect=self.toggleAdc1IndividualDelay)
-
-        self.fineLabel = self.createLabel(tr("Label", "Fine Delay"))
-
-        self.fineAdc1Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
-        self.fineAdc2Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
-        self.fineAdc3Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
-        self.fineAdc4Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
-
-        self.toggleTimeScanCheckbox = self.createCheckbox(tr("Button", "Time Scan"), tr("Tooltip", "Show time scan part"),
-                                                          connect=self.showTimeScan)
-        #---------[ End ]---------
-
-        Elements.addItem(["timing_{}".format(self.board_id), "no_board_{}".format(self.board_id)], [
-            self.coarseInput,
-            self.fineAdc1Input,
-            self.fineAdc2Input,
-            self.fineAdc3Input,
-            self.fineAdc4Input,
-            self.time_scan_button,
-            self.adc1CoarseInputSwitch
-        ])
-        Elements.addItem('acquire_{}'.format(self.board_id), self.time_scan_button)
-        def setValueSilent(value, spinbox):
-            '''set values silent to not trigger signals'''
-            spinbox.blockSignals(True)
-            spinbox.setValue(value)
-            spinbox.blockSignals(False)
-
-        # --------[ Set observers ]------------
-        def obs(who, what):
-            '''observe something'''
-            board.get_board_config(board_id).observe(
-                who,
-                lambda value=None: setValueSilent(value=value, spinbox=who),
-                what
-            )
-        obs(self.fineAdc1Input, 'chip_1_delay')
-        obs(self.fineAdc2Input, 'chip_2_delay')
-        obs(self.fineAdc3Input, 'chip_3_delay')
-        obs(self.fineAdc4Input, 'chip_4_delay')
-        obs(self.coarseInput, 'th_delay')
-        obs(self.adc1CoarseInput, 'adc_1_delay_individual')
-
-        # -------[ Create outputs ]---------------
-        self.totalLabel = self.createLabel(tr("Label", "Total Delay"))
-        self.totalAdc1Box = self.createInput("", read_only=True)
-        self.totalAdc2Box = self.createInput("", read_only=True)
-        self.totalAdc3Box = self.createInput("", read_only=True)
-        self.totalAdc4Box = self.createInput("", read_only=True)
-
-        def observe_function(x, box):
-            '''method to pass as callable to to the configuration as observer'''
-            box.setText('%i + %i' % (board.get_board_config(board_id).get('th_delay')*
-                                     board.get_board_config(board_id).get('th_delay_factor'),
-                                     x*board.get_board_config(board_id).get('chip_delay_factor')))
-
-        def observe_function_total(x, box, adc):
-            '''method to pass as callable to to the configuration as observer'''
-            box.setText('%i + %i' % (x *
-                                     board.get_board_config(board_id).get('th_delay_factor'),
-                                     board.get_board_config(board_id).get('chip_'+adc+'_delay') *
-                                     board.get_board_config(board_id).get('chip_delay_factor')))
-
-        # def adc1_observe_function_total(x, box):
-        #     adc1_individual = board.get_board_config(board_id).get("adc_1_delay_individual")
-        #     if adc1_individual == -1:
-        #         adc1_individual = 0
-        #     box.setText('%i + %i' % ((x+adc1_individual)*
-        #                              board.get_board_config(board_id).get('th_delay_factor'),
-        #                              board.get_board_config(board_id).get('chip_1_delay') *
-        #                              board.get_board_config(board_id).get('chip_delay_factor')))
-
-
-        board.get_board_config(board_id).observe(self.totalAdc1Box,
-                                                 lambda x: observe_function(x, self.totalAdc1Box), 'chip_1_delay')
-        board.get_board_config(board_id).observe(self.totalAdc1Box,
-                                                 lambda x: observe_function_total(x, self.totalAdc1Box, '1'), 'th_delay')
-        # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'th_delay')
-        # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'adc_1_delay_individual')
-        board.get_board_config(board_id).observe(self.totalAdc2Box,
-                                                 lambda x: observe_function(x, self.totalAdc2Box), 'chip_2_delay')
-        board.get_board_config(board_id).observe(self.totalAdc2Box,
-                                                 lambda x: observe_function_total(x, self.totalAdc2Box, '2'), 'th_delay')
-        board.get_board_config(board_id).observe(self.totalAdc3Box,
-                                                 lambda x: observe_function(x, self.totalAdc3Box), 'chip_3_delay')
-        board.get_board_config(board_id).observe(self.totalAdc3Box,
-                                                 lambda x: observe_function_total(x, self.totalAdc3Box, '3'), 'th_delay')
-        board.get_board_config(board_id).observe(self.totalAdc4Box,
-                                                 lambda x: observe_function(x, self.totalAdc4Box), 'chip_4_delay')
-        board.get_board_config(board_id).observe(self.totalAdc4Box,
-                                                 lambda x: observe_function_total(x, self.totalAdc4Box, '4'), 'th_delay')
-
-        #--------[ Fill Grid ]----------------
-        self.layout.addWidget(self.thdelayLabel, 0, 1)
-        self.layout.addWidget(self.coarseLabel, 1, 0)
-        self.layout.addWidget(self.coarseInput, 1, 1)
-        self.layout.addWidget(self.adc1thdelayLabel, 0, 2)
-        self.layout.addWidget(self.adc1CoarseInput, 1, 2)
-        self.layout.addWidget(self.adc1CoarseInputSwitch, 1, 3, 1, 2)
-        self.layout.addItem(QtGui.QSpacerItem(10, 15), 2, 1)
-
-        # Leave some rows free for additional things (empty rows will not be shown)
-        for i in range(4):
-            self.layout.addWidget(self.createLabel("ADC "+str(i+1)), 5, i+1)
-        self.layout.addWidget(self.fineLabel, 6, 0)
-        self.layout.addWidget(self.fineAdc1Input, 6, 1)
-        self.layout.addWidget(self.fineAdc2Input, 6, 2)
-        self.layout.addWidget(self.fineAdc3Input, 6, 3)
-        self.layout.addWidget(self.fineAdc4Input, 6, 4)
-        self.layout.addItem(QtGui.QSpacerItem(10, 15), 7, 1)
-        line = QtGui.QFrame()
-        line.setFrameStyle(QtGui.QFrame.HLine)
-        self.layout.addWidget(line, 8, 0, 1, 5)
-        self.layout.addItem(QtGui.QSpacerItem(10, 15), 9, 1)
-        self.layout.addWidget(self.totalLabel, 10, 0)
-        self.layout.addWidget(self.totalAdc1Box, 10, 1)
-        self.layout.addWidget(self.totalAdc2Box, 10, 2)
-        self.layout.addWidget(self.totalAdc3Box, 10, 3)
-        self.layout.addWidget(self.totalAdc4Box, 10, 4)
-        self.layout.addItem(QtGui.QSpacerItem(10, 15), 11, 1)
-        self.layout.addWidget(self.toggleTimeScanCheckbox, 12, 0)
-
-        #  -------[ Add TimeScan ]---------
-        self.outerLayout.addWidget(self.timeScan)
-        self.timeScan.hide()
-        self.outerLayout.addStretch(1)
-        #  -------[ End ]-----------
-
-        #  ------[ Exclude Total Delay Boxes from Tab Order ]--------
-        self.totalAdc1Box.setFocusPolicy(QtCore.Qt.ClickFocus)
-        self.totalAdc2Box.setFocusPolicy(QtCore.Qt.ClickFocus)
-        self.totalAdc3Box.setFocusPolicy(QtCore.Qt.ClickFocus)
-        self.totalAdc4Box.setFocusPolicy(QtCore.Qt.ClickFocus)
-
-        self.setTabOrder(self.fineAdc4Input, self.toggleTimeScanCheckbox)
-
-        self.setValues()
-        self.setWindowTitle(tr("Heading", "Timing"))
-
-    def toggleAdc1IndividualDelay(self):
-        """
-        Toggle to use an individual delay for adc1 or not
-        :return: -
-        """
-        self.adc1CoarseInput.setEnabled(self.adc1CoarseInputSwitch.checkState())
-        if not self.adc1CoarseInput.isEnabled():
-            board.get_board_config(self.board_id).update('adc_1_delay_individual', -1) # Be careful this does no silent update
-
-    def showAverageOneBunch(self):
-        """
-        Toggle to use an individual bunch for the averaging in the time delay scan or to use all bunches
-        :return: -
-        """
-        self.average_one_bunch_spinbox.setEnabled(not self.average_all_bunches_checkbox.checkState())
-        self.average_threshold_spinbox.setEnabled(self.average_all_bunches_checkbox.checkState() and self.average_threshold_checkbox.checkState())
-        self.average_threshold_checkbox.setEnabled(self.average_all_bunches_checkbox.checkState())
-        self.average_threshold_checkbox.setChecked(False)
-
-    def considerThreshold(self):
-        """
-        Toggle to consider the Threshold for the averaging in the time delay scan
-        :return: -
-        """
-        self.average_threshold_spinbox.setEnabled(self.average_threshold_checkbox.checkState() and self.average_all_bunches_checkbox.checkState())
-
-
-    def showTimeScan(self):
-        """
-        Show the time scan part of this window
-        :return: -
-        """
-        if self.timeScan.isHidden():
-            # self.outerLayout.addWidget(self.timeScan)
-            self.timeScan.show()
-        else:
-            self.timeScan.hide()
-            # self.outerLayout.removeWidget(self.timeScan)
-
-        self.parent.adjustSizeForTimeScan()
-
-    def time_scan(self):
-        """
-        Toggle timescan and create timingPlotWidget if it doesn't exist
-        :return: -
-        """
-        global __timing_plot_widget_id__
-        global __timing_plot_widget__
-        if not (self.board_id in __timing_plot_widget_id__):
-            nid = kcgw.idg.genid()
-            __timing_plot_widget_id__[self.board_id] = nid
-            self.plotWidget = timingPlotWidget(nid, self.board_id, global_objects.get_global('area'))
-            __timing_plot_widget__ = self.plotWidget
-            global_objects.get_global('area').newWidget(self.plotWidget, tr("Heading", "Timing result"), nid, widget_type=5)
-            self.plotWidget.parent().hide()
-            self.plotWidget.setInputs(self.coarseInput, [
-                self.fineAdc1Input,
-                self.fineAdc2Input,
-                self.fineAdc3Input,
-                self.fineAdc4Input
-            ])
-        bif.bk_time_scan(
-            self.board_id,
-            self.coarse_scan_min_spinbox.value(),
-            self.coarse_scan_max_spinbox.value(),
-            self.fine_scan_min_spinbox.value(),
-            self.fine_scan_max_spinbox.value(),
-            self.time_scan_progressbar,
-            self.plotWidget.plot,
-            orbits_observe = self.orbits_observe_spinbox.value(),
-            orbits_skip = self.orbits_skip_spinbox.value(),
-            bucket_to_use = self.average_one_bunch_spinbox.value() if self.average_one_bunch_spinbox.isEnabled() else None,
-            threshold_counts = self.average_threshold_spinbox.value() if self.average_threshold_spinbox.isEnabled() else None
-        )
-
-    def setValueSilent(self, element, value):
-        """
-        Set Values to inputs without notifying observers
-        :param element: the input
-        :param value: the value
-        :return: -
-        """
-        element.blockSignals(True)
-        element.setValue(value)
-        element.blockSignals(False)
-
-    def setValues(self):
-        """
-        Set Values to inputs (initially)
-        :return: -
-        """
-        self.setValueSilent(self.fineAdc1Input, board.get_board_config(self.board_id).get('chip_1_delay'))
-        self.setValueSilent(self.fineAdc2Input, board.get_board_config(self.board_id).get('chip_2_delay'))
-        self.setValueSilent(self.fineAdc3Input, board.get_board_config(self.board_id).get('chip_3_delay'))
-        self.setValueSilent(self.fineAdc4Input, board.get_board_config(self.board_id).get('chip_4_delay'))
-        self.setValueSilent(self.coarseInput, board.get_board_config(self.board_id).get('th_delay'))
-        if board.get_board_config(self.board_id).get('adc_1_delay_individual') != -1: # This may be nonsense Also: it is only possible to set adc1 higher than th_delay
-            self.totalAdc1Box.setText('%i + %i' % ((board.get_board_config(self.board_id).get('th_delay')+board.get_board_config(self.board_id).get('adc_1_delay_individual'))
-                                                    * board.get_board_config(self.board_id).get('th_delay_factor'),
-                                                   board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
-        else:
-            self.totalAdc1Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
-                                                   board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
-        self.totalAdc2Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
-                                               board.get_board_config(self.board_id).get('chip_2_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
-        self.totalAdc3Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
-                                               board.get_board_config(self.board_id).get('chip_3_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
-        self.totalAdc4Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
-                                               board.get_board_config(self.board_id).get('chip_4_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
-
-    def closeEvent(self, event):
-        """
-        Event handler when this window is closed
-        """
-        Elements.emptyGroup('timing_{}'.format(self.board_id))
-        board.get_board_config(self.board_id).unobserve(self.fineAdc1Input, 'chip_1_delay')
-        board.get_board_config(self.board_id).unobserve(self.fineAdc2Input, 'chip_2_delay')
-        board.get_board_config(self.board_id).unobserve(self.fineAdc3Input, 'chip_3_delay')
-        board.get_board_config(self.board_id).unobserve(self.fineAdc4Input, 'chip_4_delay')
-        board.get_board_config(self.board_id).unobserve(self.coarseInput, 'th_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'chip_1_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'chip_2_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'chip_3_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'chip_4_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'th_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'th_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'th_delay')
-        board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'th_delay')
-        board.get_board_config(self.board_id).unobserve(self.adc1CoarseInput, 'adc_1_delay_individual')
-        Elements.removeItem('no_board_{}'.format(self.board_id),
-                            [
-                                self.fineAdc1Input,
-                                self.fineAdc2Input,
-                                self.fineAdc3Input,
-                                self.fineAdc4Input,
-                                self.coarseInput,
-                                self.time_scan_button,
-                                self.adc1CoarseInputSwitch
-                            ])
-        Elements.removeItem("start_time_scan_{}".format(self.board_id), self.time_scan_button)
-        Elements.removeItem(['acquire_{}'.format(self.board_id), "start_time_scan_{}".format(self.board_id)], self.time_scan_button)
-
-
-        super(timingPart, self).closeEvent(event)
-
-    def on_adc_delay_changed(self):
-        """
-        Handler that gets called when an adc delay gets changed
-        """
-        try:
-            board.get_board_config(self.board_id).set_delay(self.coarseInput.value())
-            factors = [self.fineAdc1Input.value(), self.fineAdc2Input.value(),
-                       self.fineAdc3Input.value(), self.fineAdc4Input.value()]
-            board.get_board_config(self.board_id).set_chip_delay([0, 1, 2, 3], factors)
-        except board.BoardError as e:
-            logging.error("ADC fine delay failed: {}".format(str(e)))
-            bif.bk_status_readout(self.board_id)
-            return
-
-
-# 888   d8b             d8b                888       888d8b     888                888
-# 888   Y8P             Y8P                888   o   888Y8P     888                888
-# 888                                      888  d8b  888        888                888
-# 88888888888888b.d88b. 88888888b.  .d88b. 888 d888b 888888 .d88888 .d88b.  .d88b. 888888
-# 888   888888 "888 "88b888888 "88bd88P"88b888d88888b888888d88" 888d88P"88bd8P  Y8b888
-# 888   888888  888  888888888  888888  88888888P Y88888888888  888888  88888888888888
-# Y88b. 888888  888  888888888  888Y88b 8888888P   Y8888888Y88b 888Y88b 888Y8b.    Y88b.
-#  "Y888888888  888  888888888  888 "Y88888888P     Y888888 "Y88888 "Y88888 "Y8888  "Y888
-#                                       888                             888
-#                                  Y8b d88P                        Y8b d88P
-#                                   "Y88P"                          "Y88P"
-
-class timingWidget(kcgw.KCGWidgets):
-    """
-    This is the container that holds the tab widget which contains the timing widgets for each board
-    """
-    def __init__(self, unique_id, parent=None):
-        super(timingWidget, self).__init__()
-
-        self.id = unique_id
-        self.par = parent
-        self.setWindowTitle("Timing")
-
-        self.layout = QtGui.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.widgets = {i: timingPart(i, self) for i in available_boards}  # has to set parent with self because
-                                            # otherwise the window does not get resized correctly upon enabling timescan
-
-        if available_boards.multi_board:
-            self.tabWidget = QtGui.QTabWidget()
-            self.layout.addWidget(self.tabWidget)
-
-            for id, widget in self.widgets.items():
-                self.tabWidget.addTab(widget, available_boards.get_board_name_from_id(id))
-        else:
-            self.single_board_widget = list(self.widgets.values())[0]
-            self.layout.addWidget(self.single_board_widget)
-
-    def adjustSizeForTimeScan(self):
-        """
-        Adjust the size of the widget to accomodate the time_scan part
-        :return:
-        """
-                # self.parentWindow = self.parent().parent().parent().parent()  # one up is stacked widget, second up is
-                #                     tab widget, third up is timingWidget fourh up is KCGWSubWindow (the actual window)
-        QtCore.QCoreApplication.processEvents()
-        if self.parent().windowState() & QtCore.Qt.WindowMaximized:
-            self.parent().setWindowState(QtCore.Qt.WindowMaximized)
-        else:
-            # self.parent().resize(self.minimumSizeHint().width() * 1.2, self.minimumSizeHint().height()*1.1)
-            self.parent().adjustSize()
-
-    def closeEvent(self, event):
-        global __widget_id__
-        __widget_id__ = None
-
-        for widget in list(self.widgets.values()):
-            widget.closeEvent(event)
-        del self.par.widgets[self.id]
-        super(timingWidget, self).closeEvent(event)
-
-
-def addTimingWidget():
-    """
-    Add this widget to the gui.
-    This function will actually open the subwindow.
-    :return: -
-    """
-    global __widget_id__
-    if __widget_id__:
-        global_objects.get_global('area').widgets[__widget_id__].setFocus()
-    else:
-        nid = kcgw.idg.genid()
-        __widget_id__ = nid
-        w = timingWidget(nid, global_objects.get_global('area'))
-        global_objects.get_global('area').newWidget(w, tr("Heading", "Timing"), nid, widget_type=4, minSize=True) #TODO: proper type
-
-kcgw.register_widget(QtGui.QIcon(config.icon_path(config.timingIcon)), tr("Heading", "Timing"), addTimingWidget, "Ctrl+T")
+"""
+This Module Is the Timingsettings subWindow.
+"""
+import logging
+
+from PyQt4 import QtGui, QtCore
+import pyqtgraph as pg
+import numpy as np
+
+from ..base import kcgwidget as kcgw
+from ..base.backend import board
+from ..base.backend.board import available_boards
+from ..base import backendinterface as bif
+from ..base.groupedelements import Elements
+from ..base.globals import glob as global_objects
+from .. import config
+
+tr = kcgw.tr
+
+__widget_id__ = None
+__timing_plot_widget_id__ = {}
+__timing_plot_widget__ = None
+
+BUNCHES_PER_TURN = config.bunches_per_turn
+
+class timingPlotWidget(kcgw.KCGWidgets):
+    """
+    The Timescan result plot subwindow.
+    """
+    def __init__(self, unique_id, board_id, parent=None):
+        """
+        Initialises the timing plot window
+        :param unique_id: the id for this window
+        :param parent: parent object
+        :return: -
+        """
+        super(timingPlotWidget, self).__init__()
+
+        self.id = unique_id
+        self.par = parent
+        self.board_id = board_id
+
+        # ------[ Variable declaration ]------------
+        self.plot_type = "colour"  # The Plot Type for this window (Changeable in this window)
+        self.xrange = (None, None)
+        self.yrange = (None, None)
+        self.x = None  # Number of Ticks in the x-direction     This is set by the gui programmatically
+        self.y = None  # Number of Ticks in the y-direction     This is set by the gui programmatically
+        self.data = None  # Data to plot
+        self.levels = None  # Levels for colors
+
+        self.inputsSet = False  # If Inputs (Timing) are set
+
+        # -------[ Create Plot Elements ]-----------
+        self.adc1_plot_widget = pg.PlotWidget(title="ADC 1")
+        self.adc2_plot_widget = pg.PlotWidget(title="ADC 2")
+        self.adc3_plot_widget = pg.PlotWidget(title="ADC 3")
+        self.adc4_plot_widget = pg.PlotWidget(title="ADC 4")
+
+        # -------[ Create structure ]----------
+        self.outerLayout = QtGui.QVBoxLayout()  # Outermost layout of this window
+        self.setLayout(self.outerLayout)
+        self.headerLayout = QtGui.QHBoxLayout()  # Layout for the header (including the switch to change plots)
+        self.outerLayout.addLayout(self.headerLayout)
+        self.layout = QtGui.QGridLayout()  # Main Layout to hold the most elements in this window
+
+        # ------[ Create Some Elements ]-----------
+        self.position_label = self.createLabel("")
+        self.position_label.setAlignment(QtCore.Qt.AlignCenter)
+        self.plot_type_switcher = self.createSwitch(connect=self.switch)
+
+        # ------[ Add Elements to various layouts ]--------------
+        self.headerLayout.addWidget(self.position_label)
+        self.headerLayout.addWidget(self.createLabel("Colour Plot"))
+        self.headerLayout.addWidget(self.plot_type_switcher)
+        self.headerLayout.addWidget(self.createLabel("Line Plot"))
+        self.headerLayout.setAlignment(QtCore.Qt.AlignRight)
+        self.layout.addWidget(self.adc1_plot_widget, 0, 0)
+        self.layout.addWidget(self.adc2_plot_widget, 0, 1)
+        self.layout.addWidget(self.adc3_plot_widget, 1, 0)
+        self.layout.addWidget(self.adc4_plot_widget, 1, 1)
+        self.outerLayout.addLayout(self.layout)
+        self.setWindowTitle(tr("Heading", "Timescan Results")+" "+available_boards.get_board_name_from_id(board_id))
+
+        # Initially show the colour plot
+        self.colour_plot()
+
+    def switch(self):
+        """
+        Switch Plot types
+        :return: -
+        """
+        if self.plot_type == 'colour':
+            self.remove_colour_plot()
+            self.line_plot()
+            self.plot()
+        else:
+            self.adc1_plot_widget.plotItem.clearPlots()
+            self.adc2_plot_widget.plotItem.clearPlots()
+            self.adc3_plot_widget.plotItem.clearPlots()
+            self.adc4_plot_widget.plotItem.clearPlots()
+            self.colour_plot()
+            self.plot()
+
+    def colour_plot(self):
+        """
+        Initialize Color Plot
+        :return: -
+        """
+        self.plot_type = "colour"
+        self.adc1 = pg.ImageItem()
+        self.adc2 = pg.ImageItem()
+        self.adc3 = pg.ImageItem()
+        self.adc4 = pg.ImageItem()
+
+        self.adc1.mouseClickEvent = lambda x: self.click("ADC 1", x)
+        self.adc2.mouseClickEvent = lambda x: self.click("ADC 2", x)
+        self.adc3.mouseClickEvent = lambda x: self.click("ADC 3", x)
+        self.adc4.mouseClickEvent = lambda x: self.click("ADC 4", x)
+
+        def xtickStrings(values, scale, spacing):
+            """
+            Generate the strings for ticks
+            """
+            return [str(i + self.xrange[0]) for i in range(int(values[0]), int(values[-1]),int(spacing))]
+
+        def ytickStrings(values, scale, spacing):
+            """
+            Generate the strings for ticks
+            """
+            return [str(int(i) + self.yrange[0]) for i in values]
+
+        # ----------[ Configure and add color plots for ADC1 ]----------
+
+        for i in range(1, 5):
+            getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
+            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Coarse delay"))
+            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Fine delay"))
+            getattr(self, "adc{}_plot_widget".format(i)).plotItem.addItem(getattr(self, "adc"+str(i)))
+            bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
+            lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
+
+            bax.tickSpacing = self.spacing
+            bax.tickStrings = xtickStrings
+            lax.setTickSpacing(levels=[(1, 1.5),])
+            lax.tickStrings = ytickStrings
+
+
+        pos = np.array([0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1])
+        color = np.array([[0, 0, 255, 255], [0, 255, 255, 255], [0, 255, 0, 255], [130, 255, 0, 255], [255, 255, 0, 255], [255, 180, 0, 255], [255, 100, 0, 255], [255, 0, 0, 255]])
+        cmap = pg.ColorMap(pos, color)
+        lut = cmap.getLookupTable(0.0, 1.0, 256)
+        self.adc1.setLookupTable(lut)
+        self.adc2.setLookupTable(lut)
+        self.adc3.setLookupTable(lut)
+        self.adc4.setLookupTable(lut)
+
+    def remove_colour_plot(self):
+        """
+        Remove the color plot elements
+        :return: -
+        """
+        self.adc1_plot_widget.plotItem.removeItem(self.adc1)
+        self.adc2_plot_widget.plotItem.removeItem(self.adc2)
+        self.adc3_plot_widget.plotItem.removeItem(self.adc3)
+        self.adc4_plot_widget.plotItem.removeItem(self.adc4)
+
+    def line_plot_axis_strings(self, values, scale, spacing):
+        """
+        Plot to format the strings for the line plot
+        This is used to override the default tickStrings method of pyqtgraph.AxisItem
+        :param values: See pyqtgraph.AxisItem.tickStrings
+        :param scale: See pyqtgraph.AxisItem.tickStrings
+        :param spacing: See pyqtgraph.AxisItem.tickStrings
+        :return: See pyqtgraph.AxisItem.tickStrings
+        """
+        coarses = [self.yrange[0] + i//(self.x) for i in values]
+        fines = [self.xrange[0] + i%(self.x) for i in values]
+        return [str(int(c))+"\n"+str(int(f)) for c, f in zip(coarses, fines)]
+
+    def spacing(self, minVal, maxVal, size):
+        """
+        Calculate spacing between ticks
+        This is used to override the default tickSpacing method of pyqtgraph.AxisItem
+        :param minVal: See pyqtgraph.AxisItem.tickSpacing
+        :param maxVal: See pyqtgraph.AxisItem.tickSpacing
+        :param size: See pyqtgraph.AxisItem.tickSpacing
+        :return: See pyqtgraph.AxisItem.tickSpacing
+        """
+        if maxVal - minVal < 10.:
+            return [(1, 0),]
+        else:
+            return [(round((maxVal-minVal)/10.), 0),]
+
+    def y_axis_strings(self, values, scale, spacing):
+        """
+        Sets the strings for the y axis
+        :param values:
+        :param scale:
+        :param spacing:
+        :return:
+        """
+        return [str(int(v)) for v in values]
+
+    def line_plot(self):
+        """
+        Initialise the Line Plot
+        :return: -
+        """
+        self.plot_type = "line"
+
+        for i in range(1, 5):
+            getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
+            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Intensity"))
+            getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Coarse over Fine"))
+            bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
+            lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
+
+            bax.setTickSpacing()
+            bax.tickSpacing = self.spacing
+            bax.tickStrings = self.line_plot_axis_strings
+            lax.setTickSpacing()
+            lax.tickStrings = self.y_axis_strings
+
+    def setInputs(self, coarse_input, fine_inputs):
+        """
+        Register the Inputfields for timing in the timingsettings window
+        :param coarse_input: (QSpinBox) Spinbox for coarse timing settings
+        :param fine_inputs: (list) List of (QSpinBox)es for fine timing settings
+        :return: -
+        """
+        self.c_input = coarse_input
+        self.adc1_f_input = fine_inputs[0]
+        self.adc2_f_input = fine_inputs[1]
+        self.adc3_f_input = fine_inputs[2]
+        self.adc4_f_input = fine_inputs[3]
+        self.inputsSet = True
+
+    def click(self, adc, event):
+        """
+        Handler for the click event in color plots
+        :param adc: (str) The ADC plot that was clicked on
+        :param event: (QEvent) the event
+        :return: -
+        """
+        event.accept()
+        pos = event.pos()
+        self.position_label.setText(adc + " - " + str(int(pos.x())+self.xrange[0]) + ":"+str(int(pos.y())+self.yrange[0]))
+        if not self.inputsSet:
+            return
+
+        self.c_input.setValue(int(pos.y())+self.yrange[0])
+        if adc == "ADC 1":
+            self.adc1_f_input.setValue(int(pos.x())+self.xrange[0])
+        elif adc == "ADC 2":
+            self.adc2_f_input.setValue(int(pos.x())+self.xrange[0])
+        elif adc == "ADC 3":
+            self.adc3_f_input.setValue(int(pos.x())+self.xrange[0])
+        elif adc == "ADC 4":
+            self.adc4_f_input.setValue(int(pos.x())+self.xrange[0])
+
+    def plot(self, data=None, levels=None, ranges=None, newTitle=None, maxima=None):
+        """
+        Plot Data
+        :param data: (numpy 4d array) data to plot
+        :param levels: (tuple) Min and Max Values for color plot
+        :param ranges: (list) From to range of coarse (y) and fine (x) delay scan
+        :param newTitle: (str) The title for the window
+        :param maxima: (list) Maximum for each adc
+        :return: -
+        """
+#        if self.data is not None and data is not None: # keine ahnung
+#            return
+        if self.data is None and data is None:
+            return
+        if data is not None:
+            self.data = data # if called with no data a replot is performed (if data is set previously)
+        if levels is not None:
+            self.levels = levels
+        if ranges is not None:
+            self.yrange = ranges[0:2]
+            self.xrange = ranges[2:4]
+
+        self.x = self.data.shape[1]
+        self.y = self.data.shape[2]
+        if self.plot_type == 'colour':
+            # self.colour_plot()
+            self.adc1.setImage(self.data[0])
+            self.adc2.setImage(self.data[1])
+            self.adc3.setImage(self.data[2])
+            self.adc4.setImage(self.data[3])
+            if self.levels:
+                self.adc1.setLevels(self.levels)
+                self.adc2.setLevels(self.levels)
+                self.adc3.setLevels(self.levels)
+                self.adc4.setLevels(self.levels)
+            if newTitle:
+                self.setWindowTitle(tr("Heading", "Timescan Result:") + " " + newTitle + " " +
+                                    available_boards.get_board_name_from_id(self.board_id))
+
+            if maxima is not None:
+                self.adc1_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 1: Maximum C:{c} F:{f}")).format(
+                    c=int(maxima[0, 1]), f=int(maxima[0, 2])))
+                self.adc2_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 2: Maximum C:{c} F:{f}")).format(
+                    c=int(maxima[1, 1]), f=int(maxima[1, 2])))
+                self.adc3_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 3: Maximum C:{c} F:{f}")).format(
+                    c=int(maxima[2, 1]), f=int(maxima[2, 2])))
+                self.adc4_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 4: Maximum C:{c} F:{f}")).format(
+                    c=int(maxima[3, 1]), f=int(maxima[3, 2])))
+        if self.plot_type == 'line':
+            # self.line_plot()
+            def reshape(data):
+                ''' simply reshape the data'''
+                return np.reshape(data, data.shape[0]*data.shape[1], order='F')
+            self.adc1_plot_widget.plotItem.clear()
+            self.adc2_plot_widget.plotItem.clear()
+            self.adc3_plot_widget.plotItem.clear()
+            self.adc4_plot_widget.plotItem.clear()
+
+            self.adc1_plot_widget.plotItem.plot(reshape(self.data[0]))
+            self.adc2_plot_widget.plotItem.plot(reshape(self.data[1]))
+            self.adc3_plot_widget.plotItem.plot(reshape(self.data[2]))
+            self.adc4_plot_widget.plotItem.plot(reshape(self.data[3]))
+
+        # ---[ Show this window and set focus to it ]------
+        self.parent().show()
+        self.show()
+        self.setFocus()
+
+    def closeEvent(self, event):
+        """
+        Event handler for closing this window
+        """
+        global __timing_plot_widget_id__
+        __timing_plot_widget_id__ = {}
+        del self.par.widgets[self.id]
+
+
+class timingPart(kcgw.KCGWidgets):
+    """
+    The actual timing settings window
+    """
+    def __init__(self, board_id, parent=None):
+        """
+        Initialise the timing settings window
+        :param unique_id:
+        :param parent:
+        :return:
+        """
+        super(timingPart, self).__init__()
+
+        if __timing_plot_widget__:
+            self.plotWidget = __timing_plot_widget__
+        self.parent = parent
+        self.board_id = board_id
+        self.layout = QtGui.QGridLayout()
+        self.outerLayout = QtGui.QVBoxLayout()
+        self.outerLayout.addLayout(self.layout)
+        self.setLayout(self.outerLayout)
+        self.time_scan_enabled = False
+
+        # --------[ Create TimeScan part ]----------
+        # --------[ Create Elements ]-------------
+        self.coarse_scan_range_label = self.createLabel(tr("Label", "Coarse scan"))
+        self.fine_scan_range_label = self.createLabel(tr("Label", "Fine scan"))
+        self.from_label = self.createLabel(tr("Label", "From"))
+        self.to_label = self.createLabel(tr("Label", "To"))
+        self.coarse_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=0)
+        self.coarse_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=bif.bk_get_config(board_id, 'th_delay_max'))
+        self.fine_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=0)
+        self.fine_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=bif.bk_get_config(board_id, 'chip_delay_max'))
+
+        self.average_one_bunch_label = self.createLabel(tr("Label", "Average bunch number"))
+        self.average_all_bunches_checkbox = self.createCheckbox(tr("Button", "Average all bunches"), tr("Tooltip", "Average over signal from all bunches for timescan"),
+                                                         checked=True,connect=self.showAverageOneBunch)
+        self.average_one_bunch_spinbox = self.createSpinbox(0, BUNCHES_PER_TURN-1, start_value=0)
+        self.average_one_bunch_spinbox.setEnabled(False)
+
+        self.orbits_observe_spinbox = self.createSpinbox(1, 10000000, start_value=100)
+        self.orbits_skip_spinbox = self.createSpinbox(0, 100, start_value=2)
+
+        self.time_scan_progressbar = QtGui.QProgressBar()
+        self.time_scan_button = self.createButton(tr("Button", "Start time scan"), connect=self.time_scan)
+        Elements.addButton("start_time_scan_{}".format(board_id), self.time_scan_button)
+        self.timeScan = QtGui.QWidget()
+
+        # --------[ Create and Fill Timescan layouts ]----------------
+        self.timeScanVLayout = QtGui.QVBoxLayout()
+        self.timeScanLayout = QtGui.QGridLayout()
+        self.timeScanVLayout.addWidget(self.time_scan_progressbar)
+        self.timeScanVLayout.addLayout(self.timeScanLayout)
+        self.timeScan.setLayout(self.timeScanVLayout)
+        self.timeScanLayout.addWidget(self.from_label, 0, 1)
+        self.timeScanLayout.addWidget(self.to_label, 0, 2)
+        self.timeScanLayout.addWidget(self.coarse_scan_range_label, 1, 0)
+        self.timeScanLayout.addWidget(self.coarse_scan_min_spinbox, 1, 1)
+        self.timeScanLayout.addWidget(self.coarse_scan_max_spinbox, 1, 2)
+        self.timeScanLayout.addWidget(self.fine_scan_range_label, 2, 0)
+        self.timeScanLayout.addWidget(self.fine_scan_min_spinbox, 2, 1)
+        self.timeScanLayout.addWidget(self.fine_scan_max_spinbox, 2, 2)
+        self.timeScanLayout.addWidget(self.createLabel("Orbits to Observe"), 3, 1)
+        self.timeScanLayout.addWidget(self.orbits_observe_spinbox, 3, 2)
+        self.timeScanLayout.addWidget(self.createLabel("Orbits to Skip"), 4, 1)
+        self.timeScanLayout.addWidget(self.orbits_skip_spinbox, 4, 2)
+        self.timeScanLayout.addWidget(self.average_all_bunches_checkbox, 5, 0)
+        self.timeScanLayout.addWidget(self.average_one_bunch_label, 5, 1)
+        self.timeScanLayout.addWidget(self.average_one_bunch_spinbox, 5, 2)
+        self.timeScanLayout.addWidget(self.time_scan_button, 6, 2)
+        self.setTabOrder(self.fine_scan_max_spinbox, self.time_scan_button)
+
+        #  --------[ End Time Scan Part ]-------------
+
+        #  --------[ Create Labels and corresponding Fields ]---------
+
+        def update_delay(which, spinbox):
+            '''update the delays on the board'''
+            board.get_board_config(board_id).update(which, getattr(self, spinbox).value())
+            board.get_board_config(board_id).set_delay(self.coarseInput.value())
+        self.thdelayLabel = self.createLabel(tr("Label", "T/H Delay"))
+        self.adc1thdelayLabel = self.createLabel(tr("Label", "2. ADC1 T/H Delay"))
+        self.coarseLabel = self.createLabel(tr("Label", "Coarse Delay"))
+        self.coarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("th_delay", "coarseInput"))
+        self.adc1CoarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("adc_1_delay_individual", "adc1CoarseInput"))
+        self.adc1CoarseInput.setEnabled(False)
+        self.adc1CoarseInputSwitch = self.createCheckbox(tr("Label", "Enable second ADC1 T/H Delay"), connect=self.toggleAdc1IndividualDelay)
+
+        self.fineLabel = self.createLabel(tr("Label", "Fine Delay"))
+
+        self.fineAdc1Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
+        self.fineAdc2Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
+        self.fineAdc3Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
+        self.fineAdc4Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
+
+        self.toggleTimeScanCheckbox = self.createCheckbox(tr("Button", "Time Scan"), tr("Tooltip", "Show time scan part"),
+                                                          connect=self.showTimeScan)
+        #---------[ End ]---------
+
+        Elements.addItem(["timing_{}".format(self.board_id), "no_board_{}".format(self.board_id)], [
+            self.coarseInput,
+            self.fineAdc1Input,
+            self.fineAdc2Input,
+            self.fineAdc3Input,
+            self.fineAdc4Input,
+            self.time_scan_button,
+            self.adc1CoarseInputSwitch
+        ])
+        Elements.addItem('acquire_{}'.format(self.board_id), self.time_scan_button)
+        def setValueSilent(value, spinbox):
+            '''set values silent to not trigger signals'''
+            spinbox.blockSignals(True)
+            spinbox.setValue(value)
+            spinbox.blockSignals(False)
+
+        # --------[ Set observers ]------------
+        def obs(who, what):
+            '''observe something'''
+            board.get_board_config(board_id).observe(
+                who,
+                lambda value=None: setValueSilent(value=value, spinbox=who),
+                what
+            )
+        obs(self.fineAdc1Input, 'chip_1_delay')
+        obs(self.fineAdc2Input, 'chip_2_delay')
+        obs(self.fineAdc3Input, 'chip_3_delay')
+        obs(self.fineAdc4Input, 'chip_4_delay')
+        obs(self.coarseInput, 'th_delay')
+        obs(self.adc1CoarseInput, 'adc_1_delay_individual')
+
+        # -------[ Create outputs ]---------------
+        self.totalLabel = self.createLabel(tr("Label", "Total Delay"))
+        self.totalAdc1Box = self.createInput("", read_only=True)
+        self.totalAdc2Box = self.createInput("", read_only=True)
+        self.totalAdc3Box = self.createInput("", read_only=True)
+        self.totalAdc4Box = self.createInput("", read_only=True)
+
+        def observe_function(x, box):
+            '''method to pass as callable to to the configuration as observer'''
+            box.setText('%i + %i' % (board.get_board_config(board_id).get('th_delay')*
+                                     board.get_board_config(board_id).get('th_delay_factor'),
+                                     x*board.get_board_config(board_id).get('chip_delay_factor')))
+
+        def observe_function_total(x, box, adc):
+            '''method to pass as callable to to the configuration as observer'''
+            box.setText('%i + %i' % (x *
+                                     board.get_board_config(board_id).get('th_delay_factor'),
+                                     board.get_board_config(board_id).get('chip_'+adc+'_delay') *
+                                     board.get_board_config(board_id).get('chip_delay_factor')))
+
+        # def adc1_observe_function_total(x, box):
+        #     adc1_individual = board.get_board_config(board_id).get("adc_1_delay_individual")
+        #     if adc1_individual == -1:
+        #         adc1_individual = 0
+        #     box.setText('%i + %i' % ((x+adc1_individual)*
+        #                              board.get_board_config(board_id).get('th_delay_factor'),
+        #                              board.get_board_config(board_id).get('chip_1_delay') *
+        #                              board.get_board_config(board_id).get('chip_delay_factor')))
+
+
+        board.get_board_config(board_id).observe(self.totalAdc1Box,
+                                                 lambda x: observe_function(x, self.totalAdc1Box), 'chip_1_delay')
+        board.get_board_config(board_id).observe(self.totalAdc1Box,
+                                                 lambda x: observe_function_total(x, self.totalAdc1Box, '1'), 'th_delay')
+        # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'th_delay')
+        # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'adc_1_delay_individual')
+        board.get_board_config(board_id).observe(self.totalAdc2Box,
+                                                 lambda x: observe_function(x, self.totalAdc2Box), 'chip_2_delay')
+        board.get_board_config(board_id).observe(self.totalAdc2Box,
+                                                 lambda x: observe_function_total(x, self.totalAdc2Box, '2'), 'th_delay')
+        board.get_board_config(board_id).observe(self.totalAdc3Box,
+                                                 lambda x: observe_function(x, self.totalAdc3Box), 'chip_3_delay')
+        board.get_board_config(board_id).observe(self.totalAdc3Box,
+                                                 lambda x: observe_function_total(x, self.totalAdc3Box, '3'), 'th_delay')
+        board.get_board_config(board_id).observe(self.totalAdc4Box,
+                                                 lambda x: observe_function(x, self.totalAdc4Box), 'chip_4_delay')
+        board.get_board_config(board_id).observe(self.totalAdc4Box,
+                                                 lambda x: observe_function_total(x, self.totalAdc4Box, '4'), 'th_delay')
+
+        #--------[ Fill Grid ]----------------
+        self.layout.addWidget(self.thdelayLabel, 0, 1)
+        self.layout.addWidget(self.coarseLabel, 1, 0)
+        self.layout.addWidget(self.coarseInput, 1, 1)
+        self.layout.addWidget(self.adc1thdelayLabel, 0, 2)
+        self.layout.addWidget(self.adc1CoarseInput, 1, 2)
+        self.layout.addWidget(self.adc1CoarseInputSwitch, 1, 3, 1, 2)
+        self.layout.addItem(QtGui.QSpacerItem(10, 15), 2, 1)
+
+        # Leave some rows free for additional things (empty rows will not be shown)
+        for i in range(4):
+            self.layout.addWidget(self.createLabel("ADC "+str(i+1)), 5, i+1)
+        self.layout.addWidget(self.fineLabel, 6, 0)
+        self.layout.addWidget(self.fineAdc1Input, 6, 1)
+        self.layout.addWidget(self.fineAdc2Input, 6, 2)
+        self.layout.addWidget(self.fineAdc3Input, 6, 3)
+        self.layout.addWidget(self.fineAdc4Input, 6, 4)
+        self.layout.addItem(QtGui.QSpacerItem(10, 15), 7, 1)
+        line = QtGui.QFrame()
+        line.setFrameStyle(QtGui.QFrame.HLine)
+        self.layout.addWidget(line, 8, 0, 1, 5)
+        self.layout.addItem(QtGui.QSpacerItem(10, 15), 9, 1)
+        self.layout.addWidget(self.totalLabel, 10, 0)
+        self.layout.addWidget(self.totalAdc1Box, 10, 1)
+        self.layout.addWidget(self.totalAdc2Box, 10, 2)
+        self.layout.addWidget(self.totalAdc3Box, 10, 3)
+        self.layout.addWidget(self.totalAdc4Box, 10, 4)
+        self.layout.addItem(QtGui.QSpacerItem(10, 15), 11, 1)
+        self.layout.addWidget(self.toggleTimeScanCheckbox, 12, 0)
+
+        #  -------[ Add TimeScan ]---------
+        self.outerLayout.addWidget(self.timeScan)
+        self.timeScan.hide()
+        self.outerLayout.addStretch(1)
+        #  -------[ End ]-----------
+
+        #  ------[ Exclude Total Delay Boxes from Tab Order ]--------
+        self.totalAdc1Box.setFocusPolicy(QtCore.Qt.ClickFocus)
+        self.totalAdc2Box.setFocusPolicy(QtCore.Qt.ClickFocus)
+        self.totalAdc3Box.setFocusPolicy(QtCore.Qt.ClickFocus)
+        self.totalAdc4Box.setFocusPolicy(QtCore.Qt.ClickFocus)
+
+        self.setTabOrder(self.fineAdc4Input, self.toggleTimeScanCheckbox)
+
+        self.setValues()
+        self.setWindowTitle(tr("Heading", "Timing"))
+
+    def toggleAdc1IndividualDelay(self):
+        """
+        Toggle to use an individual delay for adc1 or not
+        :return: -
+        """
+        self.adc1CoarseInput.setEnabled(self.adc1CoarseInputSwitch.checkState())
+        if not self.adc1CoarseInput.isEnabled():
+            board.get_board_config(self.board_id).update('adc_1_delay_individual', -1) # Be careful this does no silent update
+
+    def showAverageOneBunch(self):
+        """
+        Toggle to use an individual bunch for the averaging in the time delay scan or to use all bunches
+        :return: -
+        """
+        self.average_one_bunch_spinbox.setEnabled(not self.average_all_bunches_checkbox.checkState())
+
+    def showTimeScan(self):
+        """
+        Show the time scan part of this window
+        :return: -
+        """
+        if self.timeScan.isHidden():
+            # self.outerLayout.addWidget(self.timeScan)
+            self.timeScan.show()
+        else:
+            self.timeScan.hide()
+            # self.outerLayout.removeWidget(self.timeScan)
+
+        self.parent.adjustSizeForTimeScan()
+
+    def time_scan(self):
+        """
+        Toggle timescan and create timingPlotWidget if it doesn't exist
+        :return: -
+        """
+        global __timing_plot_widget_id__
+        global __timing_plot_widget__
+        if not (self.board_id in __timing_plot_widget_id__):
+            nid = kcgw.idg.genid()
+            __timing_plot_widget_id__[self.board_id] = nid
+            self.plotWidget = timingPlotWidget(nid, self.board_id, global_objects.get_global('area'))
+            __timing_plot_widget__ = self.plotWidget
+            global_objects.get_global('area').newWidget(self.plotWidget, tr("Heading", "Timing result"), nid, widget_type=5)
+            self.plotWidget.parent().hide()
+            self.plotWidget.setInputs(self.coarseInput, [
+                self.fineAdc1Input,
+                self.fineAdc2Input,
+                self.fineAdc3Input,
+                self.fineAdc4Input
+            ])
+        bif.bk_time_scan(
+            self.board_id,
+            self.coarse_scan_min_spinbox.value(),
+            self.coarse_scan_max_spinbox.value(),
+            self.fine_scan_min_spinbox.value(),
+            self.fine_scan_max_spinbox.value(),
+            self.time_scan_progressbar,
+            self.plotWidget.plot,
+            orbits_observe = self.orbits_observe_spinbox.value(),
+            orbits_skip = self.orbits_skip_spinbox.value(),
+            bucket_to_use = self.average_one_bunch_spinbox.value() if self.average_one_bunch_spinbox.isEnabled() else None
+        )
+
+    def setValueSilent(self, element, value):
+        """
+        Set Values to inputs without notifying observers
+        :param element: the input
+        :param value: the value
+        :return: -
+        """
+        element.blockSignals(True)
+        element.setValue(value)
+        element.blockSignals(False)
+
+    def setValues(self):
+        """
+        Set Values to inputs (initially)
+        :return: -
+        """
+        self.setValueSilent(self.fineAdc1Input, board.get_board_config(self.board_id).get('chip_1_delay'))
+        self.setValueSilent(self.fineAdc2Input, board.get_board_config(self.board_id).get('chip_2_delay'))
+        self.setValueSilent(self.fineAdc3Input, board.get_board_config(self.board_id).get('chip_3_delay'))
+        self.setValueSilent(self.fineAdc4Input, board.get_board_config(self.board_id).get('chip_4_delay'))
+        self.setValueSilent(self.coarseInput, board.get_board_config(self.board_id).get('th_delay'))
+        if board.get_board_config(self.board_id).get('adc_1_delay_individual') != -1: # This may be nonsense Also: it is only possible to set adc1 higher than th_delay
+            self.totalAdc1Box.setText('%i + %i' % ((board.get_board_config(self.board_id).get('th_delay')+board.get_board_config(self.board_id).get('adc_1_delay_individual'))
+                                                    * board.get_board_config(self.board_id).get('th_delay_factor'),
+                                                   board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
+        else:
+            self.totalAdc1Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
+                                                   board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
+        self.totalAdc2Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
+                                               board.get_board_config(self.board_id).get('chip_2_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
+        self.totalAdc3Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
+                                               board.get_board_config(self.board_id).get('chip_3_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
+        self.totalAdc4Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
+                                               board.get_board_config(self.board_id).get('chip_4_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
+
+    def closeEvent(self, event):
+        """
+        Event handler when this window is closed
+        """
+        Elements.emptyGroup('timing_{}'.format(self.board_id))
+        board.get_board_config(self.board_id).unobserve(self.fineAdc1Input, 'chip_1_delay')
+        board.get_board_config(self.board_id).unobserve(self.fineAdc2Input, 'chip_2_delay')
+        board.get_board_config(self.board_id).unobserve(self.fineAdc3Input, 'chip_3_delay')
+        board.get_board_config(self.board_id).unobserve(self.fineAdc4Input, 'chip_4_delay')
+        board.get_board_config(self.board_id).unobserve(self.coarseInput, 'th_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'chip_1_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'chip_2_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'chip_3_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'chip_4_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'th_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'th_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'th_delay')
+        board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'th_delay')
+        board.get_board_config(self.board_id).unobserve(self.adc1CoarseInput, 'adc_1_delay_individual')
+        Elements.removeItem('no_board_{}'.format(self.board_id),
+                            [
+                                self.fineAdc1Input,
+                                self.fineAdc2Input,
+                                self.fineAdc3Input,
+                                self.fineAdc4Input,
+                                self.coarseInput,
+                                self.time_scan_button,
+                                self.adc1CoarseInputSwitch
+                            ])
+        Elements.removeItem("start_time_scan_{}".format(self.board_id), self.time_scan_button)
+        Elements.removeItem(['acquire_{}'.format(self.board_id), "start_time_scan_{}".format(self.board_id)], self.time_scan_button)
+
+
+        super(timingPart, self).closeEvent(event)
+
+    def on_adc_delay_changed(self):
+        """
+        Handler that gets called when an adc delay gets changed
+        """
+        try:
+            board.get_board_config(self.board_id).set_delay(self.coarseInput.value())
+            factors = [self.fineAdc1Input.value(), self.fineAdc2Input.value(),
+                       self.fineAdc3Input.value(), self.fineAdc4Input.value()]
+            board.get_board_config(self.board_id).set_chip_delay([0, 1, 2, 3], factors)
+        except board.BoardError as e:
+            logging.error("ADC fine delay failed: {}".format(str(e)))
+            bif.bk_status_readout(self.board_id)
+            return
+
+
+class timingWidget(kcgw.KCGWidgets):
+    """
+    This is the container that holds the tab widget which contains the timing widgets for each board
+    """
+    def __init__(self, unique_id, parent=None):
+        super(timingWidget, self).__init__()
+
+        self.id = unique_id
+        self.par = parent
+        self.setWindowTitle("Timing")
+
+        self.layout = QtGui.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.widgets = {i: timingPart(i, self) for i in available_boards}  # has to set parent with self because
+                                            # otherwise the window does not get resized correctly upon enabling timescan
+
+        if available_boards.multi_board:
+            self.tabWidget = QtGui.QTabWidget()
+            self.layout.addWidget(self.tabWidget)
+
+            for id, widget in self.widgets.iteritems():
+                self.tabWidget.addTab(widget, available_boards.get_board_name_from_id(id))
+        else:
+            self.single_board_widget = self.widgets.values()[0]
+            self.layout.addWidget(self.single_board_widget)
+
+    def adjustSizeForTimeScan(self):
+        """
+        Adjust the size of the widget to accomodate the time_scan part
+        :return:
+        """
+                # self.parentWindow = self.parent().parent().parent().parent()  # one up is stacked widget, second up is
+                #                     tab widget, third up is timingWidget fourh up is KCGWSubWindow (the actual window)
+        QtCore.QCoreApplication.processEvents()
+        if self.parent().windowState() & QtCore.Qt.WindowMaximized:
+            self.parent().setWindowState(QtCore.Qt.WindowMaximized)
+        else:
+            # self.parent().resize(self.minimumSizeHint().width() * 1.2, self.minimumSizeHint().height()*1.1)
+            self.parent().adjustSize()
+
+    def closeEvent(self, event):
+        global __widget_id__
+        __widget_id__ = None
+
+        for widget in self.widgets.values():
+            widget.closeEvent(event)
+        del self.par.widgets[self.id]
+        super(timingWidget, self).closeEvent(event)
+
+
+def addTimingWidget():
+    """
+    Add this widget to the gui.
+    This function will actually open the subwindow.
+    :return: -
+    """
+    global __widget_id__
+    if __widget_id__:
+        global_objects.get_global('area').widgets[__widget_id__].setFocus()
+    else:
+        nid = kcgw.idg.genid()
+        __widget_id__ = nid
+        w = timingWidget(nid, global_objects.get_global('area'))
+        global_objects.get_global('area').newWidget(w, tr("Heading", "Timing"), nid, widget_type=4, minSize=True) #TODO: proper type
+
+kcgw.register_widget(QtGui.QIcon(config.install_path + config.timingIcon), tr("Heading", "Timing"), addTimingWidget, "Ctrl+T")

+ 93 - 0
config.cfg

@@ -0,0 +1,93 @@
+[Machine]
+# Bunches per turn of accelerator (integer value)
+bunches_per_turn = 184
+# Save headerinformation in file (bool value)
+save_header = True
+# Revolution time (double value)
+tRev =3.6825476109875985e-07
+
+[Ui]
+# NOTE: This value will be overwritten when the language is changed in the gui-settings
+# possible languages:
+# en_GB - English
+# de_DE - German
+language ="en_GB"
+
+# default_save_location: use "pwd" for current working
+# directory KCG will always save in a subdirectory to
+# this given path and save files in this directory
+default_save_location = "pwd"
+
+# default_subdirectory_name_format: this is the
+# naming scheme for the subdirectory in which the
+# files are saved. Format of this string:
+# "{tag1}text{tag2}text" etc.
+# possible tags:
+# {dateG} will produce e.g. 04.01.2015
+# {dateGd} will produce e.g. 04_01_2015
+# {dateA} will pdoduce e.g. 01-04-2015
+# {times} will produce e.g. 14_04
+# {timel} will produce e.g. 14_04_12
+# {d} the Day in 2 digit format
+# {m} the Month in 2 digit format
+# {y} the Year in 4 digit Format
+# {H} the Hour in 2 digit format
+# {M} the minute in 2 digit format
+# {S} the seconds in 2 digit format
+# {timestamp} unix timestamp without msec
+# {user} the current logged in user
+# {sessionname} Ask for session name at startup
+# {ask} always ask for a foldername
+#default_subdirectory_name = "{user}_{dateGd}-{timel}_{sessionname}"
+default_subdirectory_name = "{sessionname}"
+
+# reask on cancel in dialog or use {user}_{dateGd}-{timel} as default when cancel is pressed?
+force_ask = False
+
+# Show advanced table view per default? (boolean value)
+show_advanced_control = False
+
+[Logging]
+# These are PVs that will be possible to insert into log files
+# This variable is to be a list consisting of touples of two entries,
+# the first ist the Text that describes the value and the second is the EPICS PV that
+# holds that value
+epics_log_entry_pvs = [
+    ("Beam Energy (GeV)", "A:SR:BeamInfo:01:Energy"),
+    ("Beam Current (mA)", "A:SR:BeamInfo:01:Current"),
+    ("Beam Lifetime (s)", "A:SR:BeamInfo:01:Lifetime"),
+    ("Beam Lifetime (H:M:S)", "A:SR:BeamInfo:01:Lifetime:String"),
+    ("Injection Rate (mA/s)", "A:SR:BeamInfo:01:InjRate"),
+    ("Injection Rate (mA/5s)", "A:SR:BeamInfo:01:InjRate:5s"),
+    ("Fill Number", "A:SR:OperationStatus:01:FillNumber")
+    ]
+# This pv is used to determine if epics pvs are accessible
+epics_test_pv = "A:SR:BeamInfo:01:Current"
+# Path to your epics base installation
+epics_base_path = "/opt/epics/base/"
+
+# List of Entries that are default to save in Log
+# Possible Values are:
+# "Number of Orbits"
+# "Number of Skipped Orbits"
+# "Number of Acquisitions"
+# "Time between Acquisitions"
+# "Pilot Bunch Simulator"
+# "Header saved"
+# "T/H Delay"
+# "ADC 1 Delay"
+# "ADC 2 Delay"
+# "ADC 3 Delay"
+# "ADC 4 Delay"
+# All of the description text entries in epics_log_entry_pvs, see above
+# NOTE: These entries have to match the aforementioned strings exactly
+
+default_log_entries = [
+    "Number of Orbits",
+    "Number of Skipped Orbits",
+    "Number of Acquisitions",
+    "Time between Acquisitions",
+    "Fill Number",
+    "Beam Energy (GeV)",
+    "Beam Current (mA)"
+    ]

+ 0 - 18
epics.cfg

@@ -1,18 +0,0 @@
-[EPICS]
-# These are PVs that will be possible to insert into log files
-# This variable is to be a list consisting of touples of three entries,
-# the first ist the Text that describes the value and the second is the EPICS PV that
-# holds that value. The third sets the PV to be by default aktivated for logging
-epics_log_entry_pvs = [
-    ("Beam Energy (GeV)", "A:SR:BeamInfo:01:Energy", "True"),
-    ("Beam Current (mA)", "A:SR:BeamInfo:01:Current", "True"),
-    ("Beam Lifetime (s)", "A:SR:BeamInfo:01:Lifetime", "False"),
-    ("Beam Lifetime (H:M:S)", "A:SR:BeamInfo:01:Lifetime:String", "False"),
-    ("Injection Rate (mA/s)", "A:SR:BeamInfo:01:InjRate", "False"),
-    ("Injection Rate (mA/5s)", "A:SR:BeamInfo:01:InjRate:5s", "False"),
-    ("Fill Number", "A:SR:OperationStatus:01:FillNumber", "True")
-    ]
-# This pv is used to determine if epics pvs are accessible
-epics_test_pv = "A:SR:BeamInfo:01:Current"
-# Path to your epics base installation
-epics_base_path = "/opt/epics/base/"

+ 0 - 4
kcg.py

@@ -1,7 +1,3 @@
 #!/usr/bin/python
-import sip #needed for pyQT4 to run with python 3
-sip.setapi('QString', 1)
-sip.setapi('QStringList', 1)
-
 import KCG.kcg as k
 k.run()

+ 2 - 8
setup.py

@@ -1,17 +1,11 @@
 #!/usr/bin/python
 from setuptools import setup, find_packages
-import time, subprocess
+
 DESCRIPTION = "KCG KAPTURE Control Gui"
-git_count = subprocess.check_output(["git", "rev-list", "--count", "HEAD"]).strip()
-version_time = time.strftime("%d%m%y")
-VERSION = "0.3.2."+git_count+"."+version_time+"-beta"
-with open("KCG/VERSION", 'w') as vfile:
-    vfile.write(VERSION)
 
 setup(
     name='KCG',
-#    version='0.3.2.197.0704-beta',
-    version=VERSION,
+    version='0.3.2.180.0414-beta',
     author='Patrick Schreiber',
     author_email='uldfk@student.kit.edu',
 #    license='GPL',