[
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2023 csmailis\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (\"NPYViewer\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "NPYViewer.py",
    "content": "#!/usr/bin/env python3\nimport os.path\nimport sys\nfrom PyQt5.QtWidgets import *\nfrom PyQt5.QtGui import QIcon\nfrom PyQt5.QtCore import *\nfrom pathlib import Path\nimport numpy as np\nfrom numpy import asarray\nfrom numpy import savetxt\nimport pandas as pd\nimport csv\nfrom matplotlib import pyplot as plt\nfrom mpl_toolkits.mplot3d import Axes3D\nimport os\nfrom scipy.io import savemat\nimport networkx as nx\n\n\nversion=\"1.28\"\n\ndef isint(s):\n    try:\n        print(int(s))\n        return True\n    except ValueError:\n        return False\n\n\ndef isfloat(s):\n    try:\n        print(float(s))\n        return True\n    except ValueError:\n        return False\n\n\n\ndef openNPY_CLI_noGUI (filename):\n\n        if \".npy\" in filename:\n            data = np.load(filename, allow_pickle=True)\n        else:\n            data = np.array(pd.read_csv(filename).values.tolist())\n\n        npyfile = NPYfile(data, filename)\n        print(npyfile)\n        print(data)\n\n\nclass NPYfile():\n    def __init__(self, data, filename):\n        self.data = data\n        self.filename = filename\n\n    def __str__(self):\n        if hasattr(self.data, 'dtype'):\n            return \"Filename = \" + str(self.filename) + \" \\nDtype = \" + str(self.data.dtype) + \"\\nShape = \" + str(\n                self.data.shape)\n        else:\n            return \"Filename = \" + str(self.filename) + \" \\nDtype = \" + \"\\nShape = \" + str(self.data.shape)\n\n\n\n            \nclass MainApp(QMainWindow):\n\n    def __init__(self):\n        super().__init__()\n        self.left = 0\n        self.top = 0\n        self.width = 800\n        self.height = 640\n        # np.save('/home/user/tst.npy', np.array([255.0,50.0,10.0,5.0]))\n        self.npyfile = None\n        self.initUI()\n\n    def saveAs(self):\n        home = str(Path.home())\n        if self.npyfile is not None:\n            home = os.path.dirname(self.npyfile.filename)\n        path = QFileDialog.getSaveFileName(\n            self, 'Save File', home, 'NPY (*.npy);;CSV(*.csv);;MAT(*.mat)')[0]\n        # path = QFileDialog.getSaveFileName(\n        #    self, 'Save File', home, 'CSV(*.csv)')[0]\n        if path != \"\" and \".csv\" in path:\n            with open((path.replace(\".csv\", \"\") + \".csv\"), 'w') as stream:\n                writer = csv.writer(stream)\n                for row in range(self.tableWidget.rowCount()):\n                    rowdata = []\n                    for column in range(self.tableWidget.columnCount()):\n                        item = self.tableWidget.item(row, column)\n                        if item is not None:\n                            rowdata.append(item.text())\n\n                        else:\n                            rowdata.append('')\n                    writer.writerow(rowdata)\n        else:\n            OutMatrix = []\n            for row in range(self.tableWidget.rowCount()):\n                rowdata = []\n                for column in range(self.tableWidget.columnCount()):\n                    item = self.tableWidget.item(row, column)\n                    if item is not None:\n                        #if item.text().isnumeric():\n                        rowdata.append(float(item.text()))\n\n                if rowdata != []:\n                    OutMatrix.append(rowdata)\n            OutMatrix = np.array(OutMatrix)\n            if \".csv\" in path:\n                np.save(path, np.array(OutMatrix))\n            if \".mat\" in path:\n                mdic = {\"ans\": OutMatrix}\n                print(OutMatrix)\n                savemat(path, mdic)\n\n    def openNPY(self):\n        if os.path.exists(\"lastpath.npy\"):\n            home = str(np.load(\"lastpath.npy\"))\n            print(home)\n        else:\n            home = str(Path.home())\n        if self.npyfile is not None:\n            home = os.path.dirname(self.npyfile.filename)\n        filename = QFileDialog.getOpenFileName(self, 'Open .NPY file', home, \".NPY files (*.npy);;.CSV files (*.csv)\")[\n            0]\n        if filename != \"\":\n            if \".npy\" in filename:\n                data = np.load(filename, allow_pickle=True)\n            else:\n                data = np.array(pd.read_csv(filename).values.tolist())\n\n            npyfile = NPYfile(data, filename)\n            print(npyfile)\n            self.setWindowTitle(\"NPYViewer v.\" +version+\": \"+ npyfile.filename)\n            self.infoLb.setText(\"NPY Properties:\\n\" + str(npyfile))\n            self.tableWidget.clear()\n\n            # initialise table\n            self.tableWidget.setRowCount(data.shape[0])\n            dtype_dim = len(npyfile.data.dtype)  # 0, if plain dtype, 1 or bigger if compound dtype\n            if data.ndim > 1:\n                self.tableWidget.setColumnCount(data.shape[1])\n            elif dtype_dim > 0:\n                self.tableWidget.setColumnCount(dtype_dim)\n            else:\n                self.tableWidget.setColumnCount(1)\n\n            # fill data\n            if data.ndim > 1:\n                for i, value1 in enumerate(npyfile.data):  # loop over items in first column\n                    for j, value in enumerate(value1):\n                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(value)))\n            elif dtype_dim > 0:\n                for i, value1 in enumerate(npyfile.data):\n                    for j, col_name in enumerate(npyfile.data.dtype.names):\n                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(value1[col_name])))\n            else:\n                for i, value1 in enumerate(npyfile.data):  # loop over items in first column\n                    self.tableWidget.setItem(i, 0, QTableWidgetItem(str(value1)))\n\n            self.npyfile = npyfile\n            path = os.path.dirname(filename)\n            np.save(\"lastpath.npy\", path)\n\n\n    def openNPY_CLI(self,filename):\n\n            if \".npy\" in filename:\n                data = np.load(filename, allow_pickle=True)\n            else:\n                data = np.array(pd.read_csv(filename).values.tolist())\n\n            npyfile = NPYfile(data, filename)\n            print(npyfile)\n            self.setWindowTitle(\"NPYViewer v.\" +version+\": \" +npyfile.filename)\n            self.infoLb.setText(\"NPY Properties:\\n\" + str(npyfile))\n            self.tableWidget.clear()\n\n            # initialise table\n            self.tableWidget.setRowCount(data.shape[0])\n            dtype_dim = len(npyfile.data.dtype)  # 0, if plain dtype, 1 or bigger if compound dtype\n            if data.ndim > 1:\n                self.tableWidget.setColumnCount(data.shape[1])\n            elif dtype_dim > 0:\n                self.tableWidget.setColumnCount(dtype_dim)\n            else:\n                self.tableWidget.setColumnCount(1)\n\n            # fill data\n            if data.ndim > 1:\n                for i, value1 in enumerate(npyfile.data):  # loop over items in first column\n                    for j, value in enumerate(value1):\n                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(value)))\n            elif dtype_dim > 0:\n                for i, value1 in enumerate(npyfile.data):\n                    for j, col_name in enumerate(npyfile.data.dtype.names):\n                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(value1[col_name])))\n            else:\n                for i, value1 in enumerate(npyfile.data):  # loop over items in first column\n                    self.tableWidget.setItem(i, 0, QTableWidgetItem(str(value1)))\n\n            self.npyfile = npyfile\n            path = os.path.dirname(filename)\n            np.save(\"lastpath.npy\", path)\n\n\n            \n    def createMenu(self):\n\n        exitAct = QAction(QIcon('exit.png'), '&Exit', self)\n        exitAct.setShortcut('Ctrl+Q')\n        exitAct.setStatusTip('Exit application')\n        exitAct.triggered.connect(qApp.quit)\n\n        openAct = QAction(QIcon('Open.png'), '&Open', self)\n        openAct.setShortcut('Ctrl+O')\n        openAct.setStatusTip('Open .NPY file')\n        openAct.triggered.connect(self.openNPY)\n        self.statusBar()\n\n        saveAct = QAction(QIcon('Save.png'), '&Save As', self)\n        saveAct.setShortcut('Ctrl+S')\n        saveAct.setStatusTip('Save As')\n        saveAct.triggered.connect(self.saveAs)\n        self.statusBar()\n\n        grayscalevVewAct = QAction(QIcon(None), '&View as Grayscale Image', self)\n        grayscalevVewAct.setShortcut('Ctrl+V')\n        grayscalevVewAct.setStatusTip('View as Grayscale')\n        grayscalevVewAct.triggered.connect(self.grayscaleView)\n        self.statusBar()\n\n        View3dAct = QAction(QIcon(None), 'View &3D Point Cloud', self)\n        View3dAct.setShortcut('Ctrl+3')\n        View3dAct.setStatusTip('View 3D Point Cloud')\n        View3dAct.triggered.connect(self.View3dPoints)\n        self.statusBar()\n\n        View3dImgAct = QAction(QIcon(None), 'View as &HeightMap', self)\n        View3dImgAct.setShortcut('Ctrl+H')\n        View3dImgAct.setStatusTip('View as HeightMap')\n        View3dImgAct.triggered.connect(self.ViewImageHeightMap)\n        self.statusBar()\n\n        ViewTimeSeriesAct= QAction(QIcon(None), 'View as &Time Series', self)\n        ViewTimeSeriesAct.setShortcut('Ctrl+S')\n        ViewTimeSeriesAct.setStatusTip('View as TimeSeries')\n        ViewTimeSeriesAct.triggered.connect(self.ViewTimeseries)\n        self.statusBar()\n\n        ViewGraphSeriesAct= QAction(QIcon(None), 'View as Directional &Graph', self)\n        ViewGraphSeriesAct.setShortcut('Ctrl+G')\n        ViewGraphSeriesAct.setStatusTip('View as Graph')\n        ViewGraphSeriesAct.triggered.connect(self.ViewGraphSeriesAct)\n        self.statusBar()\n        \n        menubar = self.menuBar()\n        fileMenu = menubar.addMenu('&Functionalities')\n        fileMenu.addAction(openAct)\n        fileMenu.addAction(saveAct)\n        fileMenu.addAction(grayscalevVewAct)\n        fileMenu.addAction(View3dAct)\n        fileMenu.addAction(View3dImgAct)\n        fileMenu.addAction(ViewTimeSeriesAct)\n        fileMenu.addAction(ViewGraphSeriesAct)\n        fileMenu.addAction(exitAct)\n\n    def grayscaleView(self):\n        OutMatrix = []\n        for row in range(self.tableWidget.rowCount()):\n            rowdata = []\n            for column in range(self.tableWidget.columnCount()):\n                item = self.tableWidget.item(row, column)\n                # print(item.text())\n                if item is not None:\n                    rowdata.append(np.float32(item.text()))\n\n            if len(rowdata) > 0 and rowdata != None:\n                OutMatrix.append(rowdata)\n\n        OutMatrix = np.array(OutMatrix)\n        plt.imshow(OutMatrix, cmap='gray')\n        plt.show()\n        return\n\n    def ViewGraphSeriesAct(self):\n        OutMatrix = []\n        for row in range(self.tableWidget.rowCount()):\n            rowdata = []\n            for column in range(self.tableWidget.columnCount()):\n                item = self.tableWidget.item(row, column)\n                # print(item.text())\n                if item is not None:\n                    rowdata.append(np.float32(item.text()))\n\n            if len(rowdata) > 0 and rowdata != None:\n                OutMatrix.append(rowdata)\n\n        OutMatrix = np.array(OutMatrix)\n\n\n        G = nx.DiGraph(OutMatrix)\n\n        # Set the position of the nodes in the graph\n        pos = nx.spring_layout(G)\n\n        # Draw the graph with labels and edges\n        labels = {node: str(int(node)+1) for node in G.nodes()}\n\n        nx.draw_networkx_labels(G, pos,labels=labels)\n        nx.draw_networkx_edges(G, pos)\n\n        nx.draw_networkx_edge_labels(G, pos, edge_labels={(i, j): OutMatrix[i][j] for i, j in G.edges()})\n\n        # Draw the nodes with the same color\n        nx.draw_networkx_nodes(G, pos, node_color='b')\n\n\n        # Set the title and show the plot\n        plt.title(\"Directional Graph\")\n        plt.show()\n\n    def ViewImageHeightMap(self):\n        OutMatrix = []\n        for row in range(self.tableWidget.rowCount()):\n            rowdata = []\n            for column in range(self.tableWidget.columnCount()):\n                item = self.tableWidget.item(row, column)\n\n                if item is not None:\n                    if item.text():\n                        rowdata.append(np.float32(item.text()))\n            if len(rowdata) > 0 and rowdata != None:\n                OutMatrix.append(rowdata)\n        # print(OutMatrix)\n        HeightMap = []\n        for x, row in enumerate(OutMatrix):\n            for y, val in enumerate(row):\n                HeightMap.append([x, y, val])\n        OutMatrix = np.array(HeightMap)\n\n        fig = plt.figure()\n        ax = fig.add_subplot(111, projection='3d')\n\n        xs = OutMatrix[:, 0]\n\n        ys = OutMatrix[:, 1]\n        zs = OutMatrix[:, 2]\n        ax.plot_trisurf(xs, ys, zs,\n                        cmap='Greys_r', edgecolor='none');\n\n        ax.set_xlabel('X Axis')\n        ax.set_ylabel('Y Axis')\n        ax.set_zlabel('Z Axis')\n\n        plt.show()\n        return\n\n    def View3dPoints(self):\n        OutMatrix = []\n        for row in range(self.tableWidget.rowCount()):\n            rowdata = []\n            for column in range(self.tableWidget.columnCount()):\n                item = self.tableWidget.item(row, column)\n\n                if item is not None:\n                    if item.text():\n                        rowdata.append(np.float32(item.text()))\n            if len(rowdata) > 0 and rowdata != None:\n                OutMatrix.append(rowdata)\n        # print(OutMatrix)\n        OutMatrix = np.array(OutMatrix)\n\n        fig = plt.figure()\n        ax = fig.add_subplot(111, projection='3d')\n\n        xs = OutMatrix[:, 0]\n\n        ys = OutMatrix[:, 1]\n        zs = OutMatrix[:, 2]\n        ax.scatter(xs, ys, zs, c='r', marker='o')\n\n        ax.set_xlabel('X Axis')\n        ax.set_ylabel('Y Axis')\n        ax.set_zlabel('Z Axis')\n\n        plt.show()\n        return\n    \n    def ViewTimeseries(self):\n        OutMatrix = []\n        for row in range(self.tableWidget.rowCount()):\n            rowdata = []\n            for column in range(self.tableWidget.columnCount()):\n                item = self.tableWidget.item(row, column)\n\n                if item is not None:\n                    if item.text():\n                        rowdata.append(np.float32(item.text()))\n            if len(rowdata) > 0 and rowdata != None:\n                OutMatrix.append(rowdata)\n        # print(OutMatrix)\n        #OutMatrix = np.array(OutMatrix)\n\n        fig = plt.figure()\n        ax = fig.add_subplot(111)\n        indices=range(0,len(OutMatrix))\n        plt.plot(indices, OutMatrix, label='values', linewidth=3)\n\n        ax.set_xlabel('Time Unit')\n        ax.set_ylabel('Values')\n\n        plt.show()\n        return\n\n    \n    def initUI(self):\n        self.createMenu()\n\n        self.infoLb = QLabel(\"NPY Properties:\")\n        self.tableWidget = QTableWidget()\n        self.tableWidget.setRowCount(100)\n        self.tableWidget.setColumnCount(100)\n\n        # table selection change\n        # self.tableWidget.doubleClicked.connect(self.on_click)\n\n        self.setGeometry(0, 0, 800, 600)\n        self.setWindowTitle(\"NPYViewer v.\"+version)\n\n        self.widget = QWidget(self)\n        layout = QGridLayout()\n        layout.addWidget(self.infoLb)\n        layout.addWidget(self.tableWidget)\n        self.widget.setLayout(layout)\n        self.setCentralWidget(self.widget)\n        # self.tableWidget.setItesetTextAlignmentmDelegate(AlignDelegate())\n\n        self.layout = QVBoxLayout()\n\n        self.setLayout(self.layout)\n        self.setWindowIcon(QIcon('npyviewer_128x128.png'))\n\n        self.show()\n\n\ndef main():\n    if \"-noGUI\" not in sys.argv:\n        app = QApplication(sys.argv)\n        ex = MainApp()\n        if len(sys.argv) ==2:\n            ex.openNPY_CLI(sys.argv[1])\n        sys.exit(app.exec_())\n    if len(sys.argv) ==3:\n        if sys.argv[2]==\"-noGUI\":\n            print(\"GUI not displayed\")\n            openNPY_CLI_noGUI(sys.argv[1])\n\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "README.md",
    "content": "\n<p align=\"center\">\n  <img src=\"npyviewer_128x128.png\" alt=\"screenshot\">\n</p>\n\n# NPYViewer 1.28\n###  A simple GUI tool that provides multiple ways to load and view the contents of .npy files containing 2D and 1D NumPy arrays.\n\n#### Plot 3-column 2D numpy arrays containing 3D coordinates as 3D point clouds\n![screenshot](screenshots/ScreenShot1.png)\n#### Plot 2D numpy arrays as grayscale images\n![screenshot](screenshots/ScreenShot2.png)\n#### Visualize heightmaps stored as 2D numpy arrays\n![screenshot](screenshots/ScreenShot3.png) \n![screenshot](screenshots/ScreenShot4.png)\n#### Visualize time series data stored as 1D numpy arrays\n![screenshot](screenshots/ScreenShot5.png)\n#### Visualize adjacency matrices (saved in .npy arrays) as directional edge weighted graphs\n![screenshot](screenshots/ScreenShot7.png)\n#### Print numpy arrays in terminal\n![screenshot](screenshots/ScreenShot6.png)\n\n\n### Installation:\n* Original development in Ubuntu 20.04 and Python 3.8.8\n* Also tested on Windows 10 and Ubuntu 22.04\n* pip3 install -r requirements.txt\n\n\n### Execution:\n* python3 NPYViewer.py\n\n\n### Current Features:\n* Open and view .npy files that contain 2D NumPy arrays and lists, as spreadsheets\n* Convert .npy files to .csv format\n* Convert .csv files to .npy format\n* Export .npy files as .mat files (compatible with MATLAB and Octave)\n* Plot 2D numpy arrays as grayscale images\n* Plot 2D numpy arrays containing 3D coordinates as 3D point clouds\n* Visualize heightmaps stored as 2D numpy arrays\n* Visualize time series data stored as 1D numpy arrays\n* Supports loading .npy files as command line arguments (e.g., python3 NPYViewer.py sample_npy_files/timeseries.npy)\n* Visualize adjacency matrices (saved in .npy arrays) as directional edge weighted graphs\n* Print numpy arrays in terminal through the use of the -noGUI argument (e.g., python NPYViewer.py sample_npy_files/timeseries.npy -noGUI)\n* GUI developed using PyQT5\n\n\n### TODO:\n* Add/Remove Rows & Columns\n* Copy/Paste Rows & Columns\n* Data search and filtering\n* Modify content datatypes \n* Handle data with more than 2 dimensions\n\n\n\n### Changes since last version:\n* Added application icon\n* Fixed Bug: \"View as Time Series\" option was hidden in the \"Functionalities\" menu\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.\n\n"
  },
  {
    "path": "code_for_generating_npy_samples/.gitkeep",
    "content": "\n"
  },
  {
    "path": "code_for_generating_npy_samples/3dSpiral_Example.py",
    "content": "\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n\n\nax = plt.axes(projection='3d')\n\n# Data for a three-dimensional line\nzline = np.linspace(0, 64, 64)\nxline = np.sin(zline)\nyline = np.cos(zline)\n\n# Data for three-dimensional scattered points\nzdata = 15 * np.random.random(100)\nxdata = np.sin(zdata) + 0.1 * np.random.randn(100)\nydata = np.cos(zdata) + 0.1 * np.random.randn(100)\ntemp=np.array([xdata,ydata,zdata])\ntemp=temp.T\nprint(np.shape(temp))\nnp.save(\"3DSpiral.npy\",temp)\nax.scatter3D(xdata,ydata,zdata, c='r');\n\n"
  },
  {
    "path": "code_for_generating_npy_samples/gaussian_example.py",
    "content": "\nimport numpy as np\nimport matplotlib.pyplot as plt\nsize = 24\nsigma_x = 10.\nsigma_y = 10.\n\nx = np.linspace(-10, 10, size)\ny = np.linspace(-10, 10, size)\n\nx, y = np.meshgrid(x, y)\nz = (1/(2*np.pi*sigma_x*sigma_y) * np.exp(-(x**2/(2*sigma_x**2)\n     + y**2/(2*sigma_y**2))))\nz *= (255.0/z.max()).astype(int)\nnp.save(\"gaussian.npy\",z)\nplt.imshow(z, cmap='gray')\nplt.show()\n\n\n"
  },
  {
    "path": "code_for_generating_npy_samples/graph.py",
    "content": "import numpy as np\n\n# Create a random numpy array\narr = np.random.randint(low=0, high=2, size=(5, 5))\n\n# Save the numpy array to an .npy file\nnp.save(\"graph.npy\", arr)\n"
  },
  {
    "path": "code_for_generating_npy_samples/heightmap_example.py",
    "content": "\nfrom PIL import Image\nimport numpy as np\nheightmap = Image.open(\"Heightmap.png\")\n\nnp.save(\"heightmap.npy\",heightmap)\n"
  },
  {
    "path": "requirements.txt",
    "content": "PyQt5==5.12.3\nnumpy\npandas\nmatplotlib\nscipy\nnetworkx\n"
  },
  {
    "path": "sample_npy_files/.gitkeep",
    "content": "\n"
  },
  {
    "path": "screenshots/.gitkeep",
    "content": "\n"
  }
]