diff --git a/.flake8 b/.flake8 index 2d2cb16..7b2865c 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,5 @@ +# As of now, flake8 does not natively support configuration via pyproject.toml +# https://github.com/microsoft/vscode-flake8/issues/135 [flake8] exclude = .git, @@ -5,7 +7,7 @@ exclude = build, dist, doc/source/conf.py -max-line-length = 115 +max-line-length = 79 # Ignore some style 'errors' produced while formatting by 'black' # https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#labels-why-pycodestyle-warnings extend-ignore = E203 diff --git a/.gitignore b/.gitignore index a25212e..d418364 100644 --- a/.gitignore +++ b/.gitignore @@ -90,10 +90,3 @@ target/ # Ipython Notebook .ipynb_checkpoints - -# version information -setup.cfg -/src/diffpy/*/version.cfg - -# Rever -rever/ diff --git a/.isort.cfg b/.isort.cfg index e0926f4..86f162b 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,4 +1,5 @@ [settings] -line_length = 115 +# Keep import statement below line_length character limit +line_length = 79 multi_line_output = 3 include_trailing_comma = True diff --git a/AUTHORS.rst b/AUTHORS.rst index 80a0c80..a9754aa 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,7 +1,7 @@ Authors ======= -Billinge Group and community contributors. +Billinge Group members and community contributors Contributors ------------ diff --git a/LICENSE.rst b/LICENSE.rst index 95a04ac..d9ccaf1 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -1,7 +1,6 @@ BSD 3-Clause License -Copyright (c) 2024, The Trustees of Columbia University -in the City of New York. +Copyright (c) 2022-2025, The Trustees of Columbia University in the City of New York. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index 9d87dee..a6cbda8 100644 --- a/README.rst +++ b/README.rst @@ -35,7 +35,7 @@ .. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue :target: https://github.com/diffpy/diffpy.fourigui/issues -Tool for visualizing 3D diffraction and PDF Images. +Tool for visualizing 3D diffraction and PDF images. Diffpy.fourigui is a tool to visualize and process 3D data sets written with the Python programming language. Diffpy.fourigui always displays one slice perpendicular to one axis and allows scrolling through the 3D data set along diff --git a/doc/source/conf.py b/doc/source/conf.py index 41f1e13..597b0e2 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -223,7 +223,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ("index", "diffpy.fourigui.tex", "diffpy.fourigui Documentation", ab_authors, "manual"), + ( + "index", + "diffpy.fourigui.tex", + "diffpy.fourigui Documentation", + ab_authors, + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -251,7 +257,15 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [("index", "diffpy.fourigui", "diffpy.fourigui Documentation", ab_authors, 1)] +man_pages = [ + ( + "index", + "diffpy.fourigui", + "diffpy.fourigui Documentation", + ab_authors, + 1, + ) +] # If true, show URL addresses after external links. # man_show_urls = False diff --git a/doc/source/index.rst b/doc/source/index.rst index a9902ee..6f6164e 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -4,7 +4,7 @@ .. |title| replace:: diffpy.fourigui documentation -diffpy.fourigui - Tool for visualizing 3D diffraction and PDF Images.. +diffpy.fourigui - Tool for visualizing 3D diffraction and PDF images. | Software version |release|. | Last updated |today|. @@ -37,7 +37,6 @@ Table of contents release Package API - ======= Indices ======= diff --git a/doc/source/license.rst b/doc/source/license.rst index 9ae52a9..c976190 100644 --- a/doc/source/license.rst +++ b/doc/source/license.rst @@ -9,8 +9,7 @@ OPEN SOURCE LICENSE AGREEMENT ============================= BSD 3-Clause License -Copyright (c) 2024, The Trustees of Columbia University in -the City of New York. +Copyright (c) 2022-2025, The Trustees of Columbia University in the City of New York. All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/news/copyright-styling.rst b/news/copyright-styling.rst new file mode 100644 index 0000000..539dab0 --- /dev/null +++ b/news/copyright-styling.rst @@ -0,0 +1,24 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Update copyright to 2022-2025. +* Format code to comply with group standards. + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index cf89087..9b70ac6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,13 +6,13 @@ build-backend = "setuptools.build_meta" name = "diffpy.fourigui" dynamic=['version', 'dependencies'] authors = [ - { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, + { name="Simon Billinge", email="sb2896@columbia.edu" }, ] maintainers = [ - { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, + { name="Simon Billinge", email="sb2896@columbia.edu" }, ] -description = "Tool for visualizing 3D diffraction and PDF Images." -keywords = ['diffraction', 'pdf', 'pair distribution function', 'gui'] +description = "Tool for visualizing 3D diffraction and PDF images." +keywords = ['diffraction', 'PDF', 'pair distribution function', 'gui'] readme = "README.rst" requires-python = ">=3.11, <3.14" classifiers = [ @@ -60,7 +60,7 @@ ignore-words = ".codespell/ignore_words.txt" skip = "*.cif,*.dat" [tool.black] -line-length = 115 +line-length = 79 include = '\.pyi?$' exclude = ''' /( diff --git a/src/diffpy/__init__.py b/src/diffpy/__init__.py index 5d0f50f..1df0753 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2022-2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. diff --git a/src/diffpy/fourigui/__init__.py b/src/diffpy/fourigui/__init__.py index 98bbd12..b0c11f4 100644 --- a/src/diffpy/fourigui/__init__.py +++ b/src/diffpy/fourigui/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2022-2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. @@ -12,7 +12,7 @@ # See LICENSE.rst for license information. # ############################################################################## -"""Tool for visualizing 3D diffraction and PDF Images.""" +"""Tool for visualizing 3D diffraction and PDF images.""" # package version from diffpy.fourigui.version import __version__ diff --git a/src/diffpy/fourigui/fourigui.py b/src/diffpy/fourigui/fourigui.py index c59b77e..f23e6b4 100755 --- a/src/diffpy/fourigui/fourigui.py +++ b/src/diffpy/fourigui/fourigui.py @@ -5,7 +5,10 @@ import matplotlib import numpy as np from matplotlib import pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk +from matplotlib.backends.backend_tkagg import ( + FigureCanvasTkAgg, + NavigationToolbar2Tk, +) matplotlib.use("tkagg") @@ -24,15 +27,25 @@ def initUI(self): """Initialize the GUI for fourigui.""" self.loaded = False # denotes whether a dataset is loaded - self.transformed = False # denotes whether dataset is Fourier transformed - self.cutted = False # denotes whether cutoff frequencies are applied to dataset - self.transcutted = False # denotes whether cutoff frequencies are applied and Fourier transformed + self.transformed = ( + False # denotes whether dataset is Fourier transformed + ) + self.cutted = ( + False # denotes whether cutoff frequencies are applied to dataset + ) + self.transcutted = ( + False # denotes whether cutoff frequencies are applied + ) + # and Fourier transformed self.master.title("FouriGUI") self.pack(fill=tk.BOTH, expand=True) print("\nNew Session started ...") - print("Enjoy exploring the beautiful reconstructions in real and in reciprocal space!") + print( + "Enjoy exploring the beautiful reconstructions in real and in " + "reciprocal space!" + ) # 4 frames: # frame 00: all buttons @@ -54,7 +67,9 @@ def initUI(self): self.filename_entry.grid(row=0, column=1, columnspan=3) self.filename_entry.insert(0, "/path/data.h5") - loadbutton = Button(frame00, text="load", command=lambda: self.load_cube()) + loadbutton = Button( + frame00, text="load", command=lambda: self.load_cube() + ) loadbutton.grid(row=0, column=4) # row 1: change axis area @@ -125,7 +140,9 @@ def initUI(self): self.colorbarmax.grid(row=3, column=3) self.colorbarmin = tk.Entry(frame00, width=7) self.colorbarmin.grid(row=4, column=3) - set_range = Button(frame00, text="set range", command=lambda: self.colorrange_upd()) + set_range = Button( + frame00, text="set range", command=lambda: self.colorrange_upd() + ) set_range.grid(row=2, column=4) toglobalmax = Button( frame00, @@ -151,13 +168,13 @@ def initUI(self): anilabel.grid(row=7, column=3, columnspan=2, sticky=tk.W) self.anientry = tk.Entry(frame00, width=7) self.anientry.grid(row=8, column=3) - anibutton = Button(frame00, text="animation", command=lambda: self.animation()) + anibutton = Button( + frame00, text="animation", command=lambda: self.animation() + ) anibutton.grid(row=8, column=4) # row 10-12 Fourier transformation - separator = tk.Label( - frame00, text=" " - ) # __________________________________________________________________") + separator = tk.Label(frame00, text=" ") separator.grid(row=9, column=0, columnspan=5) cutofflabel = tk.Label(frame00, text="cutoff frequency") cutofflabel.grid(row=10, column=2, columnspan=2) @@ -170,7 +187,9 @@ def initUI(self): self.qmaxentry = tk.Entry(frame00, width=7) self.qmaxentry.grid(row=12, column=3) self.cutoff = tk.IntVar() - newcutoffbutton = Button(frame00, text="new cutoff", command=lambda: self.newcutoff()) + newcutoffbutton = Button( + frame00, text="new cutoff", command=lambda: self.newcutoff() + ) newcutoffbutton.grid(row=10, column=4) cutoffon = tk.Radiobutton( frame00, @@ -225,13 +244,19 @@ def initUI(self): label="slider", orient=tk.HORIZONTAL, length=WIDTH // 2, # resolution=-1, - command=lambda x: self.multiple_funcs(self.plot_plane(), self.intensity_upd_local()), + command=lambda x: self.multiple_funcs( + self.plot_plane(), self.intensity_upd_local() + ), ) # command=lambda p: self.plot_plane()) - self.slider.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W) + self.slider.grid( + row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W + ) self.frame01_plotcell = tk.Frame(self.frame01) - self.frame01_plotcell.grid(row=1, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W) + self.frame01_plotcell.grid( + row=1, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W + ) self.frame01_toolbar = tk.Frame(self.frame01) self.frame01_toolbar.grid(row=2, column=0) @@ -239,11 +264,15 @@ def initUI(self): # 10 # # frame 10, lower left frame10 = tk.Frame(self) - frame10.place(x=5, y=HEIGHT - 30) # , height=HEIGHT//2, width=WIDTH//2) + frame10.place( + x=5, y=HEIGHT - 30 + ) # , height=HEIGHT//2, width=WIDTH//2) quit = Button( frame10, text="exit", - command=lambda: self.multiple_funcs(print("Session ended...\n", self.quit())), + command=lambda: self.multiple_funcs( + print("Session ended...\n", self.quit()) + ), ) quit.pack(side=tk.TOP) @@ -251,7 +280,9 @@ def initUI(self): # frame 00, lower right # no functionality frame11 = tk.Frame(self) - frame11.place(x=WIDTH // 2, y=HEIGHT // 2) # , height=HEIGHT//2, width=WIDTH//2) + frame11.place( + x=WIDTH // 2, y=HEIGHT // 2 + ) # , height=HEIGHT//2, width=WIDTH//2) def load_cube(self): """Loads 3D array in h5py file format from the filename input panel 3D @@ -285,9 +316,13 @@ def load_cube(self): label="slider", orient=tk.HORIZONTAL, length=WIDTH // 2, # resolution=-1, - command=lambda x: self.multiple_funcs(self.plot_plane(), self.intensity_upd_local()), + command=lambda x: self.multiple_funcs( + self.plot_plane(), self.intensity_upd_local() + ), + ) + self.slider.grid( + row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W ) - self.slider.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W) if not self.loaded: fig, ax = plt.subplots(figsize=(4.95, 4.95)) @@ -307,13 +342,17 @@ def load_cube(self): ax.set_xlabel("pixel") ax.set_ylabel("pixel") self.canvas = FigureCanvasTkAgg(fig, master=self.frame01_plotcell) - self.toolbar = NavigationToolbar2Tk(self.canvas, self.frame01_toolbar) + self.toolbar = NavigationToolbar2Tk( + self.canvas, self.frame01_toolbar + ) self.toolbar.pack(side=tk.LEFT) # self.toolbar.children['!button6'].pack_forget() # self.toolbar.children['!button7'].pack_forget() self.toolbar.update() self.canvas.draw() - self.canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=1) + self.canvas.get_tk_widget().pack( + side=tk.LEFT, fill=tk.BOTH, expand=1 + ) self.loaded = True else: self.plot_plane() @@ -367,18 +406,30 @@ def intensity_upd_local(self): elif self.axis.get() == 2: plane = self.cube[:, :, self.plane_num.get()] nan_ratio = np.count_nonzero(np.isnan(plane)) / plane.size - self.localmax["text"] = f"{np.format_float_scientific(np.nanmax(plane), 1)}" - self.localmin["text"] = f"{np.format_float_scientific(np.nanmin(plane), 1)}" - self.localsum["text"] = f"{np.format_float_scientific(np.nansum(plane), 1)}" + self.localmax["text"] = ( + f"{np.format_float_scientific(np.nanmax(plane), 1)}" + ) + self.localmin["text"] = ( + f"{np.format_float_scientific(np.nanmin(plane), 1)}" + ) + self.localsum["text"] = ( + f"{np.format_float_scientific(np.nansum(plane), 1)}" + ) self.localnanratio["text"] = f"{round(nan_ratio, 2)}" def intensity_upd_global(self): """Load global intensity minimum, maximum and sum of 3D array.""" self.intensity_upd_local() nan_ratio = np.count_nonzero(np.isnan(self.cube)) / self.cube.size - self.globalmax["text"] = f"{np.format_float_scientific(np.nanmax(self.cube), 1)}" - self.globalmin["text"] = f"{np.format_float_scientific(np.nanmin(self.cube), 1)}" - self.globalsum["text"] = f"{np.format_float_scientific(np.nansum(self.cube), 1)}" + self.globalmax["text"] = ( + f"{np.format_float_scientific(np.nanmax(self.cube), 1)}" + ) + self.globalmin["text"] = ( + f"{np.format_float_scientific(np.nanmin(self.cube), 1)}" + ) + self.globalsum["text"] = ( + f"{np.format_float_scientific(np.nansum(self.cube), 1)}" + ) self.globalnanratio["text"] = "{}".format(round(nan_ratio, 2)) def fft(self): @@ -454,8 +505,8 @@ def ifft(self): def applycutoff(self): """Shape the reciprocal-space array. - reassign all voxels with distance smaller than qmin and greater than qmax - to np.nan. + reassign all voxels with distance smaller than qmin and greater than + qmax to np.nan. parameters: ----------- @@ -475,9 +526,17 @@ def applycutoff(self): # convert qmax to pixels r2_inner = qmin**2 r2_outer = qmax**2 - i, j, k = np.meshgrid(np.arange(xdim), np.arange(ydim), np.arange(zdim)) - r2 = (i - xdim // 2) ** 2 + (j - ydim // 2) ** 2 + (k - zdim // 2) ** 2 - mask = (r2 < r2_inner) | (r2 > r2_outer) # True if voxel is out of range + i, j, k = np.meshgrid( + np.arange(xdim), np.arange(ydim), np.arange(zdim) + ) + r2 = ( + (i - xdim // 2) ** 2 + + (j - ydim // 2) ** 2 + + (k - zdim // 2) ** 2 + ) + mask = (r2 < r2_inner) | ( + r2 > r2_outer + ) # True if voxel is out of range sphere[mask] = np.nan # therefore set to np.nan if out of range if self.space.get(): @@ -547,7 +606,10 @@ def animation(self): else: anispeed = self.anientry.get() except ValueError: - print("Oops... animation speed must be an integer > 0 or empty string.") + print( + "Oops... animation speed must be an integer > 0 " + "or empty string." + ) n = self.plane_num.get() - 1 while n is not self.plane_num.get(): self.slider.after(anispeed, self.plot_next_plane()) diff --git a/src/diffpy/fourigui/version.py b/src/diffpy/fourigui/version.py index a21a124..f52dcaa 100644 --- a/src/diffpy/fourigui/version.py +++ b/src/diffpy/fourigui/version.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ############################################################################## # -# (c) 2024 The Trustees of Columbia University in the City of New York. +# (c) 2022-2025 The Trustees of Columbia University in the City of New York. # All rights reserved. # # File coded by: Billinge Group members and community contributors. diff --git a/tests/integration_test.py b/tests/integration_test.py index f343d5f..4a3d30f 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -13,11 +13,19 @@ def setUp(self): # set up test data self.test_sofq = h5py.File("tests/testdata/sofq.h5")["data"] - self.test_sofq_cut_10to40px = h5py.File("tests/testdata/sofq_cut_10to40px.h5")["data"] - self.test_sofq_cut_15to35px = h5py.File("tests/testdata/sofq_cut_15to35px.h5")["data"] + self.test_sofq_cut_10to40px = h5py.File( + "tests/testdata/sofq_cut_10to40px.h5" + )["data"] + self.test_sofq_cut_15to35px = h5py.File( + "tests/testdata/sofq_cut_15to35px.h5" + )["data"] self.test_gofr = h5py.File("tests/testdata/gofr.h5")["data"] - self.test_gofr_cut_10to40px = h5py.File("tests/testdata/gofr_from_sofq_cut_10to40px.h5")["data"] - self.test_gofr_cut_15to35px = h5py.File("tests/testdata/gofr_from_sofq_cut_15to35px.h5")["data"] + self.test_gofr_cut_10to40px = h5py.File( + "tests/testdata/gofr_from_sofq_cut_10to40px.h5" + )["data"] + self.test_gofr_cut_15to35px = h5py.File( + "tests/testdata/gofr_from_sofq_cut_15to35px.h5" + )["data"] def test_load_cube_testdataset1(self): # given @@ -34,26 +42,40 @@ def test_load_cube_testdataset1(self): def test_load_cube_testdataset2(self): # given self.test_gui.filename_entry.delete(0, "end") - self.test_gui.filename_entry.insert(0, "tests/testdata/sofq_cut_10to40px.h5") + self.test_gui.filename_entry.insert( + 0, "tests/testdata/sofq_cut_10to40px.h5" + ) # when self.test_gui.load_cube() result = self.test_gui.cube # then - self.assertTrue(np.allclose(np.nan_to_num(result), np.nan_to_num(self.test_sofq_cut_10to40px))) + self.assertTrue( + np.allclose( + np.nan_to_num(result), + np.nan_to_num(self.test_sofq_cut_10to40px), + ) + ) def test_load_cube_testdataset3(self): # given self.test_gui.filename_entry.delete(0, "end") - self.test_gui.filename_entry.insert(0, "tests/testdata/sofq_cut_15to35px.h5") + self.test_gui.filename_entry.insert( + 0, "tests/testdata/sofq_cut_15to35px.h5" + ) # when self.test_gui.load_cube() result = self.test_gui.cube # then - self.assertTrue(np.allclose(np.nan_to_num(result), np.nan_to_num(self.test_sofq_cut_15to35px))) + self.assertTrue( + np.allclose( + np.nan_to_num(result), + np.nan_to_num(self.test_sofq_cut_15to35px), + ) + ) def test_fft_testdataset1(self): # given diff --git a/tests/test_fourigui.py b/tests/test_fourigui.py index 52a556b..f3f60d9 100644 --- a/tests/test_fourigui.py +++ b/tests/test_fourigui.py @@ -60,7 +60,9 @@ def test_fft_000(self): self.test_gui.fft() # then - self.assertTrue(self.test_gui.transformed and not self.test_gui.transcutted) + self.assertTrue( + self.test_gui.transformed and not self.test_gui.transcutted + ) def test_fft_010(self): # given @@ -76,7 +78,9 @@ def test_fft_010(self): self.test_gui.fft() # then - self.assertTrue(not self.test_gui.transformed and self.test_gui.transcutted) + self.assertTrue( + not self.test_gui.transformed and self.test_gui.transcutted + ) # self.assertTrue(self.test_gui.cutted) def test_fft_001(self): @@ -94,7 +98,9 @@ def test_fft_001(self): self.test_gui.fft() # then - self.assertTrue(self.test_gui.transformed and self.test_gui.transcutted) + self.assertTrue( + self.test_gui.transformed and self.test_gui.transcutted + ) def test_fft_011(self): # given @@ -111,7 +117,9 @@ def test_fft_011(self): self.test_gui.fft() # then - self.assertTrue(not self.test_gui.transformed and self.test_gui.transcutted) + self.assertTrue( + not self.test_gui.transformed and self.test_gui.transcutted + ) def test_fft_101(self): # given @@ -128,7 +136,9 @@ def test_fft_101(self): self.test_gui.fft() # then - self.assertTrue(self.test_gui.transformed and self.test_gui.transcutted) + self.assertTrue( + self.test_gui.transformed and self.test_gui.transcutted + ) def test_fft_111(self): # given @@ -145,7 +155,9 @@ def test_fft_111(self): self.test_gui.fft() # then - self.assertTrue(self.test_gui.transformed and self.test_gui.transcutted) + self.assertTrue( + self.test_gui.transformed and self.test_gui.transcutted + ) def test_applycutoff(mocker): @@ -155,7 +167,9 @@ def test_applycutoff(mocker): # pixels as NaN's mocker.patch.object(fg.qminentry, "get", return_value=1.0) mocker.patch.object(fg.qmaxentry, "get", return_value=2.0) - mocker.patch.object(fg, "plot_plane") # we don't want it to plot anything so intercept + mocker.patch.object( + fg, "plot_plane" + ) # we don't want it to plot anything so intercept fg.cutted = False fg.cube = np.ones((5, 5, 5)) expected_ones = np.ones((5, 5, 5)) @@ -213,7 +227,8 @@ def test_applycutoff(mocker): mocker.patch.object(fg.qmaxentry, "get", return_value=2) mocker.patch.object( fg, "fft" - ) # we don't want it to do the fft so intercept. Should be tested separately (fixme). + ) # we don't want it to do the fft so intercept. + # Should be tested separately (fixme). fg.cutted = False fg.cube_reci = np.ones((5, 5, 5)) fg.cube = np.ones((5, 5, 5))