@@ -34,7 +34,7 @@ The `SubInfo` class provides a flexible and user-friendly way to collect partici
3434
3535## Detailed Usage Guide
3636
37- ### 1. Configuring the Form
37+ ### 1. Configuring the Form and Collecting Information
3838
3939#### Option A: Using a YAML Configuration
4040
@@ -89,6 +89,24 @@ subinfo_mapping:
8989 registration_failed: "Registration cancelled."
9090 invalid_input: "Invalid input for {field}"
9191```
92+ Once you've defined your configuration, collecting information is straightforward:
93+
94+ ```python
95+ from psyflow import SubInfo
96+ import yaml
97+ # Load configuration from YAML
98+ with open("subinfo_config.yaml", "r", encoding='utf-8') as f:
99+ config = yaml.safe_load(f)
100+ # Create SubInfo instance
101+ subinfo = SubInfo(config)
102+ # Show dialog and collect information
103+ subject_data = subinfo.collect()
104+ ```
105+ 
106+
107+ ```{note}
108+ Make sure you used `encoding='utf-8'` when opening the YAML file to support non-ASCII characters in localization.
109+ ```
92110
93111#### Option B: Using a Python Dictionary
94112
@@ -127,6 +145,21 @@ config = {
127145 }
128146}
129147```
148+ Once you've defined your configuration, collecting information is straightforward:
149+
150+ ```python
151+ from psyflow import SubInfo
152+ subinfo = SubInfo(config)
153+ # Show dialog and collect information
154+ subject_data = subinfo.collect()
155+ ```
156+ 
157+
158+
159+ If registration (collection) is failed, the experiment will exit and python enviroment will be closed.
160+
161+ 
162+
130163
131164### 2. Field Types and Constraints
132165
@@ -167,234 +200,77 @@ Integer fields validate that:
167200
168201Choice fields present a dropdown menu with the specified options.
169202
170- ### 3. Collecting Participant Information
171203
172- Once you've defined your configuration, collecting information is straightforward:
173204
174- ```python
175- from psyflow import SubInfo
176- import yaml
177205
178- # Load configuration from YAML
179- with open("subinfo_config.yaml", "r") as f:
180- config = yaml.safe_load(f)
181-
182- # Create SubInfo instance
183- subinfo = SubInfo(config)
184-
185- # Show dialog and collect information
186- subject_data = subinfo.collect()
187-
188- # Check if user cancelled
189- if subject_data is None:
190- print("User cancelled the form")
191- # Handle cancellation (e.g., exit experiment)
192- else:
193- print("Collected information:", subject_data)
194- # Continue with experiment using the collected data
195- ```
196-
197- ### 4. Localization
206+ ### 3. Localization
198207
199208For international studies, you can localize the form by providing translations in the `subinfo_mapping` section:
200209
201210```yaml
202- # Example for Chinese localization
211+ # Example for subinfo_config.yaml localization
203212subinfo_mapping:
204- subject_id: "参与者编号"
205- age: "年龄"
206- gender: "性别"
207- Male: "男"
208- Female: "女"
209- Non-binary: "非二元性别"
210- Prefer not to say: "不愿透露"
211- registration_successful: "注册成功!"
212- registration_failed: "注册取消。"
213- invalid_input: "无效的输入:{field}"
213+ subject_id: "참가자 ID"
214+ age: "나이"
215+ gender: "성별"
216+ handedness: "주사용 손"
217+ vision: "시력"
218+ Male: "남성"
219+ Female: "여성"
220+ Non-binary: "논바이너리"
221+ Prefer not to say: "응답하지 않음"
222+ Right: "오른손잡이"
223+ Left: "왼손잡이"
224+ Ambidextrous: "양손잡이"
225+ Normal: "정상"
226+ Corrected-to-normal: "교정된 정상"
227+ Impaired: "손상된"
228+ registration_successful: "등록 성공!"
229+ registration_failed: "등록이 취소되었습니다."
230+ invalid_input: "{field}에 대한 잘못된 입력입니다."
214231```
215-
216- The form will display these translated labels while still using the English keys internally for consistency.
217-
218- ### 5. Integration with TaskSettings
219-
220- `SubInfo` works seamlessly with `TaskSettings` for complete experiment configuration:
221-
222232```python
223- from psyflow import SubInfo, TaskSettings
233+ from psyflow import SubInfo
224234import yaml
225-
226- # Load configuration
227- with open("config.yaml", "r") as f:
235+ # Load configuration from YAML
236+ with open("subinfo_config.yaml", "r", encoding='utf-8') as f:
228237 config = yaml.safe_load(f)
229-
230- # Create SubInfo and collect participant data
231- subinfo_config = {
232- "subinfo_fields": config.get("subinfo_fields", []),
233- "subinfo_mapping": config.get("subinfo_mapping", {})
234- }
235- subinfo = SubInfo(subinfo_config)
238+ # Create SubInfo instance
239+ subinfo = SubInfo(config)
240+ # Show dialog and collect information
236241subject_data = subinfo.collect()
237-
238- if subject_data is None:
239- print("Experiment cancelled")
240- import sys
241- sys.exit(0)
242-
243- # Create TaskSettings with the collected subject info
244- settings = TaskSettings.from_dict(config.get("task", {}))
245- settings.add_subinfo(subject_data)
246-
247- # Now you have subject-specific paths and seeds
248- print(f"Data will be saved to: {settings.res_file}")
249- print(f"Using block seed: {settings.block_seed}")
250242```
243+ 
251244
252- ## Complete Example
253-
254- Here's a complete example showing how to use `SubInfo` in an experiment:
255-
256- ```python
257- from psychopy import visual, core
258- from psyflow import SubInfo, TaskSettings
259- import yaml
260- import sys
261-
262- # Load configuration
263- with open("config.yaml", "r") as f:
264- config = yaml.safe_load(f)
265-
266- # Step 1: Collect participant information
267- subinfo_config = {
268- "subinfo_fields": config.get("subinfo_fields", []),
269- "subinfo_mapping": config.get("subinfo_mapping", {})
270- }
271- subinfo = SubInfo(subinfo_config)
272- subject_data = subinfo.collect()
245+ Following same approach, you can do localization for any language by providing the appropriate translations in the `subinfo_mapping` section.
273246
274- if subject_data is None:
275- print("Experiment cancelled")
276- sys.exit(0)
247+ 
277248
278- # Step 2: Configure task settings
279- task_config = {
280- **config.get("window", {}),
281- **config.get("task", {}),
282- **config.get("timing", {})
283- }
284- settings = TaskSettings.from_dict(task_config)
285- settings.add_subinfo(subject_data)
286249
287- # Step 3: Create PsychoPy window
288- win = visual.Window(
289- size=settings.window_size,
290- fullscr=settings.fullscreen,
291- color=settings.bg_color,
292- units="deg"
293- )
294-
295- # Step 4: Show welcome message with participant info
296- welcome_text = f"Welcome, Participant {subject_data['subject_id']}!\n\n"
297- welcome_text += f"Age: {subject_data.get('age', 'N/A')}\n"
298- welcome_text += f"Gender: {subject_data.get('gender', 'N/A')}\n\n"
299- welcome_text += "Press any key to begin the experiment."
300-
301- welcome = visual.TextStim(win, text=welcome_text, height=0.8)
302- welcome.draw()
303- win.flip()
304-
305- # Wait for keypress
306- from psychopy.event import waitKeys
307- waitKeys()
308-
309- # Continue with experiment...
310- win.close()
311- core.quit()
250+ ```{note}
251+ Make sure the translations are accurate for your target users.
312252```
313253
314- ## Advanced Usage
315254
316- ### Custom Validation
317255
318- You can extend `SubInfo` with custom validation logic:
256+ ### 4. Add subject information to TaskSettings
319257
320- ```python
321- class CustomSubInfo(SubInfo):
322- def validate(self):
323- # First run the standard validation
324- if not super().validate():
325- return False
326-
327- # Add custom validation logic
328- if 'subject_id' in self.subject_data and 'group' in self.subject_data:
329- subject_id = int(self.subject_data['subject_id'])
330- group = self.subject_data['group']
331-
332- # Check if subject_id is valid for the selected group
333- if group == 'Control' and not (100 <= subject_id < 200):
334- self._show_error("Control group IDs must be between 100-199")
335- return False
336- elif group == 'Experimental' and not (200 <= subject_id < 300):
337- self._show_error("Experimental group IDs must be between 200-299")
338- return False
339-
340- return True
341- ```
342-
343- ### Programmatic Form Creation
344-
345- You can also create forms programmatically:
258+ Once collected, the subject information needs to be passed to TaskSettings to complete the experiment configuration. The information will then be automatically saved together with the other task parameters via TaskSettings.
346259
347260```python
348- def create_dynamic_form(experiment_type):
349- """Create a form based on experiment type."""
350- base_fields = [
351- {"name": "subject_id", "type": "int", "constraints": {"min": 100, "max": 999}}
352- ]
353-
354- if experiment_type == "behavioral":
355- base_fields.extend([
356- {"name": "age", "type": "int"},
357- {"name": "gender", "type": "choice", "choices": ["Male", "Female", "Other"]}
358- ])
359- elif experiment_type == "eeg":
360- base_fields.extend([
361- {"name": "cap_size", "type": "choice", "choices": ["Small", "Medium", "Large"]},
362- {"name": "impedance_check", "type": "choice", "choices": ["Pass", "Fail"]}
363- ])
364-
365- config = {"subinfo_fields": base_fields}
366- return SubInfo(config)
367-
368- # Usage
369- form = create_dynamic_form("eeg")
370- data = form.collect()
371- ```
372-
373- ## Best Practices
374-
375- 1. **Keep forms concise**: Only collect information that's necessary for your experiment.
261+ from psyflow import SubInfo, TaskSettings, load_config
376262
377- 2. **Use meaningful field names**: Choose descriptive names that match your data analysis variables.
263+ # 1. Load config
264+ cfg = load_config()
378265
379- 3. **Set appropriate constraints**: Use min/max and digit constraints to prevent data entry errors.
266+ # 2. Collect subject info
267+ subform = SubInfo(cfg['subform_config'])
268+ subject_data = subform.collect()
380269
381- 4. **Provide clear labels**: Use the mapping dictionary to make field labels clear and understandable.
382-
383- 5. **Handle cancellation gracefully**: Always check if `collect()` returns `None` and handle it appropriately.
384-
385- 6. **Store configurations externally**: Keep form definitions in YAML files for easy modification without changing code.
386-
387- 7. **Test with different languages**: If using localization, test the form with all supported languages.
388-
389- ## Troubleshooting
390-
391- - **Form doesn't appear**: Ensure PsychoPy is properly installed and can create GUI elements.
392-
393- - **Validation errors**: Check that your constraints are reasonable and that field types match expected input.
394-
395- - **Missing fields**: Verify that your configuration dictionary has the correct structure.
396-
397- - **Localization issues**: Ensure all keys in `subinfo_mapping` match field names and choice options exactly.
270+ # 3. Load task settings
271+ settings = TaskSettings.from_dict(cfg['task_config'])
272+ settings.add_subinfo(subject_data)
273+ ```
398274
399275## Next Steps
400276
0 commit comments