zeebeel
zeebeel

Reputation: 15

How to launch an interactive view of a table after file dialog in PyQt5?

Using PyQt5, I made two widgets. The first widget is a user-prompt to select a file with an alert that confirms the selected filepath. The BackEnd reads the selected data file and processes the data; for the sake of MWE, data is defined in BackEnd as a 2-d array and the selected file is only used to get a filepath. The second widget loads a view of the table for the user to look at. (It is not yet relevant to this question, but my use case is that I would like to have a user to select rows/columns by clicking on particular cells.) I can get the second widget to work by itself, but the table does not load if I first load the first widget. I don't think I am missing any .close() or .exec_() methods, so I'm not sure why the second widget will not load. What is the cause of this error and how do I circumvent it? I tried using on_clicked() methods in previous attempts, but I'm not sure how to apply that to different types of widgets/tables/etc that are not identical.

The code below is for the table-view of the 2-d array.

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
import numpy as np

class BackEnd():

    def __init__(self):
        super().__init__()
        # ...
        nrows, ncols = 50, 10
        self.data = np.arange(nrows * ncols).reshape((nrows, ncols)).astype(str)

class TableModel(QtCore.QAbstractTableModel):

    """
    An instance of this class is created inside the constructor
    of the class 'TableWindow'.
    """

    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):
        if role == QtCore.Qt.DisplayRole:
            # See below for the nested-list data structure.
            # .row() indexes into the outer list,
            # .column() indexes into the sub-list
            return self._data[index.row()][index.column()]

    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

    def columnCount(self, index):
        # The following takes the first sub-list, and returns
        # the length (only works if all rows are an equal length)
        return len(self._data[0])

class TableWindow(QtWidgets.QMainWindow):

    """
    This class is used to view the raw data file via gui.
    """

    def __init__(self, data):
        super().__init__()
        self.table = QtWidgets.QTableView()
        self.model = TableModel(data)
        self.table.setModel(self.model)
        self.setCentralWidget(self.table)
        self.setWindowTitle("Select 'row' if each student corresponds to a row; otherwise, select 'column'")

By running the code snippet below, one should be able to see a pop-up showing a view of the table (second widget).

back_end = BackEnd()

## initialize application
app = QtWidgets.QApplication(sys.argv)

## view data file
window = TableWindow(back_end.data.tolist())
window.show()

## exit application
sys.exit(app.exec_())

When trying to combine the two widgets, the code snippet above should be commented out. The code below is for the file selection widget (first widget).

class MainWindow(QtWidgets.QMainWindow):


    """
    This class contains all GUI methods.
    """

    def __init__(self):
        self._backend = BackEnd()
        self._fpath = None
        super().__init__()
        self.initialize_ui()

    @property
    def backend(self):
        return self._backend

    @property
    def fpath(self):
        return self._fpath

    def initialize_ui(self):
        self.select_input_data_file()
        self.verify_input_data_file()
        # self.close()
        self.interact_with_table()

    def select_input_data_file(self):
        dialog = QtWidgets.QFileDialog(
            self,
            "Select input file",
            "path",
            "",
            supportedSchemes=["file"],
            options=QtWidgets.QFileDialog.DontUseNativeDialog)
        fpath = dialog.getOpenFileName(None, 'Open file', '/home')[0]
        self._fpath = fpath
        # dialog.close()
        # dialog = None

    def verify_input_data_file(self):
        alert = QtWidgets.QMessageBox()
        alert.setText('The input filepath you selected is: \n{}'.format(self.fpath))
        alert.exec_()
        # alert.close()
        # alert = None

    def interact_with_table(self):
        window = TableWindow(self.backend.data.tolist())
        window.show()

The code below is my attempt at having the user use the first widget to select a file (successful) and then show the data table (unsuccessful).

## initialize application
app = QtWidgets.QApplication(sys.argv)

## view data file
window = MainWindow()
window.show()

## exit application
sys.exit(app.exec_())

Upvotes: 0

Views: 439

Answers (1)

Heike
Heike

Reputation: 24420

In MainWindow.interact_with_table, the TableWindow widget is assigned to a local variable. When interact_with_table returns, this variable goes out of scope and the reference count of TableWindow goes to zero. This will cause the TableWindow object to be deleted during the next garbage collection cycle. One solution is to make a persistant reference to the table window for example by assigning it to an instance variable of MainWindow, i.e.

def interact_with_table(self):
    self.table_window = TableWindow(self.backend.data.tolist())
    self.table_window.show()

Upvotes: 2

Related Questions