Skip to content

Commit

Permalink
Issue with saving file
Browse files Browse the repository at this point in the history
  • Loading branch information
trungleduc committed Jan 27, 2023
1 parent 7569ebf commit 5c5299c
Show file tree
Hide file tree
Showing 19 changed files with 295 additions and 94 deletions.
81 changes: 69 additions & 12 deletions examples/api.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "195e05d93a684e57a9f0e35fe770df29",
"model_id": "30c3343ee1e3461d9e6f68f9220522e8",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -36,29 +36,86 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"a.add_object(Box('newBox', 10, 10,10))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a.objects"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"all_objects ['Box', 'Cone', 'Common', 'Box', 'Cone', 'Common', 'Box', 'Cone', 'Common', 'Box', 'Cone', 'Common']\n"
"ename": "AttributeError",
"evalue": "partially initialized module 'jupytercad.fcstd_ydoc' has no attribute 'YFCStd' (most likely due to a circular import)",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mjupytercad\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfcstd_ydoc\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m YFCStd\n",
"File \u001b[0;32m~/WORK/jupytercad/jupytercad/fcstd_ydoc.py:1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mjupyter_ydoc\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mydoc\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m YBaseDoc\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01my_py\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mY\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mjupytercad\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfreecad\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mloader\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FCStd\n",
"File \u001b[0;32m~/mambaforge/envs/caddev/lib/python3.9/site-packages/jupyter_ydoc/__init__.py:12\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mimportlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmetadata\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m entry_points\n\u001b[0;32m---> 12\u001b[0m ydocs \u001b[38;5;241m=\u001b[39m {ep\u001b[38;5;241m.\u001b[39mname: ep\u001b[38;5;241m.\u001b[39mload() \u001b[38;5;28;01mfor\u001b[39;00m ep \u001b[38;5;129;01min\u001b[39;00m entry_points(group\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mjupyter_ydoc\u001b[39m\u001b[38;5;124m\"\u001b[39m)}\n\u001b[1;32m 14\u001b[0m __version__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m0.2.2\u001b[39m\u001b[38;5;124m\"\u001b[39m\n",
"File \u001b[0;32m~/mambaforge/envs/caddev/lib/python3.9/site-packages/jupyter_ydoc/__init__.py:12\u001b[0m, in \u001b[0;36m<dictcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mimportlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmetadata\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m entry_points\n\u001b[0;32m---> 12\u001b[0m ydocs \u001b[38;5;241m=\u001b[39m {ep\u001b[38;5;241m.\u001b[39mname: \u001b[43mep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mload\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m ep \u001b[38;5;129;01min\u001b[39;00m entry_points(group\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mjupyter_ydoc\u001b[39m\u001b[38;5;124m\"\u001b[39m)}\n\u001b[1;32m 14\u001b[0m __version__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m0.2.2\u001b[39m\u001b[38;5;124m\"\u001b[39m\n",
"File \u001b[0;32m~/mambaforge/envs/caddev/lib/python3.9/site-packages/importlib_metadata/__init__.py:210\u001b[0m, in \u001b[0;36mEntryPoint.load\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 208\u001b[0m module \u001b[38;5;241m=\u001b[39m import_module(match\u001b[38;5;241m.\u001b[39mgroup(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmodule\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m 209\u001b[0m attrs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mfilter\u001b[39m(\u001b[38;5;28;01mNone\u001b[39;00m, (match\u001b[38;5;241m.\u001b[39mgroup(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mattr\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m)\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[0;32m--> 210\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunctools\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mreduce\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodule\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mAttributeError\u001b[0m: partially initialized module 'jupytercad.fcstd_ydoc' has no attribute 'YFCStd' (most likely due to a circular import)"
]
}
],
"source": [
"\n",
"a.add_object(Box('newBox', 10, 10,10))"
"from jupytercad.fcstd_ydoc import YFCStd"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 4,
"metadata": {},
"outputs": [],
"outputs": [
{
"data": {
"text/plain": [
"<Task finished name='Task-4' coro=<CadWidget.__start() done, defined at /home/trungle/WORK/jupytercad/jupytercad/notebook/cad_widget.py:44> result=None>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a._widget.tk"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<YDoc at 0x7f4b9e8bc3c0>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a.objects[2]"
"a._widget.ydoc"
]
},
{
Expand All @@ -85,7 +142,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.8"
"version": "3.9.15"
}
},
"nbformat": 4,
Expand Down
1 change: 0 additions & 1 deletion jupytercad/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from ._version import __version__
from .notebook import CadWidget

def _jupyter_labextension_paths():
return [{'src': 'labextension', 'dest': 'jupytercad'}]
6 changes: 4 additions & 2 deletions jupytercad/fcstd_ydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def __init__(self, *args, **kwargs):
self._ymeta = self._ydoc.get_map('metadata')
self._virtual_file = FCStd()

@property
def objects(self) -> Y.YArray:
return self._yobjects

@property
def source(self):
fc_objects = self._yobjects.to_json()
Expand All @@ -29,12 +33,10 @@ def source(self, value):

for obj in virtual_file.objects:
objects.append(Y.YMap(obj))

with self._ydoc.begin_transaction() as t:
length = len(self._yobjects)
self._yobjects.delete_range(t, 0, length)
self._yobjects.extend(t, objects)

self._yoptions.update(t, virtual_file.options.items())
self._ymeta.update(t, virtual_file.metadata.items())

Expand Down
6 changes: 3 additions & 3 deletions jupytercad/freecad/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
from typing import Dict, List, Type
import tempfile
import base64
from pathlib import Path
import os
import xml
import zipfile

from .props.base_prop import BaseProp
from . import props as Props
Expand Down Expand Up @@ -123,6 +120,7 @@ def load(self, base64_content: str) -> None:
)

# Get objects
self._objects = []
for obj in fc_file.Objects:
self._objects.append(self._fc_to_jcad_obj(obj))

Expand All @@ -140,7 +138,9 @@ def save(self, objects: List, options: Dict, metadata: Dict) -> None:
fc_file.Meta = metadata

new_objs = dict([(o["name"], o) for o in objects])

current_objs = dict([(o.Name, o) for o in fc_file.Objects])

to_remove = [x for x in current_objs if x not in new_objs]
to_add = [x for x in new_objs if x not in current_objs]
for obj_name in to_remove:
Expand Down
1 change: 0 additions & 1 deletion jupytercad/notebook/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from .cad_widget import CadWidget
from .cad_document import CadDocument
from .objects import *
63 changes: 49 additions & 14 deletions jupytercad/notebook/cad_document.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Callable, Optional
from typing import Callable, List, Optional

from .objects import OBJECT_FACTORY
from .cad_widget import CadWidget
from .utils import normalize_path
from .objects import BaseObject
Expand All @@ -10,34 +12,67 @@ class CadDocument:
def __init__(self, path: str) -> None:
self._path = path
self._widget = CadWidget(normalize_path(os.getcwd(), self._path))
self._objects: Optional[Y.YArray] = None
self._objects_array: Optional[Y.YArray] = None

@property
def ydoc(self):
return self._widget.ydoc

@property
def objects(self) -> Optional[Y.YArray]:
if self._objects:
return self._objects
objects = self._widget.ydoc.get_array('objects')
if objects:
self._objects = objects
def objects(self) -> Optional[List[str]]:
if not self.ydoc:
return []
if not self._objects_array:
objects = self.ydoc.get_array('objects')
if not objects:
return
self._objects_array = objects

return [x['name'] for x in self._objects_array]

def get_object(self, name: str) -> Optional[BaseObject]:
if self.check_exist(name):
data = self._get_yobject_by_name(name).to_json()
return OBJECT_FACTORY.create_object(data)

def update_object(self, object: BaseObject) -> None:
if self.check_exist(object.name):
yobject = self._get_yobject_by_name(object.name)
with self.ydoc.begin_transaction() as t:
yobject.set(t, 'parameters', object.parameters)
yobject.set(t, 'visible', object.visible)

return objects
def remove_object(self, name: str) -> None:
index = self._get_yobject_index_by_name(name)
if self._objects_array and index != -1:
with self.ydoc.begin_transaction() as t:
self._objects_array.delete(t, index)

def add_object(self, new_object: BaseObject):
if self.objects and not self.check_exist(new_object.name):
def add_object(self, new_object: BaseObject) -> None:
if self._objects_array and not self.check_exist(new_object.name):
new_map = Y.YMap(new_object.to_dict())
with self.ydoc.begin_transaction() as t:
self.objects.append(t, new_map)
self._objects_array.append(t, new_map)

def check_exist(self, name: str) -> bool:
if self.objects:
all_objects = [x['name'] for x in self.objects]
return name in all_objects
return name in self.objects
return False

def _get_yobject_by_name(self, name: str) -> Optional[Y.YMap]:
if self._objects_array:
for item in self._objects_array:
if item['name'] == name:
return item
return None

def _get_yobject_index_by_name(self, name: str) -> int:
if self._objects_array:
for index, item in enumerate(self._objects_array):
if item['name'] == name:
return index
return -1

def _ipython_display_(
self,
) -> None:
Expand Down
6 changes: 5 additions & 1 deletion jupytercad/notebook/cad_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ def __connect_room(self, payload: Dict) -> None:
multi_urljoin(base_ws_url, WS_YROOM_URL, room_name)
+ f'?token={token}'
)
asyncio.create_task(self.__start(ws_url))
self.tk = asyncio.create_task(self.__start(ws_url))

