diffpy.fourigui 0.1.0rc0__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.
diffpy/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # (c) 2024 The Trustees of Columbia University in the City of New York.
5
+ # All rights reserved.
6
+ #
7
+ # File coded by: Billinge Group members and community contributors.
8
+ #
9
+ # See GitHub contributions for a more detailed list of contributors.
10
+ # https://github.com/diffpy/diffpy.fourigui/graphs/contributors
11
+ #
12
+ # See LICENSE.rst for license information.
13
+ #
14
+ ##############################################################################
15
+
16
+ """Blank namespace package for module diffpy."""
17
+
18
+
19
+ from pkgutil import extend_path
20
+
21
+ __path__ = extend_path(__path__, __name__)
22
+
23
+ # End of file
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # (c) 2024 The Trustees of Columbia University in the City of New York.
5
+ # All rights reserved.
6
+ #
7
+ # File coded by: Billinge Group members and community contributors.
8
+ #
9
+ # See GitHub contributions for a more detailed list of contributors.
10
+ # https://github.com/diffpy/diffpy.fourigui/graphs/contributors
11
+ #
12
+ # See LICENSE.rst for license information.
13
+ #
14
+ ##############################################################################
15
+
16
+ """Tool for visualizing 3D diffraction and PDF Images."""
17
+
18
+ # package version
19
+ from diffpy.fourigui.version import __version__
20
+
21
+ # silence the pyflakes syntax checker
22
+ assert __version__ or True
23
+
24
+ # End of file
@@ -0,0 +1,571 @@
1
+ import time
2
+ import tkinter as tk
3
+ from tkinter.ttk import Button
4
+
5
+ import h5py
6
+ import matplotlib
7
+ import numpy as np
8
+ from matplotlib import pyplot as plt
9
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
10
+
11
+ matplotlib.use("tkagg")
12
+
13
+ WIDTH = 920
14
+ HEIGHT = 630
15
+ XPOS = 300
16
+ YPOS = 100
17
+
18
+
19
+ class Gui(tk.Frame):
20
+ def __init__(self):
21
+ super().__init__()
22
+ self.initUI()
23
+
24
+ def initUI(self):
25
+
26
+ self.loaded = False # denotes whether a dataset is loaded
27
+ self.transformed = False # denotes whether dataset is Fourier transformed
28
+ self.cutted = False # denotes whether cutoff frequencies are applied to dataset
29
+ self.transcutted = False # denotes whether cutoff frequencies are applied and Fourier transformed
30
+
31
+ self.master.title("FouriGUI")
32
+ self.pack(fill=tk.BOTH, expand=True)
33
+
34
+ print("\nNew Session started ...")
35
+ print("Enjoy exploring the beautiful reconstructions in real and in reciprocal space!")
36
+
37
+ # 4 frames:
38
+ # frame 00: all buttons
39
+ # frame 01: plot area
40
+ # frame 10: exit button
41
+ # frame 11: not used
42
+
43
+ # 00 #
44
+ # frame 00, upper left
45
+
46
+ frame00 = tk.Frame(self)
47
+ frame00.place(x=5, y=0)
48
+
49
+ filelabel = tk.Label(frame00, text="filename: ")
50
+ filelabel.grid(row=0, column=0)
51
+
52
+ # row 0: load file area
53
+ self.filename_entry = tk.Entry(frame00)
54
+ self.filename_entry.grid(row=0, column=1, columnspan=3)
55
+ self.filename_entry.insert(0, "/path/data.h5")
56
+
57
+ loadbutton = Button(frame00, text="load", command=lambda: self.load_cube())
58
+ loadbutton.grid(row=0, column=4)
59
+
60
+ # row 1: change axis area
61
+ axislabel = tk.Label(frame00, text="axis: ")
62
+ axislabel.grid(row=1, column=0, pady=7, sticky=tk.W)
63
+
64
+ self.axis = tk.IntVar()
65
+
66
+ rb0 = tk.Radiobutton(
67
+ frame00,
68
+ text="0",
69
+ variable=self.axis,
70
+ value=0,
71
+ command=lambda: self.plot_plane(),
72
+ )
73
+ rb0.grid(row=1, column=1)
74
+ rb1 = tk.Radiobutton(
75
+ frame00,
76
+ text="1",
77
+ variable=self.axis,
78
+ value=1,
79
+ command=lambda: self.plot_plane(),
80
+ )
81
+ rb1.grid(row=1, column=2)
82
+ rb2 = tk.Radiobutton(
83
+ frame00,
84
+ text="2",
85
+ variable=self.axis,
86
+ value=2,
87
+ command=lambda: self.plot_plane(),
88
+ )
89
+ rb2.grid(row=1, column=3)
90
+
91
+ # row 2-4: intensity specs
92
+ intlabel = tk.Label(frame00, text="intensity:")
93
+ intlabel.grid(row=2, column=0, pady=1, sticky=tk.W)
94
+ maxintlabel = tk.Label(frame00, text="max:")
95
+ maxintlabel.grid(row=3, column=0, pady=1, sticky=tk.E)
96
+ minintlabel = tk.Label(frame00, text="min:")
97
+ minintlabel.grid(row=4, column=0, pady=1, sticky=tk.E)
98
+ sumintlabel = tk.Label(frame00, text="sum:")
99
+ sumintlabel.grid(row=5, column=0, pady=1, sticky=tk.E)
100
+ nanratiolabel = tk.Label(frame00, text="nan ratio:")
101
+ nanratiolabel.grid(row=6, column=0, pady=1, sticky=tk.E)
102
+ globallabel = tk.Label(frame00, text="global", width=7)
103
+ globallabel.grid(row=2, column=1)
104
+ self.globalmax = tk.Label(frame00, text="")
105
+ self.globalmax.grid(row=3, column=1)
106
+ self.globalmin = tk.Label(frame00, text="")
107
+ self.globalmin.grid(row=4, column=1)
108
+ self.globalsum = tk.Label(frame00, text="")
109
+ self.globalsum.grid(row=5, column=1)
110
+ self.globalnanratio = tk.Label(frame00, text="")
111
+ self.globalnanratio.grid(row=6, column=1)
112
+ inplanelabel = tk.Label(frame00, text="in plane", width=7)
113
+ inplanelabel.grid(row=2, column=2)
114
+ self.localmax = tk.Label(frame00, text="")
115
+ self.localmax.grid(row=3, column=2)
116
+ self.localmin = tk.Label(frame00, text="")
117
+ self.localmin.grid(row=4, column=2)
118
+ self.localsum = tk.Label(frame00, text="")
119
+ self.localsum.grid(row=5, column=2)
120
+ self.localnanratio = tk.Label(frame00, text="")
121
+ self.localnanratio.grid(row=6, column=2)
122
+ colorbarlabel = tk.Label(frame00, text="colorbar")
123
+ colorbarlabel.grid(row=2, column=3)
124
+ self.colorbarmax = tk.Entry(frame00, width=7)
125
+ self.colorbarmax.grid(row=3, column=3)
126
+ self.colorbarmin = tk.Entry(frame00, width=7)
127
+ self.colorbarmin.grid(row=4, column=3)
128
+ set_range = Button(frame00, text="set range", command=lambda: self.colorrange_upd())
129
+ set_range.grid(row=2, column=4)
130
+ toglobalmax = Button(
131
+ frame00,
132
+ text="global max",
133
+ command=lambda: self.multiple_funcs(
134
+ self.colorbarmax.delete(0, len(self.colorbarmax.get())),
135
+ self.colorbarmax.insert(0, self.globalmax["text"]),
136
+ ),
137
+ )
138
+ toglobalmax.grid(row=3, column=4)
139
+ toglobalmin = Button(
140
+ frame00,
141
+ text="global min",
142
+ command=lambda: self.multiple_funcs(
143
+ self.colorbarmin.delete(0, len(self.colorbarmin.get())),
144
+ self.colorbarmin.insert(0, self.globalmin["text"]),
145
+ ),
146
+ )
147
+ toglobalmin.grid(row=4, column=4)
148
+
149
+ # row 7-8: animation - automatic slicing through the planes
150
+ anilabel = tk.Label(frame00, text="animation speed [ms]")
151
+ anilabel.grid(row=7, column=3, columnspan=2, sticky=tk.W)
152
+ self.anientry = tk.Entry(frame00, width=7)
153
+ self.anientry.grid(row=8, column=3)
154
+ anibutton = Button(frame00, text="animation", command=lambda: self.animation())
155
+ anibutton.grid(row=8, column=4)
156
+
157
+ # row 10-12 Fourier transformation
158
+ seperator = tk.Label(
159
+ frame00, text=" "
160
+ ) # __________________________________________________________________")
161
+ seperator.grid(row=9, column=0, columnspan=5)
162
+ cutofflabel = tk.Label(frame00, text="cutoff frequency")
163
+ cutofflabel.grid(row=10, column=2, columnspan=2)
164
+ qminlabel = tk.Label(frame00, text="qmin [px]:")
165
+ qminlabel.grid(row=11, column=2, sticky=tk.E)
166
+ qmaxlabel = tk.Label(frame00, text="qmax [px]:")
167
+ qmaxlabel.grid(row=12, column=2, sticky=tk.E)
168
+ self.qminentry = tk.Entry(frame00, width=7)
169
+ self.qminentry.grid(row=11, column=3)
170
+ self.qmaxentry = tk.Entry(frame00, width=7)
171
+ self.qmaxentry.grid(row=12, column=3)
172
+ self.cutoff = tk.IntVar()
173
+ newcutoffbutton = Button(frame00, text="new cutoff", command=lambda: self.newcutoff())
174
+ newcutoffbutton.grid(row=10, column=4)
175
+ cutoffon = tk.Radiobutton(
176
+ frame00,
177
+ text="on",
178
+ variable=self.cutoff,
179
+ value=1,
180
+ command=lambda: self.applycutoff(),
181
+ )
182
+ cutoffon.grid(row=11, column=4, sticky=tk.W)
183
+ cutoffoff = tk.Radiobutton(
184
+ frame00,
185
+ text="off",
186
+ variable=self.cutoff,
187
+ value=0,
188
+ command=lambda: self.redocutuff(),
189
+ )
190
+ cutoffoff.grid(row=12, column=4, sticky=tk.W)
191
+
192
+ spacelabel = tk.Label(frame00, text="Space Selection")
193
+ spacelabel.grid(row=10, column=0, columnspan=2, sticky=tk.W)
194
+ self.space = tk.IntVar()
195
+ reciprocal = tk.Radiobutton(
196
+ frame00,
197
+ text="reciprocal space",
198
+ variable=self.space,
199
+ value=0,
200
+ command=lambda: self.ifft(),
201
+ pady=5,
202
+ )
203
+ reciprocal.grid(row=11, column=0, columnspan=2, sticky=tk.W)
204
+ fft = tk.Radiobutton(
205
+ frame00,
206
+ text="real space",
207
+ variable=self.space,
208
+ value=1,
209
+ command=lambda: self.fft(),
210
+ )
211
+ fft.grid(row=12, column=0, columnspan=2, sticky=tk.W)
212
+
213
+ # 01 #
214
+ # frame 01, upper right
215
+ self.frame01 = tk.Frame(self, bg="#cccccc")
216
+ self.frame01.place(x=400, y=0) # , height=HEIGHT//2, width=WIDTH//2)
217
+
218
+ self.plane_num = tk.IntVar()
219
+
220
+ self.slider = tk.Scale(
221
+ self.frame01,
222
+ variable=self.plane_num,
223
+ from_=0,
224
+ to=500,
225
+ label="slider",
226
+ orient=tk.HORIZONTAL,
227
+ length=WIDTH // 2, # resolution=-1,
228
+ command=lambda x: self.multiple_funcs(self.plot_plane(), self.intensity_upd_local()),
229
+ )
230
+ # command=lambda p: self.plot_plane())
231
+ self.slider.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W)
232
+
233
+ self.frame01_plotcell = tk.Frame(self.frame01)
234
+ self.frame01_plotcell.grid(row=1, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W)
235
+
236
+ self.frame01_toolbar = tk.Frame(self.frame01)
237
+ self.frame01_toolbar.grid(row=2, column=0)
238
+
239
+ # 10 #
240
+ # frame 10, lower left
241
+ frame10 = tk.Frame(self)
242
+ frame10.place(x=5, y=HEIGHT - 30) # , height=HEIGHT//2, width=WIDTH//2)
243
+ quit = Button(
244
+ frame10,
245
+ text="exit",
246
+ command=lambda: self.multiple_funcs(print("Session ended...\n", self.quit())),
247
+ )
248
+ quit.pack(side=tk.TOP)
249
+
250
+ # 11 #
251
+ # frame 00, lower right
252
+ # no functionality
253
+ frame11 = tk.Frame(self)
254
+ frame11.place(x=WIDTH // 2, y=HEIGHT // 2) # , height=HEIGHT//2, width=WIDTH//2)
255
+
256
+ def load_cube(self):
257
+ """
258
+ loads 3D array in h5py file format from the filename input panel
259
+ 3D array is expected to be a reconstructed reciprocal scattering volume
260
+ when executed, one slide perpendicular to the selected axis will be plotted in the plot panel
261
+ """
262
+
263
+ filename = self.filename_entry.get()
264
+ f = h5py.File(filename, "r")
265
+ try:
266
+ if "data" in f.keys():
267
+ self.cube = np.array(f["data"])
268
+ elif "rebinned_data" in f.keys():
269
+ self.cube = np.array(f["rebinned_data"])
270
+ except Exception:
271
+ raise KeyError(
272
+ "- No data found in "
273
+ + filename
274
+ + " :( ..."
275
+ + "\nchange to alternative keys: "
276
+ + str(list(f.keys()))
277
+ )
278
+ print("- file loaded: {}".format(filename))
279
+
280
+ self.slider.destroy()
281
+ self.slider = tk.Scale(
282
+ self.frame01,
283
+ variable=self.plane_num,
284
+ from_=0,
285
+ to=len(self.cube) - 1,
286
+ label="slider",
287
+ orient=tk.HORIZONTAL,
288
+ length=WIDTH // 2, # resolution=-1,
289
+ command=lambda x: self.multiple_funcs(self.plot_plane(), self.intensity_upd_local()),
290
+ )
291
+ self.slider.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W)
292
+
293
+ if not self.loaded:
294
+
295
+ fig, ax = plt.subplots(figsize=(4.95, 4.95))
296
+ fig = plt.gcf()
297
+ DPI = fig.get_dpi()
298
+ fig.set_size_inches(500 / float(DPI), 500 / float(DPI))
299
+
300
+ self.plane_num.set(np.shape(self.cube)[0] // 2)
301
+
302
+ if self.axis.get() == 0:
303
+ self.im = plt.imshow(self.cube[self.plane_num.get(), :, :])
304
+ elif self.axis.get() == 1:
305
+ self.im = plt.imshow(self.cube[:, self.plane_num.get(), :])
306
+ elif self.axis.get() == 2:
307
+ self.im = plt.imshow(self.cube[:, :, self.plane_num.get()])
308
+ else:
309
+ raise ValueError("axis must be 0,1,2")
310
+ plt.colorbar(shrink=0.81)
311
+ ax.set_xlabel("pixel")
312
+ ax.set_ylabel("pixel")
313
+ self.canvas = FigureCanvasTkAgg(fig, master=self.frame01_plotcell)
314
+ self.toolbar = NavigationToolbar2Tk(self.canvas, self.frame01_toolbar)
315
+ self.toolbar.pack(side=tk.LEFT)
316
+ # self.toolbar.children['!button6'].pack_forget()
317
+ # self.toolbar.children['!button7'].pack_forget()
318
+ self.toolbar.update()
319
+ self.canvas.draw()
320
+ self.canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
321
+ self.loaded = True
322
+
323
+ else:
324
+ self.plot_plane()
325
+ self.transformed = False
326
+ self.transcutted = False
327
+ self.cutted = False
328
+ self.cutoff.set(0)
329
+ self.space.set(0)
330
+
331
+ self.intensity_upd_global()
332
+
333
+ def plot_plane(self):
334
+ """update plotted plane perpendicular to the selected axis"""
335
+ if self.axis.get() == 0:
336
+ self.im.set_data(self.cube[self.plane_num.get(), :, :])
337
+ elif self.axis.get() == 1:
338
+ self.im.set_data(self.cube[:, self.plane_num.get(), :])
339
+ elif self.axis.get() == 2:
340
+ self.im.set_data(self.cube[:, :, self.plane_num.get()])
341
+ else:
342
+ raise ValueError("axis must be 0,1,2")
343
+ self.canvas.draw()
344
+
345
+ def colorrange_upd(self):
346
+ """change color range in plot"""
347
+ try:
348
+ if self.colorbarmin.get() and self.colorbarmax.get():
349
+ vmin = float(self.colorbarmin.get())
350
+ vmax = float(self.colorbarmax.get())
351
+ elif self.colorbarmin.get():
352
+ vmin = float(self.colorbarmin.get())
353
+ vmax = self.globalmax["text"]
354
+ elif self.colorbarmax.get():
355
+ vmin = self.globalmin["text"]
356
+ vmax = float(self.colorbarmax.get())
357
+ else:
358
+ vmin = self.globalmin["text"]
359
+ vmax = self.globalmax["text"]
360
+ except ValueError:
361
+ print("Oops... colorbar range must be a number or empty string.")
362
+ self.im.set_clim(vmin, vmax)
363
+ self.plot_plane()
364
+
365
+ def intensity_upd_local(self):
366
+ """show local intensity minimum, maximum and sum of current plotted plane"""
367
+ if self.axis.get() == 0:
368
+ plane = self.cube[self.plane_num.get(), :, :]
369
+ elif self.axis.get() == 1:
370
+ plane = self.cube[:, self.plane_num.get(), :]
371
+ elif self.axis.get() == 2:
372
+ plane = self.cube[:, :, self.plane_num.get()]
373
+ nan_ratio = np.count_nonzero(np.isnan(plane)) / plane.size
374
+ self.localmax["text"] = "{}".format(np.format_float_scientific(np.nanmax(plane), 1))
375
+ self.localmin["text"] = "{}".format(np.format_float_scientific(np.nanmin(plane), 1))
376
+ self.localsum["text"] = "{}".format(np.format_float_scientific(np.nansum(plane), 1))
377
+ self.localnanratio["text"] = "{}".format(round(nan_ratio, 2))
378
+
379
+ def intensity_upd_global(self):
380
+ """show global intensity minimum, maximum and sum of 3D array"""
381
+ self.intensity_upd_local()
382
+ nan_ratio = np.count_nonzero(np.isnan(self.cube)) / self.cube.size
383
+ self.globalmax["text"] = "{}".format(np.format_float_scientific(np.nanmax(self.cube), 1))
384
+ self.globalmin["text"] = "{}".format(np.format_float_scientific(np.nanmin(self.cube), 1))
385
+ self.globalsum["text"] = "{}".format(np.format_float_scientific(np.nansum(self.cube), 1))
386
+ self.globalnanratio["text"] = "{}".format(round(nan_ratio, 2))
387
+
388
+ def fft(self):
389
+ """
390
+ Fourier transform 3D array from reciprocal to real space
391
+ the origin of reciprocal and real space is expected to be the central voxel
392
+ """
393
+
394
+ def perform_fft(fftholder):
395
+ time0 = time.time()
396
+ fftholder = np.nan_to_num(fftholder)
397
+ size = list(fftholder.shape)
398
+ fftholder = np.fft.ifftshift(fftholder)
399
+ fftholder = np.fft.fftn(fftholder, s=size, norm="ortho")
400
+ fftholder = np.fft.fftshift(fftholder)
401
+ fftholder = fftholder.real
402
+ fftdur = time.time() - time0
403
+ print("- FFT performed in {} sec.".format(round(fftdur, 4)))
404
+ return fftholder
405
+
406
+ if not self.transformed and not self.transcutted: # no fft at all yet
407
+ if not self.cutoff.get():
408
+ self.cube_reci = self.cube
409
+ self.cube = perform_fft(self.cube)
410
+ self.cube_real = self.cube
411
+ self.transformed = True
412
+ else:
413
+ self.cube_recicut = self.cube
414
+ self.cube = perform_fft(self.cube)
415
+ self.cube_realcut = self.cube
416
+ self.transcutted = True
417
+
418
+ elif not self.transformed and self.transcutted:
419
+ if not self.cutoff.get():
420
+ self.cube = perform_fft(self.cube_reci)
421
+ self.cube_real = self.cube
422
+ self.transformed = True
423
+ else:
424
+ self.cube = self.cube_realcut
425
+
426
+ elif self.transformed and not self.transcutted:
427
+ if not self.cutoff.get():
428
+ self.cube_reci = self.cube
429
+ self.cube = self.cube_real
430
+ else:
431
+ self.cube = perform_fft(self.cube_recicut)
432
+ # self.cube = self.cube_realcut
433
+ self.transcutted = True
434
+
435
+ else:
436
+ if not self.cutoff.get():
437
+ self.cube = self.cube_real
438
+ else:
439
+ self.cube = self.cube_realcut
440
+
441
+ print("- Switching to real space")
442
+
443
+ self.plot_plane()
444
+ self.intensity_upd_global()
445
+
446
+ def ifft(self):
447
+ """
448
+ Inverse Fourier transform 3D array from real to reciprocal space
449
+ the origin of real and reciprocal space is expected to be the central voxel
450
+ """
451
+ if not self.cutoff.get():
452
+ self.cube_real = self.cube
453
+ self.cube = self.cube_reci
454
+ else:
455
+ self.cube_realcut = self.cube
456
+ self.cube = self.cube_recicut
457
+
458
+ print("- Switching to reciprocal space")
459
+
460
+ self.plot_plane()
461
+ self.intensity_upd_global()
462
+
463
+ def applycutoff(self):
464
+ """
465
+ reassign all voxels with distance smaller than qmin and greater than qmax
466
+ from the central voxel to 0.0
467
+ qmin, qmax is loaded from the qmin, qmax input panel
468
+ currently opperates in units of pixels
469
+ """
470
+ if not self.cutted:
471
+
472
+ time0 = time.time()
473
+ X, Y, Z = self.cube.shape
474
+ sphere = np.ones((X, Y, Z))
475
+ qmin = float(self.qminentry.get())
476
+ qmax = float(self.qmaxentry.get())
477
+ # convert qmin to pixels
478
+ # convert qmax to pixels
479
+ r2_inner = qmin**2
480
+ r2_outer = qmax**2
481
+ XS, YS, ZS = np.meshgrid(np.arange(X), np.arange(Y), np.arange(Z))
482
+ R2 = (XS - X // 2) ** 2 + (YS - Y // 2) ** 2 + (ZS - Z // 2) ** 2
483
+ mask = (R2 <= r2_inner) | (R2 >= r2_outer)
484
+ sphere[mask] = np.nan
485
+ cutdur = time.time() - time0
486
+
487
+ if self.space.get():
488
+ self.cube_real = self.cube
489
+ self.cube = self.cube_reci * sphere
490
+ self.cube_recicut = self.cube
491
+ print("- Cutoff below {} and beyond {} in {} sec.".format(qmin, qmax, round(cutdur, 4)))
492
+ self.fft()
493
+ else:
494
+ self.cube_reci = self.cube
495
+ self.cube = self.cube * sphere
496
+ self.cube_recicut = self.cube
497
+ self.plot_plane()
498
+ self.intensity_upd_global()
499
+ print("- Cutoff below {} and beyond {} in {} sec.".format(qmin, qmax, round(cutdur, 4)))
500
+
501
+ self.cutted = True
502
+
503
+ else:
504
+ if self.space.get(): # in real space
505
+ self.cube = self.cube_realcut
506
+ else:
507
+ self.cube = self.cube_recicut
508
+ self.plot_plane()
509
+ self.intensity_upd_global()
510
+
511
+ def redocutuff(self):
512
+ if self.space.get(): # in real space
513
+ self.cube_realcut = self.cube
514
+ if not self.transformed:
515
+ self.fft()
516
+ self.cube = self.cube_real
517
+ else:
518
+ self.cube_recicut = self.cube
519
+ self.cube = self.cube_reci
520
+ self.plot_plane()
521
+ self.intensity_upd_global()
522
+
523
+ def newcutoff(self):
524
+ if self.cutoff.get():
525
+ if self.space.get() and self.transformed:
526
+ self.cube = self.cube_real
527
+ else:
528
+ self.cube = self.cube_reci
529
+ self.cutted = False
530
+ self.transcutted = False
531
+ self.applycutoff()
532
+
533
+ def plot_next_plane(self):
534
+ n = self.plane_num.get()
535
+ if n == len(self.cube[self.axis.get()]) - 1:
536
+ n = 0
537
+ else:
538
+ n += 1
539
+ self.plane_num.set(n)
540
+ self.plot_plane()
541
+
542
+ def animation(self):
543
+ """
544
+ slices through the 3D array along the selcted axis
545
+ """
546
+ try:
547
+ if not self.anientry.get():
548
+ anispeed = 1
549
+ else:
550
+ anispeed = self.anientry.get()
551
+ except ValueError:
552
+ print("Oops... animation speed must be an integer > 0 or empty string.")
553
+ n = self.plane_num.get() - 1
554
+ while n is not self.plane_num.get():
555
+ self.slider.after(anispeed, self.plot_next_plane())
556
+ self.plot_next_plane()
557
+
558
+ def multiple_funcs(*funcs):
559
+ for func in funcs:
560
+ func
561
+
562
+
563
+ def main():
564
+ root = tk.Tk()
565
+ root.geometry("{}x{}+{}+{}".format(WIDTH, HEIGHT, XPOS, YPOS))
566
+ Gui()
567
+ root.mainloop()
568
+
569
+
570
+ if __name__ == "__main__":
571
+ main()
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # (c) 2024 The Trustees of Columbia University in the City of New York.
5
+ # All rights reserved.
6
+ #
7
+ # File coded by: Billinge Group members and community contributors.
8
+ #
9
+ # See GitHub contributions for a more detailed list of contributors.
10
+ # https://github.com/diffpy/diffpy.fourigui/graphs/contributors
11
+ #
12
+ # See LICENSE.rst for license information.
13
+ #
14
+ ##############################################################################
15
+
16
+ """Definition of __version__."""
17
+
18
+ # We do not use the other three variables, but can be added back if needed.
19
+ # __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"]
20
+
21
+ # obtain version information
22
+ from importlib.metadata import version
23
+
24
+ __version__ = version("diffpy.fourigui")
25
+
26
+ # End of file
@@ -0,0 +1,10 @@
1
+ Authors
2
+ =======
3
+
4
+ Billinge Group and community contributors.
5
+
6
+ Contributors
7
+ ------------
8
+
9
+ For a list of contributors, visit
10
+ https://github.com/diffpy/diffpy.fourigui/graphs/contributors
@@ -0,0 +1,30 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, The Trustees of Columbia University
4
+ in the City of New York.
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation
15
+ and/or other materials provided with the distribution.
16
+
17
+ 3. Neither the name of the copyright holder nor the names of its contributors
18
+ may be used to endorse or promote products derived from this software
19
+ without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.1
2
+ Name: diffpy.fourigui
3
+ Version: 0.1.0rc0
4
+ Summary: Tool for visualizing 3D diffraction and PDF Images.
5
+ Author-email: "Simon J.L. Billinge group" <simon.billinge@gmail.com>
6
+ Maintainer-email: "Simon J.L. Billinge group" <simon.billinge@gmail.com>
7
+ Project-URL: Homepage, https://github.com/diffpy/diffpy.fourigui/
8
+ Project-URL: Issues, https://github.com/diffpy/diffpy.fourigui/issues/
9
+ Keywords: diffraction,pdf,pair distribution function,gui
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: BSD License
15
+ Classifier: Operating System :: MacOS :: MacOS X
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX
18
+ Classifier: Operating System :: Unix
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Physics
23
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE.rst
27
+ License-File: AUTHORS.rst
28
+ Requires-Dist: h5py
29
+ Requires-Dist: time
30
+ Requires-Dist: tk
31
+ Requires-Dist: matplotlib
32
+ Requires-Dist: numpy
33
+
34
+ |Icon| |title|_
35
+ ===============
36
+
37
+ .. |title| replace:: diffpy.fourigui
38
+ .. _title: https://diffpy.github.io/diffpy.fourigui
39
+
40
+ .. |Icon| image:: https://avatars.githubusercontent.com/diffpy
41
+ :target: https://diffpy.github.io/diffpy.fourigui
42
+ :height: 100px
43
+
44
+ |PyPi| |Forge| |PythonVersion| |PR|
45
+
46
+ |CI| |Codecov| |Black| |Tracking|
47
+
48
+ .. |Black| image:: https://img.shields.io/badge/code_style-black-black
49
+ :target: https://github.com/psf/black
50
+
51
+ .. |CI| image:: https://github.com/diffpy/diffpy.fourigui/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg
52
+ :target: https://github.com/diffpy/diffpy.fourigui/actions/workflows/matrix-and-codecov-on-merge-to-main.yml
53
+
54
+ .. |Codecov| image:: https://codecov.io/gh/diffpy/diffpy.fourigui/branch/main/graph/badge.svg
55
+ :target: https://codecov.io/gh/diffpy/diffpy.fourigui
56
+
57
+ .. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/diffpy.fourigui
58
+ :target: https://anaconda.org/conda-forge/diffpy.fourigui
59
+
60
+ .. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff
61
+
62
+ .. |PyPi| image:: https://img.shields.io/pypi/v/diffpy.fourigui
63
+ :target: https://pypi.org/project/diffpy.fourigui/
64
+
65
+ .. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/diffpy.fourigui
66
+ :target: https://pypi.org/project/diffpy.fourigui/
67
+
68
+ .. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue
69
+ :target: https://github.com/diffpy/diffpy.fourigui/issues
70
+
71
+ Tool for visualizing 3D diffraction and PDF Images.
72
+
73
+ * LONGER DESCRIPTION HERE
74
+
75
+ For more information about the diffpy.fourigui library, please consult our `online documentation <https://diffpy.github.io/diffpy.fourigui>`_.
76
+
77
+ Citation
78
+ --------
79
+
80
+ If you use diffpy.fourigui in a scientific publication, we would like you to cite this package as
81
+
82
+ diffpy.fourigui Package, https://github.com/diffpy/diffpy.fourigui
83
+
84
+ Installation
85
+ ------------
86
+
87
+ The preferred method is to use `Miniconda Python
88
+ <https://docs.conda.io/projects/miniconda/en/latest/miniconda-install.html>`_
89
+ and install from the "conda-forge" channel of Conda packages.
90
+
91
+ To add "conda-forge" to the conda channels, run the following in a terminal. ::
92
+
93
+ conda config --add channels conda-forge
94
+
95
+ We want to install our packages in a suitable conda environment.
96
+ The following creates and activates a new environment named ``diffpy.fourigui_env`` ::
97
+
98
+ conda create -n diffpy.fourigui_env python=3
99
+ conda activate diffpy.fourigui_env
100
+
101
+ Then, to fully install ``diffpy.fourigui`` in our active environment, run ::
102
+
103
+ conda install diffpy.fourigui
104
+
105
+ Another option is to use ``pip`` to download and install the latest release from
106
+ `Python Package Index <https://pypi.python.org>`_.
107
+ To install using ``pip`` into your ``diffpy.fourigui_env`` environment, we will also have to install dependencies ::
108
+
109
+ pip install -r https://raw.githubusercontent.com/diffpy/diffpy.fourigui/main/requirements/run.txt
110
+
111
+ and then install the package ::
112
+
113
+ pip install diffpy.fourigui
114
+
115
+ If you prefer to install from sources, after installing the dependencies, obtain the source archive from
116
+ `GitHub <https://github.com/diffpy/diffpy.fourigui/>`_. Once installed, ``cd`` into your ``diffpy.fourigui`` directory
117
+ and run the following ::
118
+
119
+ pip install .
120
+
121
+ Support and Contribute
122
+ ----------------------
123
+
124
+ `Diffpy user group <https://groups.google.com/g/diffpy-users>`_ is the discussion forum for general questions and discussions about the use of diffpy.fourigui. Please join the diffpy.fourigui users community by joining the Google group. The diffpy.fourigui project welcomes your expertise and enthusiasm!
125
+
126
+ If you see a bug or want to request a feature, please `report it as an issue <https://github.com/diffpy/diffpy.fourigui/issues>`_ and/or `submit a fix as a PR <https://github.com/diffpy/diffpy.fourigui/pulls>`_. You can also post it to the `Diffpy user group <https://groups.google.com/g/diffpy-users>`_.
127
+
128
+ Feel free to fork the project and contribute. To install diffpy.fourigui
129
+ in a development mode, with its sources being directly used by Python
130
+ rather than copied to a package directory, use the following in the root
131
+ directory ::
132
+
133
+ pip install -e .
134
+
135
+ To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit
136
+ hooks.
137
+
138
+ 1. Install pre-commit in your working environment by running ``conda install pre-commit``.
139
+
140
+ 2. Initialize pre-commit (one time only) ``pre-commit install``.
141
+
142
+ Thereafter your code will be linted by black and isort and checked against flake8 before you can commit.
143
+ If it fails by black or isort, just rerun and it should pass (black and isort will modify the files so should
144
+ pass after they are modified). If the flake8 test fails please see the error messages and fix them manually before
145
+ trying to commit again.
146
+
147
+ Improvements and fixes are always appreciated.
148
+
149
+ Before contribuing, please read our `Code of Conduct <https://github.com/diffpy/diffpy.fourigui/blob/main/CODE_OF_CONDUCT.rst>`_.
150
+
151
+ Contact
152
+ -------
153
+
154
+ For more information on diffpy.fourigui please visit the project `web-page <https://diffpy.github.io/>`_ or email Prof. Simon Billinge at sb2896@columbia.edu.
@@ -0,0 +1,10 @@
1
+ diffpy/__init__.py,sha256=w43pJCjwSRowjr294Q6vQ_hWvIrnJRz3nbO9dLyluDU,672
2
+ diffpy/fourigui/__init__.py,sha256=XjMy6FaPr4PIhzyGWX2yYKUagX2Bl8B7Gw4i3wYML8Y,736
3
+ diffpy/fourigui/fourigui.py,sha256=UkS8hrO2bgUo6HbNYELuWAf0XZO7aeNvrep4b0TR1e0,21217
4
+ diffpy/fourigui/version.py,sha256=kPQ5HapKeTQzwpb53v984CBIDzBOI1Z0jGQIfAy1jys,843
5
+ diffpy.fourigui-0.1.0rc0.dist-info/AUTHORS.rst,sha256=3booMNorzYNNI3cqkYs-sWbimnSegFURpo7iSX6oKiU,184
6
+ diffpy.fourigui-0.1.0rc0.dist-info/LICENSE.rst,sha256=FFZAG9Hh02sJbGQEu06NuMxdgx_i1GuezTT4cKTyLkc,1568
7
+ diffpy.fourigui-0.1.0rc0.dist-info/METADATA,sha256=qbvUc8Lll6ilgTwTMtiUxvK3EvFbgtmbpGPxxTy4tU4,6583
8
+ diffpy.fourigui-0.1.0rc0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
9
+ diffpy.fourigui-0.1.0rc0.dist-info/top_level.txt,sha256=34qLZyi9mxikpHepVDTMHrrNbe8VoOuz3ShYeiWA7KA,7
10
+ diffpy.fourigui-0.1.0rc0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ diffpy