Source code for webcface.canvas2d

from typing import Optional, Callable, List, SupportsFloat, Union, Dict
import webcface.field
import webcface.canvas2d_base
import webcface.geometries
import webcface.client_data
import webcface.transform
import webcface.view_base
import webcface.temporal_component


[docs] class Canvas2DData: tmp_components: "List[webcface.temporal_component.TemporalComponent]" components: "Dict[str, webcface.canvas2d_base.Canvas2DComponentBase]" ids: List[str] width: float height: float def __init__(self, width: float, height: float) -> None: if width <= 0 or height <= 0: raise ValueError(f"Invalid Canvas2D size ({width} x {height})") self.tmp_components = [] self.components = {} self.ids = [] self.width = width self.height = height
[docs] class Canvas2DComponent(webcface.canvas2d_base.Canvas2DComponentBase): _data: Optional[webcface.client_data.ClientData] _id: str def __init__( self, base: "webcface.canvas2d_base.Canvas2DComponentBase", data: "Optional[webcface.client_data.ClientData]", id: str, ) -> None: super().__init__( base._type, base._origin_pos, base._origin_rot, base._color, base._fill, base._stroke_width, base._geometry_type, base._geometry_properties, ) self._data = data self._id = id def __eq__(self, other) -> bool: """プロパティの比較 (ver3.0〜) :return: id以外のプロパティが全部等しければTrueになる """ return isinstance( other, Canvas2DComponent ) and webcface.canvas2d_base.Canvas2DComponentBase.__eq__(self, other) def __ne__(self, other) -> bool: return not self == other @property def id(self) -> str: """要素のid (ver3.0〜)""" return self._id @property def type(self) -> int: """コンポーネントの種類 Canvas2DComponentType Enumを使う """ return self._type @property def origin(self) -> "webcface.transform.Transform": """表示する要素の移動""" return webcface.transform.Transform(self._origin_pos, self._origin_rot) @property def color(self) -> int: """色 (ViewColor)""" return self._color @property def fill(self) -> int: """塗りつぶしの色 (ViewColor)""" return self._fill @property def stroke_width(self) -> float: """線の太さ""" return self._stroke_width @property def geometry(self) -> "webcface.geometries.Geometry": """表示する図形""" return webcface.geometries.Geometry( self._geometry_type, self._geometry_properties )
[docs] class Canvas2D(webcface.field.Field): _c2data: "Optional[Canvas2DData]" _modified: bool def __init__( self, base: "webcface.field.Field", field: str = "", width: Optional[SupportsFloat] = None, height: Optional[SupportsFloat] = None, ) -> None: """Canvas2Dを指すクラス 引数にwidthとheightを渡すとinitされる このコンストラクタを直接使わず、 Member.canvas2d(), Member.canvas2d_entries(), Member.on_canvas2d_entry などを使うこと 詳細は `Canvas2Dのドキュメント <https://na-trium-144.github.io/webcface/md_14__canvas2d.html>`_ を参照 """ super().__init__( base._data, base._member, field if field != "" else base._field ) self._c2data = None self._modified = False if width is not None and height is not None: self.init(width, height) @property def member(self) -> "webcface.member.Member": """Memberを返す""" return webcface.member.Member(self) @property def name(self) -> str: """field名を返す""" return self._field
[docs] def on_change(self, func: Callable) -> Callable: """値が変化したときのイベント (ver2.0〜) コールバックの引数にはCanvas2Dオブジェクトが渡される。 まだ値をリクエストされてなければ自動でリクエストされる """ self.request() data = self._data_check() if self._member not in data.on_canvas2d_change: data.on_canvas2d_change[self._member] = {} data.on_canvas2d_change[self._member][self._field] = func return func
[docs] def child(self, field: str) -> "Canvas2D": """子フィールドを返す :return: 「(thisのフィールド名).(子フィールド名)」をフィールド名とするView """ return Canvas2D(self, self._field + "." + field)
[docs] def request(self) -> None: """値の受信をリクエストする""" req = self._data_check().canvas2d_store.add_req(self._member, self._field) if req > 0: self._data_check().queue_msg_req( [webcface.message.Canvas2DReq.new(self._member, self._field, req)] )
[docs] def try_get(self) -> "Optional[List[Canvas2DComponent]]": """CanvasをlistまたはNoneで返す、まだリクエストされてなければ自動でリクエストされる""" self.request() v = self._data_check().canvas2d_store.get_recv(self._member, self._field) v2: Optional[List[Canvas2DComponent]] = None if v is not None: v2 = [ Canvas2DComponent(v.components[v_id], self._data, v_id) for v_id in v.ids ] return v2
[docs] def get(self) -> "List[Canvas2DComponent]": """Canvasをlistで返す、まだリクエストされてなければ自動でリクエストされる""" v = self.try_get() return v if v is not None else []
[docs] def exists(self) -> bool: """このフィールドにデータが存在すればtrue (ver2.0〜) try_get() などとは違って、実際のデータを受信しない。 リクエストもしない。 """ return self._field in self._data_check().canvas2d_store.get_entry(self._member)
@property def width(self) -> float: """Canvasのサイズを返す、まだリクエストされてなければ自動でリクエストされる init()されている場合はその値を返す""" if self._c2data is not None: return self._c2data.width else: self.request() v = self._data_check().canvas2d_store.get_recv(self._member, self._field) if v is not None: return v.width else: return 0 @property def height(self) -> float: """Canvasのサイズを返す、まだリクエストされてなければ自動でリクエストされる init()されている場合はその値を返す""" if self._c2data is not None: return self._c2data.height else: self.request() v = self._data_check().canvas2d_store.get_recv(self._member, self._field) if v is not None: return v.height else: return 0 def __enter__(self) -> "Canvas2D": """with構文の最初でなにもしない""" return self
[docs] def init(self, width: SupportsFloat, height: SupportsFloat) -> "Canvas2D": """このCanvas2Dオブジェクトにaddした内容を初期化する and Canvas2Dのサイズを指定する """ self._c2data = Canvas2DData(float(width), float(height)) self._modified = True return self
def __exit__(self, type, value, tb) -> None: """with構文の終わりに自動でsync()を呼ぶ""" self.sync()
[docs] def sync(self) -> "Canvas2D": """Viewの内容をclientに反映し送信可能にする""" self._set_check() if self._modified and self._c2data is not None: data = self._set_check() data_idx: Dict[int, int] = {} for c in self._c2data.tmp_components: idx = data_idx.get(c._canvas2d_type, 0) data_idx[c._canvas2d_type] = idx + 1 c.lock_tmp(data, "c2", self._field, f"..{c._canvas2d_type}.{idx}") self._c2data.components[c.id] = c.to_canvas2d() self._c2data.ids.append(c.id) self._set_check().canvas2d_store.set_send(self._field, self._c2data) self._modified = False on_change = ( self._data_check().on_canvas2d_change.get(self._member, {}).get(self._field) ) if on_change is not None: on_change(self) return self
[docs] def add( self, *args: Union[ "webcface.temporal_component.TemporalComponent", "webcface.geometries.Geometry2D", ], **kwargs, ) -> "Canvas2D": """要素を追加 初期化時またはinit()で事前にサイズを指定していなければエラー :arg args: 追加する要素 (ver3.0〜:複数指定した場合すべて追加される。) :arg kwargs: (ver3.0〜) argsが初期化済みの要素でない場合、要素の初期化時に渡すオプション。 詳細は TemporalComponent のコンストラクタを参照 """ if self._c2data is None: raise ValueError("Canvas2D not initialized") assert len(args) > 0, "no components given to Canvas2D.add()" for c in args: if isinstance(c, webcface.temporal_component.TemporalComponent): if len(kwargs) > 0: raise ValueError( f"kwargs is not allowed because {c} is already a component" ) self._c2data.tmp_components.append(c) elif isinstance(c, webcface.geometries.Geometry): self._c2data.tmp_components.append( webcface.temporal_component.TemporalComponent( canvas2d_type=webcface.canvas2d_base.Canvas2DComponentType.GEOMETRY, geometry=c, **kwargs, ) ) else: raise ValueError(f"Invalid component {c}") self._modified = True return self