jsongrapher 1.6__py3-none-any.whl → 2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- JSONGrapher/JSONRecordCreator.py +898 -85
- JSONGrapher/UnitPytesting.py +125 -0
- JSONGrapher/UnitpyCustomUnitsTesting.py +28 -0
- JSONGrapher/__init__.py +1 -1
- JSONGrapher/drag_and_drop_gui.py +109 -0
- JSONGrapher/units_list.py +27 -0
- {jsongrapher-1.6.data → jsongrapher-2.8.data}/data/README.md +88 -79
- {jsongrapher-1.6.dist-info → jsongrapher-2.8.dist-info}/METADATA +29 -17
- jsongrapher-2.8.dist-info/RECORD +13 -0
- jsongrapher-1.6.dist-info/RECORD +0 -10
- {jsongrapher-1.6.data → jsongrapher-2.8.data}/data/LICENSE +0 -0
- {jsongrapher-1.6.dist-info → jsongrapher-2.8.dist-info}/LICENSE +0 -0
- {jsongrapher-1.6.dist-info → jsongrapher-2.8.dist-info}/WHEEL +0 -0
- {jsongrapher-1.6.dist-info → jsongrapher-2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
import unitpy
|
2
|
+
import unitpy.definitions
|
3
|
+
import unitpy.definitions.entry
|
4
|
+
|
5
|
+
print(unitpy.U("kg/m"))
|
6
|
+
|
7
|
+
|
8
|
+
print(unitpy.U("(((kg)/m))/s"))
|
9
|
+
|
10
|
+
units1 = unitpy.U("(((kg)/m))/s")
|
11
|
+
units2 = unitpy.U("(((g)/m))/s")
|
12
|
+
unitsRatio = units1/units2
|
13
|
+
print(unitsRatio)
|
14
|
+
|
15
|
+
print(units2)
|
16
|
+
units1multiplied =1*unitpy.U("(((kg)/m))/s")
|
17
|
+
print("line 14")
|
18
|
+
ratioWithUnits = units1multiplied.to("(((g)/m))/s")
|
19
|
+
print(ratioWithUnits)
|
20
|
+
print(str(ratioWithUnits).split(' '))
|
21
|
+
|
22
|
+
units1 = unitpy.Q("1 (((kg)/m))/s")
|
23
|
+
units2 = unitpy.Q("1 (((g)/m))/s")
|
24
|
+
print("line 22", units2.base_unit)
|
25
|
+
unitsRatio = units1/units2
|
26
|
+
print("line 23")
|
27
|
+
print(unitsRatio)
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
print(units2)
|
32
|
+
units1multiplied =1*unitpy.U("(((kg)/m))*(s**-1)")
|
33
|
+
print("line 14")
|
34
|
+
ratioWithUnits = units1multiplied.to("(((g)/m))/s")
|
35
|
+
print(ratioWithUnits)
|
36
|
+
print(str(ratioWithUnits).split(' '))
|
37
|
+
print(units1multiplied.base_unit)
|
38
|
+
|
39
|
+
units5 = unitpy.Q("1 (((g)/m))/s")
|
40
|
+
print(units5.base_unit)
|
41
|
+
|
42
|
+
|
43
|
+
def convert_inverse_units(expression, depth=100):
|
44
|
+
import re
|
45
|
+
# Patterns to match valid reciprocals while ignoring multiplied units
|
46
|
+
patterns = [r"1/\((1/.*?)\)", r"1/([a-zA-Z]+)"]
|
47
|
+
|
48
|
+
for _ in range(depth):
|
49
|
+
new_expression = expression
|
50
|
+
for pattern in patterns:
|
51
|
+
new_expression = re.sub(pattern, r"(\1)**(-1)", new_expression)
|
52
|
+
|
53
|
+
# Stop early if no more changes are made
|
54
|
+
if new_expression == expression:
|
55
|
+
break
|
56
|
+
expression = new_expression
|
57
|
+
return expression
|
58
|
+
|
59
|
+
|
60
|
+
expression_original = "1/(1/bar)"
|
61
|
+
expression_altered = convert_inverse_units(expression_original)
|
62
|
+
units6 = unitpy.Q('1*'+expression_altered)
|
63
|
+
print(units6.unit)
|
64
|
+
|
65
|
+
from unitpy import U, Unit
|
66
|
+
import unitpy
|
67
|
+
newunit = unitpy.Unit("meter")
|
68
|
+
from unitpy.definitions.entry import Entry
|
69
|
+
# new_entry = Entry("frog", "frog", "frog", 1.0)
|
70
|
+
# unitpy.ledger.add_unit(new_entry)
|
71
|
+
def add_custom_unit_to_unitpy(unit_string):
|
72
|
+
import unitpy
|
73
|
+
from unitpy.definitions.entry import Entry
|
74
|
+
#need to put an entry into "bases" because the BaseSet class will pull from that dictionary.
|
75
|
+
unitpy.definitions.unit_base.bases[unit_string] = unitpy.definitions.unit_base.BaseUnit(label=unit_string, abbr=unit_string,dimension=unitpy.definitions.dimensions.dimensions["amount_of_substance"])
|
76
|
+
#Then need to make a BaseSet object to put in. Confusingly, we *do not* put a BaseUnit object into the base_unit argument, below.
|
77
|
+
#We use "mole" to avoid conflicting with any other existing units.
|
78
|
+
base_unit =unitpy.definitions.unit_base.BaseSet(mole = 1)
|
79
|
+
#base_unit = unitpy.definitions.unit_base.BaseUnit(label=unit_string, abbr=unit_string,dimension=unitpy.definitions.dimensions.dimensions["amount_of_substance"])
|
80
|
+
new_entry = Entry(label = unit_string, abbr = unit_string, base_unit = base_unit, multiplier= 1)
|
81
|
+
#only add the entry if it is missing. A duplicate entry would cause crashing later.
|
82
|
+
if not unitpy.ledger.get_entry(new_entry):
|
83
|
+
unitpy.ledger.add_unit(new_entry) #implied return is here. No return needed.
|
84
|
+
|
85
|
+
add_custom_unit_to_unitpy("frog")
|
86
|
+
#TODO: now know one way how to add custom units to unitpy.
|
87
|
+
#Cannot put "<>" inside unitpy, but could filter those out, and then put them back. Would need to make a list of unique entries with <> because there could be more than one.
|
88
|
+
|
89
|
+
another_test = "1/bar/(1/bar)*bar*frog"
|
90
|
+
another_test = convert_inverse_units(another_test)
|
91
|
+
print("line 65", another_test)
|
92
|
+
units7 = unitpy.U(another_test)
|
93
|
+
print("line 66", units7)
|
94
|
+
print("line 86", 1*units7)
|
95
|
+
print(unitpy.ledger.get_entry("frog"))
|
96
|
+
units_string_1 = 'm*frog'
|
97
|
+
print("line 87", 1*unitpy.U(units_string_1))
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
print("line 94")
|
103
|
+
|
104
|
+
units_string_2 = 'm*frog'
|
105
|
+
|
106
|
+
units_string_1_multiplied = 1*unitpy.U(units_string_1 )
|
107
|
+
units_string_1_multiplied.to(units_string_2)
|
108
|
+
|
109
|
+
|
110
|
+
print(units2)
|
111
|
+
units1multiplied =1*unitpy.U("(((kg)/m))/s")
|
112
|
+
print("line 85")
|
113
|
+
string2 = "(((g)/m))*1/s"
|
114
|
+
string2 = convert_inverse_units(string2)
|
115
|
+
print(string2)
|
116
|
+
ratioWithUnits = units1multiplied.to(string2)
|
117
|
+
print(ratioWithUnits)
|
118
|
+
print(str(ratioWithUnits).split(' '))
|
119
|
+
|
120
|
+
|
121
|
+
#micrometer symbol, "μ" will result in error, also typing out "microm" will result in error, but "micrometer" works
|
122
|
+
units1 = unitpy.U("(((kg)/mm))/s")
|
123
|
+
#units2 = unitpy.U("(((g)/μm))/s")
|
124
|
+
#units2 = unitpy.U("(((g)/microm))/s")
|
125
|
+
units2 = unitpy.U("(((g)/micrometer))/s")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import unitpy
|
2
|
+
import re
|
3
|
+
from unitpy.definitions.entry import Entry
|
4
|
+
|
5
|
+
def add_custom_unit(unit_string):
|
6
|
+
# Need to put an entry into "bases" because the BaseSet class will pull from that dictionary.
|
7
|
+
unitpy.definitions.unit_base.bases[unit_string] = unitpy.definitions.unit_base.BaseUnit(
|
8
|
+
label=unit_string, abbr=unit_string, dimension=unitpy.definitions.dimensions.dimensions["amount_of_substance"]
|
9
|
+
)
|
10
|
+
|
11
|
+
# Then need to make a BaseSet object to put in. Confusingly, we *do not* put a BaseUnit object into the base_unit argument, below.
|
12
|
+
# We use "mole" to avoid conflicting with any other existing units.
|
13
|
+
base_unit = unitpy.definitions.unit_base.BaseSet(mole=1)
|
14
|
+
|
15
|
+
new_entry = Entry(label=unit_string, abbr=unit_string, base_unit=base_unit, multiplier=1)
|
16
|
+
|
17
|
+
# Only add the entry if it is missing. A duplicate entry would cause crashing later.
|
18
|
+
if 'frog' not in unitpy.ledger._lookup:
|
19
|
+
unitpy.ledger.add_unit(new_entry) # Implied return is here. No return needed.
|
20
|
+
|
21
|
+
add_custom_unit("frog")
|
22
|
+
add_custom_unit("frog")
|
23
|
+
|
24
|
+
units_string_1 = 'm*frog'
|
25
|
+
units_string_2 = 'm*frog'
|
26
|
+
units_string_1_multiplied = 1*unitpy.U(units_string_1 )
|
27
|
+
print("line 25", type(units_string_1_multiplied))
|
28
|
+
units_string_1_multiplied.to(units_string_2)
|
JSONGrapher/__init__.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
#Try to do things a bit like https://github.com/python/cpython/blob/master/Lib/collections/__init__.py
|
2
2
|
#Except instead of putting things here directly in __init__, we'll import them so they are accessible by importing the module.
|
3
|
-
from
|
3
|
+
from JSONGrapher.JSONRecordCreator import *
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import os
|
2
|
+
import tkinter as tk
|
3
|
+
from tkinter import filedialog
|
4
|
+
from tkinterdnd2 import DND_FILES, TkinterDnD
|
5
|
+
|
6
|
+
|
7
|
+
#The below class creates a window for dragging and dropping or browsing and selecting files
|
8
|
+
#And each time one or more file is added, the full file list and most recently added files will be passed to
|
9
|
+
#The function supplied by the user (function_for_after_file_addition)
|
10
|
+
#with the two variables passed being all_selected_file_paths, newly_added_file_paths
|
11
|
+
#This class **cannot** be initiated directly, it should initiated using the
|
12
|
+
#companion function create_and_launch
|
13
|
+
class DragDropApp:
|
14
|
+
def __init__(self, root, app_name = '', function_for_after_file_addition = None):
|
15
|
+
self.root = root
|
16
|
+
self.root.title(app_name)
|
17
|
+
self.function_for_after_file_addition = function_for_after_file_addition
|
18
|
+
|
19
|
+
# Enable native drag-and-drop capability
|
20
|
+
self.root.drop_target_register(DND_FILES)
|
21
|
+
self.root.dnd_bind("<<Drop>>", self.drop_files)
|
22
|
+
|
23
|
+
# Create a drop zone
|
24
|
+
self.drop_frame = tk.Label(root, text="Drag and drop files here \n\n Click End When Finished", bg="lightgray", width=50, height=10)
|
25
|
+
self.drop_frame.pack(pady=10)
|
26
|
+
|
27
|
+
# Create a listbox to display selected files
|
28
|
+
self.file_listbox = tk.Listbox(root, width=60, height=10)
|
29
|
+
self.file_listbox.pack(pady=10)
|
30
|
+
|
31
|
+
# Buttons for manual selection and finalizing selection
|
32
|
+
self.select_button = tk.Button(root, text="Select Files By Browsing", command=self.open_file_dialog)
|
33
|
+
self.select_button.pack(pady=5)
|
34
|
+
|
35
|
+
# Create a frame for the middle buttons
|
36
|
+
button_frame_middle = tk.Frame(root)
|
37
|
+
button_frame_middle.pack(pady=5)
|
38
|
+
|
39
|
+
self.clear_button = tk.Button(button_frame_middle, text="Clear Files List", command=self.clear_file_list) # New "Clear" button
|
40
|
+
self.clear_button.pack(side = tk.LEFT, pady=5)
|
41
|
+
|
42
|
+
# "Download Output" button
|
43
|
+
self.download_button = tk.Button(button_frame_middle, text="Download Output", command=self.download_output)
|
44
|
+
self.download_button.pack(side = tk.RIGHT, pady=5)
|
45
|
+
|
46
|
+
self.done_button = tk.Button(root, text="End", command=self.finish_selection)
|
47
|
+
self.done_button.pack(pady=5)
|
48
|
+
|
49
|
+
# Store selected file paths
|
50
|
+
self.all_selected_file_paths = []
|
51
|
+
|
52
|
+
def clear_file_list(self):
|
53
|
+
"""Clears the listbox and resets selected files."""
|
54
|
+
self.file_listbox.delete(0, tk.END) # Clear listbox
|
55
|
+
self.all_selected_file_paths = [] # Reset file list
|
56
|
+
self.function_for_after_file_addition(all_selected_file_paths=[], newly_added_file_paths=[])
|
57
|
+
print("File list cleared!") # Optional debug message
|
58
|
+
|
59
|
+
def open_file_dialog(self):
|
60
|
+
"""Opens a file dialog to manually select files."""
|
61
|
+
newly_added_file_paths = self.root.tk.splitlist(tk.filedialog.askopenfilenames(title="Select files"))
|
62
|
+
if newly_added_file_paths:
|
63
|
+
self.all_selected_file_paths.extend(newly_added_file_paths)
|
64
|
+
self.update_file_list(newly_added_file_paths)
|
65
|
+
|
66
|
+
def drop_files(self, event):
|
67
|
+
"""Handles dropped files into the window."""
|
68
|
+
newly_added_file_paths = self.root.tk.splitlist(event.data)
|
69
|
+
if newly_added_file_paths:
|
70
|
+
self.all_selected_file_paths.extend(newly_added_file_paths)
|
71
|
+
self.update_file_list(newly_added_file_paths)
|
72
|
+
|
73
|
+
def update_file_list(self, newly_added_file_paths):
|
74
|
+
"""Updates the listbox with selected filenames."""
|
75
|
+
self.file_listbox.delete(0, tk.END) # Clear listbox
|
76
|
+
for filename_and_path in self.all_selected_file_paths:
|
77
|
+
self.file_listbox.insert(tk.END, os.path.basename(filename_and_path)) # Show filenames only
|
78
|
+
# If there is a function_for_after_file_addition, pass full list and newly added files into function_for_after_file_addition
|
79
|
+
if self.function_for_after_file_addition is not None:
|
80
|
+
output = self.function_for_after_file_addition(self.all_selected_file_paths, newly_added_file_paths)
|
81
|
+
self.output_for_download = output[0] #store the first part of the output for download.
|
82
|
+
|
83
|
+
def download_output(self):
|
84
|
+
"""Allows user to choose where to save the output."""
|
85
|
+
if hasattr(self, "output_for_download"):
|
86
|
+
file_path = filedialog.asksaveasfilename(filetypes=[("*.*", "*.txt")], title="Save Output As")
|
87
|
+
if file_path: # If a valid path is chosen
|
88
|
+
with open(file_path, "w") as file:
|
89
|
+
file.write(str(self.output_for_download))
|
90
|
+
print(f"Output saved as '{file_path}'!")
|
91
|
+
else:
|
92
|
+
print("File save operation canceled.")
|
93
|
+
else:
|
94
|
+
print("No output available to download.")
|
95
|
+
|
96
|
+
|
97
|
+
def finish_selection(self):
|
98
|
+
"""Closes the window and returns selected files."""
|
99
|
+
self.root.quit() # Close the window
|
100
|
+
|
101
|
+
# This function is a companion function to
|
102
|
+
# The class DragDropApp for creating a file selection and function call app
|
103
|
+
# The function_for_after_file_addition should return a list where the first item is something that can be downloaded.
|
104
|
+
def create_and_launch(app_name = '', function_for_after_file_addition=None):
|
105
|
+
"""Starts the GUI and returns selected files."""
|
106
|
+
root = TkinterDnD.Tk()
|
107
|
+
app = DragDropApp(root, app_name=app_name, function_for_after_file_addition=function_for_after_file_addition)
|
108
|
+
root.mainloop() # Runs the Tkinter event loop
|
109
|
+
return app.all_selected_file_paths # Returns selected files after the window closes
|
JSONGrapher/units_list.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
'''
|
3
3
|
#This units list came from the UUC units list. https://github.com/Lemonexe/UUC/blob/master/app/data.js
|
4
4
|
# it has been converted to python and contains all of the program constants, the unit database, and database of prefixes
|
5
|
+
#In the python JSONGrapher repository, this is used (as of May 2025) simply to remove "plural" units.
|
6
|
+
# In python JSONGrapher, the unitpy package is what is used for unit conversion.
|
5
7
|
# 'en' is for English, 'cz' is for Czech, and "ae" is for American English.
|
6
8
|
|
7
9
|
metre → meter
|
@@ -14,6 +16,31 @@
|
|
14
16
|
tonne → metric ton (often written as ton in American English)
|
15
17
|
cubic centimetre → cubic centimeter
|
16
18
|
|
19
|
+
This file is adapted from UUC and is covered by the MIT License
|
20
|
+
|
21
|
+
MIT License
|
22
|
+
|
23
|
+
Copyright (c) 2017 Jiri Zbytovsky
|
24
|
+
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
26
|
+
of this software and associated documentation files (the "Software"), to deal
|
27
|
+
in the Software without restriction, including without limitation the rights
|
28
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
29
|
+
copies of the Software, and to permit persons to whom the Software is
|
30
|
+
furnished to do so, subject to the following conditions:
|
31
|
+
|
32
|
+
The above copyright notice and this permission notice shall be included in all
|
33
|
+
copies or substantial portions of the Software.
|
34
|
+
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
41
|
+
SOFTWARE.
|
42
|
+
|
43
|
+
|
17
44
|
'''
|
18
45
|
import math as Math
|
19
46
|
#program constants
|
@@ -1,79 +1,88 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
To use
|
5
|
-
<pre>
|
6
|
-
pip install
|
7
|
-
</pre>
|
8
|
-
|
9
|
-
Alternatively, you can download the directory directly.<br>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
Record.
|
36
|
-
Record.
|
37
|
-
Record.
|
38
|
-
Record.
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
"
|
61
|
-
"
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
1
|
+
# JSONGrapher (python)
|
2
|
+
This is the python version of JSONGrapher with JSONRecordCreator. This package is for plotting JSON records with drag and drop and has tools for creating the JSON records.
|
3
|
+
|
4
|
+
To use python JSONGrapher, first install it using pip:
|
5
|
+
<pre>
|
6
|
+
pip install JSONGrapher[COMPLETE]
|
7
|
+
</pre>
|
8
|
+
|
9
|
+
Alternatively, you can download the directory directly.<br>
|
10
|
+
|
11
|
+
## **0\. Plotting a JSON Record**
|
12
|
+
It's as simple as one line! Then drag a json record into the window to plot.
|
13
|
+
<pre>
|
14
|
+
import JSONGrapher
|
15
|
+
JSONGrapher.launch()
|
16
|
+
</pre>
|
17
|
+
|
18
|
+
[](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/JSONGrapher/JSONGrapherWindowShortened.png) [](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/examples/example_1/UAN_DTA_image.png)
|
19
|
+
|
20
|
+
## **1\. Preparing to Create a Record**
|
21
|
+
|
22
|
+
The remainder of this landing page follows a json record tutorial [example file](https://github.com/AdityaSavara/JSONGrapher/blob/main/examples/example_2/example_2_json_record_tutorial.py) which shows how to create graphable .json records and to plot them. The .json files can then be dragged into the python JSONgrapher or into www.jsongrapher.com<br>
|
23
|
+
|
24
|
+
Let's create an example where we plot the height of a pear tree over several years. Assuming a pear tree grows approximately 0.40 meters per year, we'll generate sample data with some variation.
|
25
|
+
<pre>
|
26
|
+
x_label_including_units = "Time (years)"
|
27
|
+
y_label_including_units = "Height (m)"
|
28
|
+
time_in_years = [0, 1, 2, 3, 4]
|
29
|
+
tree_heights = [0, 0.42, 0.86, 1.19, 1.45]
|
30
|
+
</pre>
|
31
|
+
|
32
|
+
## **2\. Creating and Populating a New JSONGrapher Record**
|
33
|
+
|
34
|
+
<pre>
|
35
|
+
Record = JSONRecordCreator.create_new_JSONGrapherRecord()
|
36
|
+
Record.set_comments("Tree Growth Data collected from the US National Arboretum")
|
37
|
+
Record.set_datatype("Tree_Growth_Curve")
|
38
|
+
Record.set_x_axis_label_including_units(x_label_including_units)
|
39
|
+
Record.set_y_axis_label_including_units(y_label_including_units)
|
40
|
+
Record.add_data_series(series_name="pear tree growth", x_values=time_in_years, y_values=tree_heights, plot_type="scatter_spline")
|
41
|
+
Record.set_graph_title("Pear Tree Growth Versus Time")
|
42
|
+
</pre>
|
43
|
+
|
44
|
+
## **3\. Exporting to File**
|
45
|
+
|
46
|
+
We can export it to a .json file, which can then be used with JSONGrapher.
|
47
|
+
<pre>
|
48
|
+
Record.export_to_json_file("ExampleFromTutorial.json")
|
49
|
+
Record.print_to_inspect()
|
50
|
+
</pre>
|
51
|
+
|
52
|
+
<p><strong>Expected Output:</strong></p>
|
53
|
+
<pre>
|
54
|
+
JSONGrapher Record exported to, ./ExampleFromTutorial.json
|
55
|
+
{
|
56
|
+
"comments": "Tree Growth Data collected from the US National Arboretum",
|
57
|
+
"datatype": "Tree_Growth_Curve",
|
58
|
+
"data": [
|
59
|
+
{
|
60
|
+
"name": "pear tree growth",
|
61
|
+
"x": [0, 1, 2, 3, 4],
|
62
|
+
"y": [0, 0.42, 0.86, 1.19, 1.45],
|
63
|
+
"type": "scatter",
|
64
|
+
"line": { "shape": "spline" }
|
65
|
+
}
|
66
|
+
],
|
67
|
+
"layout": {
|
68
|
+
"title": "Pear Tree Growth Versus Time",
|
69
|
+
"xaxis": { "title": "Time (year)" },
|
70
|
+
"yaxis": { "title": "Height (m)" }
|
71
|
+
}
|
72
|
+
}
|
73
|
+
</pre>
|
74
|
+
|
75
|
+
## **4\. Plotting to Inspect**
|
76
|
+
|
77
|
+
We can also plot the data using Matplotlib and export the plot as a PNG file.
|
78
|
+
<pre>
|
79
|
+
Record.plot_with_matplotlib()
|
80
|
+
Record.export_to_matplotlib_png("image_from_tutorial_matplotlib_fig")
|
81
|
+
</pre>
|
82
|
+
|
83
|
+
And we can create an interactive graph with plotly:
|
84
|
+
<pre>
|
85
|
+
Record.plot_with_plotly() #Try hovering your mouse over points after this command!
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
[](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/examples/example_2/image_from_tutorial_matplotlib_fig.png)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: jsongrapher
|
3
|
-
Version:
|
4
|
-
Summary:
|
5
|
-
Home-page: https://github.com/AdityaSavara/
|
3
|
+
Version: 2.8
|
4
|
+
Summary: The python version of JSONGrapher with tools for creating JSONGrapher Records.
|
5
|
+
Home-page: https://github.com/AdityaSavara/jsongrapher-py
|
6
6
|
Author: Aditya Savara
|
7
7
|
Author-email: AditySavara2008@u.northwestern.edu
|
8
8
|
License: Unlicense
|
@@ -18,22 +18,34 @@ License-File: LICENSE
|
|
18
18
|
Requires-Dist: numpy
|
19
19
|
Provides-Extra: complete
|
20
20
|
Requires-Dist: matplotlib; extra == "complete"
|
21
|
+
Requires-Dist: plotly; extra == "complete"
|
22
|
+
Requires-Dist: unitpy; extra == "complete"
|
23
|
+
Requires-Dist: tkinterdnd2; extra == "complete"
|
21
24
|
|
22
25
|
|
23
|
-
#
|
24
|
-
|
26
|
+
# JSONGrapher (python)
|
27
|
+
This is the python version of JSONGrapher with JSONRecordCreator. This package is for plotting JSON records with drag and drop and has tools for creating the JSON records.
|
25
28
|
|
26
|
-
To use
|
29
|
+
To use python JSONGrapher, first install it using pip:
|
27
30
|
<pre>
|
28
|
-
pip install
|
31
|
+
pip install JSONGrapher[COMPLETE]
|
29
32
|
</pre>
|
30
33
|
|
31
34
|
Alternatively, you can download the directory directly.<br>
|
32
|
-
It is easiest to then follow the [example file](https://github.com/AdityaSavara/JSONGrapherRC/blob/main/example/exampleUsageJSONRecordCreator.py) to learn.<br>
|
33
35
|
|
36
|
+
## **0\. Plotting a JSON Record**
|
37
|
+
It's as simple as one line! Then drag a json record into the window to plot.
|
38
|
+
<pre>
|
39
|
+
import JSONGrapher
|
40
|
+
JSONGrapher.launch()
|
41
|
+
</pre>
|
42
|
+
|
43
|
+
[](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/JSONGrapher/JSONGrapherWindowShortened.png) [](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/examples/example_1/UAN_DTA_image.png)
|
34
44
|
|
35
45
|
## **1\. Preparing to Create a Record**
|
36
46
|
|
47
|
+
The remainder of this landing page follows a json record tutorial [example file](https://github.com/AdityaSavara/JSONGrapher/blob/main/examples/example_2/example_2_json_record_tutorial.py) which shows how to create graphable .json records and to plot them. The .json files can then be dragged into the python JSONgrapher or into www.jsongrapher.com<br>
|
48
|
+
|
37
49
|
Let's create an example where we plot the height of a pear tree over several years. Assuming a pear tree grows approximately 0.40 meters per year, we'll generate sample data with some variation.
|
38
50
|
<pre>
|
39
51
|
x_label_including_units = "Time (years)"
|
@@ -44,13 +56,7 @@ tree_heights = [0, 0.42, 0.86, 1.19, 1.45]
|
|
44
56
|
|
45
57
|
## **2\. Creating and Populating a New JSONGrapher Record**
|
46
58
|
|
47
|
-
The easiest way to start is with the `create_new_JSONGrapherRecord()` function. While you *can* instantiate the JSONGrapherRecord class directly, this function is generally more convenient. We'll create a record and inspect its default fields.
|
48
59
|
<pre>
|
49
|
-
try:
|
50
|
-
from JSONGRapherRC import JSONRecordCreator # Normal usage
|
51
|
-
except ImportError:
|
52
|
-
import JSONRecordCreator # If the class file is local
|
53
|
-
|
54
60
|
Record = JSONRecordCreator.create_new_JSONGrapherRecord()
|
55
61
|
Record.set_comments("Tree Growth Data collected from the US National Arboretum")
|
56
62
|
Record.set_datatype("Tree_Growth_Curve")
|
@@ -62,7 +68,7 @@ Record.set_graph_title("Pear Tree Growth Versus Time")
|
|
62
68
|
|
63
69
|
## **3\. Exporting to File**
|
64
70
|
|
65
|
-
We
|
71
|
+
We can export it to a .json file, which can then be used with JSONGrapher.
|
66
72
|
<pre>
|
67
73
|
Record.export_to_json_file("ExampleFromTutorial.json")
|
68
74
|
Record.print_to_inspect()
|
@@ -91,11 +97,17 @@ JSONGrapher Record exported to, ./ExampleFromTutorial.json
|
|
91
97
|
}
|
92
98
|
</pre>
|
93
99
|
|
100
|
+
## **4\. Plotting to Inspect**
|
94
101
|
|
95
102
|
We can also plot the data using Matplotlib and export the plot as a PNG file.
|
96
103
|
<pre>
|
97
104
|
Record.plot_with_matplotlib()
|
98
|
-
Record.export_to_matplotlib_png("
|
105
|
+
Record.export_to_matplotlib_png("image_from_tutorial_matplotlib_fig")
|
106
|
+
</pre>
|
107
|
+
|
108
|
+
And we can create an interactive graph with plotly:
|
109
|
+
<pre>
|
110
|
+
Record.plot_with_plotly() #Try hovering your mouse over points after this command!
|
99
111
|
</pre>
|
100
112
|
|
101
|
-
[](https://raw.githubusercontent.com/AdityaSavara/JSONGrapher-py/main/examples/example_2/image_from_tutorial_matplotlib_fig.png)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
JSONGrapher/JSONRecordCreator.py,sha256=rClBw0XfaGNx3LGwuT9gWwfW8TNzlnnZnaLbWqdsE9g,96177
|
2
|
+
JSONGrapher/UnitPytesting.py,sha256=xizJ-2fg9C5oNMFJyfavbBLMusayE9KWQiYIRrQQd4A,4363
|
3
|
+
JSONGrapher/UnitpyCustomUnitsTesting.py,sha256=Rwq5p8HXN0FP54lRFgLTV0kgJ9TpcFaD3_C0MEOoEzw,1297
|
4
|
+
JSONGrapher/__init__.py,sha256=cc5qXQidP_ABPXdnXy_DTdgPanHuR7ya45LV-BdWVh8,277
|
5
|
+
JSONGrapher/drag_and_drop_gui.py,sha256=-7QJHLhzadHotWhONH4inerMaZ_xuwoTQSMRF_MPVe0,5632
|
6
|
+
JSONGrapher/units_list.py,sha256=ROrlAsD66oPozZmOaE65xjNUEg3CxkSmA8iPE2_ihYI,34438
|
7
|
+
jsongrapher-2.8.data/data/LICENSE,sha256=KrPIO2Ij3T_47dfpfngKzpC4EXohIZMsHauG0R4M-kM,1234
|
8
|
+
jsongrapher-2.8.data/data/README.md,sha256=0gza4X7_6Sb7NAgeaiQfaizwmT7-PT_YsOHZMt0oQ_8,3921
|
9
|
+
jsongrapher-2.8.dist-info/LICENSE,sha256=KrPIO2Ij3T_47dfpfngKzpC4EXohIZMsHauG0R4M-kM,1234
|
10
|
+
jsongrapher-2.8.dist-info/METADATA,sha256=KfobXnAyNaLZV9fZuEUNYrQ6PcR090Ujhj2JHSSNnOg,4886
|
11
|
+
jsongrapher-2.8.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
12
|
+
jsongrapher-2.8.dist-info/top_level.txt,sha256=5f7Ui2hCKCPTQOjD540WKghKNYOvUDNfdpnDbIlAD3Y,12
|
13
|
+
jsongrapher-2.8.dist-info/RECORD,,
|
jsongrapher-1.6.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
JSONGrapher/JSONRecordCreator.py,sha256=_dfVxwy7itw3mUq7G_akyeup5KEy6KHXLeiINAzhHqg,45957
|
2
|
-
JSONGrapher/__init__.py,sha256=YvI9lnzN8e6jfCF--fuyiNZYq_YiVM72osipYjmWLAA,279
|
3
|
-
JSONGrapher/units_list.py,sha256=ARlPgvKWZM8um1FA7OtzDjnJhl0zy1X4vGbTQN93MnQ,33074
|
4
|
-
jsongrapher-1.6.data/data/LICENSE,sha256=KrPIO2Ij3T_47dfpfngKzpC4EXohIZMsHauG0R4M-kM,1234
|
5
|
-
jsongrapher-1.6.data/data/README.md,sha256=9UIVwsfi9CtLWzRFTnMiWvRiFf7MZLzmlWOpuOrj8e8,3036
|
6
|
-
jsongrapher-1.6.dist-info/LICENSE,sha256=KrPIO2Ij3T_47dfpfngKzpC4EXohIZMsHauG0R4M-kM,1234
|
7
|
-
jsongrapher-1.6.dist-info/METADATA,sha256=sLXqe5taldI3P4ASryDjexbue9uH8p2bY3Gnb3nZvHw,3913
|
8
|
-
jsongrapher-1.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
9
|
-
jsongrapher-1.6.dist-info/top_level.txt,sha256=5f7Ui2hCKCPTQOjD540WKghKNYOvUDNfdpnDbIlAD3Y,12
|
10
|
-
jsongrapher-1.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|