def _handle_task_result(self, task: asyncio.Task) -> None:

print('ret', task.result())

def __handle_frontend_msg(
self, model: 'CadWidget', msg: Dict, buffer: List
Expand Down
8 changes: 6 additions & 2 deletions jupytercad/notebook/objects/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .box import Box
from .factory_manager import ObjectFactoryManager
from .box import Box, BoxFactory
from .placement import Placement
from .base import BaseObject
from .base import BaseObject

OBJECT_FACTORY = ObjectFactoryManager()
OBJECT_FACTORY.register_factory(BoxFactory())
24 changes: 21 additions & 3 deletions jupytercad/notebook/objects/base.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
from abc import ABC, abstractmethod
from typing import Dict
from typing import Any, Dict


class BaseObject(ABC):

@property
@abstractmethod
def name(self) -> str:
pass

@property
@abstractmethod
def visible(self) -> bool:
pass

@property
@abstractmethod
def shape(self) -> str:
pass

@property
@abstractmethod
def parameters(self) -> Dict:
pass

@abstractmethod
def to_dict(self) -> Dict:
pass

@abstractmethod
def from_dict(self, value: Dict) -> None:
def from_dict(self, value: Dict[str, Any]) -> None:
pass

def __repr__ (self) -> str:
return str(self.to_dict())
44 changes: 0 additions & 44 deletions jupytercad/notebook/objects/box.py

This file was deleted.

2 changes: 2 additions & 0 deletions jupytercad/notebook/objects/box/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .box import Box
from .factory import BoxFactory
Loading

0 comments on commit 5c5299c

Please sign in to comment.