Reputation: 15
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
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