{"action":"created","comment":{"url":"https://api.github.com/repos/pupil-labs/pupil/pulls/comments/228565534","pull_request_review_id":168872454,"id":228565534,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyODU2NTUzNA==","diff_hunk":"@@ -10,220 +10,297 @@\n \"\"\"\n \n import csv\n-\n-# logging\n import logging\n import os\n-from itertools import chain\n+from abc import ABCMeta, abstractmethod\n+from collections import namedtuple\n \n-import numpy as np\n-from OpenGL.GL import *\n from pyglui import ui\n-from pyglui.cygl.utils import RGBA, draw_polyline\n-from pyglui.pyfontstash import fontstash\n-from pyglui.ui import get_opensans_font_path\n \n-import player_methods as pm\n import file_methods as fm\n-from glfw import glfwGetCurrentContext, glfwGetWindowSize\n+import player_methods as pm\n+import zmq_tools\n from plugin import Analysis_Plugin_Base, Plugin\n \n logger = logging.getLogger(__name__)\n \n \n-class Annotation_Capture(Plugin):\n-    \"\"\"Describe your plugin here\n+def create_annotation(label, timestamp, duration=0.0, **custom_fields):\n+    \"\"\"\n+    Returns a dictionary in the format needed to send annotations\n+    to an annotation plugin via the ICP.\n+    \n+    See python/remote_annotations.py in pupil-helpers for an example.\n+\n+    :param custom_fields:\n+    \"\"\"\n+    return {\n+        \"topic\": \"annotation\",\n+        \"label\": label,\n+        \"timestamp\": timestamp,\n+        \"duration\": duration,\n+        **custom_fields,\n+    }\n+\n+\n+AnnotationDefinition = namedtuple(\"AnnotationDefinition\", \"label hotkey\")\n+\n+\n+class AnnotationPlugin(Plugin, metaclass=ABCMeta):\n+    \"\"\"\n+    Base for player and capture plugins that support adding and removing\n+    annotations and the corresponding quickbar buttons\n     \"\"\"\n \n-    icon_chr = chr(0xE866)\n+    # class _AnnotationUI:\n+    #     # no namedtuple because we cannot set the menu button at creation\n+    #     # (this button references the _AnnotationInfo for deletion)\n+    #     def __init__(self, definition, button_quickbar=None, button_menu=None):\n+    #         self.definition = definition\n+    #         self.button_quickbar = button_quickbar\n+    #         self.button_menu = button_menu\n+\n+    _AnnotationButtons = namedtuple(\"_AnnotationButtons\", \"quickbar menu\")\n+\n+    icon_chr = chr(0xe866)\n     icon_font = \"pupil_icons\"\n \n-    def __init__(self, g_pool, annotation_definitions=((\"My annotation\", \"E\"),)):\n+    def __init__(self, g_pool, annotation_definitions=None):\n         super().__init__(g_pool)\n         self.menu = None\n-        self.sub_menu = None\n-        self.buttons = []\n+        self._annotation_list_menu = None\n \n-        self.annotation_definitions = list(annotation_definitions)\n+        if annotation_definitions is None:\n+            annotation_definitions = [[\"My annotation\", \"E\"]]\n+        self._initial_annotation_definitions = annotation_definitions\n+        self._definition_to_buttons = {}\n \n-        self.new_annotation_name = \"new annotation name\"\n-        self.new_annotation_hotkey = \"e\"\n+        self._new_annotation_label = \"new annotation label\"\n+        self._new_annotation_hotkey = \"E\"\n \n-        self.current_frame = -1\n+    def get_init_dict(self):\n+        annotation_definitions = list(self._definition_to_buttons.keys())\n+        return {\"annotation_definitions\": annotation_definitions}\n \n     def init_ui(self):\n         self.add_menu()\n-        self.menu.label = \"Annotations\"\n-        self.menu.append(ui.Text_Input(\"new_annotation_name\", self))\n-        self.menu.append(ui.Text_Input(\"new_annotation_hotkey\", self))\n-        self.menu.append(ui.Button(\"add annotation type\", self.append_annotation))\n-        self.sub_menu = ui.Growing_Menu(\"Events - click to remove\")\n-        self.menu.append(self.sub_menu)\n-        self.update_buttons()\n-\n-    def update_buttons(self):\n-        for b in self.buttons:\n-            self.g_pool.quickbar.remove(b)\n-            self.sub_menu.elements[:] = []\n-        self.buttons = []\n-\n-        for e_name, hotkey in self.annotation_definitions:\n-\n-            def make_fire(e_name, hotkey):\n-                return lambda _: self.fire_annotation(e_name)\n-\n-            def make_remove(e_name, hotkey):\n-                return lambda: self.remove_annotation((e_name, hotkey))\n-\n-            thumb = ui.Thumb(\n-                e_name,\n-                setter=make_fire(e_name, hotkey),\n-                getter=lambda: False,\n-                label=hotkey,\n-                hotkey=hotkey,\n-            )\n-            self.buttons.append(thumb)\n-            self.g_pool.quickbar.append(thumb)\n-            self.sub_menu.append(\n-                ui.Button(e_name + \" <\" + hotkey + \">\", make_remove(e_name, hotkey))\n-            )\n+        self.customize_menu()\n+        self.menu.append(\n+            ui.Text_Input(\"_new_annotation_label\", self, label=\"New Label\")\n+        )\n+        self.menu.append(\n+            ui.Text_Input(\"_new_annotation_hotkey\", self, label=\"New Hotkey\")\n+        )\n+        self.menu.append(ui.Button(\"Add Annotation Type\", self._on_add_annotation_clicked))\n+        self._annotation_list_menu = ui.Growing_Menu(\n+            \"Annotation List - Click to Remove\"\n+        )\n+        self.menu.append(self._annotation_list_menu)\n+        self._create_initial_annotation_list()\n+\n+    def _create_initial_annotation_list(self):\n+        for label, hotkey in self._initial_annotation_definitions:\n+            self._add_annotation_definition(label, hotkey)\n+\n+    @abstractmethod\n+    def customize_menu(self):\n+        pass\n \n     def deinit_ui(self):\n-        self.menu.remove(self.sub_menu)\n-        self.sub_menu = None\n+        self._clear_button_quickbars()","path":"pupil_src/shared_modules/annotations.py","position":150,"original_position":157,"commit_id":"ef4a2d4bf5d4697fcdcb3e1ed066bf9b1c980541","original_commit_id":"ca550b9fd359f743d42e77d2fe93c0d52269f1d6","user":{"login":"papr","id":168390,"node_id":"MDQ6VXNlcjE2ODM5MA==","avatar_url":"https://avatars3.githubusercontent.com/u/168390?v=4","gravatar_id":"","url":"https://api.github.com/users/papr","html_url":"https://github.com/papr","followers_url":"https://api.github.com/users/papr/followers","following_url":"https://api.github.com/users/papr/following{/other_user}","gists_url":"https://api.github.com/users/papr/gists{/gist_id}","starred_url":"https://api.github.com/users/papr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/papr/subscriptions","organizations_url":"https://api.github.com/users/papr/orgs","repos_url":"https://api.github.com/users/papr/repos","events_url":"https://api.github.com/users/papr/events{/privacy}","received_events_url":"https://api.github.com/users/papr/received_events","type":"User","site_admin":false},"body":"Important is the plural/singular here, order is only a secondary thought. ","created_at":"2018-10-26T15:14:42Z","updated_at":"2018-10-26T15:14:42Z","html_url":"https://github.com/pupil-labs/pupil/pull/1334#discussion_r228565534","pull_request_url":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/comments/228565534"},"html":{"href":"https://github.com/pupil-labs/pupil/pull/1334#discussion_r228565534"},"pull_request":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334"}},"in_reply_to_id":228446571},"pull_request":{"url":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334","id":223667433,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIzNjY3NDMz","html_url":"https://github.com/pupil-labs/pupil/pull/1334","diff_url":"https://github.com/pupil-labs/pupil/pull/1334.diff","patch_url":"https://github.com/pupil-labs/pupil/pull/1334.patch","issue_url":"https://api.github.com/repos/pupil-labs/pupil/issues/1334","number":1334,"state":"open","locked":false,"title":"Fix Annotations","user":{"login":"ckbaumann","id":7406171,"node_id":"MDQ6VXNlcjc0MDYxNzE=","avatar_url":"https://avatars1.githubusercontent.com/u/7406171?v=4","gravatar_id":"","url":"https://api.github.com/users/ckbaumann","html_url":"https://github.com/ckbaumann","followers_url":"https://api.github.com/users/ckbaumann/followers","following_url":"https://api.github.com/users/ckbaumann/following{/other_user}","gists_url":"https://api.github.com/users/ckbaumann/gists{/gist_id}","starred_url":"https://api.github.com/users/ckbaumann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ckbaumann/subscriptions","organizations_url":"https://api.github.com/users/ckbaumann/orgs","repos_url":"https://api.github.com/users/ckbaumann/repos","events_url":"https://api.github.com/users/ckbaumann/events{/privacy}","received_events_url":"https://api.github.com/users/ckbaumann/received_events","type":"User","site_admin":false},"body":"fixes #1275 ","created_at":"2018-10-17T16:15:00Z","updated_at":"2018-10-26T15:14:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c5dbf0151e3abcaa5770c14238ba0162cb13f08f","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334/commits","review_comments_url":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334/comments","review_comment_url":"https://api.github.com/repos/pupil-labs/pupil/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pupil-labs/pupil/issues/1334/comments","statuses_url":"https://api.github.com/repos/pupil-labs/pupil/statuses/ef4a2d4bf5d4697fcdcb3e1ed066bf9b1c980541","head":{"label":"ckbaumann:1275-fix-annotations","ref":"1275-fix-annotations","sha":"ef4a2d4bf5d4697fcdcb3e1ed066bf9b1c980541","user":{"login":"ckbaumann","id":7406171,"node_id":"MDQ6VXNlcjc0MDYxNzE=","avatar_url":"https://avatars1.githubusercontent.com/u/7406171?v=4","gravatar_id":"","url":"https://api.github.com/users/ckbaumann","html_url":"https://github.com/ckbaumann","followers_url":"https://api.github.com/users/ckbaumann/followers","following_url":"https://api.github.com/users/ckbaumann/following{/other_user}","gists_url":"https://api.github.com/users/ckbaumann/gists{/gist_id}","starred_url":"https://api.github.com/users/ckbaumann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ckbaumann/subscriptions","organizations_url":"https://api.github.com/users/ckbaumann/orgs","repos_url":"https://api.github.com/users/ckbaumann/repos","events_url":"https://api.github.com/users/ckbaumann/events{/privacy}","received_events_url":"https://api.github.com/users/ckbaumann/received_events","type":"User","site_admin":false},"repo":{"id":150243978,"node_id":"MDEwOlJlcG9zaXRvcnkxNTAyNDM5Nzg=","name":"pupil","full_name":"ckbaumann/pupil","private":false,"owner":{"login":"ckbaumann","id":7406171,"node_id":"MDQ6VXNlcjc0MDYxNzE=","avatar_url":"https://avatars1.githubusercontent.com/u/7406171?v=4","gravatar_id":"","url":"https://api.github.com/users/ckbaumann","html_url":"https://github.com/ckbaumann","followers_url":"https://api.github.com/users/ckbaumann/followers","following_url":"https://api.github.com/users/ckbaumann/following{/other_user}","gists_url":"https://api.github.com/users/ckbaumann/gists{/gist_id}","starred_url":"https://api.github.com/users/ckbaumann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ckbaumann/subscriptions","organizations_url":"https://api.github.com/users/ckbaumann/orgs","repos_url":"https://api.github.com/users/ckbaumann/repos","events_url":"https://api.github.com/users/ckbaumann/events{/privacy}","received_events_url":"https://api.github.com/users/ckbaumann/received_events","type":"User","site_admin":false},"html_url":"https://github.com/ckbaumann/pupil","description":"Open source eye tracking ","fork":true,"url":"https://api.github.com/repos/ckbaumann/pupil","forks_url":"https://api.github.com/repos/ckbaumann/pupil/forks","keys_url":"https://api.github.com/repos/ckbaumann/pupil/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ckbaumann/pupil/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ckbaumann/pupil/teams","hooks_url":"https://api.github.com/repos/ckbaumann/pupil/hooks","issue_events_url":"https://api.github.com/repos/ckbaumann/pupil/issues/events{/number}","events_url":"https://api.github.com/repos/ckbaumann/pupil/events","assignees_url":"https://api.github.com/repos/ckbaumann/pupil/assignees{/user}","branches_url":"https://api.github.com/repos/ckbaumann/pupil/branches{/branch}","tags_url":"https://api.github.com/repos/ckbaumann/pupil/tags","blobs_url":"https://api.github.com/repos/ckbaumann/pupil/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ckbaumann/pupil/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ckbaumann/pupil/git/refs{/sha}","trees_url":"https://api.github.com/repos/ckbaumann/pupil/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ckbaumann/pupil/statuses/{sha}","languages_url":"https://api.github.com/repos/ckbaumann/pupil/languages","stargazers_url":"https://api.github.com/repos/ckbaumann/pupil/stargazers","contributors_url":"https://api.github.com/repos/ckbaumann/pupil/contributors","subscribers_url":"https://api.github.com/repos/ckbaumann/pupil/subscribers","subscription_url":"https://api.github.com/repos/ckbaumann/pupil/subscription","commits_url":"https://api.github.com/repos/ckbaumann/pupil/commits{/sha}","git_commits_url":"https://api.github.com/repos/ckbaumann/pupil/git/commits{/sha}","comments_url":"https://api.github.com/repos/ckbaumann/pupil/comments{/number}","issue_comment_url":"https://api.github.com/repos/ckbaumann/pupil/issues/comments{/number}","contents_url":"https://api.github.com/repos/ckbaumann/pupil/contents/{+path}","compare_url":"https://api.github.com/repos/ckbaumann/pupil/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ckbaumann/pupil/merges","archive_url":"https://api.github.com/repos/ckbaumann/pupil/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ckbaumann/pupil/downloads","issues_url":"https://api.github.com/repos/ckbaumann/pupil/issues{/number}","pulls_url":"https://api.github.com/repos/ckbaumann/pupil/pulls{/number}","milestones_url":"https://api.github.com/repos/ckbaumann/pupil/milestones{/number}","notifications_url":"https://api.github.com/repos/ckbaumann/pupil/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ckbaumann/pupil/labels{/name}","releases_url":"https://api.github.com/repos/ckbaumann/pupil/releases{/id}","deployments_url":"https://api.github.com/repos/ckbaumann/pupil/deployments","created_at":"2018-09-25T09:57:46Z","updated_at":"2018-10-26T07:59:14Z","pushed_at":"2018-10-26T13:57:26Z","git_url":"git://github.com/ckbaumann/pupil.git","ssh_url":"git@github.com:ckbaumann/pupil.git","clone_url":"https://github.com/ckbaumann/pupil.git","svn_url":"https://github.com/ckbaumann/pupil","homepage":"https://pupil-labs.com","size":24485,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pupil-labs:master","ref":"master","sha":"7d1007482e1d14eb40bd100b528abf119c6d44a3","user":{"login":"pupil-labs","id":5248635,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUyNDg2MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/5248635?v=4","gravatar_id":"","url":"https://api.github.com/users/pupil-labs","html_url":"https://github.com/pupil-labs","followers_url":"https://api.github.com/users/pupil-labs/followers","following_url":"https://api.github.com/users/pupil-labs/following{/other_user}","gists_url":"https://api.github.com/users/pupil-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/pupil-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pupil-labs/subscriptions","organizations_url":"https://api.github.com/users/pupil-labs/orgs","repos_url":"https://api.github.com/users/pupil-labs/repos","events_url":"https://api.github.com/users/pupil-labs/events{/privacy}","received_events_url":"https://api.github.com/users/pupil-labs/received_events","type":"Organization","site_admin":false},"repo":{"id":12173486,"node_id":"MDEwOlJlcG9zaXRvcnkxMjE3MzQ4Ng==","name":"pupil","full_name":"pupil-labs/pupil","private":false,"owner":{"login":"pupil-labs","id":5248635,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUyNDg2MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/5248635?v=4","gravatar_id":"","url":"https://api.github.com/users/pupil-labs","html_url":"https://github.com/pupil-labs","followers_url":"https://api.github.com/users/pupil-labs/followers","following_url":"https://api.github.com/users/pupil-labs/following{/other_user}","gists_url":"https://api.github.com/users/pupil-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/pupil-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pupil-labs/subscriptions","organizations_url":"https://api.github.com/users/pupil-labs/orgs","repos_url":"https://api.github.com/users/pupil-labs/repos","events_url":"https://api.github.com/users/pupil-labs/events{/privacy}","received_events_url":"https://api.github.com/users/pupil-labs/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/pupil-labs/pupil","description":"Open source eye tracking ","fork":false,"url":"https://api.github.com/repos/pupil-labs/pupil","forks_url":"https://api.github.com/repos/pupil-labs/pupil/forks","keys_url":"https://api.github.com/repos/pupil-labs/pupil/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pupil-labs/pupil/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pupil-labs/pupil/teams","hooks_url":"https://api.github.com/repos/pupil-labs/pupil/hooks","issue_events_url":"https://api.github.com/repos/pupil-labs/pupil/issues/events{/number}","events_url":"https://api.github.com/repos/pupil-labs/pupil/events","assignees_url":"https://api.github.com/repos/pupil-labs/pupil/assignees{/user}","branches_url":"https://api.github.com/repos/pupil-labs/pupil/branches{/branch}","tags_url":"https://api.github.com/repos/pupil-labs/pupil/tags","blobs_url":"https://api.github.com/repos/pupil-labs/pupil/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pupil-labs/pupil/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pupil-labs/pupil/git/refs{/sha}","trees_url":"https://api.github.com/repos/pupil-labs/pupil/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pupil-labs/pupil/statuses/{sha}","languages_url":"https://api.github.com/repos/pupil-labs/pupil/languages","stargazers_url":"https://api.github.com/repos/pupil-labs/pupil/stargazers","contributors_url":"https://api.github.com/repos/pupil-labs/pupil/contributors","subscribers_url":"https://api.github.com/repos/pupil-labs/pupil/subscribers","subscription_url":"https://api.github.com/repos/pupil-labs/pupil/subscription","commits_url":"https://api.github.com/repos/pupil-labs/pupil/commits{/sha}","git_commits_url":"https://api.github.com/repos/pupil-labs/pupil/git/commits{/sha}","comments_url":"https://api.github.com/repos/pupil-labs/pupil/comments{/number}","issue_comment_url":"https://api.github.com/repos/pupil-labs/pupil/issues/comments{/number}","contents_url":"https://api.github.com/repos/pupil-labs/pupil/contents/{+path}","compare_url":"https://api.github.com/repos/pupil-labs/pupil/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pupil-labs/pupil/merges","archive_url":"https://api.github.com/repos/pupil-labs/pupil/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pupil-labs/pupil/downloads","issues_url":"https://api.github.com/repos/pupil-labs/pupil/issues{/number}","pulls_url":"https://api.github.com/repos/pupil-labs/pupil/pulls{/number}","milestones_url":"https://api.github.com/repos/pupil-labs/pupil/milestones{/number}","notifications_url":"https://api.github.com/repos/pupil-labs/pupil/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pupil-labs/pupil/labels{/name}","releases_url":"https://api.github.com/repos/pupil-labs/pupil/releases{/id}","deployments_url":"https://api.github.com/repos/pupil-labs/pupil/deployments","created_at":"2013-08-17T04:40:25Z","updated_at":"2018-10-26T09:24:55Z","pushed_at":"2018-10-26T14:21:40Z","git_url":"git://github.com/pupil-labs/pupil.git","ssh_url":"git@github.com:pupil-labs/pupil.git","clone_url":"https://github.com/pupil-labs/pupil.git","svn_url":"https://github.com/pupil-labs/pupil","homepage":"https://pupil-labs.com","size":24418,"stargazers_count":607,"watchers_count":607,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":419,"mirror_url":null,"archived":false,"open_issues_count":123,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":419,"open_issues":123,"watchers":607,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334"},"html":{"href":"https://github.com/pupil-labs/pupil/pull/1334"},"issue":{"href":"https://api.github.com/repos/pupil-labs/pupil/issues/1334"},"comments":{"href":"https://api.github.com/repos/pupil-labs/pupil/issues/1334/comments"},"review_comments":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334/comments"},"review_comment":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pupil-labs/pupil/pulls/1334/commits"},"statuses":{"href":"https://api.github.com/repos/pupil-labs/pupil/statuses/ef4a2d4bf5d4697fcdcb3e1ed066bf9b1c980541"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"papr"}}