solarviewer 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solar_radio_image_viewer/__init__.py +12 -0
- solar_radio_image_viewer/assets/add_tab_default.png +0 -0
- solar_radio_image_viewer/assets/add_tab_default_light.png +0 -0
- solar_radio_image_viewer/assets/add_tab_hover.png +0 -0
- solar_radio_image_viewer/assets/add_tab_hover_light.png +0 -0
- solar_radio_image_viewer/assets/browse.png +0 -0
- solar_radio_image_viewer/assets/browse_light.png +0 -0
- solar_radio_image_viewer/assets/close_tab_default.png +0 -0
- solar_radio_image_viewer/assets/close_tab_default_light.png +0 -0
- solar_radio_image_viewer/assets/close_tab_hover.png +0 -0
- solar_radio_image_viewer/assets/close_tab_hover_light.png +0 -0
- solar_radio_image_viewer/assets/ellipse_selection.png +0 -0
- solar_radio_image_viewer/assets/ellipse_selection_light.png +0 -0
- solar_radio_image_viewer/assets/icons8-ellipse-90.png +0 -0
- solar_radio_image_viewer/assets/icons8-ellipse-90_light.png +0 -0
- solar_radio_image_viewer/assets/icons8-info-90.png +0 -0
- solar_radio_image_viewer/assets/icons8-info-90_light.png +0 -0
- solar_radio_image_viewer/assets/profile.png +0 -0
- solar_radio_image_viewer/assets/profile_light.png +0 -0
- solar_radio_image_viewer/assets/rectangle_selection.png +0 -0
- solar_radio_image_viewer/assets/rectangle_selection_light.png +0 -0
- solar_radio_image_viewer/assets/reset.png +0 -0
- solar_radio_image_viewer/assets/reset_light.png +0 -0
- solar_radio_image_viewer/assets/ruler.png +0 -0
- solar_radio_image_viewer/assets/ruler_light.png +0 -0
- solar_radio_image_viewer/assets/search.png +0 -0
- solar_radio_image_viewer/assets/search_light.png +0 -0
- solar_radio_image_viewer/assets/settings.png +0 -0
- solar_radio_image_viewer/assets/settings_light.png +0 -0
- solar_radio_image_viewer/assets/splash.fits +0 -0
- solar_radio_image_viewer/assets/zoom_60arcmin.png +0 -0
- solar_radio_image_viewer/assets/zoom_60arcmin_light.png +0 -0
- solar_radio_image_viewer/assets/zoom_in.png +0 -0
- solar_radio_image_viewer/assets/zoom_in_light.png +0 -0
- solar_radio_image_viewer/assets/zoom_out.png +0 -0
- solar_radio_image_viewer/assets/zoom_out_light.png +0 -0
- solar_radio_image_viewer/create_video.py +1345 -0
- solar_radio_image_viewer/dialogs.py +2665 -0
- solar_radio_image_viewer/from_simpl/__init__.py +184 -0
- solar_radio_image_viewer/from_simpl/caltable_visualizer.py +1001 -0
- solar_radio_image_viewer/from_simpl/dynamic_spectra_dialog.py +332 -0
- solar_radio_image_viewer/from_simpl/make_dynamic_spectra.py +351 -0
- solar_radio_image_viewer/from_simpl/pipeline_logger_gui.py +1232 -0
- solar_radio_image_viewer/from_simpl/simpl_theme.py +352 -0
- solar_radio_image_viewer/from_simpl/utils.py +984 -0
- solar_radio_image_viewer/from_simpl/view_dynamic_spectra_GUI.py +1975 -0
- solar_radio_image_viewer/helioprojective.py +1916 -0
- solar_radio_image_viewer/helioprojective_viewer.py +817 -0
- solar_radio_image_viewer/helioviewer_browser.py +1514 -0
- solar_radio_image_viewer/main.py +148 -0
- solar_radio_image_viewer/move_phasecenter.py +1269 -0
- solar_radio_image_viewer/napari_viewer.py +368 -0
- solar_radio_image_viewer/noaa_events/__init__.py +32 -0
- solar_radio_image_viewer/noaa_events/noaa_events.py +430 -0
- solar_radio_image_viewer/noaa_events/noaa_events_gui.py +1922 -0
- solar_radio_image_viewer/norms.py +293 -0
- solar_radio_image_viewer/radio_data_downloader/__init__.py +25 -0
- solar_radio_image_viewer/radio_data_downloader/radio_data_downloader.py +756 -0
- solar_radio_image_viewer/radio_data_downloader/radio_data_downloader_gui.py +528 -0
- solar_radio_image_viewer/searchable_combobox.py +220 -0
- solar_radio_image_viewer/solar_context/__init__.py +41 -0
- solar_radio_image_viewer/solar_context/active_regions.py +371 -0
- solar_radio_image_viewer/solar_context/cme_alerts.py +234 -0
- solar_radio_image_viewer/solar_context/context_images.py +297 -0
- solar_radio_image_viewer/solar_context/realtime_data.py +528 -0
- solar_radio_image_viewer/solar_data_downloader/__init__.py +35 -0
- solar_radio_image_viewer/solar_data_downloader/solar_data_downloader.py +1667 -0
- solar_radio_image_viewer/solar_data_downloader/solar_data_downloader_cli.py +901 -0
- solar_radio_image_viewer/solar_data_downloader/solar_data_downloader_gui.py +1210 -0
- solar_radio_image_viewer/styles.py +643 -0
- solar_radio_image_viewer/utils/__init__.py +32 -0
- solar_radio_image_viewer/utils/rate_limiter.py +255 -0
- solar_radio_image_viewer/utils.py +952 -0
- solar_radio_image_viewer/video_dialog.py +2629 -0
- solar_radio_image_viewer/video_utils.py +656 -0
- solar_radio_image_viewer/viewer.py +11174 -0
- solarviewer-1.0.2.dist-info/METADATA +343 -0
- solarviewer-1.0.2.dist-info/RECORD +82 -0
- solarviewer-1.0.2.dist-info/WHEEL +5 -0
- solarviewer-1.0.2.dist-info/entry_points.txt +8 -0
- solarviewer-1.0.2.dist-info/licenses/LICENSE +21 -0
- solarviewer-1.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Basic napari viewer for Fits/CASA images.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import numpy as np
|
|
9
|
+
import napari
|
|
10
|
+
from PyQt5.QtWidgets import (
|
|
11
|
+
QApplication,
|
|
12
|
+
QFileDialog,
|
|
13
|
+
QPushButton,
|
|
14
|
+
QVBoxLayout,
|
|
15
|
+
QHBoxLayout,
|
|
16
|
+
QWidget,
|
|
17
|
+
QLabel,
|
|
18
|
+
QComboBox,
|
|
19
|
+
QRadioButton,
|
|
20
|
+
QButtonGroup,
|
|
21
|
+
QMessageBox,
|
|
22
|
+
QGroupBox,
|
|
23
|
+
QLineEdit,
|
|
24
|
+
)
|
|
25
|
+
from PyQt5.QtCore import Qt
|
|
26
|
+
from .utils import get_pixel_values_from_image
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NapariViewer(QWidget):
|
|
30
|
+
def __init__(self, imagename=None):
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.viewer = None
|
|
33
|
+
self.current_image_data = None
|
|
34
|
+
self.current_wcs = None
|
|
35
|
+
self.psf = None
|
|
36
|
+
self.image_layer = None
|
|
37
|
+
self.imagename = imagename
|
|
38
|
+
|
|
39
|
+
self.init_ui()
|
|
40
|
+
|
|
41
|
+
# If an image was provided, load it
|
|
42
|
+
if self.imagename:
|
|
43
|
+
self.load_data(self.imagename)
|
|
44
|
+
self.plot_image()
|
|
45
|
+
|
|
46
|
+
def init_ui(self):
|
|
47
|
+
self.setWindowTitle("Napari Image Viewer")
|
|
48
|
+
self.resize(1200, 800)
|
|
49
|
+
|
|
50
|
+
# Main layout
|
|
51
|
+
main_layout = QHBoxLayout()
|
|
52
|
+
self.setLayout(main_layout)
|
|
53
|
+
|
|
54
|
+
# Left column for controls
|
|
55
|
+
left_column = QVBoxLayout()
|
|
56
|
+
main_layout.addLayout(left_column, 1)
|
|
57
|
+
|
|
58
|
+
# Right column for napari viewer
|
|
59
|
+
right_column = QVBoxLayout()
|
|
60
|
+
main_layout.addLayout(right_column, 3)
|
|
61
|
+
|
|
62
|
+
# File selection group
|
|
63
|
+
file_group = QGroupBox("File Selection")
|
|
64
|
+
file_layout = QVBoxLayout()
|
|
65
|
+
|
|
66
|
+
# Add help button
|
|
67
|
+
help_layout = QHBoxLayout()
|
|
68
|
+
help_button = QPushButton("Help")
|
|
69
|
+
help_button.clicked.connect(self.show_help)
|
|
70
|
+
help_layout.addStretch()
|
|
71
|
+
help_layout.addWidget(help_button)
|
|
72
|
+
left_column.addLayout(help_layout)
|
|
73
|
+
|
|
74
|
+
# Radio buttons for file type selection
|
|
75
|
+
radio_layout = QVBoxLayout() # Changed to vertical layout for better spacing
|
|
76
|
+
self.radio_fits = QRadioButton("FITS File")
|
|
77
|
+
self.radio_casa_image = QRadioButton("CASA Image")
|
|
78
|
+
self.radio_fits.setChecked(True)
|
|
79
|
+
|
|
80
|
+
self.file_type_group = QButtonGroup()
|
|
81
|
+
self.file_type_group.addButton(self.radio_fits)
|
|
82
|
+
self.file_type_group.addButton(self.radio_casa_image)
|
|
83
|
+
|
|
84
|
+
radio_layout.addWidget(self.radio_fits)
|
|
85
|
+
radio_layout.addWidget(self.radio_casa_image)
|
|
86
|
+
file_layout.addLayout(radio_layout)
|
|
87
|
+
|
|
88
|
+
# File selection button
|
|
89
|
+
self.file_button = QPushButton("Open Image")
|
|
90
|
+
self.file_button.setMinimumHeight(40) # Make button taller
|
|
91
|
+
self.file_button.clicked.connect(self.select_file_or_directory)
|
|
92
|
+
file_layout.addWidget(self.file_button)
|
|
93
|
+
|
|
94
|
+
# Add a spacer for better layout
|
|
95
|
+
file_layout.addStretch()
|
|
96
|
+
|
|
97
|
+
file_group.setLayout(file_layout)
|
|
98
|
+
left_column.addWidget(file_group)
|
|
99
|
+
|
|
100
|
+
# Add current file display
|
|
101
|
+
file_info_group = QGroupBox("Current File")
|
|
102
|
+
file_info_layout = QVBoxLayout()
|
|
103
|
+
self.file_label = QLabel("No file loaded")
|
|
104
|
+
self.file_label.setWordWrap(True)
|
|
105
|
+
file_info_layout.addWidget(self.file_label)
|
|
106
|
+
file_info_group.setLayout(file_info_layout)
|
|
107
|
+
left_column.addWidget(file_info_group)
|
|
108
|
+
|
|
109
|
+
# Add a spacer at the bottom of the left column
|
|
110
|
+
left_column.addStretch()
|
|
111
|
+
|
|
112
|
+
# Display controls group
|
|
113
|
+
display_group = QGroupBox("Display Controls")
|
|
114
|
+
display_layout = QVBoxLayout()
|
|
115
|
+
|
|
116
|
+
# Stokes parameter selection
|
|
117
|
+
stokes_layout = QHBoxLayout()
|
|
118
|
+
self.stokes_label = QLabel("Stokes:")
|
|
119
|
+
stokes_layout.addWidget(self.stokes_label)
|
|
120
|
+
|
|
121
|
+
self.stokes_combo = QComboBox()
|
|
122
|
+
self.stokes_combo.addItems(
|
|
123
|
+
["I", "Q", "U", "V", "L", "Lfrac", "Vfrac", "Q/I", "U/I", "U/V"]
|
|
124
|
+
)
|
|
125
|
+
self.stokes_combo.currentTextChanged.connect(self.on_stokes_changed)
|
|
126
|
+
stokes_layout.addWidget(self.stokes_combo)
|
|
127
|
+
display_layout.addLayout(stokes_layout)
|
|
128
|
+
|
|
129
|
+
# Add threshold textbox
|
|
130
|
+
threshold_layout = QHBoxLayout()
|
|
131
|
+
self.threshold_label = QLabel("Threshold:")
|
|
132
|
+
threshold_layout.addWidget(self.threshold_label)
|
|
133
|
+
self.threshold_textbox = QLineEdit("10.0")
|
|
134
|
+
threshold_layout.addWidget(self.threshold_textbox)
|
|
135
|
+
self.threshold_textbox.editingFinished.connect(
|
|
136
|
+
lambda: self.on_threshold_changed(self.threshold_textbox.text())
|
|
137
|
+
)
|
|
138
|
+
display_layout.addLayout(threshold_layout)
|
|
139
|
+
|
|
140
|
+
# Add a spacer for better layout
|
|
141
|
+
display_layout.addStretch()
|
|
142
|
+
|
|
143
|
+
display_group.setLayout(display_layout)
|
|
144
|
+
right_column.addWidget(display_group)
|
|
145
|
+
|
|
146
|
+
# Image statistics group
|
|
147
|
+
stats_group = QGroupBox("Image Statistics")
|
|
148
|
+
stats_layout = QVBoxLayout()
|
|
149
|
+
|
|
150
|
+
self.stats_label = QLabel("No image loaded")
|
|
151
|
+
stats_layout.addWidget(self.stats_label)
|
|
152
|
+
|
|
153
|
+
stats_group.setLayout(stats_layout)
|
|
154
|
+
right_column.addWidget(stats_group)
|
|
155
|
+
|
|
156
|
+
# Add a spacer at the bottom of the right column
|
|
157
|
+
right_column.addStretch()
|
|
158
|
+
|
|
159
|
+
# Initialize napari viewer
|
|
160
|
+
self.init_napari()
|
|
161
|
+
|
|
162
|
+
def init_napari(self):
|
|
163
|
+
"""Initialize the napari viewer"""
|
|
164
|
+
self.viewer = napari.Viewer(show=False)
|
|
165
|
+
self.viewer.window.add_dock_widget(self, area="bottom")
|
|
166
|
+
self.viewer.show()
|
|
167
|
+
|
|
168
|
+
def select_file_or_directory(self):
|
|
169
|
+
"""Open a file dialog to select a FITS file or CASA image directory"""
|
|
170
|
+
if self.radio_casa_image.isChecked():
|
|
171
|
+
# Select CASA image directory
|
|
172
|
+
directory = QFileDialog.getExistingDirectory(
|
|
173
|
+
self, "Select a CASA Image Directory"
|
|
174
|
+
)
|
|
175
|
+
if directory:
|
|
176
|
+
try:
|
|
177
|
+
self.load_data(directory)
|
|
178
|
+
self.plot_image()
|
|
179
|
+
except Exception as e:
|
|
180
|
+
QMessageBox.critical(
|
|
181
|
+
self, "Error", f"Failed to load CASA image: {str(e)}"
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
# Select FITS file
|
|
185
|
+
file_path, _ = QFileDialog.getOpenFileName(
|
|
186
|
+
self, "Select a FITS file", "", "FITS files (*.fits);;All files (*)"
|
|
187
|
+
)
|
|
188
|
+
if file_path:
|
|
189
|
+
try:
|
|
190
|
+
self.load_data(file_path)
|
|
191
|
+
self.plot_image()
|
|
192
|
+
except Exception as e:
|
|
193
|
+
QMessageBox.critical(
|
|
194
|
+
self, "Error", f"Failed to load FITS file: {str(e)}"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def load_data(self, imagename, threshold=10):
|
|
198
|
+
"""Load data from a FITS file or CASA image directory"""
|
|
199
|
+
stokes = self.stokes_combo.currentText()
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
pix, csys, psf = get_pixel_values_from_image(imagename, stokes, threshold)
|
|
203
|
+
pix = pix.transpose()
|
|
204
|
+
pix = np.flip(pix, axis=0)
|
|
205
|
+
|
|
206
|
+
self.current_image_data = pix
|
|
207
|
+
self.current_wcs = csys
|
|
208
|
+
self.psf = psf
|
|
209
|
+
self.imagename = imagename # Store the imagename for later use
|
|
210
|
+
|
|
211
|
+
# Update file label
|
|
212
|
+
self.file_label.setText(
|
|
213
|
+
f"File: {os.path.basename(imagename)}\nType: {'CASA Image' if os.path.isdir(imagename) else 'FITS File'}\nStokes: {stokes}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Update window title with filename
|
|
217
|
+
self.viewer.title = (
|
|
218
|
+
f"Solar Radio Image Viewer (Napari) - {os.path.basename(imagename)}"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print(f"Error loading data: {e}")
|
|
223
|
+
raise
|
|
224
|
+
|
|
225
|
+
def plot_image(self):
|
|
226
|
+
"""Display the image in napari"""
|
|
227
|
+
if self.current_image_data is None:
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
data = self.current_image_data
|
|
231
|
+
cmap = "yellow"
|
|
232
|
+
|
|
233
|
+
# Remove existing layer if it exists
|
|
234
|
+
if self.image_layer is not None:
|
|
235
|
+
self.viewer.layers.remove(self.image_layer)
|
|
236
|
+
|
|
237
|
+
# Add the new image layer
|
|
238
|
+
self.image_layer = self.viewer.add_image(
|
|
239
|
+
data,
|
|
240
|
+
name="Image",
|
|
241
|
+
colormap=cmap,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Update statistics
|
|
245
|
+
self.update_statistics()
|
|
246
|
+
|
|
247
|
+
# Reset view
|
|
248
|
+
self.viewer.reset_view()
|
|
249
|
+
|
|
250
|
+
def update_statistics(self):
|
|
251
|
+
"""Update the statistics display"""
|
|
252
|
+
if self.current_image_data is None:
|
|
253
|
+
self.stats_label.setText("No image loaded")
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
data = self.current_image_data
|
|
257
|
+
min_val = np.nanmin(data)
|
|
258
|
+
max_val = np.nanmax(data)
|
|
259
|
+
mean_val = np.nanmean(data)
|
|
260
|
+
median_val = np.nanmedian(data)
|
|
261
|
+
std_val = np.nanstd(data[0:200, 0:200])
|
|
262
|
+
rms_val = np.sqrt(np.nanmean(data[0:200, 0:200] ** 2))
|
|
263
|
+
positive_DR = max_val / rms_val
|
|
264
|
+
negative_DR = min_val / rms_val
|
|
265
|
+
|
|
266
|
+
stats_text = (
|
|
267
|
+
f"Min: {min_val:.4g} Max: {max_val:.4g} Mean: {mean_val:.4g} Median: {median_val:.4g}\n"
|
|
268
|
+
f"Std Dev: {std_val:.4g} RMS: {rms_val:.4g}\n"
|
|
269
|
+
f"Positive DR: {positive_DR:.4g} Negative DR: {negative_DR:.4g}\n"
|
|
270
|
+
f"Shape: {data.shape}"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
self.stats_label.setText(stats_text)
|
|
274
|
+
|
|
275
|
+
def on_stokes_changed(self, stokes):
|
|
276
|
+
"""Handle changes to the Stokes parameter"""
|
|
277
|
+
if self.current_image_data is not None and hasattr(self, "imagename"):
|
|
278
|
+
try:
|
|
279
|
+
self.load_data(self.imagename)
|
|
280
|
+
self.plot_image()
|
|
281
|
+
except Exception as e:
|
|
282
|
+
print(f"Error updating Stokes parameter: {e}")
|
|
283
|
+
QMessageBox.critical(
|
|
284
|
+
self, "Error", f"Failed to update Stokes parameter: {str(e)}"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def on_threshold_changed(self, text):
|
|
288
|
+
"""Handle changes to the threshold textbox"""
|
|
289
|
+
try:
|
|
290
|
+
if text == "":
|
|
291
|
+
threshold = 10.0
|
|
292
|
+
elif float(text) < 0:
|
|
293
|
+
print("Invalid threshold value")
|
|
294
|
+
QMessageBox.critical(
|
|
295
|
+
self,
|
|
296
|
+
"Error",
|
|
297
|
+
"Invalid threshold value. Please enter a valid number.",
|
|
298
|
+
)
|
|
299
|
+
return
|
|
300
|
+
else:
|
|
301
|
+
threshold = float(text)
|
|
302
|
+
self.load_data(self.imagename, threshold=threshold)
|
|
303
|
+
self.plot_image()
|
|
304
|
+
except ValueError:
|
|
305
|
+
print("Invalid threshold value")
|
|
306
|
+
QMessageBox.critical(
|
|
307
|
+
self, "Error", "Invalid threshold value. Please enter a valid number."
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def show_help(self):
|
|
311
|
+
"""Display help information about the Napari viewer"""
|
|
312
|
+
help_text = """
|
|
313
|
+
<h2>Napari Fast Viewer Help</h2>
|
|
314
|
+
|
|
315
|
+
<h3>Overview</h3>
|
|
316
|
+
<p>The Napari Fast Viewer is a lightweight tool for quickly visualizing solar radio images.</p>
|
|
317
|
+
|
|
318
|
+
<h3>Features</h3>
|
|
319
|
+
<ul>
|
|
320
|
+
<li><b>Fast Loading:</b> Quickly loads and displays FITS and CASA images</li>
|
|
321
|
+
<li><b>Stokes Parameters:</b> View different Stokes parameters (I, Q, U, V, etc.)</li>
|
|
322
|
+
<li><b>Threshold Control:</b> Adjust threshold for better visualization</li>
|
|
323
|
+
<li><b>Basic Statistics:</b> View basic image statistics</li>
|
|
324
|
+
</ul>
|
|
325
|
+
|
|
326
|
+
<h3>Usage</h3>
|
|
327
|
+
<ol>
|
|
328
|
+
<li>Select a file using the "Select File" button</li>
|
|
329
|
+
<li>Choose a Stokes parameter from the dropdown</li>
|
|
330
|
+
<li>Adjust the threshold value if needed</li>
|
|
331
|
+
</ol>
|
|
332
|
+
|
|
333
|
+
<h3>Command Line Usage</h3>
|
|
334
|
+
<p>You can launch this viewer directly from the command line:</p>
|
|
335
|
+
<pre>
|
|
336
|
+
solarviewer -f [image_file]
|
|
337
|
+
sv --fast [image_file]
|
|
338
|
+
</pre>
|
|
339
|
+
"""
|
|
340
|
+
msg_box = QMessageBox(self)
|
|
341
|
+
msg_box.setWindowTitle("Napari Viewer Help")
|
|
342
|
+
msg_box.setTextFormat(Qt.RichText)
|
|
343
|
+
msg_box.setText(help_text)
|
|
344
|
+
msg_box.setStandardButtons(QMessageBox.Ok)
|
|
345
|
+
msg_box.exec_()
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def main(imagename=None):
|
|
349
|
+
"""Main function to run the application"""
|
|
350
|
+
# Check if QApplication already exists (when called from main app)
|
|
351
|
+
app = QApplication.instance()
|
|
352
|
+
if app is None:
|
|
353
|
+
app = QApplication(sys.argv)
|
|
354
|
+
standalone = True
|
|
355
|
+
else:
|
|
356
|
+
standalone = False
|
|
357
|
+
|
|
358
|
+
viewer = NapariViewer(imagename)
|
|
359
|
+
|
|
360
|
+
# Only exit if running standalone
|
|
361
|
+
if standalone:
|
|
362
|
+
sys.exit(app.exec_())
|
|
363
|
+
else:
|
|
364
|
+
app.exec_()
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
if __name__ == "__main__":
|
|
368
|
+
main()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NOAA Solar Events module - Fetch and display NOAA solar events.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .noaa_events import (
|
|
6
|
+
SolarEvent,
|
|
7
|
+
EVENT_TYPES,
|
|
8
|
+
FLARE_CLASS_COLORS,
|
|
9
|
+
fetch_events_raw,
|
|
10
|
+
parse_events,
|
|
11
|
+
fetch_and_parse_events,
|
|
12
|
+
categorize_events,
|
|
13
|
+
get_event_statistics,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .noaa_events_gui import (
|
|
17
|
+
NOAAEventsViewer,
|
|
18
|
+
show_noaa_events_viewer,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"SolarEvent",
|
|
23
|
+
"EVENT_TYPES",
|
|
24
|
+
"FLARE_CLASS_COLORS",
|
|
25
|
+
"fetch_events_raw",
|
|
26
|
+
"parse_events",
|
|
27
|
+
"fetch_and_parse_events",
|
|
28
|
+
"categorize_events",
|
|
29
|
+
"get_event_statistics",
|
|
30
|
+
"NOAAEventsViewer",
|
|
31
|
+
"show_noaa_events_viewer",
|
|
32
|
+
]
|