11"""
22Qt Renderer for pymiro.
33"""
4+
45from typing import Any
6+
7+ from PySide6 .QtCore import QMetaObject , QObject , Qt , Slot
58from PySide6 .QtWidgets import QWidget
6- from PySide6 .QtCore import QObject , QMetaObject , Qt , Slot
79
8- from pymiro .core .reconciler import CreateNode , UpdateProps , DestroyNode , SetChildren , MoveNode , Patch
9- from pymiro .backends .qt .widgets import create_widget , apply_prop , remove_prop
10+ from pymiro .backends .qt .widgets import apply_prop , create_widget , remove_prop
11+ from pymiro .core .reconciler import (
12+ CreateNode ,
13+ DestroyNode ,
14+ MoveNode ,
15+ Patch ,
16+ SetChildren ,
17+ UpdateProps ,
18+ )
19+
1020
1121class QtRenderer (QObject ):
1222 """
1323 Implements the Renderer protocol for Qt.
1424 Mutates Qt widgets based on reconciler patches.
1525 """
26+
1627 def __init__ (self , root_widget : QWidget | None = None , logger : Any = None ) -> None :
1728 super ().__init__ ()
1829 self .logger = logger
@@ -23,11 +34,7 @@ def __init__(self, root_widget: QWidget | None = None, logger: Any = None) -> No
2334 self ._pending_patches : list [Patch ] = []
2435
2536 def create (
26- self ,
27- node_id : str ,
28- type : str ,
29- props : dict [str , Any ],
30- parent_id : str | None
37+ self , node_id : str , type : str , props : dict [str , Any ], parent_id : str | None
3138 ) -> None :
3239 widget = create_widget (type )
3340 self .registry [node_id ] = widget
@@ -51,12 +58,7 @@ def create(
5158 if layout is not None :
5259 layout .addWidget (widget )
5360
54- def update (
55- self ,
56- node_id : str ,
57- changed : dict [str , Any ],
58- removed : list [str ]
59- ) -> None :
61+ def update (self , node_id : str , changed : dict [str , Any ], removed : list [str ]) -> None :
6062 widget = self .registry .get (node_id )
6163 if widget is None :
6264 return
@@ -77,8 +79,10 @@ def destroy(self, node_id: str) -> None:
7779 widget = self .registry .pop (node_id , None )
7880 if widget is None :
7981 return
80-
81- events_to_unbind = [evt for (nid , evt ) in list (self .event_connections .keys ()) if nid == node_id ]
82+
83+ events_to_unbind = [
84+ evt for (nid , evt ) in list (self .event_connections .keys ()) if nid == node_id
85+ ]
8286 for evt in events_to_unbind :
8387 self .unbind_event (node_id , evt )
8488
@@ -87,88 +91,86 @@ def destroy(self, node_id: str) -> None:
8791 layout = parent_widget .layout ()
8892 if layout is not None :
8993 layout .removeWidget (widget )
90-
94+
9195 widget .deleteLater ()
9296
93- def set_children (
94- self ,
95- parent_id : str ,
96- child_ids : list [str ]
97- ) -> None :
97+ def set_children (self , parent_id : str , child_ids : list [str ]) -> None :
9898 parent = self .registry .get (parent_id )
9999 if parent is None :
100100 return
101101 layout = parent .layout ()
102102 if layout is None :
103103 return
104-
104+
105105 for i in reversed (range (layout .count ())):
106106 item = layout .itemAt (i )
107107 if item is not None :
108108 w = item .widget ()
109109 if w is not None :
110110 layout .removeWidget (w )
111-
112- is_grid = hasattr (layout , "addWidget" ) and parent .property ("grid_cols" ) is not None
111+
112+ is_grid = (
113+ hasattr (layout , "addWidget" ) and parent .property ("grid_cols" ) is not None
114+ )
113115 cols = parent .property ("grid_cols" ) or 2
114-
116+
115117 for idx , cid in enumerate (child_ids ):
116118 child = self .registry .get (cid )
117119 if child is not None :
118120 if is_grid :
119- layout .addWidget (child , idx // cols , idx % cols ) # type: ignore
121+ layout .addWidget (child , idx // cols , idx % cols ) # type: ignore
120122 else :
121123 layout .addWidget (child )
122124
123- def move (
124- self ,
125- node_id : str ,
126- new_parent_id : str
127- ) -> None :
125+ def move (self , node_id : str , new_parent_id : str ) -> None :
128126 widget = self .registry .get (node_id )
129127 new_parent = self .registry .get (new_parent_id )
130128 if widget is None or new_parent is None :
131129 return
132-
130+
133131 old_parent = widget .parentWidget ()
134132 if old_parent is not None :
135133 old_layout = old_parent .layout ()
136134 if old_layout is not None :
137135 old_layout .removeWidget (widget )
138-
136+
139137 new_layout = new_parent .layout ()
140138 if new_layout is not None :
141- is_grid = hasattr (new_layout , "addWidget" ) and new_parent .property ("grid_cols" ) is not None
139+ is_grid = (
140+ hasattr (new_layout , "addWidget" )
141+ and new_parent .property ("grid_cols" ) is not None
142+ )
142143 if is_grid :
143144 # Approximate move appending to the end
144145 idx = new_layout .count ()
145146 cols = new_parent .property ("grid_cols" ) or 2
146- new_layout .addWidget (widget , idx // cols , idx % cols ) # type: ignore
147+ new_layout .addWidget (widget , idx // cols , idx % cols ) # type: ignore
147148 else :
148149 new_layout .addWidget (widget )
149150
150- def bind_event (
151- self ,
152- node_id : str ,
153- event : str ,
154- handler : Any
155- ) -> None :
151+ def bind_event (self , node_id : str , event : str , handler : Any ) -> None :
156152 widget = self .registry .get (node_id )
157153 if widget is None :
158154 return
159-
155+
160156 if (node_id , event ) in self .event_connections :
161157 # Update the existing wrapper without disconnecting Qt signal
162158 self .event_connections [(node_id , event )]["state" ]["handler" ] = handler
163159 return
164-
160+
165161 state = {"handler" : handler }
166-
162+
167163 def wrapper (* args : Any , ** kwargs : Any ) -> None :
168164 state ["handler" ](* args , ** kwargs )
169-
170- from PySide6 .QtWidgets import QLineEdit , QCheckBox , QComboBox , QSlider , QTabWidget
171-
165+
166+ from PySide6 .QtWidgets import (
167+ QCheckBox ,
168+ QComboBox ,
169+ QLineEdit ,
170+ QSlider ,
171+ QTabWidget ,
172+ )
173+
172174 conn = None
173175 if event == "on_click" and hasattr (widget , "clicked" ):
174176 conn = widget .clicked .connect (wrapper )
@@ -183,22 +185,29 @@ def wrapper(*args: Any, **kwargs: Any) -> None:
183185 conn = widget .valueChanged .connect (wrapper )
184186 elif isinstance (widget , QTabWidget ):
185187 conn = widget .currentChanged .connect (wrapper )
186-
188+
187189 if conn is not None :
188- self .event_connections [(node_id , event )] = {"conn" : conn , "wrapper" : wrapper , "state" : state }
190+ self .event_connections [(node_id , event )] = {
191+ "conn" : conn ,
192+ "wrapper" : wrapper ,
193+ "state" : state ,
194+ }
189195
190- def unbind_event (
191- self ,
192- node_id : str ,
193- event : str
194- ) -> None :
196+ def unbind_event (self , node_id : str , event : str ) -> None :
195197 data = self .event_connections .pop ((node_id , event ), None )
196198 if data is not None :
197199 conn = data ["conn" ]
198200 widget = self .registry .get (node_id )
199201 if widget is not None :
200202 try :
201- from PySide6 .QtWidgets import QLineEdit , QCheckBox , QComboBox , QSlider , QTabWidget
203+ from PySide6 .QtWidgets import (
204+ QCheckBox ,
205+ QComboBox ,
206+ QLineEdit ,
207+ QSlider ,
208+ QTabWidget ,
209+ )
210+
202211 if event == "on_click" and hasattr (widget , "clicked" ):
203212 widget .clicked .disconnect (conn )
204213 elif event == "on_change" :
@@ -236,6 +245,9 @@ def commit(self, patches: list[Patch]) -> None:
236245 for patch in patches :
237246 self .logger .commit (patch )
238247 self ._pending_patches .extend (patches )
239- QMetaObject .invokeMethod (self , "_apply_patches" , Qt .ConnectionType .QueuedConnection )
248+ QMetaObject .invokeMethod (
249+ self , "_apply_patches" , Qt .ConnectionType .QueuedConnection
250+ )
251+
240252
241253__all__ = ["QtRenderer" ]
0 commit comments