{"action":"created","comment":{"url":"https://api.github.com/repos/slaclab/pydm/pulls/comments/128072970","pull_request_review_id":50718503,"id":128072970,"diff_hunk":"@@ -1,160 +1,255 @@\n from ..PyQt.QtGui import QLabel, QApplication, QColor\n-from ..PyQt.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, QTimer\n-from pyqtgraph import PlotWidget, ViewBox, AxisItem, PlotItem\n-from pyqtgraph import PlotCurveItem\n+from ..PyQt.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, QTimer, QObject\n+from pyqtgraph import PlotWidget, ViewBox, AxisItem, PlotItem, GraphicsObject, PlotCurveItem\n import numpy as np\n import time\n+import json\n+from collections import OrderedDict\n from .baseplot import BasePlot\n from .channel import PyDMChannel\n+from .. import utilities\n+\n+\n+class TimePlotCurveItem(PlotCurveItem):\n+  def __init__(self, channel_address, **kws):\n+    if not 'name' in kws or kws['name'] is None:\n+      try:\n+        name = channel_address.split(\"://\")[1]\n+      except IndexError:\n+        name = channel_address\n+      kws['name'] = name\n+    self._bufferSize = 1200\n+    self._update_mode = PyDMTimePlot.SynchronousMode\n+    self.data_buffer = np.zeros((2,self._bufferSize), order='f',dtype=float)\n+    self.connected = False\n+    self.points_accumulated = 0\n+    self.latest_value = None\n+    self.channel = None\n+    self.address = channel_address\n+    super(TimePlotCurveItem, self).__init__(**kws)\n+  \n+  def to_dict(self):\n+    return OrderedDict([(\"channel\", self.address), (\"name\", self.name()), (\"color\", self.color_string)])\n+    \n+  @property\n+  def address(self):\n+    if self.channel is None:\n+      return None\n+    return self.channel.address\n+  \n+  @address.setter\n+  def address(self, new_address):\n+    if new_address is None or len(str(new_address)) < 1:\n+      self.channel = None\n+      return\n+    self.channel = PyDMChannel(address=new_address, connection_slot=self.connectionStateChanged, value_slot=self.receiveNewValue)\n+  \n+  @property\n+  def color_string(self):\n+    return str(utilities.colors.svg_color_from_hex(self.color.name(), hex_on_fail=True))\n+  \n+  @color_string.setter\n+  def color_string(self, new_color_string):\n+    self.setPen(QColor(str(new_color_string)))\n+  \n+  @property\n+  def color(self):\n+    return self.opts['pen'].color()\n+    \n+  @pyqtSlot(bool)\n+  def connectionStateChanged(self, connected):\n+    #Maybe change pen stroke?\n+    self.connected = connected\n+  \n+  @pyqtSlot(float)\n+  @pyqtSlot(int)\n+  def receiveNewValue(self, new_value):\n+    if self._update_mode == PyDMTimePlot.SynchronousMode:\n+      self.data_buffer = np.roll(self.data_buffer, -1)\n+      self.data_buffer[0, self._bufferSize - 1] = time.time()\n+      self.data_buffer[1, self._bufferSize - 1] = new_value\n+      if self.points_accumulated < self._bufferSize:\n+        self.points_accumulated = self.points_accumulated + 1\n+    elif self._update_mode == PyDMTimePlot.AsynchronousMode:\n+      self.latest_value = new_value\n+      \n+  @pyqtSlot()\n+  def asyncUpdate(self):\n+    if self._update_mode != PyDMTimePlot.AsynchronousMode:\n+      return\n+    self.data_buffer = np.roll(self.data_buffer, -1)\n+    self.data_buffer[0, self._bufferSize - 1] = time.time()\n+    self.data_buffer[1, self._bufferSize - 1] = self.latest_value\n+    if self.points_accumulated < self._bufferSize:\n+      self.points_accumulated = self.points_accumulated + 1    \n+    \n+  def initialize_buffer(self):\n+    self.points_accumulated = 0\n+    #If you don't specify dtype=float, you don't have enough resolution for the timestamp data.\n+    self.data_buffer = np.zeros((2,self._bufferSize), order='f',dtype=float)\n+    self.data_buffer[0].fill(time.time())\n+    \n+  def getBufferSize(self):\n+    return int(self._bufferSize)\n+  \n+  def setBufferSize(self, value):\n+    if self._bufferSize != int(value):\n+      self._bufferSize = max(int(value),1)\n+      self.initialize_buffer()\n+\n+  def resetBufferSize(self):\n+    if self._bufferSize != 1200:\n+      self._bufferSize = 1200\n+      self.initialize_buffer()\n+  \n+  @pyqtSlot()\n+  def redrawCurve(self):\n+    if self.connected:\n+      self.setData(y=self.data_buffer[1, -self.points_accumulated:], x=self.data_buffer[0, -self.points_accumulated:])\n+  \n+  def setUpdatesAsynchronously(self, value):\n+    if value == True:","path":"pydm/widgets/timeplot.py","position":114,"original_position":114,"commit_id":"b0578a34dd6af0b28bcb209d63d40247d3976b8f","original_commit_id":"b0578a34dd6af0b28bcb209d63d40247d3976b8f","user":{"login":"hhslepicka","id":8185425,"avatar_url":"https://avatars0.githubusercontent.com/u/8185425?v=4","gravatar_id":"","url":"https://api.github.com/users/hhslepicka","html_url":"https://github.com/hhslepicka","followers_url":"https://api.github.com/users/hhslepicka/followers","following_url":"https://api.github.com/users/hhslepicka/following{/other_user}","gists_url":"https://api.github.com/users/hhslepicka/gists{/gist_id}","starred_url":"https://api.github.com/users/hhslepicka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hhslepicka/subscriptions","organizations_url":"https://api.github.com/users/hhslepicka/orgs","repos_url":"https://api.github.com/users/hhslepicka/repos","events_url":"https://api.github.com/users/hhslepicka/events{/privacy}","received_events_url":"https://api.github.com/users/hhslepicka/received_events","type":"User","site_admin":false},"body":"I agree! 👍 ","created_at":"2017-07-18T19:30:35Z","updated_at":"2017-07-18T19:30:35Z","html_url":"https://github.com/slaclab/pydm/pull/60#discussion_r128072970","pull_request_url":"https://api.github.com/repos/slaclab/pydm/pulls/60","_links":{"self":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/comments/128072970"},"html":{"href":"https://github.com/slaclab/pydm/pull/60#discussion_r128072970"},"pull_request":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/60"}}},"pull_request":{"url":"https://api.github.com/repos/slaclab/pydm/pulls/60","id":130959623,"html_url":"https://github.com/slaclab/pydm/pull/60","diff_url":"https://github.com/slaclab/pydm/pull/60.diff","patch_url":"https://github.com/slaclab/pydm/pull/60.patch","issue_url":"https://api.github.com/repos/slaclab/pydm/issues/60","number":60,"state":"open","locked":false,"title":"Plot widget improvements","user":{"login":"mattgibbs","id":1923248,"avatar_url":"https://avatars2.githubusercontent.com/u/1923248?v=4","gravatar_id":"","url":"https://api.github.com/users/mattgibbs","html_url":"https://github.com/mattgibbs","followers_url":"https://api.github.com/users/mattgibbs/followers","following_url":"https://api.github.com/users/mattgibbs/following{/other_user}","gists_url":"https://api.github.com/users/mattgibbs/gists{/gist_id}","starred_url":"https://api.github.com/users/mattgibbs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattgibbs/subscriptions","organizations_url":"https://api.github.com/users/mattgibbs/orgs","repos_url":"https://api.github.com/users/mattgibbs/repos","events_url":"https://api.github.com/users/mattgibbs/events{/privacy}","received_events_url":"https://api.github.com/users/mattgibbs/received_events","type":"User","site_admin":false},"body":"This pull request adds support for multiple curves in PyDMTimePlot and PyDMWaveformPlot.  In designer, you can add or remove curves from a plot by right clicking it and choosing 'Edit Curves', or by double-clicking the plot.  This will bring up a dialog that lets you edit the curves.  All curves info is stored in a QStringList property called 'curves', each item in the list is a JSON dictionary with properties for the curve.  You can edit this directly, if you like, but using the Edit Curves interface is nicer.\r\n\r\nManipulating curves programmatically is also possible - each plot widget has methods for adding and removing curves in code.\r\n\r\nThis is a pretty large change for plots - people using PyDM in the wild (@laispc, @gabrielfedel, @mennodoor) may want to try pulling this branch and testing it out.","created_at":"2017-07-17T21:49:51Z","updated_at":"2017-07-18T19:30:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"154d079a4b0541acf238e02f9f67ce826180bedb","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/slaclab/pydm/pulls/60/commits","review_comments_url":"https://api.github.com/repos/slaclab/pydm/pulls/60/comments","review_comment_url":"https://api.github.com/repos/slaclab/pydm/pulls/comments{/number}","comments_url":"https://api.github.com/repos/slaclab/pydm/issues/60/comments","statuses_url":"https://api.github.com/repos/slaclab/pydm/statuses/b0578a34dd6af0b28bcb209d63d40247d3976b8f","head":{"label":"slaclab:lais_plot_improvements","ref":"lais_plot_improvements","sha":"b0578a34dd6af0b28bcb209d63d40247d3976b8f","user":{"login":"slaclab","id":16196854,"avatar_url":"https://avatars2.githubusercontent.com/u/16196854?v=4","gravatar_id":"","url":"https://api.github.com/users/slaclab","html_url":"https://github.com/slaclab","followers_url":"https://api.github.com/users/slaclab/followers","following_url":"https://api.github.com/users/slaclab/following{/other_user}","gists_url":"https://api.github.com/users/slaclab/gists{/gist_id}","starred_url":"https://api.github.com/users/slaclab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slaclab/subscriptions","organizations_url":"https://api.github.com/users/slaclab/orgs","repos_url":"https://api.github.com/users/slaclab/repos","events_url":"https://api.github.com/users/slaclab/events{/privacy}","received_events_url":"https://api.github.com/users/slaclab/received_events","type":"Organization","site_admin":false},"repo":{"id":59700161,"name":"pydm","full_name":"slaclab/pydm","owner":{"login":"slaclab","id":16196854,"avatar_url":"https://avatars2.githubusercontent.com/u/16196854?v=4","gravatar_id":"","url":"https://api.github.com/users/slaclab","html_url":"https://github.com/slaclab","followers_url":"https://api.github.com/users/slaclab/followers","following_url":"https://api.github.com/users/slaclab/following{/other_user}","gists_url":"https://api.github.com/users/slaclab/gists{/gist_id}","starred_url":"https://api.github.com/users/slaclab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slaclab/subscriptions","organizations_url":"https://api.github.com/users/slaclab/orgs","repos_url":"https://api.github.com/users/slaclab/repos","events_url":"https://api.github.com/users/slaclab/events{/privacy}","received_events_url":"https://api.github.com/users/slaclab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/slaclab/pydm","description":"Python Display Manager","fork":false,"url":"https://api.github.com/repos/slaclab/pydm","forks_url":"https://api.github.com/repos/slaclab/pydm/forks","keys_url":"https://api.github.com/repos/slaclab/pydm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/slaclab/pydm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/slaclab/pydm/teams","hooks_url":"https://api.github.com/repos/slaclab/pydm/hooks","issue_events_url":"https://api.github.com/repos/slaclab/pydm/issues/events{/number}","events_url":"https://api.github.com/repos/slaclab/pydm/events","assignees_url":"https://api.github.com/repos/slaclab/pydm/assignees{/user}","branches_url":"https://api.github.com/repos/slaclab/pydm/branches{/branch}","tags_url":"https://api.github.com/repos/slaclab/pydm/tags","blobs_url":"https://api.github.com/repos/slaclab/pydm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/slaclab/pydm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/slaclab/pydm/git/refs{/sha}","trees_url":"https://api.github.com/repos/slaclab/pydm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/slaclab/pydm/statuses/{sha}","languages_url":"https://api.github.com/repos/slaclab/pydm/languages","stargazers_url":"https://api.github.com/repos/slaclab/pydm/stargazers","contributors_url":"https://api.github.com/repos/slaclab/pydm/contributors","subscribers_url":"https://api.github.com/repos/slaclab/pydm/subscribers","subscription_url":"https://api.github.com/repos/slaclab/pydm/subscription","commits_url":"https://api.github.com/repos/slaclab/pydm/commits{/sha}","git_commits_url":"https://api.github.com/repos/slaclab/pydm/git/commits{/sha}","comments_url":"https://api.github.com/repos/slaclab/pydm/comments{/number}","issue_comment_url":"https://api.github.com/repos/slaclab/pydm/issues/comments{/number}","contents_url":"https://api.github.com/repos/slaclab/pydm/contents/{+path}","compare_url":"https://api.github.com/repos/slaclab/pydm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/slaclab/pydm/merges","archive_url":"https://api.github.com/repos/slaclab/pydm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/slaclab/pydm/downloads","issues_url":"https://api.github.com/repos/slaclab/pydm/issues{/number}","pulls_url":"https://api.github.com/repos/slaclab/pydm/pulls{/number}","milestones_url":"https://api.github.com/repos/slaclab/pydm/milestones{/number}","notifications_url":"https://api.github.com/repos/slaclab/pydm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/slaclab/pydm/labels{/name}","releases_url":"https://api.github.com/repos/slaclab/pydm/releases{/id}","deployments_url":"https://api.github.com/repos/slaclab/pydm/deployments","created_at":"2016-05-25T21:50:56Z","updated_at":"2017-06-29T13:58:16Z","pushed_at":"2017-07-17T23:32:23Z","git_url":"git://github.com/slaclab/pydm.git","ssh_url":"git@github.com:slaclab/pydm.git","clone_url":"https://github.com/slaclab/pydm.git","svn_url":"https://github.com/slaclab/pydm","homepage":null,"size":630,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":10,"mirror_url":null,"open_issues_count":21,"forks":10,"open_issues":21,"watchers":17,"default_branch":"master"}},"base":{"label":"slaclab:master","ref":"master","sha":"43ad87e548644508d5165bd44aea43c89be4aba2","user":{"login":"slaclab","id":16196854,"avatar_url":"https://avatars2.githubusercontent.com/u/16196854?v=4","gravatar_id":"","url":"https://api.github.com/users/slaclab","html_url":"https://github.com/slaclab","followers_url":"https://api.github.com/users/slaclab/followers","following_url":"https://api.github.com/users/slaclab/following{/other_user}","gists_url":"https://api.github.com/users/slaclab/gists{/gist_id}","starred_url":"https://api.github.com/users/slaclab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slaclab/subscriptions","organizations_url":"https://api.github.com/users/slaclab/orgs","repos_url":"https://api.github.com/users/slaclab/repos","events_url":"https://api.github.com/users/slaclab/events{/privacy}","received_events_url":"https://api.github.com/users/slaclab/received_events","type":"Organization","site_admin":false},"repo":{"id":59700161,"name":"pydm","full_name":"slaclab/pydm","owner":{"login":"slaclab","id":16196854,"avatar_url":"https://avatars2.githubusercontent.com/u/16196854?v=4","gravatar_id":"","url":"https://api.github.com/users/slaclab","html_url":"https://github.com/slaclab","followers_url":"https://api.github.com/users/slaclab/followers","following_url":"https://api.github.com/users/slaclab/following{/other_user}","gists_url":"https://api.github.com/users/slaclab/gists{/gist_id}","starred_url":"https://api.github.com/users/slaclab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slaclab/subscriptions","organizations_url":"https://api.github.com/users/slaclab/orgs","repos_url":"https://api.github.com/users/slaclab/repos","events_url":"https://api.github.com/users/slaclab/events{/privacy}","received_events_url":"https://api.github.com/users/slaclab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/slaclab/pydm","description":"Python Display Manager","fork":false,"url":"https://api.github.com/repos/slaclab/pydm","forks_url":"https://api.github.com/repos/slaclab/pydm/forks","keys_url":"https://api.github.com/repos/slaclab/pydm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/slaclab/pydm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/slaclab/pydm/teams","hooks_url":"https://api.github.com/repos/slaclab/pydm/hooks","issue_events_url":"https://api.github.com/repos/slaclab/pydm/issues/events{/number}","events_url":"https://api.github.com/repos/slaclab/pydm/events","assignees_url":"https://api.github.com/repos/slaclab/pydm/assignees{/user}","branches_url":"https://api.github.com/repos/slaclab/pydm/branches{/branch}","tags_url":"https://api.github.com/repos/slaclab/pydm/tags","blobs_url":"https://api.github.com/repos/slaclab/pydm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/slaclab/pydm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/slaclab/pydm/git/refs{/sha}","trees_url":"https://api.github.com/repos/slaclab/pydm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/slaclab/pydm/statuses/{sha}","languages_url":"https://api.github.com/repos/slaclab/pydm/languages","stargazers_url":"https://api.github.com/repos/slaclab/pydm/stargazers","contributors_url":"https://api.github.com/repos/slaclab/pydm/contributors","subscribers_url":"https://api.github.com/repos/slaclab/pydm/subscribers","subscription_url":"https://api.github.com/repos/slaclab/pydm/subscription","commits_url":"https://api.github.com/repos/slaclab/pydm/commits{/sha}","git_commits_url":"https://api.github.com/repos/slaclab/pydm/git/commits{/sha}","comments_url":"https://api.github.com/repos/slaclab/pydm/comments{/number}","issue_comment_url":"https://api.github.com/repos/slaclab/pydm/issues/comments{/number}","contents_url":"https://api.github.com/repos/slaclab/pydm/contents/{+path}","compare_url":"https://api.github.com/repos/slaclab/pydm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/slaclab/pydm/merges","archive_url":"https://api.github.com/repos/slaclab/pydm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/slaclab/pydm/downloads","issues_url":"https://api.github.com/repos/slaclab/pydm/issues{/number}","pulls_url":"https://api.github.com/repos/slaclab/pydm/pulls{/number}","milestones_url":"https://api.github.com/repos/slaclab/pydm/milestones{/number}","notifications_url":"https://api.github.com/repos/slaclab/pydm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/slaclab/pydm/labels{/name}","releases_url":"https://api.github.com/repos/slaclab/pydm/releases{/id}","deployments_url":"https://api.github.com/repos/slaclab/pydm/deployments","created_at":"2016-05-25T21:50:56Z","updated_at":"2017-06-29T13:58:16Z","pushed_at":"2017-07-17T23:32:23Z","git_url":"git://github.com/slaclab/pydm.git","ssh_url":"git@github.com:slaclab/pydm.git","clone_url":"https://github.com/slaclab/pydm.git","svn_url":"https://github.com/slaclab/pydm","homepage":null,"size":630,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":10,"mirror_url":null,"open_issues_count":21,"forks":10,"open_issues":21,"watchers":17,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/60"},"html":{"href":"https://github.com/slaclab/pydm/pull/60"},"issue":{"href":"https://api.github.com/repos/slaclab/pydm/issues/60"},"comments":{"href":"https://api.github.com/repos/slaclab/pydm/issues/60/comments"},"review_comments":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/60/comments"},"review_comment":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/slaclab/pydm/pulls/60/commits"},"statuses":{"href":"https://api.github.com/repos/slaclab/pydm/statuses/b0578a34dd6af0b28bcb209d63d40247d3976b8f"}}}}
{"actor":{"display_login":"hhslepicka"}}