PQEnalyzer 0.6.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.
PQEnalyzer/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """
2
+ This is the main module of the package. It is used to import the
3
+ main classes and functions of the package.
4
+ """
5
+ import sys
6
+
7
+ # beartype
8
+ from beartype.claw import beartype_this_package
9
+
10
+ beartype_this_package()
11
+
12
+ # zero traceback limit
13
+ # sys.tracebacklimit = 0
14
+
15
+ # base path
16
+ __base__ = __file__.split("/__init__.py")[0]
PQEnalyzer/__main__.py ADDED
@@ -0,0 +1,85 @@
1
+ """
2
+ This is the main file of the PQEnalyzer project. It contains the
3
+ main function that is executed when the program is run.
4
+ """
5
+
6
+ import sys
7
+ import argparse
8
+ import argcomplete
9
+ from PQAnalysis.traj import MDEngineFormat
10
+
11
+ from .__version__ import __version__
12
+ from .apps import App, TermApp
13
+ from .readers import Reader
14
+
15
+
16
+ def main():
17
+ """
18
+ The main function of the PQEnalyzer project. It reads the data
19
+ from the energy files and plots the data in the GUI or in the terminal.
20
+ Use the -h or --help flag to see the command line arguments.
21
+
22
+ Parameters
23
+ ----------
24
+ None
25
+
26
+ Returns
27
+ -------
28
+ None
29
+ """
30
+ # parse command line arguments
31
+ parser = argparse.ArgumentParser(description="PQEnalyzer - MolarVerse")
32
+ parser.add_argument(
33
+ "filenames",
34
+ metavar="filenames",
35
+ nargs="+",
36
+ help="The name of the energy files to read the data from.")
37
+ parser.add_argument("-q",
38
+ "--qmcfc",
39
+ action="store_true",
40
+ help="Use the QMCFC output as input.")
41
+ parser.add_argument("-n",
42
+ "--no-gui",
43
+ action="store_true",
44
+ help="Opens the terminal plotting feature.")
45
+ parser.add_argument("-v",
46
+ "--version",
47
+ action="version",
48
+ version=f"PQEnalyzer {__version__}")
49
+
50
+ # autocomplete the command line arguments
51
+ argcomplete.autocomplete(parser)
52
+
53
+ # get command line arguments
54
+ filenames = parser.parse_args().filenames
55
+
56
+ # set the md format
57
+ md_format = MDEngineFormat.PQ
58
+
59
+ # if the user wants to use the QMCFC output as input
60
+ if parser.parse_args().qmcfc:
61
+ md_format = MDEngineFormat.QMCFC # set the md format to QMCFC
62
+
63
+ # create the reader
64
+ try:
65
+ reader = Reader(filenames, md_format)
66
+ except Exception as e:
67
+ print(e)
68
+ sys.exit(1)
69
+
70
+ # if the user wants to use the terminal plotting feature
71
+ if parser.parse_args().no_gui:
72
+ # create the termplot
73
+ termapp = TermApp(reader)
74
+ termapp.start()
75
+ else:
76
+ # create the app
77
+ app = App(reader)
78
+ app.build()
79
+ app.mainloop()
80
+
81
+ return None
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.6.2'
16
+ __version_tuple__ = version_tuple = (0, 6, 2)
@@ -0,0 +1,5 @@
1
+ """
2
+ This module includes the App class that is used to run the application.
3
+ """
4
+ from .app import App
5
+ from .termapp import TermApp
PQEnalyzer/apps/app.py ADDED
@@ -0,0 +1,409 @@
1
+ """
2
+ This module contains the App class that is used to create the main application
3
+ window for the PQEnalyzer application.
4
+ """
5
+
6
+ import os
7
+ import signal
8
+ import tkinter
9
+
10
+ import customtkinter as ctk
11
+ from PIL import Image, ImageTk
12
+ import matplotlib.pyplot as plt
13
+
14
+ from .. import __base__
15
+ from ..plots import PlotTime, PlotHistogram
16
+
17
+
18
+ class App(ctk.CTk):
19
+ """
20
+ The main application class for the PQEnalyzer application. This class inherits
21
+ from the CTK class.
22
+
23
+ ...
24
+
25
+ Attributes
26
+ ----------
27
+ reader : Reader
28
+ The reader object that contains the data.
29
+
30
+ Methods
31
+ -------
32
+ build()
33
+ Build the main window.
34
+ """
35
+
36
+ def __init__(self, reader=None):
37
+ """
38
+ Constructs all the necessary attributes for the App object.
39
+ """
40
+ super().__init__()
41
+ self.__default_theme()
42
+ self.title("PQEnalyzer - MolarVerse")
43
+
44
+ # load icon photo
45
+ img = Image.open(os.path.join(__base__, "icons", "icon.png"))
46
+ self.iconphoto(False, ImageTk.PhotoImage(img))
47
+
48
+ # set the window size
49
+ self.resizable(False, False)
50
+
51
+ # set reader object and info parameters
52
+ self.reader = reader
53
+ self.info = [
54
+ *self.reader.energies[0].info
55
+ ][1:] # get list of info parameters from first data object
56
+
57
+ self.list_of_plots = []
58
+
59
+ # sigint handler
60
+ signal.signal(signal.SIGINT,
61
+ lambda sig, frame: self.destroy()) # close the app
62
+
63
+ def destroy(self):
64
+ """
65
+ Destroy the app.
66
+ """
67
+ self.quit()
68
+ del (self)
69
+
70
+ def build(self):
71
+ """
72
+ Build the main window.
73
+ """
74
+ self.__build_sidebar()
75
+ self.__build_button_menu()
76
+ self.__build_info_option_menu()
77
+ self.__build_settings_menu()
78
+
79
+ def __default_theme(self):
80
+ """
81
+ Sets the default theme for the application.
82
+ """
83
+ ctk.set_appearance_mode("System")
84
+ ctk.set_default_color_theme("blue")
85
+
86
+ def __build_sidebar(self):
87
+ """
88
+ Build the main window.
89
+
90
+ Returns
91
+ -------
92
+ None
93
+ """
94
+ # sidebar frame
95
+ self.sidebar_frame = ctk.CTkFrame(self, width=140, corner_radius=0)
96
+ self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew")
97
+ self.sidebar_frame.grid_rowconfigure(4, weight=1)
98
+ self.sidebar_frame.grid_columnconfigure(0, weight=1)
99
+
100
+ # logo
101
+ self.logo = ctk.CTkImage(
102
+ Image.open(os.path.join(__base__, "icons", "icon.png")),
103
+ size=(100, 100),
104
+ )
105
+ self.sidebar_image_label = ctk.CTkLabel(self.sidebar_frame,
106
+ image=self.logo,
107
+ text="")
108
+ self.sidebar_image_label.grid(row=0, column=0, pady=10, padx=10)
109
+ self.logo_label = ctk.CTkLabel(
110
+ self.sidebar_frame,
111
+ text="PQEnalyzer",
112
+ font=ctk.CTkFont(size=20, weight="bold"),
113
+ )
114
+ self.logo_label.grid(row=1, column=0, padx=10, pady=10)
115
+
116
+ # change appearance mode
117
+ self.appearance_mode_label = ctk.CTkLabel(self.sidebar_frame,
118
+ text="Appearance Mode:",
119
+ anchor="w")
120
+ self.appearance_mode_label.grid(row=5, column=0, padx=20, pady=(10, 0))
121
+ self.appearance_mode_optionemenu = ctk.CTkOptionMenu(
122
+ self.sidebar_frame,
123
+ values=["System", "Light", "Dark"],
124
+ command=self.__change_appearance_mode_event,
125
+ )
126
+ self.appearance_mode_optionemenu.grid(row=6,
127
+ column=0,
128
+ padx=20,
129
+ pady=(10, 10))
130
+ self.appearance_mode_optionemenu.set(
131
+ "System") # set the default appearance mode to System
132
+
133
+ def __build_button_menu(self):
134
+ """
135
+ Build the main window.
136
+
137
+ Returns
138
+ -------
139
+ None
140
+ """
141
+ self.plot_frame = ctk.CTkFrame(self, width=200)
142
+ self.plot_frame.grid(row=2,
143
+ column=1,
144
+ sticky="nsew",
145
+ padx=(20, 20),
146
+ pady=(10, 10))
147
+ self.plot_frame.grid_rowconfigure(4, weight=1)
148
+ self.plot_frame.grid_columnconfigure(2, weight=1)
149
+
150
+ self.follow = tkinter.BooleanVar()
151
+ self.interval = None # Initially None
152
+ self.check_follow = ctk.CTkCheckBox(
153
+ master=self.plot_frame,
154
+ border_width=2,
155
+ text="Follow",
156
+ variable=self.follow,
157
+ command=lambda: self.toggle_entry_state(
158
+ self.check_follow, self.interval, default="1.0"))
159
+ self.check_follow.grid(row=0,
160
+ column=1,
161
+ padx=(10, 10),
162
+ pady=(10, 10),
163
+ sticky="nsew")
164
+
165
+ self.plot_main_data = tkinter.BooleanVar()
166
+ self.check_nodata = ctk.CTkCheckBox(
167
+ master=self.plot_frame,
168
+ border_width=2,
169
+ text="No Data",
170
+ variable=self.plot_main_data,
171
+ )
172
+ self.check_nodata.grid(row=0,
173
+ column=0,
174
+ padx=(10, 10),
175
+ pady=(10, 10),
176
+ sticky="nsew")
177
+
178
+ self.interval = ctk.CTkEntry(
179
+ self.plot_frame,
180
+ width=10,
181
+ validate="key",
182
+ validatecommand=(self.register(self.validate_number), "%P"),
183
+ )
184
+ self.interval.grid(row=1, column=1, padx=10, pady=5, sticky="we")
185
+ self.interval.configure(state="disabled") # Initially disabled
186
+
187
+ self.interval_label = ctk.CTkLabel(self.plot_frame,
188
+ text="Interval (s):",
189
+ anchor="w")
190
+ self.interval_label.grid(row=1, column=0, padx=10, pady=5, sticky="w")
191
+
192
+ self.button_plot = ctk.CTkButton(
193
+ master=self.plot_frame,
194
+ fg_color="transparent",
195
+ border_width=2,
196
+ text_color=("gray10", "#DCE4EE"),
197
+ text="Plot",
198
+ command=lambda: self.__plot_button_event(0),
199
+ )
200
+
201
+ self.button_plot.grid(row=2,
202
+ column=0,
203
+ columnspan=2,
204
+ padx=(10, 10),
205
+ pady=(10, 10),
206
+ sticky="nsew")
207
+
208
+ self.button_hist = ctk.CTkButton(
209
+ master=self.plot_frame,
210
+ fg_color="transparent",
211
+ border_width=2,
212
+ text_color=("gray10", "#DCE4EE"),
213
+ text="Histogram",
214
+ command=lambda: self.__plot_button_event(1),
215
+ )
216
+ self.button_hist.grid(row=3,
217
+ column=0,
218
+ columnspan=2,
219
+ padx=(10, 10),
220
+ pady=(10, 10),
221
+ sticky="nsew")
222
+
223
+ self.button_refresh = ctk.CTkButton( # refresh button
224
+ master=self.plot_frame,
225
+ fg_color="transparent",
226
+ border_width=2,
227
+ text_color=("gray10", "#DCE4EE"),
228
+ text="Refresh",
229
+ command=self.__refresh_plots,
230
+ )
231
+ self.button_refresh.grid(row=4,
232
+ column=0,
233
+ columnspan=2,
234
+ padx=(10, 10),
235
+ pady=(10, 10),
236
+ sticky="nsew")
237
+
238
+ def __build_info_option_menu(self):
239
+ """
240
+ Build the info selection window.
241
+
242
+ Returns
243
+ -------
244
+ None
245
+ """
246
+ self.info_frame = ctk.CTkFrame(self, width=200)
247
+ self.info_frame.grid(row=0,
248
+ column=1,
249
+ sticky="nsew",
250
+ padx=(20, 20),
251
+ pady=(10, 10))
252
+ self.info_frame.grid_rowconfigure(2, weight=1)
253
+ self.info_frame.grid_columnconfigure(1, weight=1)
254
+
255
+ self.info_label = ctk.CTkLabel(self.info_frame,
256
+ text="Parameter:",
257
+ font=ctk.CTkFont(size=15,
258
+ weight="bold"))
259
+ self.info_label.grid(row=0, column=1, padx=20, pady=10, sticky="w")
260
+ self.info_optionmenu = ctk.CTkOptionMenu(
261
+ self.info_frame,
262
+ values=self.info,
263
+ command=self.__change_info_event,
264
+ width=150,
265
+ anchor="c",
266
+ )
267
+ self.info_optionmenu.grid(row=1, column=1, padx=20, pady=10)
268
+ self.__change_info_event(
269
+ self.info[0]) # set the default info parameter to the first one
270
+
271
+ def __build_settings_menu(self):
272
+ """
273
+ Build the settings window.
274
+
275
+ Returns
276
+ -------
277
+ None
278
+ """
279
+ self.settings_frame = ctk.CTkFrame(self, width=200)
280
+ self.settings_frame.grid(row=1,
281
+ column=1,
282
+ sticky="nsew",
283
+ padx=(20, 20),
284
+ pady=(10, 10))
285
+ self.settings_frame.grid_rowconfigure(8, weight=1)
286
+ self.settings_frame.grid_columnconfigure(0, weight=1)
287
+
288
+ self.settings_label = ctk.CTkLabel(
289
+ self.settings_frame,
290
+ text="Statistics:",
291
+ font=ctk.CTkFont(size=15, weight="bold"),
292
+ )
293
+ self.settings_label.grid(row=0, column=0, padx=10, pady=5, sticky="w")
294
+ self.mean = ctk.CTkCheckBox(self.settings_frame, text="Mean")
295
+ self.mean.grid(row=1, column=0, padx=10, pady=5, sticky="w")
296
+ self.median = ctk.CTkCheckBox(self.settings_frame, text="Median")
297
+ self.median.grid(row=2, column=0, padx=10, pady=5, sticky="w")
298
+ self.cummulative_average = ctk.CTkCheckBox(self.settings_frame,
299
+ text="Cummulative Average")
300
+ self.cummulative_average.grid(row=3,
301
+ column=0,
302
+ padx=10,
303
+ pady=5,
304
+ sticky="w")
305
+ self.auto_correlation = ctk.CTkCheckBox(self.settings_frame,
306
+ text="Auto Correlation")
307
+ self.auto_correlation.grid(row=4,
308
+ column=0,
309
+ padx=10,
310
+ pady=5,
311
+ sticky="w")
312
+
313
+ self.window_size = None # Initially None
314
+ self.running_average = ctk.CTkCheckBox(
315
+ self.settings_frame,
316
+ text="Running Average",
317
+ command=lambda: self.toggle_entry_state(
318
+ self.running_average, self.window_size, default="10"))
319
+ self.running_average.grid(row=5, column=0, padx=10, pady=5, sticky="w")
320
+ self.running_average_window_size_label = ctk.CTkLabel(
321
+ self.settings_frame, text="Window Size:", anchor="w")
322
+ self.running_average_window_size_label.grid(row=6,
323
+ column=0,
324
+ padx=10,
325
+ pady=5,
326
+ sticky="w")
327
+ self.window_size = ctk.CTkEntry(
328
+ self.settings_frame,
329
+ width=10,
330
+ validate="key",
331
+ validatecommand=(self.register(self.validate_number), "%P"),
332
+ )
333
+ self.window_size.grid(row=7, column=0, padx=10, pady=5, sticky="we")
334
+ self.window_size.configure(state="disabled") # Initially disabled
335
+
336
+ def validate_number(self, value):
337
+ """
338
+ Validate if the input is a number.
339
+ """
340
+ return value == "" or value.replace(".", "", 1).isdigit()
341
+
342
+ def toggle_entry_state(self, event, entry, default=""):
343
+ """
344
+ Toggle the state of the entry.
345
+ """
346
+ if event.get():
347
+ entry.configure(state="normal")
348
+ entry.insert(0, default)
349
+ else:
350
+ entry.delete(0, ctk.END)
351
+ entry.configure(state="disabled")
352
+
353
+ def __change_appearance_mode_event(self, new_appearance_mode: str):
354
+ """
355
+ Change the appearance mode of the app.
356
+ """
357
+
358
+ ctk.set_appearance_mode(new_appearance_mode)
359
+
360
+ def __change_info_event(self, new_info: str):
361
+ """
362
+ Change the info parameter to plot.
363
+ """
364
+
365
+ self.__selected_info = new_info
366
+
367
+ def __refresh_plots(self):
368
+ """
369
+ Refresh the plots. If the plot is closed, remove it from the list.
370
+
371
+ Returns
372
+ -------
373
+ None
374
+ """
375
+ for plot in self.list_of_plots:
376
+
377
+ if plot.figure.number not in plt.get_fignums():
378
+ self.list_of_plots.remove(plot)
379
+ continue
380
+
381
+ plot.refresh()
382
+
383
+ def __plot_button_event(self, event):
384
+ """
385
+ Plot the data and checks if the user wants to follow the plot.
386
+ Appends the plot to the list of plots. If the user wants to follow the plot,
387
+ the plot is updated at a given interval.
388
+
389
+ Parameters
390
+ ----------
391
+ event : int
392
+ The event that triggered the function. 0 for time plot, 1 for histogram plot.
393
+
394
+ Returns
395
+ -------
396
+ None
397
+ """
398
+
399
+ if event == 0:
400
+ plot = PlotTime(self)
401
+ elif event == 1:
402
+ plot = PlotHistogram(self)
403
+
404
+ self.list_of_plots.append(plot)
405
+
406
+ if self.follow.get():
407
+ plot.follow(self.__selected_info, float(self.interval.get()))
408
+ else:
409
+ plot.simple(self.__selected_info)
@@ -0,0 +1,100 @@
1
+ """
2
+ This module allows the user to plot the data in the terminal.
3
+ """
4
+ import signal
5
+ from InquirerPy import inquirer
6
+
7
+ from ..plots import TermPlot
8
+
9
+
10
+ class TermApp:
11
+ """
12
+ A class to plot the data in the terminal.
13
+
14
+ Attributes
15
+ ----------
16
+ reader : Reader
17
+ The Reader object to read the data.
18
+
19
+ Methods
20
+ -------
21
+ """
22
+
23
+ def __init__(self, reader):
24
+ """
25
+ Constructs all the necessary attributes for the TermApp object.
26
+
27
+ Parameters
28
+ ----------
29
+ reader : Reader
30
+ The Reader object to read the data.
31
+
32
+ Returns
33
+ -------
34
+ None
35
+ """
36
+
37
+ self.reader = reader
38
+ self.info = [
39
+ *self.reader.energies[0].info,
40
+ ][1:]
41
+
42
+ return None
43
+
44
+ def run(self):
45
+ """
46
+ Run the terminal application.
47
+
48
+ Parameters
49
+ ----------
50
+ None
51
+
52
+ Returns
53
+ -------
54
+ None
55
+ """
56
+
57
+ result = inquirer.select(
58
+ message="Select the information parameter to plot",
59
+ choices=self.info,
60
+ vi_mode=True,
61
+ mandatory=True,
62
+ ).execute()
63
+
64
+ termplot = TermPlot(self.reader)
65
+ termplot.plot(result)
66
+
67
+ return None
68
+
69
+ def start(self):
70
+ """
71
+ Start the terminal application.
72
+
73
+ Parameters
74
+ ----------
75
+ None
76
+
77
+ Returns
78
+ -------
79
+ None
80
+ """
81
+ try:
82
+ self.run()
83
+ _exit = inquirer.confirm(
84
+ message="Do you want to exit?",
85
+ default=False,
86
+ vi_mode=True,
87
+ keybindings={
88
+ "interrupt": [{
89
+ "key": "c-d"
90
+ }]
91
+ },
92
+ ).execute()
93
+
94
+ if _exit:
95
+ return None
96
+
97
+ except KeyboardInterrupt:
98
+ return None
99
+
100
+ return self.start()
Binary file
@@ -0,0 +1,7 @@
1
+ """
2
+ This module contains the classes for plotting the histogram and time series of the data.
3
+ """
4
+
5
+ from .plot_histogram import PlotHistogram
6
+ from .plot_time import PlotTime
7
+ from .termplot import TermPlot