1+ #!/usr/bin/env python3
2+ """
3+ Script to generate views.rst documentation from the view classes.
4+
5+ This script reads the viewlist.py file, extracts _gui_help_txt from each view,
6+ converts markdown to RST, and includes side-by-side screenshots.
7+ """
8+
9+ import sys
10+ import re
11+ from pathlib import Path
12+
13+
14+ def markdown_to_rst (markdown_text ):
15+ """Convert markdown text to RST format."""
16+ lines = markdown_text .strip ().split ('\n ' )
17+ rst_lines = []
18+
19+ for line in lines :
20+ line = line .strip ()
21+ if not line :
22+ rst_lines .append ('' )
23+ continue
24+
25+ # Convert headers
26+ if line .startswith ('### ' ):
27+ title = line [4 :]
28+ rst_lines .append (title )
29+ rst_lines .append ('~' * len (title ))
30+ elif line .startswith ('## ' ):
31+ title = line [3 :]
32+ rst_lines .append (title )
33+ rst_lines .append ('-' * len (title ))
34+ elif line .startswith ('# ' ):
35+ title = line [2 :]
36+ rst_lines .append (title )
37+ rst_lines .append ('=' * len (title ))
38+ # Convert bold text (keep RST format)
39+ elif '**' in line :
40+ line = re .sub (r'\*\*(.*?)\*\*' , r'**\1**' , line )
41+ rst_lines .append (line )
42+ # Convert bullet points
43+ elif line .startswith ('* ' ):
44+ rst_lines .append ('- ' + line [2 :])
45+ else :
46+ rst_lines .append (line )
47+
48+ return '\n ' .join (rst_lines )
49+
50+
51+ def check_image_exists (image_path ):
52+ """Check if an image file exists."""
53+ return Path (image_path ).exists ()
54+
55+
56+ def generate_views_rst ():
57+ """Generate the views.rst file from view classes."""
58+ # Get script directory and related paths
59+ script_dir = Path (__file__ ).parent .absolute ()
60+ gui_dir = script_dir / "../../spikeinterface_gui"
61+ output_file = script_dir / "views.rst"
62+
63+ if output_file .exists ():
64+ print (f"Overwriting existing file: { output_file } " )
65+ output_file .unlink ()
66+
67+ # Add GUI directory to Python path
68+ sys .path .insert (0 , str (gui_dir .absolute ()))
69+
70+ # Define view information by parsing files directly (avoiding import issues)
71+ view_files = {
72+ 'probe' : 'probeview.py' ,
73+ 'unitlist' : 'unitlistview.py' ,
74+ 'spikelist' : 'spikelistview.py' ,
75+ 'merge' : 'mergeview.py' ,
76+ 'trace' : 'traceview.py' ,
77+ 'waveform' : 'waveformview.py' ,
78+ 'waveformheatmap' : 'waveformheatmapview.py' ,
79+ 'isi' : 'isiview.py' ,
80+ 'correlogram' : 'crosscorrelogramview.py' ,
81+ 'ndscatter' : 'ndscatterview.py' ,
82+ 'similarity' : 'similarityview.py' ,
83+ 'spikeamplitude' : 'spikeamplitudeview.py' ,
84+ 'spikedepth' : 'spikedepthview.py' ,
85+ 'tracemap' : 'tracemapview.py' ,
86+ 'curation' : 'curationview.py' ,
87+ 'spikerate' : 'spikerateview.py' ,
88+ 'metrics' : 'metricsview.py' ,
89+ 'mainsettings' : 'mainsettingsview.py' ,
90+ }
91+
92+ # Extract help text from each view file
93+ possible_class_views = {}
94+
95+ for view_key , filename in view_files .items ():
96+ view_file_path = gui_dir / filename
97+ if not view_file_path .exists ():
98+ print (f"Warning: View file not found: { view_file_path } " )
99+ continue
100+
101+ try :
102+ with open (view_file_path , 'r' , encoding = 'utf-8' ) as f :
103+ content = f .read ()
104+
105+ # Extract _gui_help_txt using regex
106+ help_match = re .search (r'_gui_help_txt\s*=\s*"""(.*?)"""' , content , re .DOTALL )
107+ if help_match :
108+ help_text = help_match .group (1 ).strip ()
109+ # Create a simple class-like object to store the help text
110+ class ViewInfo :
111+ def __init__ (self , help_txt ):
112+ self ._gui_help_txt = help_txt
113+
114+ possible_class_views [view_key ] = ViewInfo (help_text )
115+ else :
116+ print (f"Warning: No _gui_help_txt found in { filename } " )
117+ possible_class_views [view_key ] = ViewInfo ("No help text available." )
118+
119+ except Exception as e :
120+ print (f"Error reading { filename } : { e } " )
121+ continue
122+
123+
124+ # Start building RST content
125+ rst_content = '''Views
126+ =====
127+
128+ This page documents all available views in the SpikeInterface GUI, showing their functionality and appearance across different backends (desktop Qt and web panel).
129+
130+ '''
131+
132+ print (f"Processing { len (possible_class_views )} views..." )
133+
134+ for view_key , view_class in possible_class_views .items ():
135+ print (f" Processing view: { view_key } " )
136+
137+ # Get the help text
138+ help_text = getattr (view_class , '_gui_help_txt' , 'No help text available.' )
139+
140+ # Convert markdown to RST
141+ rst_help = markdown_to_rst (help_text )
142+
143+ # Define image paths (relative to the RST file)
144+ desktop_image = f"images/views/desktop/{ view_key } .png"
145+ panel_image = f"images/views/web/{ view_key } .png"
146+
147+ # Check if images exist (absolute paths for checking)
148+ desktop_image_abs = script_dir / desktop_image
149+ panel_image_abs = script_dir / panel_image
150+
151+ desktop_exists = check_image_exists (desktop_image_abs )
152+ panel_exists = check_image_exists (panel_image_abs )
153+
154+ if not desktop_exists :
155+ print (f" Warning: Desktop image not found: { desktop_image_abs } " )
156+ if not panel_exists :
157+ print (f" Warning: Panel image not found: { panel_image_abs } " )
158+
159+ # Add view section
160+ rst_content += f"{ rst_help } \n \n "
161+
162+ # Add screenshots section if at least one image exists
163+ if desktop_exists or panel_exists :
164+ rst_content += "Screenshots\n "
165+ rst_content += "~~~~~~~~~~~\n \n "
166+
167+ rst_content += ".. list-table::\n "
168+ rst_content += " :widths: 50 50\n "
169+ rst_content += " :header-rows: 1\n \n "
170+ rst_content += " * - Desktop (Qt)\n "
171+ rst_content += " - Web (Panel)\n "
172+
173+ # Desktop image
174+ if desktop_exists :
175+ rst_content += f" * - .. image:: { desktop_image } \n "
176+ rst_content += " :width: 100%\n "
177+ else :
178+ rst_content += " * - *Image not available*\n "
179+
180+ # Panel image
181+ if panel_exists :
182+ rst_content += f" - .. image:: { panel_image } \n "
183+ rst_content += " :width: 100%\n \n "
184+ else :
185+ rst_content += " - *Image not available*\n \n "
186+ else :
187+ print (f" No images found for view: { view_key } " )
188+ rst_content += "*Screenshots not available for this view.*\n \n "
189+
190+ # Write the RST file
191+ try :
192+ with open (output_file , 'w' , encoding = 'utf-8' ) as f :
193+ f .write (rst_content )
194+ print (f"\n Views documentation generated successfully at: { output_file } " )
195+ return True
196+ except Exception as e :
197+ print (f"Error writing output file: { e } " )
198+ return False
199+
200+
201+ if __name__ == "__main__" :
202+ success = generate_views_rst ()
203+ sys .exit (0 if success else 1 )
0 commit comments