292 lines
9.3 KiB
Python
292 lines
9.3 KiB
Python
import bpy
|
|
import os
|
|
import json
|
|
import numpy
|
|
from .. import __package__
|
|
|
|
JSON_DEFAULT_WIDGETS = "widgets.json"
|
|
JSON_USER_WIDGETS = "user_widgets.json"
|
|
|
|
widget_data = {}
|
|
|
|
|
|
def objectDataToDico(object, custom_image):
|
|
verts = []
|
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
|
mesh = object.evaluated_get(depsgraph).to_mesh()
|
|
for v in mesh.vertices:
|
|
verts.append(tuple(numpy.array(tuple(v.co)) *
|
|
(object.scale[0], object.scale[1], object.scale[2])))
|
|
|
|
polygons = []
|
|
for p in mesh.polygons:
|
|
polygons.append(tuple(p.vertices))
|
|
|
|
edges = []
|
|
for e in mesh.edges:
|
|
edges.append(e.key)
|
|
|
|
custom_image = custom_image if custom_image != "" else "user_defined.png"
|
|
|
|
wgts = {"vertices": verts, "edges": edges, "faces": polygons, "image": custom_image}
|
|
|
|
return(wgts)
|
|
|
|
|
|
def readWidgets(filename = ""):
|
|
global widget_data
|
|
wgts = {}
|
|
|
|
if not filename:
|
|
files = [JSON_DEFAULT_WIDGETS, JSON_USER_WIDGETS]
|
|
else:
|
|
files = [filename]
|
|
|
|
for file in files:
|
|
jsonFile = os.path.join(os.path.dirname(os.path.dirname(__file__)), file)
|
|
if os.path.exists(jsonFile):
|
|
f = open(jsonFile, 'r')
|
|
wgts.update(json.load(f))
|
|
f.close()
|
|
|
|
if not filename: # if both files have been read
|
|
widget_data = wgts.copy()
|
|
|
|
return (wgts)
|
|
|
|
|
|
def getWidgetData(widget):
|
|
return widget_data[widget]
|
|
|
|
|
|
def writeWidgets(wgts, file):
|
|
jsonFile = os.path.join(os.path.dirname(os.path.dirname(__file__)), file)
|
|
#if os.path.exists(jsonFile):
|
|
f = open(jsonFile, 'w')
|
|
f.write(json.dumps(wgts))
|
|
f.close()
|
|
|
|
|
|
def addRemoveWidgets(context, addOrRemove, items, widgets, widget_name="", custom_image=""):
|
|
wgts = {}
|
|
|
|
# file from where the widget should be read or written to
|
|
file = JSON_USER_WIDGETS
|
|
|
|
widget_items = []
|
|
for widget_item in items:
|
|
widget_items.append(widget_item[1])
|
|
|
|
activeShape = None
|
|
ob_name = None
|
|
return_message = ""
|
|
if addOrRemove == 'add':
|
|
wgts = readWidgets(file)
|
|
bw_widget_prefix = bpy.context.preferences.addons[__package__].preferences.widget_prefix
|
|
for ob in widgets:
|
|
if not widget_name:
|
|
if ob.name.startswith(bw_widget_prefix):
|
|
ob_name = ob.name[len(bw_widget_prefix):]
|
|
else:
|
|
ob_name = ob.name
|
|
else:
|
|
ob_name = widget_name
|
|
|
|
if (ob_name) not in widget_items:
|
|
widget_items.append(ob_name)
|
|
wgts[ob_name] = objectDataToDico(ob, custom_image)
|
|
activeShape = ob_name
|
|
return_message = "Widget - " + ob_name + " has been added!"
|
|
|
|
elif addOrRemove == 'remove':
|
|
user_widgets = readWidgets(file)
|
|
if widgets in user_widgets:
|
|
wgts = user_widgets
|
|
else:
|
|
file = JSON_DEFAULT_WIDGETS
|
|
wgts = readWidgets(file)
|
|
|
|
del wgts[widgets]
|
|
if widgets in widget_items:
|
|
widget_index = widget_items.index(widgets)
|
|
activeShape = widget_items[widget_index + 1] if widget_index == 0 else widget_items[widget_index - 1]
|
|
widget_items.remove(widgets)
|
|
return_message = "Widget - " + widgets + " has been removed!"
|
|
|
|
if activeShape is not None:
|
|
|
|
writeWidgets(wgts, file)
|
|
|
|
# to handle circular import error
|
|
from .functions import createPreviewCollection
|
|
|
|
createPreviewCollection()
|
|
|
|
# trigger an update and display widget
|
|
bpy.context.window_manager.widget_list = activeShape
|
|
|
|
return 'INFO', return_message
|
|
elif ob_name is not None:
|
|
return 'WARNING', "Widget - " + ob_name + " already exists!"
|
|
|
|
|
|
def exportWidgetLibrary(filepath):
|
|
wgts = readWidgets(JSON_USER_WIDGETS)
|
|
|
|
if wgts:
|
|
# variables needed for exporting widgets
|
|
dest_dir = os.path.dirname(filepath)
|
|
json_dir = os.path.dirname(os.path.dirname(__file__))
|
|
image_folder = 'custom_thumbnails'
|
|
custom_image_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', image_folder))
|
|
|
|
filename = os.path.basename(filepath)
|
|
if not filename: filename = "widgetLibrary.zip"
|
|
elif not filename.endswith('.zip'): filename += ".zip"
|
|
|
|
# start the zipping process
|
|
from zipfile import ZipFile
|
|
with ZipFile(os.path.join(dest_dir, filename), "w") as zip:
|
|
# write the json file
|
|
file = os.path.join(json_dir, JSON_USER_WIDGETS)
|
|
arcname = os.path.basename(file)
|
|
zip.write(file, arcname=arcname)
|
|
|
|
# write the custom images if present
|
|
if os.path.exists(custom_image_dir):
|
|
from pathlib import Path
|
|
for filepath in Path(custom_image_dir).iterdir():
|
|
arcname = os.path.join(image_folder, os.path.basename(filepath))
|
|
zip.write(filepath, arcname=arcname)
|
|
|
|
return len(wgts)
|
|
|
|
|
|
class WidgetImportStats:
|
|
def __init__(self):
|
|
self.new_widgets = 0
|
|
self.failed_widgets = {}
|
|
self.skipped_widgets = [] # to make sure the data won't change order
|
|
self.widgets = {}
|
|
|
|
def skipped(self):
|
|
return len(self.skipped_widgets)
|
|
|
|
def failed(self):
|
|
return len(self.failed_widgets)
|
|
|
|
|
|
def importWidgetLibrary(filepath, action=""):
|
|
wgts = {}
|
|
|
|
from zipfile import ZipFile
|
|
dest_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..'))
|
|
|
|
if os.path.exists(filepath) and action:
|
|
try:
|
|
with ZipFile(filepath, 'r') as zip_file:
|
|
# extract images
|
|
for file in zip_file.namelist():
|
|
if file.startswith('custom_thumbnails/'):
|
|
zip_file.extract(file, dest_dir)
|
|
elif file.endswith('.json'): # extract data from the .json file
|
|
f = zip_file.read(file)
|
|
json_data = f.decode('utf8').replace("'", '"')
|
|
wgts = json.loads(json_data)
|
|
|
|
current_wgts = readWidgets(JSON_USER_WIDGETS)
|
|
|
|
widgetImport = WidgetImportStats()
|
|
|
|
# check for duplicate names
|
|
for name, data in sorted(wgts.items()): # sorting by keys
|
|
if action == "ASK":
|
|
widgetImport.skipped_widgets.append({name : data})
|
|
elif action == "OVERWRITE":
|
|
widgetImport.widgets.update({name : data})
|
|
widgetImport.new_widgets += 1
|
|
elif action == "SKIP":
|
|
if not name in current_wgts:
|
|
widgetImport.widgets.update({name : data})
|
|
widgetImport.new_widgets += 1
|
|
else:
|
|
widgetImport.skipped_widgets.append({name : data})
|
|
else:
|
|
widgetImport.failed_widgets.update({name : data})
|
|
|
|
except:
|
|
pass
|
|
return widgetImport
|
|
|
|
|
|
def updateWidgetLibrary(new_widgets, new_images, zip_filepath):
|
|
current_widget = bpy.context.window_manager.widget_list
|
|
wgts = readWidgets(JSON_USER_WIDGETS)
|
|
|
|
wgts.update(new_widgets)
|
|
|
|
writeWidgets(wgts, JSON_USER_WIDGETS)
|
|
|
|
# extract any images needed from zip library
|
|
if new_images:
|
|
from zipfile import ZipFile
|
|
dest_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..'))
|
|
if os.path.exists(zip_filepath):
|
|
try:
|
|
with ZipFile(zip_filepath, 'r') as zip_file:
|
|
for file in zip_file.namelist():
|
|
if file.startswith('custom_thumbnails/') and file.split("/")[1] in new_images:
|
|
zip_file.extract(file, dest_dir)
|
|
except:
|
|
pass
|
|
|
|
# update the preview panel
|
|
from .functions import createPreviewCollection
|
|
createPreviewCollection()
|
|
|
|
# trigger an update and display original but updated widget
|
|
bpy.context.window_manager.widget_list = current_widget
|
|
|
|
|
|
def updateCustomImage(image_name):
|
|
current_widget = bpy.context.window_manager.widget_list
|
|
current_widget_data = getWidgetData(current_widget)
|
|
|
|
# swap out the image
|
|
current_widget_data['image'] = image_name
|
|
|
|
# update and write the new data
|
|
wgts = readWidgets(JSON_USER_WIDGETS)
|
|
if current_widget in wgts:
|
|
wgts[current_widget] = current_widget_data
|
|
writeWidgets(wgts, JSON_USER_WIDGETS)
|
|
else:
|
|
wgts = readWidgets(JSON_DEFAULT_WIDGETS)
|
|
wgts[current_widget] = current_widget_data
|
|
writeWidgets(wgts, JSON_DEFAULT_WIDGETS)
|
|
|
|
# update the preview panel
|
|
from .functions import createPreviewCollection
|
|
createPreviewCollection()
|
|
|
|
# trigger an update and display original but updated widget
|
|
bpy.context.window_manager.widget_list = current_widget
|
|
|
|
|
|
def resetDefaultImages():
|
|
current_widget = bpy.context.window_manager.widget_list
|
|
wgts = readWidgets(JSON_DEFAULT_WIDGETS)
|
|
|
|
for name, data in wgts.items():
|
|
image = f"{name}.png"
|
|
data["image"] = image
|
|
|
|
writeWidgets(wgts, JSON_DEFAULT_WIDGETS)
|
|
|
|
# update the preview panel
|
|
from .functions import createPreviewCollection
|
|
createPreviewCollection()
|
|
|
|
# trigger an update and display original but updated widget
|
|
bpy.context.window_manager.widget_list = current_widget
|