{ "info": { "author": "Michael MacDonald", "author_email": "macdonald.michael.anthon@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# StreamEngine\nStreamEngine is an extention of the matplotlib Animation class which enables the user to easily plot real time streaming data.\n[![StreamEngine Example](https://i.imgur.com/6w3vfG3.png)](https://www.youtube.com/watch?v=bgwOTcpNV9Q)\n## Install\n`pip install stream_engine`\n\n## Basic Example\n```Python\nimport matplotlib.pyplot as plt\nfrom stream_engine.stream import Stream, StreamAnimation\n\nif __name__ == '__main__':\n from psutil import cpu_percent\n\n # Define a function that returns data. This function will be repetedly called.\n def cpu_average():\n return [cpu_percent()]\n\n fig = plt.figure(figsize=(10, 3))\n ax1 = fig.add_subplot(111)\n ax1.set_xlim(0, 600)\n ax1.set_ylim(0, 100)\n\n anim = StreamAnimation(fig, interval=100) # Create a StreamAnimation object.\n anim.add_stream(Stream(ax1, cpu_average)) # Add a Stream with a data function to it.\n\n plt.tight_layout()\n plt.show()\n```\n![StreamEngine Example](https://i.imgur.com/oSIavjc.png)\n\n## Streams With Multiple Inputs.\nA new `Stream.thread` is created for each value in a list retured by the defined data function. A `Stream.thread` is simply an objects that holds the line and line data to be plotted for the Stream.\n**Note:** A data function must return a list of data. The above data function returns one value so we bracket the return value.\n\nFor example if we were to create a new function that returns the cpu % for each individual cpu im our computer, instead of a single average. A new `Stream.thread` will be added to the Stream for each cpu in our computer (a new line to be plotted).\n```Python\nfrom psutil import cpu_percent\ndef all_cpu():\n return cpu_percent(percpu=True) # Note: we do not have to bracket the return because\n # psutil.cpu_percent(percpu=True) returns a list by default.\n # returns --> [cpu1%, cpu2%, cpu3%, cpu4%] (4 stream.threads created)\n```\nNow lets create a new Stream with our new data function and add it to our StreamAnimation object.\n```Python\nave_stream = Stream(ax1, cpu_average)\ncpu_stream = Stream(ax1, all_cpu)\nanim.add_stream(ave_stream, cpu_stream)\n```\n![StreamEngine Example](https://i.imgur.com/yLscUM1.png)\n\n## Processing Data\nBy default no processing is done to the data. It is simply graphed as it is recieved. However you can define a custom data processor to process Stream data before it is plotted.\n\nIn our example above we are plotting the raw cpu percent values which are very sporadic and hard to read. We can define a custom processor to smooth the data before we plot it which will make it more readable and mimic a standard system monitor tool.\n\nA Stream processor takes in a `thread` (the line object and the line data) and `data` (the next value to be added tot the thread).\n```Python\n# The default data processor.\ndef stream_proc(thread, data):\n thread['data'].appendleft(data) # adds the new data from our data function to the thread.\n thread['line'].set_ydata(thread['data']) # updates the line with our new data.\n return thread['line'] # sends the line to be plotted\n```\nIf we want to filter the data. We can define a filter processor.\n```Python\nfrom scipy.ndimage.filters import gaussian_filter1d\n\ndef filter_proc(thread, data):\n thread['data'].appendleft(data)\n thread['line'].set_ydata(gaussian_filter1d(thread['data'], 3)) # apply a guassian filter to our data.\n return thread['line']\n\n# We add the filter_proc to the Streams we want to be filtered.\n# In this case we will filter the cpu_stream and leave the ave_stream unfiltered.\ncpu_stream = Stream(ax1, all_cpu, proc=filter_proc)\n```\n![StreamEngine Example](https://i.imgur.com/ddgmVUt.png)\n\n\nThe data now looks much cleaner. Lets put each Stream on its own axes.\n```Python\n# Just create a new axes and reassign one of the streams to the new axes.\nax1 = fig.add_subplot(211) # change the fig configuration to hold more than one plot\nax1.set_xlim(0, 600)\nax1.set_ylim(0, 100)\n\nax2 = fig.add_subplot(212)\nax2.set_xlim(0, 600)\nax2.set_ylim(0, 100)\n\nave_stream = Stream(ax1, cpu_average)\ncpu_stream = Stream(ax2, all_cpu, proc=filter_proc) # Set to ax2\nanim.add_stream(ave_stream, cpu_stream)\n```\n![StreamEngine Example](https://i.imgur.com/LmmCbpO.png)\n\n## Styles\nEach Stream line can be styled like any other matplotlib line.\n```Python\n# A style should be passed as a list containing dicts eg.--> style=[{'color': 'r', 'label': 'foo'}]\n\n# For single Streams\nave_style = [{'linestyle': '--', 'label': 'cpu average'}]\nave_stream = Stream(ax1, cpu_average, style=ave_style)\n\n# For multiple streams.\ncpu_style = [{'linestyle': '-', 'label': 'cpu1'},\n {'linestyle': '-', 'label': 'cpu2'},\n {'linestyle': '-', 'label': 'cpu3'},\n {'linestyle': '-', 'label': 'cpu4'}]\ncpu_stream = Stream(ax2, all_cpu, proc=filter_proc, style=cpu_style)\n\nax1.legend(framealpha=.0, loc=9)\nax2.legend(framealpha=.0, loc=9, ncol=4)\n```\n![StreamEngine Example](https://i.imgur.com/WBPt1WC.png)\n\n## Taking It Further.\nLets add a new Stream that represents our computers memory usage.\n```python\n# Lets make our new data function that returns our computers memory usage.\nfrom psutil import virtual_memory, swap_memory\n\ndef memory_percent():\n return virtual_memory().percent, swap_memory().percent # returns a tuple of data which is also exceptable.\n\n# We want this stream to be on it's own axes so make a third axes.\nax3 = fig.add_subplot(313)\nax3.set_xlim(0, 600)\nax3.set_ylim(0, 100)\n\n# Lets create our Stream object.\n# Lets also create a style for it while we're at it.\nmem_style = [{'linestyle': '--', 'label': 'main', 'color': 'g'},\n {'linestyle': '--', 'label': 'swap', 'color': 'r'}]\nmem_stream = Stream(ax3, mem_percent, style=mem_style)\nax3.legend(framealpha=.0, loc=9, ncol=2)\n\n# Add to our StreamAnimation object.\n anim.add_stream(ave_stream, cpu_stream, mem_stream)\n```\n![StreamEngine Example](https://i.imgur.com/A6sIA99.png)\n\n## Putting It All Together\n\n```Python\nimport matplotlib.pyplot as plt\nfrom stream_engine.stream import Stream, StreamAnimation\nfrom matplotlib.ticker import FuncFormatter, FormatStrFormatter\n\nbase_style = {'figure.facecolor': '2d2d2d',\n 'axes.facecolor': '404040',\n 'axes.labelcolor': 'b0bdbb',\n 'axes.prop_cycle': \"cycler('color', ['74af60', '49b6d2', 'db4743', 'eba92b'])\",\n 'axes.grid': 'True',\n 'grid.color': '343434',\n 'grid.linestyle': '-',\n 'grid.linewidth': '1.0',\n 'text.color': 'b0bdbb',\n 'xtick.color': 'efefef',\n 'ytick.color': 'efefef'}\n\nave_style = [{'label': 'cpu average'}]\n\ncpu_style = [{'label': 'cpu1'},\n {'label': 'cpu2'},\n {'label': 'cpu3'},\n {'label': 'cpu4'}]\n\nmem_style = [{'label': 'main', 'color': '#49b6d2'},\n {'label': 'swap', 'color': '#db4743'}]\n\n\ndef config(*args):\n def x_format(x, pos):\n return '%1.0f' % abs(x / 10)\n for ax in args:\n ax.xaxis.set_major_formatter(FuncFormatter(x_format))\n ax.yaxis.set_major_formatter(FormatStrFormatter('%.0f%%'))\n ax.set_xlabel('Time (s)')\n\n leg = ax.legend(loc=9, ncol=6, framealpha=.2)\n for text in leg.get_texts():\n text.set_color('w')\n\nwith plt.style.context(base_style):\n fig = plt.figure(figsize=(8, 5))\n ax1 = fig.add_subplot(311)\n ax1.set_xlim(0, 600)\n ax1.set_ylim(0, 100)\n\n ax2 = fig.add_subplot(312)\n ax2.set_xlim(0, 600)\n ax2.set_ylim(0, 100)\n\n ax3 = fig.add_subplot(313)\n ax3.set_xlim(0, 600)\n ax3.set_ylim(0, 100)\n\n\nif __name__ == '__main__':\n from psutil import cpu_percent, virtual_memory, swap_memory\n\n def cpu_percents():\n return cpu_percent(percpu=True)\n\n def cpu_average():\n return [cpu_percent()]\n\n def memory_percent():\n return virtual_memory().percent, swap_memory().percent\n\n ave_stream = Stream(ax1, cpu_average, buffer=5, style=ave_style)\n cpu_stream = Stream(ax2, cpu_percents, buffer=5, style=cpu_style, filt=True)\n mem_stream = Stream(ax3, memory_percent, buffer=5, style=mem_style)\n\n config(ax1, ax2, ax3)\n\n anim = StreamAnimation(fig, interval=100)\n anim.add_stream(ave_stream, cpu_stream, mem_stream)\n\n plt.tight_layout()\n plt.show()\n```\n![StreamEngine Example](https://i.imgur.com/fbOT57t.png)\n\n\n\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/MA-MacDonald/stream-engine", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "stream-engine", "package_url": "https://pypi.org/project/stream-engine/", "platform": "", "project_url": "https://pypi.org/project/stream-engine/", "project_urls": { "Homepage": "https://github.com/MA-MacDonald/stream-engine" }, "release_url": "https://pypi.org/project/stream-engine/0.1.0/", "requires_dist": [ "matplotlib (<=2.2.0,>=2.0)" ], "requires_python": "", "summary": "Matplotlib extension to plot real time data", "version": "0.1.0" }, "last_serial": 3915776, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "d833ecc562c12d0d7ae26cd30bfc9001", "sha256": "c8a2debfb90a44377d6cc04e28246fea73eef04587601209c886f4806b44a506" }, "downloads": -1, "filename": "stream_engine-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "d833ecc562c12d0d7ae26cd30bfc9001", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4890, "upload_time": "2018-05-31T04:50:28", "url": "https://files.pythonhosted.org/packages/ab/4c/a8fd4e2be66aac0414c5c751fcb6b9d35f0a2db9e4a11f940097adb69a7f/stream_engine-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c9d08ad53f8cb867f8dd8a24a42a5b0c", "sha256": "4c75fafc2bb6f8b867bc2a1ac56c044562a151e42cc25dfdeee0e4afb7fe6e96" }, "downloads": -1, "filename": "stream_engine-0.1.0.tar.gz", "has_sig": false, "md5_digest": "c9d08ad53f8cb867f8dd8a24a42a5b0c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5241, "upload_time": "2018-05-31T04:50:29", "url": "https://files.pythonhosted.org/packages/b7/7f/f4d2fb9e05e6efa6a0a10b1b8925f9be3caa12be6ba40c68648a70bff6ff/stream_engine-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d833ecc562c12d0d7ae26cd30bfc9001", "sha256": "c8a2debfb90a44377d6cc04e28246fea73eef04587601209c886f4806b44a506" }, "downloads": -1, "filename": "stream_engine-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "d833ecc562c12d0d7ae26cd30bfc9001", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4890, "upload_time": "2018-05-31T04:50:28", "url": "https://files.pythonhosted.org/packages/ab/4c/a8fd4e2be66aac0414c5c751fcb6b9d35f0a2db9e4a11f940097adb69a7f/stream_engine-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c9d08ad53f8cb867f8dd8a24a42a5b0c", "sha256": "4c75fafc2bb6f8b867bc2a1ac56c044562a151e42cc25dfdeee0e4afb7fe6e96" }, "downloads": -1, "filename": "stream_engine-0.1.0.tar.gz", "has_sig": false, "md5_digest": "c9d08ad53f8cb867f8dd8a24a42a5b0c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5241, "upload_time": "2018-05-31T04:50:29", "url": "https://files.pythonhosted.org/packages/b7/7f/f4d2fb9e05e6efa6a0a10b1b8925f9be3caa12be6ba40c68648a70bff6ff/stream_engine-0.1.0.tar.gz" } ] }