I'm very new to GUIs and I have a quick question on inheritance of variables.
In my GUI i have two buttons, one selects an xlsx file, the other graphs it. The first class below sets the buttons and selects the file:
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
vBoxLayout = QtGui.QVBoxLayout(self)
file_btn = QtGui.QPushButton('Select File', self)
file_btn.clicked.connect(self.get_graph_file)
graphing_btn = QtGui.QPushButton('Plot Graph', self)
graphing_btn.clicked.connect(Plotting_Graph)
self.show()
def get_graph_file(self):
fname_graphfile = QtGui.QFileDialog.getOpenFileName(self, 'Open file', '/Users/.../', 'excel files (*.xlsx)')
... and the second should inherit fname_graphfile and graph it (I've only added in a bit of the graphing code)...
class Plotting_Graph(Example):
def __init__(self):
self.PlottingGraph()
def PlottingGraph(self):
xl = ef(fname_graphfile[0])......
When run, it gives an error global name 'fname_graphfile' is not defined.
How do I get the second class to remember something I've defined in the previous class?
fname_graphfile is a local variable in the get_graph_file so other methods don't have access to it what you should do is make it an instance attribute so it can be accessed from any method and then add an argument to Plotting_Graph.PlottingGraph that accepts the xlsx file as a parameter to the method and pass self.fname_graphfile to it from the button click
your final code should look like this
from PyQt4 import QtCore, QtGui
import sys
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.fname_graph_file = ''
file_btn = QtGui.QPushButton('Select File', self)
file_btn.setGeometry(100, 100, 150, 30)
file_btn.clicked.connect(self.get_graph_file)
graphing_btn = QtGui.QPushButton('Plot Graph', self)
plot = Plotting_Graph()
graphing_btn.clicked.connect(lambda: plot.PlottingGraph(self.fname_graph_file))
self.show()
def get_graph_file(self):
self.fname_graph_file = QtGui.QFileDialog.getOpenFileName(self, 'Open file', '/home', 'excel files (*.xlsx)')
class Plotting_Graph(Example):
def __init__(self):
pass
def PlottingGraph(self, fname_graphfile):
print(fname_graphfile)
# xl = ef(fname_graphfile[0])
app = QtGui.QApplication([])
a = Example()
app.exec_()
Related
My main window has a variable self.x setup in the INIT. Then I need to access this value from the popup password box that is created. This is just a test script to get my point accross. I'm assuming it's something with the inheritance stuff, all that is still a little foreign to me.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtWidgets
# Created by MyWindow
class LoginDlg(QtWidgets.QDialog):
def __init__(self):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
# THIS IS THE LINE I NEED IT TO PULL X FROM PARENT
self.password.setText(self.x)
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout = QtWidgets.QFormLayout()
layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
layout.addRow('Password', self.password)
layout.addWidget(self.button_box)
self.setLayout(layout)
self.setWindowTitle("Login")
self.setMinimumWidth(350)
# Main parent window
class MyWindow(QtWidgets.QWidget):
def __init__(self):
super(MyWindow, self).__init__()
self.edit = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Get input from dialog")
button.clicked.connect(self.get_login)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
self.x = "PASS THIS STRING"
def get_login(self):
login = LoginDlg()
if login.exec_():
self.edit.setText(login.password.text())
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Normally I would just pass this data through the constructor, but let's say I have a lot of data that I don't want to pass back and forth, is there a way to access parent attributes another way?
Don't get too complicated and just pass the value of that variable through the constructor:
class LoginDlg(QtWidgets.QDialog):
def __init__(self, x):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
self.password.setText(x)
# ...
class MyWindow(QtWidgets.QWidget):
# ...
def get_login(self):
login = LoginDlg(self.x)
if login.exec_():
self.edit.setText(login.password.text())
Another similar option is to access "password" in get_login:
class LoginDlg(QtWidgets.QDialog):
def __init__(self):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
# self.password.setText(self.x)
self.button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
)
# ...
class MyWindow(QtWidgets.QWidget):
# ...
def get_login(self):
login = LoginDlg()
login.password.setText(self.x)
if login.exec_():
self.edit.setText(login.password.text())
Note: my answer does not try to implement what the OP asks is to "access a class from another" (there is no relationship so using parent-child is incorrect) since that complicates the modularization because if a object could affect the other, in general. I think the OP has an XY problem as it asks how to implement a possible solution instead of the underlying problem which is "how to share information between classes"
I am making a GUI and have a code with several files and uses a controller file to switch between the files. However, I need several of the variables to be available in the other files and also want an own file where I can keep track of the values for all the variables.
I have now instantiated the variables on top of the file, and tried to change the values in the class below, but if I then import to another file it will only give the value which was instantiated first (which is fair since I did not call the class, but is a problem).
Please help.
Under i some of the code:
From file firstwindow
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
LEVELS = 2
NUM_ICE = 4
NUM_CONES = 8
class Login(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('First')
def setupUi(self, FirstWindow):
FirstWindow.setObjectName("First")
FirstWindow.setEnabled(True)
FirstWindow.resize(675,776)
FirstWindow.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
self.spinBoxNUM_ICE = QtWidgets.QSpinBox()
self.spinBoxNUM_CONES = QtWidgets.QSpinBox()
self.spinBoxLEVELS = QtWidgets.QSpinBox()
layout.addWidget(self.spinBoxNUM_MASTERS,1,2)
layout.addWidget(self.spinBoxNUM_SLAVES,2,2)
layout.addWidget(self.spinBoxPRIORITY_LEVELS,11,2)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(FirstWindow.close)
self.QContinueButton.clicked.connect(self.login)
def login(self):
#global NUM_ICE
self.NUM_ICE = self.spinBoxNUM_ICE.value()
global NUM_CONES
NUM_CONES = self.spinBoxNUM_CONES.value()
global LEVELS
LEVELS = self.spinBoxLEVELS.value()
self.switch_window.emit()
And in the controller file
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
And in the MainWindow file where I want to use LEVELS
import sys
from PyQt5 import QtCore, QtWidgets
from firstwindow import LEVELS
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
#switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self, LEVELS)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap, LEVELS):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
#CREATING ELEMENTS
for i in range(0,LEVELS+2):
for j in range(0,5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
#LAYOUT
layout.addWidget(self.QuitButton,15,1)
layout.addWidget(self.QContinueButton,15,2)
self.setLayout(layout)
def switch(self):
self.switch_window.emit()
Avoid abusing the global variables(1), and in this case it is not necessary, you must make the dynamic creation of widgets a moment before making the change in the show_main method:
class Controller:
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
levels = self.login.spinBoxLEVELS.value()
self.window.setLevels(levels)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
# switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QVBoxLayout(self)
self.m_content_widget = QtWidgets.QWidget()
layout.addWidget(self.m_content_widget, stretch=1)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
w = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(w)
hlay.addWidget(self.QuitButton)
hlay.addWidget(self.QContinueButton)
layout.addWidget(w, alignment=QtCore.Qt.AlignRight)
def setLevels(self, levels):
layout = QtWidgets.QGridLayout(self.m_content_widget)
for i in range(0,levels+2):
for j in range(0, 5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
def switch(self):
self.switch_window.emit()
(1) Why are global variables evil?
Ended up doing this, which worked!
Instantiated all variables from login window before calling login.close. Also passed in variables needed in next function. In this way I was also able to create a function which prints out the parameters.
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.LEVELS = self.login.LEVELS
self.window = MainWindow(self.LEVELS)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
def writetofile(Controller):
f = open("f.txt", "w+")
f.write("int LEVELS = %d;\n\n" %Controller.LEVELS)
f.close()
I'm building the desktop app. Before class MyMainWindow(QMainWindow, Ui_MainWindow): I have a section, that processes some initial data (code 1)
sample_directory_2 = []
sample_files_2 = []
for (dirpath, dirnames, filenames) in walk('./Processed'):
filenames = [f for f in filenames if not f[0] == '.']
sample_files_2.extend(filenames)
break
the_dir = "Processed"
paths_2 = [os.path.abspath(os.path.join(the_dir,filename)) for filename in os.listdir(the_dir) if not filename.startswith('.')]
sample_directory_2.append(sample_files_2)
sample_directory_2.append(paths_2)
processed_info = []
for i in range(len(sample_directory_2[0])):
file_info = []
sample_file_2 = sample_directory_2[0][i]
sample_path_2 = sample_directory_2[1][i]
sample_info_2 = pd.read_excel(ospath(sample_path_2), header = None, sheetname = 3)
sample_info_2 = sample_info_2.iloc[0][0:3]
file_info.append(sample_file_2)
sample_info_2_list = numpy.array(sample_info_2).tolist()
file_info.extend(sample_info_2_list)
processed_info.append(file_info)
After this section in class MyMainWindow(QMainWindow, Ui_MainWindow): I have the code that creates QTableList and sets values to its items(code 2):
self.clickSample_list.setRowCount(len(processed_info))
self.clickSample_list.setColumnCount(len(processed_info[0]))
labels = ['Имя', 'Массовые отклики', 'Процентранг', 'Валидность']
self.clickSample_list.setHorizontalHeaderLabels(labels)
for row in range(len(processed_info)):
for column in range(len(processed_info[row])):
self.clickSample_list.setItem(row, column, QTableWidgetItem(str(processed_info[row][column])))
Code 1 section takes pretty long time, and only after that, the UI begins to load.
I guess, what I need to do is: to make Code 1 a separate function and call it after UI loads. How to do that? Is there a command that calls the function after the loading of UI?
I think what you probably need to do is place the Code 1 section in a separate function in your class and call it at some event in the GUI.
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
#Initializing the UI
self.setupUi(self)
self.buttonName.clicked.connect(self.functionName)
def functionName(self):
...
#your code
...
Or if your Application restricts the use of this code outside the class then you can try calling it in the main function
from sys import argv, exit
def functionName():
...
#code
....
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
....
def main():
app = QApplication(argv)
gui = Window()
gui.show()
functionName()
exit(app.exec_())
I need to create multi-window GUI, first I tried it with QWidgets, but finally I discover QStackWidget tool I need to use. So Im trying to, but Ive got some problems. Thanks for Your time.
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.mainWidget = MainWidget()
self.searchWidget = SearchWidget()
self.sWidget = QStackedWidget()
self.sWidget.addWidget(self.mainWidget)
self.sWidget.addWidget(self.searchWidget)
self.initUI()
and calling setCurrentWidget from the sub_widget class:
class MainWidget(QWidget):
def __init__(self, parent=MainWindow):
super(MainWidget,self).__init__()
self.initUI()
def initUI(self):
searchButton = QPushButton('searchButton',self)
optionButton = QPushButton('optionButton',self)
quitButton = QPushButton('quitButton',self)
listButton = QPushButton('listButton',self)
searchButton.clicked.connect(self.goSearch)
hbox = QHBoxLayout()
hbox.addWidget(listButton)
hbox.addWidget(quitButton)
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addWidget(searchButton)
vbox.addWidget(optionButton)
vbox.addLayout(hbox)
self.setLayout(vbox)
def goSearch(self):
self.parent().sWidget.setCurrentWidget(self.parent().searchWidget)
Ive got this message from IDE:
self.parent().sWidget.setCurrentWidget(self.parent().searchWidget)
AttributeError: 'PySide.QtGui.QStackedWidget' object has no attribute 'sWidget'
What is the thing Im doing wrong?
I'm going to comment on the code you posted here: http://pastebin.com/fBfS1X5m
An important thing to know is that you can put widgets within widgets and so on. For example:
class Widget(QWidget):
def __init__(self, parent=None):
layout = QVBoxLayout(self)
childWidget = QWidget(parent=self)
layout.addWidget(childWidget)
Just a quick note: You don't need setLayout if you pass self to the main layout constructor - via the docs.
Anyways, what I'm trying to illustrate here is that the QStackedWidget and the SearchWidget really shouldn't be a part of the MainWindow, but should live inside their own relevant widget that will handle switching between the QStackedWidget pages.
For example the MainWindow.__init__ would only look like this:
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.mainWidget = MainWidget(self)
self.setCentralWidget(self.mainWidget)
self.initUI()
Your MainWidget would then look something like:
class MainWidget(QtGui.QWidget):
...
def initUI(self):
...
self.stack = QtGui.QStackedWidget(parent=self)
self.searchWidget = SearchWidget(parent=self)
self.searchWidget.searchButton.clicked.connect(self.goSearch)
self.backWidget = BackWidget(parent=self)
self.backWidget.backButton.clicked.connect(self.goBack)
...
def goSearch(self):
self.stack.setCurrentWidget(self.backWidget)
def goBack(self):
self.stack.setCurrentWidget(self.searchWidget)
I've renamed some of the class names to make a little more sense (To me at least). SearchWidget was your old MainWidget. BackWidget was your old SearchWidget. Following those changes SearchWidget would look the same as your old MainWidget with one exception - we save a reference to the search button so we can access it in the MainWidget class as seen above (when we connect their signals to our slots). We do the same for the button in BackWidget.
The two renamed "child" widgets:
class SearchWidget(QtGui.QWidget):
...
def initUI(self):
self.searchButton = QtGui.QPushButton('searchButton', parent=self)
optionButton = QtGui.QPushButton('optionButton', parent=self)
quitButton = QtGui.QPushButton('quitButton', parent=self)
listButton = QtGui.QPushButton('listButton', parent=self)
vbox = QtGui.QVBoxLayout(self)
vbox.addStretch(1)
vbox.addWidget(self.searchButton)
vbox.addWidget(optionButton)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(listButton)
hbox.addWidget(quitButton)
vbox.addLayout(hbox)
class BackWidget(QtGui.QWidget):
...
def initUI(self):
self.backButton = QtGui.QPushButton('GoBack', parent=self)
So now we have something like:
MainWindow
|---MainWidget
|---QStackedWidget
|---SearchWidget
|---BackWidget
You can find the full working code here.
This line:
def __init__(self, parent=MainWindow):
sets the MainWindow class as a default argument, when you actually need an instance. But even if it was an instance, in the next line, you also fail to pass it on to the base-class:
super(MainWidget,self).__init__()
What you need to do instead is something like this:
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
# pass an instance of MainWindow here
self.mainWidget = MainWidget(self)
...
class MainWidget(QWidget):
def __init__(self, parent):
# pass the parent to the base-class
super(MainWidget, self).__init__(parent)
...
UPDATE:
The stack-widget will re-parent any widgets added to it, so that it becomes the parent. There are ways of working around this, but I think the real problem with your code is that you have the structure backwards. The buttons that set the current widget should be controlled by the main-window, and the widgets in the stack should work completely independantly of that.
I have created the code to create multiple classes with different class names (I.e. Question1, Question2 etc.)
import sys
from PyQt4 import QtCore, QtGui
class StartTest(QtGui.QMainWindow):
def __init__(self, parent=None):
super(StartTest, self).__init__(parent)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
question1 = Question1(self)
self.central_widget.addWidget(question1)
self.central_widget.setCurrentWidget(question1)
question1.proceed.clicked.connect(self.question2)
def question2(self):
question2 = Question2(self)
self.central_widget.addWidget(question2)
self.central_widget.setCurrentWidget(question2)
i = 0
while i<2:
class Question+i(QtGui.QWidget):
def __init__(self, parent=None):
super(Question+i, self).__init__(parent)
question = QtGui.QLabel('What is 5+5?')
self.proceed = QtGui.QPushButton("Proceed")
self.Answer = QtGui.QLineEdit(self)
layout = QtGui.QFormLayout()
layout.addRow(question, self.Answer)
layout2 = QtGui.QVBoxLayout()
layout2.addLayout(layout)
layout2.addWidget(self.proceed)
self.setLayout(layout2)
print('Question'+i)
if __name__ == '__main__':
User = ''
app = QtGui.QApplication([])
window = StartTest()
window.showFullScreen()
app.exec_()
However when I write:
i = 0
while i<2:
class Question+i(QtGui.QWidget):
i obtain a syntax error on the Question+i part error, which is undertandable. But how would i overcome this whilst still creating multiple classes with different class names???
See this related question with answers on how to set class names dynamically.
You need to implement a factory method. For you it might look like this:
def factory(num) :
BaseClass = QtGui.QWidget
class NewClass(BaseClass): pass
NewClass.__name__ = "factory_%s%d" % (BaseClass.__name__, num)
return NewClass