Repository: lukingroup/pylabnet (language: python)

Issue:
New pylabnet icon
Added new pylabnet icon
Old devices.ico is still there for alternative preference.
![image](https://github.com/user-attachments/assets/01faa738-03aa-4452-8134-92f91cac0429)



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 39062368..260c6588 100644
--- a/.gitignore
+++ b/.gitignore
@@ -164,3 +164,4 @@ pylabnet/utils/iq_upconversion/results/*
 temp/*
 pylabnet/YOUR_PYRPL_DESTINATION_FOLDER/*
 
+*.ttbin
\ No newline at end of file
diff --git a/README.md b/README.md
index bf1aa990..8e2a0a8c 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Client-server, Python-based laboratory software
 
 [![DOI](https://zenodo.org/badge/219227835.svg)](https://zenodo.org/badge/latestdoi/219227835)
 
- ![Devices](https://raw.githubusercontent.com/lukingroup/pylabnet/master/devices.ico)
+ ![Devices](https://raw.githubusercontent.com/lukingroup/pylabnet/master/pylabnet_newlogo.ico)
 
 This is the repository for pylabnet, a software package for client-server, Python-based experiment control, designed for use in solid-state quantum optics and quantum network experiments in the Lukin group.
 
@@ -110,7 +110,7 @@ You will likely need to allow Python through Windows Firewall the first time you
 
 ## Usage
 
-The main executables used for launching the Launch Control GUI are `pylabnet_master` and `pylabnet_staticproxy` located in the folder `<env_path>\Scripts` (Windows) or `<env_path>/bin` (Linux). If desired, you can create shortcuts for these executables together with the `devices.ico` icon (shown above) for bonus style.
+The main executables used for launching the Launch Control GUI are `pylabnet_master` and `pylabnet_staticproxy` located in the folder `<env_path>\Scripts` (Windows) or `<env_path>/bin` (Linux). If desired, you can create shortcuts for these executables together with the `pylabnet_newlogo.ico` icon (shown above) for bonus style.
 
 The master Launch Control `pylabnet_master` starts a `LogServer` and also keeps track of all servers on the network, while proxy Launch Control instances simply connect to the master logger and mirror its information for convenience on remote machines. The general workflow for working with Launch Control is the following:
 
diff --git a/demo/NI_cDaq/cDAQ_server.ipynb b/demo/NI_cDaq/cDAQ_server.ipynb
index 76b4cddc..38c4801e 100644
--- a/demo/NI_cDaq/cDAQ_server.ipynb
+++ b/demo/NI_cDaq/cDAQ_server.ipynb
@@ -24,13 +24,29 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 2,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "c:\\users\\brain\\pylabnet\\pylabnet\\utils\\helper_methods.py:941: ResourceWarning: unclosed file <_io.TextIOWrapper name='c:\\\\users\\\\brain\\\\pylabnet\\\\pylabnet\\\\configs\\\\network_configuration.json' mode='r' encoding='cp1252'>\n",
+      "  subnet = load_config('network_configuration')['subnet']\n",
+      "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n"
+     ]
+    }
+   ],
    "source": [
+    "# logger = LogClient(\n",
+    "#     host='140.247.189.82', \n",
+    "#     port=36864, \n",
+    "#     module_tag='DAQ Log Server'\n",
+    "# )\n",
+    "\n",
     "logger = LogClient(\n",
-    "    host='140.247.189.82', \n",
-    "    port=36864, \n",
+    "    host='172.22.16.1', \n",
+    "    port=40035, \n",
     "    module_tag='DAQ Log Server'\n",
     ")"
    ]
@@ -44,29 +60,29 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
-    "device_name = 'PXI1Slot6'\n",
+    "device_name = 'PXI1Slot6_2'\n",
     "\n",
     "daq = nidaqmx.Driver(device_name=device_name, logger=logger)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 34,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
-     "output_type": "execute_result",
      "data": {
       "text/plain": [
-       "[1.4884824975992295]"
+       "[2.3780943220817274]"
       ]
      },
+     "execution_count": 6,
      "metadata": {},
-     "execution_count": 34
+     "output_type": "execute_result"
     }
    ],
    "source": [
@@ -75,27 +91,27 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [],
    "source": [
-    "device_name = 'PXI1Slot4'\n",
+    "device_name = 'PXI1Slot4_2'\n",
     "daq_ao = nidaqmx.Driver(device_name=device_name, logger=logger)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
-    "daq_ao.set_ao_voltage('ao2', 0)"
+    "daq_ao.set_ao_voltage('ao2', 1.5)"
    ]
   },
   {
-   "source": [],
    "cell_type": "markdown",
-   "metadata": {}
+   "metadata": {},
+   "source": []
   },
   {
    "cell_type": "markdown",
@@ -248,9 +264,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.3-final"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
  "nbformat_minor": 2
-}
\ No newline at end of file
+}
diff --git a/demo/ifm/ifm_client.ipynb b/demo/ifm/ifm_client.ipynb
index c12d2379..fd369c40 100644
--- a/demo/ifm/ifm_client.ipynb
+++ b/demo/ifm/ifm_client.ipynb
@@ -2,7 +2,775 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 152,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "import time\n",
+    "import numpy as np\n",
+    "import sys\n",
+    "import traceback\n",
+    "import re\n",
+    "import os\n",
+    "import importlib.util\n",
+    "from pylabnet.utils.logging import logger\n",
+    "from pylabnet.utils.helper_methods import get_ip, parse_args, hide_console, create_server, load_config, load_script_config, load_device_config, launch_device_server\n",
+    "from pylabnet.network.client_server import external_gui\n",
+    "from pylabnet.network.core.service_base import ServiceBase\n",
+    "from pylabnet.network.core.generic_server import GenericServer\n",
+    "from pylabnet.gui.pyqt.external_gui import ParameterPopup, fresh_popup, warning_popup\n",
+    "from pylabnet.network.client_server import thorlabs_pm320e\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 153,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "ModuleSpec(name='thorlabs_pm320e', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000020F13A8CAC0>, origin='C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\launchers\\\\servers\\\\thorlabs_pm320e.py')\n"
+     ]
+    }
+   ],
+   "source": [
+    "\n",
+    "module = 'thorlabs_pm320e'\n",
+    "server = module\n",
+    "\n",
+    "spec = importlib.util.spec_from_file_location(\n",
+    "    module,\n",
+    "    os.path.join(\n",
+    "        os.path.dirname(os.path.realpath('./../')),\n",
+    "        'pylabnet',\n",
+    "        'launchers',\n",
+    "        'servers',\n",
+    "        module + '.py'\n",
+    "    )\n",
+    ")\n",
+    "\n",
+    "print(spec)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 154,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mod = importlib.util.module_from_spec(spec)\n",
+    "spec.loader.exec_module(mod)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 155,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 155,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client = mod.Client(host='192.168.50.102', port=14928)\n",
+    "isinstance(pm_client, thorlabs_pm320e.Client)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 156,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<module 'thorlabs_pm320e' from 'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\launchers\\\\servers\\\\thorlabs_pm320e.py'>"
+      ]
+     },
+     "execution_count": 156,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "mod"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 157,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "pylabnet.network.client_server.thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 157,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "mod.Client"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 158,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "8.40439315e-11"
+      ]
+     },
+     "execution_count": 158,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client.get_power(1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 159,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "pylabnet.network.client_server.thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 159,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(pm_client)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 164,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import imp\n",
+    "mypath = 'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 165,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import imp\n",
+    "\n",
+    "mod2 = imp.load_source(module, mypath + module + \".py\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 166,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 166,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client_2 = mod2.Client(host='192.168.50.102', port=14928)\n",
+    "isinstance(pm_client_2, thorlabs_pm320e.Client)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 167,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "2263777232208"
+      ]
+     },
+     "execution_count": 167,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "id(pm_client_2)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 168,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3.75808343e-11"
+      ]
+     },
+     "execution_count": 168,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client_2.get_power(1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 169,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "pylabnet.network.client_server.thorlabs_pm320e\n"
+     ]
+    }
+   ],
+   "source": [
+    "module = 'thorlabs_pm320e'\n",
+    "full_module_name = 'pylabnet.network.client_server.' + module\n",
+    "print(full_module_name)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 200,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "ModuleSpec(name='thorlabs_pm320e', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000020F13A9A820>, origin='C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e.py')\n"
+     ]
+    }
+   ],
+   "source": [
+    "\n",
+    "server = module\n",
+    "\n",
+    "spec1 = importlib.util.spec_from_file_location(\n",
+    "    module,\n",
+    "    os.path.join(\n",
+    "        os.path.dirname(os.path.realpath('./../')),\n",
+    "        'pylabnet',\n",
+    "        'network',\n",
+    "        'client_server',\n",
+    "        module + '.py'\n",
+    "    )\n",
+    ")\n",
+    "\n",
+    "print(spec1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 201,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mod1 = importlib.util.module_from_spec(spec1)\n",
+    "spec1.loader.exec_module(mod1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 202,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<module 'thorlabs_pm320e' from 'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e.py'>"
+      ]
+     },
+     "execution_count": 202,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "mod1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 208,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 208,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "mod1.Client"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 204,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 204,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client_1 = mod1.Client(host='192.168.50.102', port=14928)\n",
+    "\n",
+    "from pylabnet.network.client_server import thorlabs_pm320e\n",
+    "isinstance(pm_client_1, thorlabs_pm320e.Client)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 205,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 205,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(pm_client_1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 176,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "pylabnet.network.client_server.thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 176,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(pm_client)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 206,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "4.98796351e-11"
+      ]
+     },
+     "execution_count": 206,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pm_client_1.get_power(1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 182,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "<class 'pylabnet.network.client_server.thorlabs_pm320e.Client'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "print( type(pm_client_1) )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 179,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "<class 'pylabnet.network.client_server.thorlabs_pm320e.Client'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "print( type(pm_client) )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 181,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "pylabnet.network.client_server.thorlabs_pm320e.Client"
+      ]
+     },
+     "execution_count": 181,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 196,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 196,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import pylabnet.network.client_server.thorlabs_pm320e as tmp\n",
+    "type(pm_client) is tmp.Client"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 194,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 211,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e.py'"
+      ]
+     },
+     "execution_count": 211,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "mypath + module + '.py'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 212,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ModuleNotFoundError",
+     "evalue": "No module named 'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e'",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
+      "\u001b[1;32m<ipython-input-212-8c5c1240383e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m class_ = getattr(importlib.import_module(mypath + module + '.py'), \\\n\u001b[0m\u001b[0;32m      2\u001b[0m                  'Client')\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\__init__.py\u001b[0m in \u001b[0;36mimport_module\u001b[1;34m(name, package)\u001b[0m\n\u001b[0;32m    125\u001b[0m                 \u001b[1;32mbreak\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    126\u001b[0m             \u001b[0mlevel\u001b[0m \u001b[1;33m+=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 127\u001b[1;33m     \u001b[1;32mreturn\u001b[0m \u001b[0m_bootstrap\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_gcd_import\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mlevel\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpackage\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlevel\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    128\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    129\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_gcd_import\u001b[1;34m(name, package, level)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_find_and_load\u001b[1;34m(name, import_)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_find_and_load_unlocked\u001b[1;34m(name, import_)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_call_with_frames_removed\u001b[1;34m(f, *args, **kwds)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_gcd_import\u001b[1;34m(name, package, level)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_find_and_load\u001b[1;34m(name, import_)\u001b[0m\n",
+      "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python38\\lib\\importlib\\_bootstrap.py\u001b[0m in \u001b[0;36m_find_and_load_unlocked\u001b[1;34m(name, import_)\u001b[0m\n",
+      "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'C:\\\\Users\\\\Roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e'"
+     ]
+    }
+   ],
+   "source": [
+    "class_ = getattr(importlib.import_module(mypath + module + '.py'), \\\n",
+    "                 'Client')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 216,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<module 'pylabnet.network.client_server.thorlabs_pm320e' from 'c:\\\\users\\\\roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e.py'>"
+      ]
+     },
+     "execution_count": 216,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "test = mypath + module + '.py'\n",
+    "test1 = \"pylabnet.network.client_server.thorlabs_pm320e\"\n",
+    "\n",
+    "importlib.import_module(test1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 74,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "pylabnet.network.client_server.thorlabs_pm320e\n"
+     ]
+    }
+   ],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 78,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "module = importlib.import_module(test_str)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 79,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<module 'pylabnet.network.client_server.thorlabs_pm320e' from 'c:\\\\users\\\\roger\\\\pylabnet\\\\pylabnet\\\\network\\\\client_server\\\\thorlabs_pm320e.py'>"
+      ]
+     },
+     "execution_count": 79,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "module"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -19,6 +787,40 @@
     "import time"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "pylabnet.network.client_server.ifm.Client"
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ifm.Client"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -143,11 +945,8 @@
   }
  ],
  "metadata": {
-  "interpreter": {
-   "hash": "5c0372ed38b372118c24adb00d45654d76c8d10261533c5724e3f5fc1d75489a"
-  },
   "kernelspec": {
-   "display_name": "Python 3.8.8 64-bit",
+   "display_name": "env",
    "language": "python",
    "name": "python3"
   },
@@ -161,14 +960,19 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.8"
+   "version": "3.8.8rc1"
   },
   "metadata": {
    "interpreter": {
     "hash": "ae9bda92a3193a9528aa9c24f8faf75759760232ca9dd8afb106f849eeb1a235"
    }
   },
-  "orig_nbformat": 3
+  "orig_nbformat": 3,
+  "vscode": {
+   "interpreter": {
+    "hash": "841d85210f60d2f5a71540c9c0103733ac8aa5dde32f2591f9574d67eac287fa"
+   }
+  }
  },
  "nbformat": 4,
  "nbformat_minor": 2
diff --git a/demo/timetagger/timetagger.ipynb b/demo/timetagger/timetagger.ipynb
new file mode 100644
index 00000000..7c802057
--- /dev/null
+++ b/demo/timetagger/timetagger.ipynb
@@ -0,0 +1,254 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<style>.container { width:100% !important; }</style>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "import TimeTagger as TT\n",
+    "from datetime import datetime\n",
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "from IPython.display import display, HTML\n",
+    "display(HTML(\"<style>.container { width:100% !important; }</style>\"))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "('222300114Y',)\n",
+      "()\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(TT.scanTimeTagger())\n",
+    "tagger = TT.createTimeTagger('222300114Y')\n",
+    "print(TT.scanTimeTagger())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tagger.setTriggerLevel(1, 0.1)\n",
+    "tagger.setTriggerLevel(2, 0.1)\n",
+    "tagger.setTriggerLevel(7, 0.1)\n",
+    "tagger.enableFpgaLink(channels=[1, 2, 8, -8], destination_mac=\"\", interface=TT.FpgaLinkInterface_SFPP_10GE)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "()\n"
+     ]
+    }
+   ],
+   "source": [
+    "# del tagger\n",
+    "print(TT.scanTimeTagger())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### TimeTag Stream"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 76,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Starting timetag stream...\n"
+     ]
+    }
+   ],
+   "source": [
+    "tts = TT.TimeTagStream(tagger, 1000, [1, 2, -8, 8])\n",
+    "print(\"Starting timetag stream...\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 77,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Stopped timetag stream!\n"
+     ]
+    }
+   ],
+   "source": [
+    "tts_data = tts.getData()\n",
+    "tts.stop()\n",
+    "print(\"Stopped timetag stream!\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 78,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[[                8 22848514224010350]\n",
+      " [                1 22848768034445169]\n",
+      " [                1 22849106911344054]\n",
+      " [                1 22849326068236259]\n",
+      " [               -8 22849514204046365]\n",
+      " [                1 22849731738382742]\n",
+      " [                1 22849769848122992]\n",
+      " [                1 22849896520291676]\n",
+      " [                8 22850514224128769]\n",
+      " [                1 22850527084791735]]\n"
+     ]
+    }
+   ],
+   "source": [
+    "data  = np.array(list(zip(tts_data.getChannels(), tts_data.getTimestamps()))).astype(np.int64)\n",
+    "print(data[:10, :])\n",
+    "\n",
+    "np.savetxt(\"ttsream.txt\", data, fmt=\"%d\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### TimeTag FileWriter"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Starting 20230515-205921...\n"
+     ]
+    }
+   ],
+   "source": [
+    "from datetime import datetime\n",
+    "dt = datetime.now()\n",
+    "timestamp = dt.strftime(\"%Y%m%d-%H%M%S\")\n",
+    "print(f\"Starting {timestamp}...\")\n",
+    "\n",
+    "fw = TT.FileWriter(tagger, f'{timestamp}.ttbin', [1, 2, -8, 8])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Stopped 20230515-205921.\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(f\"Stopped {timestamp}.\")\n",
+    "fw.stop()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print(f\"Reading {timestamp}...\")\n",
+    "fr = TT.FileReader(f\"{timestamp}.ttbin\")\n",
+    "\n",
+    "counter = 0\n",
+    "while fr.hasData():\n",
+    "    data = fr.getData(1000) \n",
+    "    ts = data.getTimestamps() / 1E12\n",
+    "    ch = data.getChannels()\n",
+    "    \n",
+    "#     with open(\"testing.txt\", \"w\") as f:\n",
+    "#         for c, t in zip(ch, ts):\n",
+    "#             f.write(f\"{c} {t:.15f}\\n\")\n",
+    "\n",
+    "    for c, t in zip(ch, ts):\n",
+    "        print(c, t)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 65,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/demo/timetagger/ttsream.txt b/demo/timetagger/ttsream.txt
new file mode 100644
index 00000000..9e3117cf
--- /dev/null
+++ b/demo/timetagger/ttsream.txt
@@ -0,0 +1,132 @@
+8 22848514224010350
+1 22848768034445169
+1 22849106911344054
+1 22849326068236259
+-8 22849514204046365
+1 22849731738382742
+1 22849769848122992
+1 22849896520291676
+8 22850514224128769
+1 22850527084791735
+1 22851172160884820
+1 22851172761736375
+1 22851224034642427
+1 22851333246585231
+1 22851377240882894
+1 22851377240910861
+1 22851477066814167
+-8 22851514204163024
+8 22852514224246563
+1 22852688353278113
+1 22852688353306165
+1 22853137307462705
+1 22853443069131634
+-8 22853514204280610
+1 22853588452794383
+1 22853639012686494
+1 22853870441695021
+1 22853898890340918
+1 22854153707935794
+1 22854153707963723
+1 22854179272771913
+1 22854281565180896
+1 22854343678121908
+1 22854343678150277
+8 22854514224365705
+-8 22855514204398957
+1 22855590081895842
+1 22856013152833750
+1 22856251256802838
+1 22856251256830909
+8 22856514224483676
+1 22856635637122929
+1 22856803814506756
+1 22856803814534536
+1 22857041774760978
+1 22857251030011171
+1 22857440829967289
+1 22857440829995507
+1 22857489994845245
+1 22857505511094348
+-8 22857514204518414
+1 22857544612308606
+1 22857623858927353
+1 22857623858955381
+1 22857766492206855
+1 22857766492234618
+1 22857899290737707
+1 22858001453058995
+1 22858061008630557
+1 22858061008658315
+1 22858122321248097
+1 22858122321276069
+8 22858514224600693
+1 22859092147749317
+1 22859092147777247
+-8 22859514204633759
+1 22859730857240793
+1 22859732286809150
+1 22859734906909338
+1 22859850382436713
+1 22860002212434387
+1 22860053703673116
+1 22860270375650968
+1 22860445613151895
+1 22860445613179345
+8 22860514224719312
+1 22860651151653384
+1 22860719280239669
+1 22860801813474020
+1 22860801813501607
+1 22861022675199991
+1 22861022675227882
+1 22861048976414861
+1 22861338305845011
+1 22861338305872597
+1 22861423393664689
+-8 22861514204753863
+1 22861679655720605
+1 22862045351219579
+1 22862117981437717
+1 22862250099153369
+1 22862482579251232
+1 22862482579279009
+8 22862514224837673
+1 22862684351505021
+1 22862799396076654
+1 22862799396104428
+1 22862901049641388
+1 22863341519927137
+-8 22863514204873709
+1 22863655798250268
+1 22863722167812301
+1 22863831445445960
+1 22864195245166754
+1 22864330861303903
+1 22864330861331795
+8 22864514224955961
+1 22864651189178423
+1 22864651189206281
+1 22864697270038013
+1 22864721703571305
+1 22865463976951411
+1 22865509898859179
+-8 22865514204990694
+1 22865517429805378
+1 22865877773967269
+1 22865877773995093
+1 22865879005054207
+1 22865962725956533
+1 22865962725985443
+1 22866045329967850
+1 22866104881336704
+1 22866104881364989
+1 22866243469840163
+1 22866409677233266
+8 22866514225073249
+1 22866525461568408
+1 22866716066347353
+1 22866827656868634
+1 22867045794934393
+-8 22867514205109072
+1 22867530721483799
diff --git a/pylabnet/gui/pyqt/external_gui.py b/pylabnet/gui/pyqt/external_gui.py
index f36a3eae..ae417b94 100644
--- a/pylabnet/gui/pyqt/external_gui.py
+++ b/pylabnet/gui/pyqt/external_gui.py
@@ -92,7 +92,7 @@ def __init__(self, app=None, gui_template=None, run=True, host=None, port=None,
                 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
             self.app = QtWidgets.QApplication(sys.argv)
             self.app.setWindowIcon(
-                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
             )
 
         # Initialize parent class QWidgets.QMainWindow
@@ -878,7 +878,7 @@ def return_params(self):
 
 
 class GraphPopup(QtWidgets.QWidget):
-    """ Widget class for holding new graphs """
+    """ Widget class for holding new graphs in tabbed mode """
 
     def __init__(self, **kwargs):
 
@@ -899,6 +899,57 @@ def __init__(self, **kwargs):
         self.show()
 
 
+class GraphPopupTabs(QtWidgets.QWidget):
+    """ Widget class for holding new graphs """
+
+    def __init__(self, **kwargs):
+
+        QtWidgets.QWidget.__init__(self)
+
+        if 'window_title' in kwargs:
+            window_title = kwargs['window_title']
+        else:
+            window_title = 'Graph Holder'
+
+        if 'size' in kwargs:
+            self.setMinimumSize(*kwargs['size'])
+
+        self.setWindowTitle(window_title)
+
+        self.tabs_enabled = True
+
+        self.outer_layout = QtWidgets.QVBoxLayout()
+        self.outer_layout.setContentsMargins(30, 30, 30, 30)
+
+        # keep track of number of tabs that are filled
+        self.num_tabs = 0
+
+        # Prep first tab
+        self.tabs = QtWidgets.QTabWidget()
+        self.tab1 = QtWidgets.QWidget()
+        self.tabs.addTab(self.tab1, kwargs["tablabel"])
+
+        self.outer_layout.addWidget(self.tabs)
+
+        self.setWindowTitle(window_title)
+        self.setLayout(self.outer_layout)
+
+        # Prep the Graphlayout for
+        self.tab1.GraphLayout = QtWidgets.QVBoxLayout()
+        self.tab1.setLayout(self.tab1.GraphLayout)
+
+        self.show()
+
+    def add_graph_to_new_tab(self, graph, label):
+        new_tab = QtWidgets.QWidget()
+        self.tabs.addTab(new_tab, label)
+        new_tab.GraphLayout = QtWidgets.QVBoxLayout()
+        new_tab.setLayout(new_tab.GraphLayout)
+        new_tab.GraphLayout.addWidget(graph)
+        self.num_tabs += 1
+        return
+
+
 class Plot:
     """ Class for plot widgets inside of a Window
 
@@ -1389,6 +1440,101 @@ def upload_pic(self):
         return
 
 
+class Confluence_support_GraphPopupTabs(QtWidgets.QWidget):
+    """ Widget class for holding new graphs """
+
+    def __init__(self, **kwargs):
+
+        QtWidgets.QWidget.__init__(self)
+
+        self.tabs_enabled = True
+
+        # self.app = app
+
+        if 'window_title' in kwargs:
+            window_title = kwargs['window_title']
+        else:
+            window_title = 'Graph Holder'
+
+        if 'log' in kwargs:
+            self.log = kwargs['log']
+        else:
+            self.log = None
+
+        if 'app' in kwargs:
+            self.app = kwargs['app']
+        else:
+            self.app = None
+
+        if 'size' in kwargs:
+            self.setMinimumSize(*kwargs['size'])
+
+        # Confluence handler and its button
+        enable_confluence = True
+
+        self.confluence_handler = None
+
+        if(self.log is None):
+            enable_confluence = False
+
+        if(enable_confluence is True):
+            self.confluence_handler = Confluence_Handler(self, self.app, log_client=self.log)
+
+            extractAction_Upload = QtWidgets.QAction("&UPLOAD to CONFLUENCE", self)
+            extractAction_Upload.setShortcut("Ctrl+S")
+            extractAction_Upload.setStatusTip('Upload to the confluence page')
+            extractAction_Upload.triggered.connect(self.upload_pic)
+
+            extractAction_Update = QtWidgets.QAction("&CONFLUENCE SETTING", self)
+            extractAction_Update.setShortcut("Ctrl+X")
+            extractAction_Update.setStatusTip('The space and page names of confluence')
+            extractAction_Update.triggered.connect(self.update_setting)
+
+            mainMenu = QtWidgets.QMenuBar(self)
+
+            ActionMenu = mainMenu.addMenu('&Action')
+            ActionMenu.addAction(extractAction_Upload)
+            ActionMenu.addAction(extractAction_Update)
+
+        self.outer_layout = QtWidgets.QVBoxLayout()
+        self.outer_layout.setContentsMargins(30, 30, 30, 30)
+
+        # keep track of number of tabs that are filled
+        self.num_tabs = 0
+
+        # Prep first tab
+        self.tabs = QtWidgets.QTabWidget()
+        self.tab1 = QtWidgets.QWidget()
+        self.tabs.addTab(self.tab1, kwargs["tablabel"])
+
+        self.outer_layout.addWidget(self.tabs)
+
+        self.setWindowTitle(window_title)
+        self.setLayout(self.outer_layout)
+
+        # Prep the Graphlayout for
+        self.tab1.GraphLayout = QtWidgets.QVBoxLayout()
+        self.tab1.setLayout(self.tab1.GraphLayout)
+
+        self.show()
+
+    def update_setting(self):
+        self.confluence_handler.confluence_popup.Popup_Update()
+
+    def upload_pic(self):
+        self.confluence_handler.confluence_popup.Popup_Upload()
+        return
+
+    def add_graph_to_new_tab(self, graph, label):
+        new_tab = QtWidgets.QWidget()
+        self.tabs.addTab(new_tab, label)
+        new_tab.GraphLayout = QtWidgets.QVBoxLayout()
+        new_tab.setLayout(new_tab.GraphLayout)
+        new_tab.GraphLayout.addWidget(graph)
+        self.num_tabs += 1
+        return
+
+
 class Confluence_Handler():
     """ Handle the gui's confluence handler except main window (log server) """
 
@@ -1456,7 +1602,7 @@ def __init__(self, parent_wins, app, log_client=None, template="Confluence_info_
         #         ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
         #     self.app = QtWidgets.QApplication(sys.argv)
         #     self.app.setWindowIcon(
-        #         QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+        #         QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
         #     )
 
         # Initialize parent class QtWidgets.QDialog
@@ -1865,7 +2011,7 @@ def __init__(self, controller, app, template='Confluence_info_from_LaunchControl
                 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
             self.app = QtWidgets.QApplication(sys.argv)
             self.app.setWindowIcon(
-                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
             )
 
         # confluence
@@ -2047,7 +2193,7 @@ def fresh_popup(**params):
     app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
     operating_system = get_os()
     app.setWindowIcon(
-        QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+        QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
     )
     if operating_system == 'Windows':
         ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
@@ -2065,7 +2211,7 @@ def warning_popup(message):
     app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
     operating_system = get_os()
     app.setWindowIcon(
-        QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+        QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
     )
     if operating_system == 'Windows':
         ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
diff --git a/pylabnet/gui/pyqt/pylabnet_newlogo.ico b/pylabnet/gui/pyqt/pylabnet_newlogo.ico
new file mode 100644
index 00000000..66d4e886
Binary files /dev/null and b/pylabnet/gui/pyqt/pylabnet_newlogo.ico differ
diff --git a/pylabnet/hardware/awg/zi_hdawg.py b/pylabnet/hardware/awg/zi_hdawg.py
index eca1c709..6eaeef57 100644
--- a/pylabnet/hardware/awg/zi_hdawg.py
+++ b/pylabnet/hardware/awg/zi_hdawg.py
@@ -43,7 +43,7 @@
 
 class Driver():
 
-    def __init__(self, device_id, logger, dummy=False, api_level=6, reset_dio=False, disable_everything=False, **kwargs):
+    def __init__(self, device_id, interface, logger, dummy=False, api_level=6, reset_dio=False, disable_everything=False, **kwargs):
         """ Instantiate AWG
 
         :logger: instance of LogClient class
@@ -58,7 +58,7 @@ def __init__(self, device_id, logger, dummy=False, api_level=6, reset_dio=False,
         self.dummy = dummy
 
         # Setup HDAWG
-        self._setup_hdawg(device_id, logger, api_level, reset_dio, disable_everything)
+        self._setup_hdawg(device_id, interface, logger, api_level, reset_dio, disable_everything)
 
     @dummy_wrap
     def reset_DIO_outputs(self):
@@ -92,15 +92,13 @@ def _convert_to_list(self, input_argument):
         return input_argument
 
     @dummy_wrap
-    def _setup_hdawg(self, device_id, logger, api_level, reset_dio, disable_everything):
+    def _setup_hdawg(self, device_id, interface, logger, api_level, reset_dio, disable_everything):
         ''' Sets up HDAWG '''
         # try finding the server address
         discovery = zhinst.ziPython.ziDiscovery()
         device_properties = discovery.get(discovery.find(device_id))
         server_address = device_properties["serveraddress"]
 
-        interface = "1GbE"  # For Ethernet connection.
-
         server_host = "localhost"
         server_port = 8004
         api_level = 6  # Maximum API level supported for all instruments.
diff --git a/pylabnet/hardware/mcc_usb_daq/mcc_usb_1608GX.py b/pylabnet/hardware/mcc_usb_daq/mcc_usb_1608GX.py
new file mode 100644
index 00000000..f1195136
--- /dev/null
+++ b/pylabnet/hardware/mcc_usb_daq/mcc_usb_1608GX.py
@@ -0,0 +1,84 @@
+import numpy as np
+
+from pylabnet.utils.logging.logger import LogHandler
+from mcculw import ul
+from mcculw.enums import ULRange, InterfaceType, DigitalIODirection, DigitalPortType, AnalogInputMode
+from mcculw.ul import ULError
+
+RESOLUTION = 16 #bits
+
+
+class Driver:
+    """Driver for Measurement Computing USB-1608GX AO/DIO device.
+    Make sure InstaCal is installed to access the device.
+    """
+
+    def __init__(self, device_id, board_number, logger=None, dummy=False):
+        """Instantiate USB-1608GX board
+
+        :device_id: (str) S/N of specific USB-1608GX board
+        :board_number: (str) chosen board number used to identify this board
+        """
+
+        # Device name
+        self.d_id = device_id
+        self.bn = int(board_number)
+
+        # Log
+        self.log = LogHandler(logger=logger)
+
+        # ignore_instacal() is used as recommended in mcculw examples. It prevents conflicts
+        # with previous setting of DAQ parameters by InstaCal. Reminder that InstaCal MUST be
+        # installed in order to detect the DAQ device. From mcculw documentation:
+        # ignore_instacal():
+        # Prevents the Universal Library from automatically adding a DAQ device that has been stored
+        # in the cb.cfg file by InstaCal. This function must be the first Universal Library function
+        # invoked in the application. Devices can then be added and configured at runtime using the
+        # device discovery features.
+        ul.ignore_instacal()
+
+        devices = ul.get_daq_device_inventory(InterfaceType.ANY)
+        if not devices:
+            self.log.error('Error: No MCC USB DAQ devices found')
+
+        self.log.info('Found ' + str(len(devices)) + ' DAQ device(s):')
+
+        found_device = False
+
+        for device in devices:
+            if (device.product_name == "USB-1608GX") and (device.unique_id == self.d_id):
+                self.log.info('  ' + str(device.product_name) + ' (' + str(device.unique_id) + ') - ' +
+                              'Device ID = ' + str(device.product_id) + ' -- Match!')
+
+                self.device = device
+                found_device = True
+
+            else:
+                self.log.info('  ' + str(device.product_name) + ' (' + str(device.unique_id) + ') - ' +
+                              'Device ID = ' + str(device.product_id))
+
+        if found_device:
+            ul.create_daq_device(self.bn, self.device)
+            self.log.info('MCC USB-1608GX, device ID: ' + str(self.d_id) + ' set with board number ' + str(self.bn))
+
+        else:
+            self.log.error('Error: No MCC USB-1608GX devices with device ID ' + str(self.d_id) + ' found.')
+
+        #configure all channels to be Single-Ended
+        ul.a_input_mode(self.bn, AnalogInputMode.SINGLE_ENDED)
+
+    #analog input
+
+    def get_ai_voltage(self, ai_channel, range=1):
+        """Get analog input
+
+        :ai_channel: (int) Input channel (0-15) 16 SE or 8 DIFF
+        :voltage: (float) voltage value from -10 V to 10 V
+        :range: (int) 1 (-10 to +10 V) , 0 (-5 to +5 V), 4 (-1 to +1 V), or 14 (-2 to +2 V)
+        """
+        #the a_in function returns the base 10 conversion of the binary value read
+        raw_value = ul.a_in(self.bn, ai_channel, ULRange(range))
+        #so we need to convert it to more useful units
+        value_volts = ul.to_eng_units(self.bn, ULRange(range), raw_value)
+
+        return value_volts
diff --git a/pylabnet/launchers/launch_control.py b/pylabnet/launchers/launch_control.py
index 3c7ba6ea..51ecdc58 100644
--- a/pylabnet/launchers/launch_control.py
+++ b/pylabnet/launchers/launch_control.py
@@ -52,8 +52,6 @@ def __init__(self, app, controller, gui_template=None, run=True):
 
         self.terminal.setReadOnly(True)
 
-
-
     def closeEvent(self, event):
         """ Occurs when window is closed. Overwrites parent class method"""
 
@@ -79,7 +77,7 @@ def __init__(self, proxy=False, master=False, staticproxy=False):
         self.operating_system = get_os()
         self.app = QtWidgets.QApplication(sys.argv)
         self.app.setWindowIcon(
-            QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+            QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
         )
         # Instantiate GUI application
         if self.operating_system == 'Windows':
@@ -408,7 +406,7 @@ def initialize_gui(self):
         self.main_window.logfile_status_button.setHidden(True)
         self.main_window.log_previous.setHidden(True)
         self.main_window.logfile_status_indicator.setEnabled(False)
-        
+
         self.main_window.confluence_update.clicked.connect(self.confluence_info_update)
 
         # Configure list of scripts to run and clicking actions
@@ -932,7 +930,7 @@ def start_stop_logging(self, master_log=False):
                     filepath = config_dict['logger_path']
                 except:
                     self.main_window.terminal.setPlainText('Critical error: '
-                                                      'no logger_path found in static_proxy.json')
+                                                           'no logger_path found in static_proxy.json')
                     self.main_window.force_update()
                     time.sleep(10)
                     raise
@@ -1103,6 +1101,7 @@ def run(self):
 
             self.controller.last_seen_buffer = buffer_terminal
 
+
 def main():
     """ Runs the launch controller """
 
diff --git a/pylabnet/launchers/launcher.py b/pylabnet/launchers/launcher.py
index 75a50983..f5d1669a 100644
--- a/pylabnet/launchers/launcher.py
+++ b/pylabnet/launchers/launcher.py
@@ -229,18 +229,23 @@ def _add_to_clients(self, module, device_id, host, port):
         :param port: (int) port number of server
         """
         server = module
+        try:
+            full_module_name = 'pylabnet.network.client_server.' + module
+            client_class = getattr(importlib.import_module(full_module_name), "Client")
 
-        spec = importlib.util.spec_from_file_location(
-            module,
-            os.path.join(
-                os.path.dirname(os.path.realpath(__file__)),
-                'servers',
-                module + '.py'
+            self.clients[(server, device_id)] = client_class(host=host, port=port)
+        except:
+            spec = importlib.util.spec_from_file_location(
+                module,
+                os.path.join(
+                    os.path.dirname(os.path.realpath(__file__)),
+                    'servers',
+                    module + '.py'
+                )
             )
-        )
-        mod = importlib.util.module_from_spec(spec)
-        spec.loader.exec_module(mod)
-        self.clients[(server, device_id)] = mod.Client(host=host, port=port)
+            mod = importlib.util.module_from_spec(spec)
+            spec.loader.exec_module(mod)
+            self.clients[(server, device_id)] = mod.Client(host=host, port=port)
 
     def _launch_servers(self):
         """ Searches through active servers and connects/launches them """
diff --git a/pylabnet/launchers/pylabnet_newlogo.ico b/pylabnet/launchers/pylabnet_newlogo.ico
new file mode 100644
index 00000000..66d4e886
Binary files /dev/null and b/pylabnet/launchers/pylabnet_newlogo.ico differ
diff --git a/pylabnet/launchers/servers/mcc_usb_1608GX.py b/pylabnet/launchers/servers/mcc_usb_1608GX.py
new file mode 100644
index 00000000..bdc3a175
--- /dev/null
+++ b/pylabnet/launchers/servers/mcc_usb_1608GX.py
@@ -0,0 +1,46 @@
+""" Implements connection and server launching of MCC USB-1608GX DAQ"""
+
+
+from pylabnet.hardware.mcc_usb_daq import mcc_usb_1608GX
+from pylabnet.network.client_server.mcc_usb_1608GX import Service, Client
+from pylabnet.network.core.generic_server import GenericServer
+from pylabnet.utils.helper_methods import get_ip, hide_console, load_config, load_device_config
+
+
+def launch(**kwargs):
+    """ Connects to MCC USB-1608GX DAQ and launches server
+
+    :param kwargs: (dict) containing relevant kwargs
+        :logger: instance of LogClient for logging purposes
+        :port: (int) port number for the Cnt Monitor server
+    """
+
+    # Instantiate driver
+    mcc_usb_1608GX_logger = kwargs['logger']
+    try:
+        config = load_device_config('mcc_usb_1608GX', kwargs['config'], logger=mcc_usb_1608GX_logger)
+
+        mcc_usb_1608GX_driver = mcc_usb_1608GX.Driver(
+            device_id=config['device_id'],
+            board_number=config['board_number'],
+            logger=mcc_usb_1608GX_logger
+        )
+    except AttributeError:
+        mcc_usb_1608GX_logger.error('Please provide valid config file')
+        raise
+    except KeyError:
+        mcc_usb_1608GX_logger.error('No device id or board_number provided. '
+                                    'Please make sure proper config file is provided')
+        raise
+
+    # Instantiate server
+    mcc_usb_1608GX_service = Service()
+    mcc_usb_1608GX_service.assign_module(module=mcc_usb_1608GX_driver)
+    mcc_usb_1608GX_service.assign_logger(logger=mcc_usb_1608GX_logger)
+    mcc_usb_1608GX_server = GenericServer(
+        service=mcc_usb_1608GX_service,
+        host=get_ip(),
+        port=kwargs['port']
+    )
+
+    mcc_usb_1608GX_server.start()
diff --git a/pylabnet/launchers/servers/zi_hdawg.py b/pylabnet/launchers/servers/zi_hdawg.py
index 4b69a078..383206e4 100644
--- a/pylabnet/launchers/servers/zi_hdawg.py
+++ b/pylabnet/launchers/servers/zi_hdawg.py
@@ -9,8 +9,31 @@ def launch(**kwargs):
     # Instantiate driver
     zi_logger = kwargs['logger']
 
-    device_id = load_device_config('zi_hdawg', kwargs['config'], logger=kwargs['logger'])['device_id']
-    hd = Driver(device_id, zi_logger)
+    config_dict = load_device_config('zi_hdawg', kwargs['config'], logger=kwargs['logger'])
+
+    if('device_id' in config_dict):
+        device_id = config_dict['device_id']
+    else:
+        zi_logger.error(
+            f"A device ID for the HDAWG is required! Please ensure there is a 'device_id' key-value pair in the config file."
+        )
+        device_id = None
+
+    if('interface' in config_dict):
+        interface = config_dict['interface'] #1GbE, USB etc.
+    else:
+        zi_logger.error(
+            f"An interface for the HDAWG is required! Please ensure there is an 'interface' key-value pair in the config file."
+        )
+        interface = None
+
+    if(interface is None or device_id is None):
+        zi_logger.error(
+            f"Missing required parameters in the config file. Failed to launch the zi_hdawg server."
+        )
+        return
+
+    hd = Driver(device_id, interface, zi_logger)
 
     # Instantiate server
     hd_service = Service()
diff --git a/pylabnet/network/client_server/mcc_usb_1608GX.py b/pylabnet/network/client_server/mcc_usb_1608GX.py
new file mode 100644
index 00000000..e4edd560
--- /dev/null
+++ b/pylabnet/network/client_server/mcc_usb_1608GX.py
@@ -0,0 +1,21 @@
+
+from pylabnet.network.core.service_base import ServiceBase
+from pylabnet.network.core.client_base import ClientBase
+
+
+class Service(ServiceBase):
+
+    def exposed_get_ai_voltage(self, ai_channel, range):
+        return self._module.get_ai_voltage(
+            ai_channel=ai_channel,
+            range=range
+        )
+
+
+class Client(ClientBase):
+
+    def get_ai_voltage(self, ai_channel, range=1):
+        return self._service.exposed_get_ai_voltage(
+            ai_channel=ai_channel,
+            range=range
+        )
diff --git a/pylabnet/network/core/client_base.py b/pylabnet/network/core/client_base.py
index cb04a814..dc145ec2 100644
--- a/pylabnet/network/core/client_base.py
+++ b/pylabnet/network/core/client_base.py
@@ -84,8 +84,7 @@ def connect(self, host='place_holder', port=-1, key='pylabnet.pem'):
             self._connection = None
             self._service = None
             print(
-                'Tried to establish a secure SSL connection to a generic (insecure) server.\n'
-                'Please try establishing an insecure client by setting key=None'
+                f'Connection to server hostname: {host}, port: {port} timed out.'
             )
 
         # No server running with host/port parameters
diff --git a/pylabnet/scripts/data_center/datasets.py b/pylabnet/scripts/data_center/datasets.py
index a297c048..9caef8e3 100644
--- a/pylabnet/scripts/data_center/datasets.py
+++ b/pylabnet/scripts/data_center/datasets.py
@@ -6,13 +6,41 @@
 from PyQt5 import QtWidgets, QtCore
 
 from pyqtgraph.widgets.MatplotlibWidget import MatplotlibWidget
-from pylabnet.gui.pyqt.external_gui import Window, ParameterPopup, GraphPopup, Confluence_support_GraphPopup, Confluence_Handler
+from pylabnet.gui.pyqt.external_gui import Window, ParameterPopup, GraphPopup, Confluence_support_GraphPopup, Confluence_Handler, GraphPopupTabs, Confluence_support_GraphPopupTabs
 from pylabnet.utils.logging.logger import LogClient, LogHandler
 from pylabnet.utils.helper_methods import save_metadata, generic_save, npy_generic_save, pyqtgraph_save, fill_2dlist, TimeAxisItem
 
 import sys
 
 
+def get_color_index(dataset, kwargs):
+
+    if 'color_index' in kwargs:
+        color_index = kwargs['color_index']
+    else:
+
+        try:
+            tabs_enabled = dataset.gui.windows[kwargs['window']].tabs_enabled
+        
+        # If Window has not tabs
+        except AttributeError:
+            tabs_enabled = False
+
+        # If dataset is initial dataset
+        except KeyError:
+            tabs_enabled = False
+        
+        if tabs_enabled:
+            color_index = dataset.gui.windows[kwargs['window']].num_tabs - 1
+        elif 'window' in kwargs:
+            color_index = dataset.gui.windows[kwargs['window']].graph_layout.count() - 1
+        else:
+            color_index = dataset.gui.graph_layout.count() - 1
+
+    return color_index
+
+
+
 class Dataset():
 
     def __init__(self, gui: Window, log: LogClient = None, data=None,
@@ -172,10 +200,8 @@ def visualize(self, graph, **kwargs):
 
         self.handle_new_window(graph, **kwargs)
 
-        if 'color_index' in kwargs:
-            color_index = kwargs['color_index']
-        else:
-            color_index = self.gui.graph_layout.count() - 1
+        color_index = get_color_index(self, kwargs)
+
         self.curve = self.graph.plot(
             pen=pg.mkPen(self.gui.COLOR_LIST[
                 np.mod(color_index, len(self.gui.COLOR_LIST))
@@ -355,10 +381,24 @@ def handle_new_window(self, graph, **kwargs):
 
         if graph is None:
             # If we want to use a separate window
+
+    
+
             if 'window' in kwargs:
                 # Check whether this window exists
                 if not kwargs['window'] in self.gui.windows:
 
+                    # Check if we want to enable tabs 
+                    if 'tabs_enabled' in kwargs:
+                        tabs_enabled = kwargs['tabs_enabled']
+
+                        if 'tablabel' in kwargs:
+                            tablabel = kwargs["tablabel"]
+                        else:
+                            tablabel = "Tab"
+                    else:
+                        tabs_enabled = False
+                    
                     if 'window_title' in kwargs:
                         window_title = kwargs['window_title']
                     else:
@@ -367,31 +407,86 @@ def handle_new_window(self, graph, **kwargs):
                     # self.gui.windows[kwargs['window']] = GraphPopup(
                     #     window_title=window_title, size=(700, 300))
 
+            
                     if(self.enable_confluence):
-                        self.gui.windows[kwargs['window']] = Confluence_support_GraphPopup(
-                            app=None, gui=self.gui, log=self.log, window_title=window_title, size=(1000, 500)
+                        if tabs_enabled:
+
+                            self.gui.windows[kwargs['window']] = Confluence_support_GraphPopupTabs(
+                                app=None, gui=self.gui, log=self.log, window_title=window_title, size=(1000, 500), tablabel = tablabel
+                            )
+
+                            self.gui.windows[kwargs['window']].tabs_enabled = True
+                        else:
+                            self.gui.windows[kwargs['window']] = Confluence_support_GraphPopup(
+                                app=None, gui=self.gui, log=self.log, window_title=window_title, size=(1000, 500)
+                            )
+                    elif tabs_enabled:
+                        self.gui.windows[kwargs['window']] = GraphPopupTabs(
+                            window_title=window_title, size=(700, 300), tablabel = tablabel
                         )
                     else:
                         self.gui.windows[kwargs['window']] = GraphPopup(
-                            window_title=window_title, size=(700, 300)
+                            window_title=window_title, size=(700, 300), 
                         )
 
+                     # Window already exists
+                else:
+                    # Check if we want to enable tabs 
+                    tabs_enabled = self.gui.windows[kwargs['window']].tabs_enabled 
+                    
+
+                
                 if('datetime_axis' in kwargs and kwargs['datetime_axis']):
                     date_axis = TimeAxisItem(orientation='bottom')
                     self.graph = pg.PlotWidget(axisItems={'bottom': date_axis})
                 else:
                     self.graph = pg.PlotWidget()
 
-                self.gui.windows[kwargs['window']].graph_layout.addWidget(
-                    self.graph
-                )
+                if tabs_enabled:
+
+                    num_tabs = self.gui.windows[kwargs['window']].num_tabs
+
+                    if 'tablabel' in kwargs:
+                        tablabel = kwargs["tablabel"]
+                    else:
+                        tablabel = f"Tab{num_tabs}"
+
+                    tab_widget_labels = [self.gui.windows[kwargs['window']].tabs.tabText(i) for i in range(num_tabs)]
+
+                    if tablabel in tab_widget_labels:
+                        tab_widget_graphlayout = self.gui.windows[kwargs['window']].tabs.widget(tab_widget_labels.index(tablabel)).GraphLayout
+
+                        # add to retrieved tab
+                        tab_widget_graphlayout.addWidget(
+                            self.graph
+                        )
+                        
+                    elif num_tabs == 0:
+
+                        # add to first tab
+                        self.gui.windows[kwargs['window']].tab1.GraphLayout.addWidget(
+                            self.graph
+                        )
+                        self.gui.windows[kwargs['window']].num_tabs += 1
+
+                    else:
+                        self.gui.windows[kwargs['window']].add_graph_to_new_tab(               
+                            graph = self.graph,
+                            label = tablabel
+                        )
+                else:
+ 
+                    self.gui.windows[kwargs['window']].graph_layout.addWidget(self.graph)
 
             # Otherwise, add a graph to the main layout
             else:
+                
                 if('datetime_axis' in kwargs and kwargs['datetime_axis']):
                     self.graph = self.gui.add_graph(datetime_axis=True)
                 else:
                     self.graph = self.gui.add_graph()
+
+    
             self.graph.getPlotItem().setTitle(self.name)
 
         # Reuse a PlotWidget if provided
@@ -752,10 +847,8 @@ def fill_params(self, config):
     def visualize(self, graph, **kwargs):
         self.handle_new_window(graph, **kwargs)
 
-        if 'color_index' in kwargs:
-            color_index = kwargs['color_index']
-        else:
-            color_index = self.gui.graph_layout.count() - 1
+        color_index = get_color_index(self, kwargs)
+
         self.curve = self.graph.plot(
             pen=pg.mkPen(self.gui.COLOR_LIST[
                 np.mod(color_index, len(self.gui.COLOR_LIST))
@@ -1902,13 +1995,8 @@ def visualize(self, graph, **kwargs):
         self.error = None
         self.handle_new_window(graph, **kwargs)
 
-        if 'color_index' in kwargs:
-            color_index = kwargs['color_index']
-        else:
-            if 'window' in kwargs:
-                color_index = self.gui.windows[kwargs['window']].graph_layout.count() - 1
-            else:
-                color_index = self.gui.graph_layout.count() - 1
+        color_index = get_color_index(self, kwargs)
+
         self.curve = pg.BarGraphItem(x=[0], height=[0], brush=pg.mkBrush(self.gui.COLOR_LIST[
             np.mod(color_index, len(self.gui.COLOR_LIST))
         ]), width=0.5)
@@ -1996,10 +2084,8 @@ def visualize(self, graph, **kwargs):
         self.error = None
         self.handle_new_window(graph, **kwargs)
 
-        if 'color_index' in kwargs:
-            color_index = kwargs['color_index']
-        else:
-            color_index = self.gui.graph_layout.count() - 1
+        color_index = get_color_index(self, kwargs)
+
         self.curve = pg.ErrorBarItem(pen=pg.mkPen(self.gui.COLOR_LIST[
             np.mod(color_index, len(self.gui.COLOR_LIST))
         ]), symbol='o')
diff --git a/pylabnet/scripts/staticlines/staticline.py b/pylabnet/scripts/staticlines/staticline.py
index 2d507ef1..1f423fdf 100644
--- a/pylabnet/scripts/staticlines/staticline.py
+++ b/pylabnet/scripts/staticlines/staticline.py
@@ -172,7 +172,7 @@ def run(self):
             ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
         self.app = QtWidgets.QApplication(sys.argv)
         self.app.setWindowIcon(
-            QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+            QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
         )
 
         # Create a GUI window with layout determined by the config file
diff --git a/pylabnet/utils/confluence_handler/confluence_handler.py b/pylabnet/utils/confluence_handler/confluence_handler.py
index fc5203f8..9ac28a20 100644
--- a/pylabnet/utils/confluence_handler/confluence_handler.py
+++ b/pylabnet/utils/confluence_handler/confluence_handler.py
@@ -79,7 +79,7 @@ def __init__(self, parent_wins, app, log_client=None, template="Confluence_info_
         #         ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
         #     self.app = QtWidgets.QApplication(sys.argv)
         #     self.app.setWindowIcon(
-        #         QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+        #         QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
         #     )
 
         # Initialize parent class QtWidgets.QDialog
@@ -506,7 +506,7 @@ def __init__(self, controller, app, template='Confluence_info_from_LaunchControl
                 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('pylabnet')
             self.app = QtWidgets.QApplication(sys.argv)
             self.app.setWindowIcon(
-                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))
+                QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pylabnet_newlogo.ico'))
             )
 
         # confluence
diff --git a/pylabnet/utils/slackbot/pylabnet_newlogo.ico b/pylabnet/utils/slackbot/pylabnet_newlogo.ico
new file mode 100644
index 00000000..66d4e886
Binary files /dev/null and b/pylabnet/utils/slackbot/pylabnet_newlogo.ico differ
diff --git a/pylabnet/utils/slackbot/slackbot.py b/pylabnet/utils/slackbot/slackbot.py
index 2bacea67..8e476704 100644
--- a/pylabnet/utils/slackbot/slackbot.py
+++ b/pylabnet/utils/slackbot/slackbot.py
@@ -60,7 +60,7 @@ def main():
     slackbot = PylabnetSlackBot()
     slackbot.subscribe_channel(['#pylabnet_slackbot'])
     slackbot.broadcast_to_channels('Lorem Ipsum')
-    slackbot.upload_to_channels('devices.ico')
+    slackbot.upload_to_channels('pylabnet_newlogo.ico')
 
 
 if __name__ == "__main__":
diff --git a/pylabnet_newlogo.ico b/pylabnet_newlogo.ico
new file mode 100644
index 00000000..66d4e886
Binary files /dev/null and b/pylabnet_newlogo.ico differ


Test patch:
diff --git a/demo/timetagger/testing.txt b/demo/timetagger/testing.txt
new file mode 100644
index 00000000..057fd099
--- /dev/null
+++ b/demo/timetagger/testing.txt
@@ -0,0 +1,155 @@
+1 16566.564809249837708
+1 16567.423375989121269
+-8 16567.513827835296979
+1 16567.948770480052190
+1 16568.063283964620496
+8 16568.513847916750819
+1 16568.565050014003646
+1 16568.565050042139774
+1 16568.758454522747343
+1 16568.798957625836920
+1 16568.843104152394517
+1 16569.073568236468418
+1 16569.288152050303324
+-8 16569.513827954062435
+1 16569.556175891681050
+1 16569.578122094651917
+1 16569.712923707222217
+1 16570.308948927653546
+1 16570.354657513053098
+1 16570.354657541687629
+1 16570.422432051494980
+8 16570.513848034566763
+1 16570.560723430655344
+1 16570.581966996483970
+1 16570.625090446483227
+1 16570.867645735332189
+1 16571.045623042580701
+1 16571.045623070618603
+1 16571.047299322483013
+1 16571.047299350360845
+1 16571.093097472588852
+1 16571.131667002904578
+1 16571.146565758048382
+1 16571.311449048032955
+1 16571.480320954924537
+1 16571.480320982911508
+-8 16571.513828073628247
+1 16572.034231650595757
+1 16572.179988888310618
+1 16572.250272404646239
+1 16572.408069954683015
+8 16572.513848155856977
+1 16572.833681086893193
+-8 16573.513828191789798
+1 16573.547997621855757
+1 16573.917033954625367
+1 16574.305914106029377
+1 16574.330023044138215
+1 16574.330023071801406
+1 16574.388175545642298
+8 16574.513848271519237
+1 16575.052563507884770
+1 16575.132091949464666
+1 16575.183917293888953
+1 16575.202856207419245
+1 16575.322566946673760
+1 16575.322566974398796
+1 16575.353266689791781
+1 16575.353266718306259
+1 16575.365946865742444
+-8 16575.513828309300152
+1 16575.880656662597175
+1 16576.151270506470610
+1 16576.151270535181538
+1 16576.285384528535360
+1 16576.340309675677418
+1 16576.340309703424282
+8 16576.513848392314685
+1 16576.554362340426451
+1 16576.554362369035516
+1 16577.000576495433052
+-8 16577.513828429327987
+1 16577.649730789675232
+1 16578.087623474319116
+1 16578.363563793023786
+1 16578.407692299781047
+1 16578.407692327902623
+1 16578.425336709351541
+1 16578.442534401696321
+8 16578.513848510254320
+1 16578.556508654302888
+1 16579.266537221545150
+1 16579.438566543893103
+-8 16579.513828550490871
+1 16579.834851132476615
+1 16579.900145396826701
+1 16580.085031120808708
+1 16580.154013587500231
+1 16580.372039071527979
+1 16580.455047154162457
+1 16580.455047182018461
+8 16580.513848632581357
+1 16580.638843721815647
+1 16580.684819547350344
+1 16580.762642313511606
+1 16580.844039439332846
+1 16581.058100310259761
+1 16581.370349361193803
+1 16581.501539502882224
+1 16581.501539531152957
+-8 16581.513828670555085
+1 16582.015156445952016
+8 16582.513848750677425
+1 16582.535155360874342
+1 16582.535155389115971
+1 16582.670066901813698
+1 16582.680172789590870
+1 16582.680172817690618
+1 16583.304275282232993
+1 16583.304275310416415
+1 16583.325400811547297
+-8 16583.513828788549290
+1 16583.779672760367248
+1 16583.857529974698991
+1 16583.868020937032270
+1 16583.868020964688185
+1 16583.903231213338586
+1 16584.088380049761327
+1 16584.177619192767452
+1 16584.177619220929046
+1 16584.217893617224036
+1 16584.331576156662777
+1 16584.356866941601766
+1 16584.356866969133989
+1 16584.489068693852460
+8 16584.513848866645276
+1 16584.860875829741417
+1 16584.957867463541334
+1 16585.167140018562350
+1 16585.287275952428899
+1 16585.478344509305316
+-8 16585.513828905532137
+1 16585.557872186083841
+1 16585.835732295752678
+1 16586.029472377278580
+1 16586.029472405305569
+1 16586.340910752933269
+8 16586.513848984988726
+1 16586.656634176360967
+1 16587.161764658812899
+-8 16587.513829025676387
+1 16587.562753934507782
+1 16587.874447740225150
+1 16587.949353051699291
+1 16588.339681498466234
+1 16588.351120114286459
+1 16588.454979938440374
+1 16588.472587654858216
+8 16588.513849108003342
+1 16588.528687620004348
+1 16588.528687648191408
+1 16588.654461285736033
+1 16588.654461313853972
+1 16588.750155069930770
+1 16588.750155097768584