11from PySide6 .QtCore import Qt
2- from PySide6 .QtWidgets import QHBoxLayout
2+ from PySide6 .QtWidgets import QFrame , QHBoxLayout
33from qfluentwidgets import FluentIcon , ExpandSettingCard , PushButton
44
55from ok import og
@@ -14,9 +14,14 @@ def __init__(self, task, name, config, description, default_config, config_descr
1414 super ().__init__ (config_icon or FluentIcon .INFO , og .app .tr (name ), og .app .tr (description ))
1515 self .config = config
1616 self .config_widgets = []
17+ self .config_widget_by_key = {}
18+ self .config_keys = []
1719 self .default_config = default_config
1820 self .config_description = config_description
1921 self .config_type = config_type
22+ self .sub_configs_rules = {}
23+ self .sub_configs_controlled_keys = {}
24+ self .sub_configs_dividers = {}
2025 self .task = task
2126 self .reset_config = None
2227 self .__initWidget ()
@@ -48,28 +53,252 @@ def __initWidget(self):
4853 self .viewLayout .setSpacing (0 )
4954 self .viewLayout .setAlignment (Qt .AlignTop )
5055 self .viewLayout .setContentsMargins (10 , 0 , 10 , 0 )
56+ self .sub_configs_rules = self .__collect_sub_configs_rules ()
57+ self .sub_configs_controlled_keys = self .__collect_sub_configs_controlled_keys ()
5158 if not self .config or not (self .config .has_user_config () or self .default_config or self .config_type ):
5259 self .card .expandButton .hide ()
5360 else :
5461 added_keys = set ()
5562 for key , value in self .config .items ():
56- if not key .startswith ('_' ):
57- self .__addConfig (key , value )
58- added_keys .add (key )
63+ if not key .startswith ('_' ) and not self .__is_sub_config_key (key ):
64+ self .__addConfigWithSubConfigs (key , value , added_keys , set ())
5965 if self .config_type :
6066 for key , the_type in self .config_type .items ():
6167 if key not in added_keys and not key .startswith ('_' ):
62- if isinstance (the_type , dict ) and the_type .get ('type' ) == 'button' :
63- self .__addConfig (key , None )
68+ if self .__is_button_config (the_type ) and not self .__is_sub_config_key (key ):
69+ self .__addConfigWithSubConfigs (key , None , added_keys , set ())
70+ self .__setup_sub_configs ()
6471 self .add_buttons ()
6572 self ._adjustViewSize ()
6673
74+ def __addConfigWithSubConfigs (self , key : str , value , added_keys , adding_keys ):
75+ if key in added_keys or key in adding_keys :
76+ return
77+
78+ adding_keys .add (key )
79+ has_sub_configs = self .__has_renderable_sub_configs (key )
80+ if has_sub_configs :
81+ self .__add_sub_configs_divider (key , 'top' )
82+
83+ self .__addConfig (key , value )
84+ added_keys .add (key )
85+
86+ for sub_config_key in self .__get_sub_config_keys (key ):
87+ if sub_config_key .startswith ('_' ):
88+ continue
89+
90+ sub_config_value = self .__get_config_value (sub_config_key )
91+ if not self .__can_render_config (sub_config_key , sub_config_value ):
92+ continue
93+
94+ self .__addConfigWithSubConfigs (sub_config_key , sub_config_value , added_keys , adding_keys )
95+
96+ if has_sub_configs :
97+ self .__add_sub_configs_divider (key , 'bottom' )
98+
99+ adding_keys .remove (key )
100+
67101 def __addConfig (self , key : str , value ):
68102 widget = config_widget (self .config_type , self .config_description , self .config , key , value , self .task )
69103 self .config_widgets .append (widget )
104+ self .config_widget_by_key [key ] = widget
105+ self .config_keys .append (key )
70106 self .viewLayout .addWidget (widget )
71107
108+ def __add_sub_configs_divider (self , key , position ):
109+ divider = QFrame ()
110+ divider .setFrameShape (QFrame .HLine )
111+ divider .setFrameShadow (QFrame .Plain )
112+ divider .setObjectName ('subConfigsDivider' )
113+ divider .setFixedHeight (1 )
114+ divider .setStyleSheet ("color: rgba(128, 128, 128, 90); background-color: rgba(128, 128, 128, 90);" )
115+ self .sub_configs_dividers .setdefault (key , {})[position ] = divider
116+ self .viewLayout .addWidget (divider )
117+
118+ def __is_button_config (self , the_type ):
119+ return (
120+ isinstance (the_type , dict )
121+ and (
122+ the_type .get ('type' ) == 'button'
123+ or ('type' not in the_type and ('buttons' in the_type or 'callback' in the_type ))
124+ )
125+ )
126+
127+ def __setup_sub_configs (self ):
128+ if not self .sub_configs_rules :
129+ return
130+
131+ for key in self .sub_configs_rules :
132+ widget = self .config_widget_by_key .get (key )
133+ combo_box = getattr (widget , 'combo_box' , None )
134+ if combo_box is not None :
135+ combo_box .currentTextChanged .connect (self .__apply_sub_config_visibility )
136+
137+ self .__apply_sub_config_visibility ()
138+
139+ def __collect_sub_configs_rules (self ):
140+ rules = {}
141+ if not self .config_type :
142+ return rules
143+
144+ for key , the_type in self .config_type .items ():
145+ if not isinstance (the_type , dict ):
146+ continue
147+
148+ sub_configs = the_type .get ('sub_configs' )
149+ if not isinstance (sub_configs , dict ):
150+ continue
151+
152+ rules [key ] = {
153+ choice : self .__normalize_sub_config_keys (config_keys )
154+ for choice , config_keys in sub_configs .items ()
155+ }
156+
157+ return rules
158+
159+ def __collect_sub_configs_controlled_keys (self ):
160+ return {
161+ key : set ().union (* rule .values ()) if rule else set ()
162+ for key , rule in self .sub_configs_rules .items ()
163+ }
164+
165+ def __normalize_sub_config_keys (self , config_keys ):
166+ if config_keys is None :
167+ return []
168+ if isinstance (config_keys , str ):
169+ return [config_keys ]
170+ return list (config_keys )
171+
172+ def __is_sub_config_key (self , key ):
173+ return any (key in keys for keys in self .sub_configs_controlled_keys .values ())
174+
175+ def __get_config_type (self , key ):
176+ if self .config_type is None :
177+ return None
178+ return self .config_type .get (key )
179+
180+ def __get_config_value (self , key ):
181+ if self .config is not None and key in self .config :
182+ return self .config .get (key )
183+ return None
184+
185+ def __can_render_config (self , key , value ):
186+ return value is not None or self .__is_button_config (self .__get_config_type (key ))
187+
188+ def __has_renderable_sub_configs (self , key ):
189+ for sub_config_key in self .__get_sub_config_keys (key ):
190+ if sub_config_key .startswith ('_' ):
191+ continue
192+ if self .__can_render_config (sub_config_key , self .__get_config_value (sub_config_key )):
193+ return True
194+ return False
195+
196+ def __get_sub_config_keys (self , key ):
197+ keys = []
198+ for config_keys in self .sub_configs_rules .get (key , {}).values ():
199+ for config_key in config_keys :
200+ if config_key not in keys :
201+ keys .append (config_key )
202+ return keys
203+
204+ def __get_active_sub_config_keys (self , key ):
205+ try :
206+ config_keys = self .sub_configs_rules .get (key , {}).get (self .config .get (key ), [])
207+ except TypeError :
208+ return []
209+ return [
210+ config_key for config_key in config_keys
211+ if config_key in self .config_widget_by_key
212+ ]
213+
214+ def __apply_sub_config_visibility (self , * args ):
215+ self .__sync_sub_config_order ()
216+ for key , widget in self .config_widget_by_key .items ():
217+ widget .setVisible (self .__is_config_visible (key , set ()))
218+ for key , dividers in self .sub_configs_dividers .items ():
219+ visible = self .__is_sub_configs_group_visible (key )
220+ for divider in dividers .values ():
221+ divider .setVisible (visible )
222+ self ._adjustViewSize ()
223+
224+ def __sync_sub_config_order (self ):
225+ for widget in self .config_widget_by_key .values ():
226+ self .viewLayout .removeWidget (widget )
227+ for dividers in self .sub_configs_dividers .values ():
228+ for divider in dividers .values ():
229+ self .viewLayout .removeWidget (divider )
230+
231+ insert_index = 0
232+ for key in self .config_keys :
233+ if self .__is_sub_config_key (key ):
234+ continue
235+ insert_index = self .__insert_config_group (key , insert_index , set ())
236+
237+ def __insert_config_group (self , key , insert_index , inserting_keys ):
238+ if key in inserting_keys or key not in self .config_widget_by_key :
239+ return insert_index
240+
241+ inserting_keys .add (key )
242+ active_sub_config_keys = self .__get_active_sub_config_keys (key )
243+ has_visible_sub_configs = any (
244+ self .__is_config_visible (sub_config_key , set ())
245+ for sub_config_key in active_sub_config_keys
246+ )
247+
248+ if has_visible_sub_configs :
249+ insert_index = self .__insert_sub_configs_divider (key , 'top' , insert_index )
250+
251+ self .viewLayout .insertWidget (insert_index , self .config_widget_by_key [key ])
252+ insert_index += 1
253+
254+ for sub_config_key in active_sub_config_keys :
255+ insert_index = self .__insert_config_group (sub_config_key , insert_index , inserting_keys )
256+
257+ if has_visible_sub_configs :
258+ insert_index = self .__insert_sub_configs_divider (key , 'bottom' , insert_index )
259+
260+ inserting_keys .remove (key )
261+ return insert_index
262+
263+ def __insert_sub_configs_divider (self , key , position , insert_index ):
264+ divider = self .sub_configs_dividers .get (key , {}).get (position )
265+ if divider is None :
266+ return insert_index
267+
268+ self .viewLayout .insertWidget (insert_index , divider )
269+ return insert_index + 1
270+
271+ def __is_sub_configs_group_visible (self , key ):
272+ if not self .__is_config_visible (key , set ()):
273+ return False
274+ for sub_config_key in self .__get_active_sub_config_keys (key ):
275+ if sub_config_key in self .config_widget_by_key and self .__is_config_visible (sub_config_key , set ()):
276+ return True
277+ return False
278+
279+ def __is_config_visible (self , key , checking ):
280+ if key in checking :
281+ return False
282+
283+ checking = checking | {key }
284+ for parent_key , rule in self .sub_configs_rules .items ():
285+ if key not in self .sub_configs_controlled_keys .get (parent_key , set ()):
286+ continue
287+
288+ if not self .__is_config_visible (parent_key , checking ):
289+ return False
290+
291+ try :
292+ visible_config_keys = rule .get (self .config .get (parent_key ), [])
293+ except TypeError :
294+ visible_config_keys = []
295+
296+ if key not in visible_config_keys :
297+ return False
298+
299+ return True
72300
73301 def update_config (self ):
74302 for widget in self .config_widgets :
75303 widget .update_value ()
304+ self .__apply_sub_config_visibility ()
0 commit comments