I am using PyQt4 in Python 3.3, making a GUI and have multiple classes, some of which I don't want running until I have clicked a certain button to do so. How can I connect such a class to only run when the button is clicked, and not on the start-up of the program.
Here is how I am currently connecting this class to my button within another class.
btns.clicked.connect(self.tableshow2)
def tableshow2(self):
table5.show()
This is the first class where the button is.
class CustTableSearch(QtGui.QDialog):
def __init__(self, parent=None):
super(CustTableSearch, self).__init__(parent)
with sqlite3.connect('database.db') as db:
cursor=db.cursor()
num = QtGui.QInputDialog.getText(self, 'Insert TelephoneNumber',
'Enter TeleNum:')
table5 = CustTableSearch()
This is part of the class which the button activates, which runs on start-up of the python shell. I have tried putting this in a function within the class with the button, but then i can't have it show up using .show() (it's a screen with a table).
Assuming that both classes are in the same module, you can create an instance of CustomTableSearch in tableshow2(self) method.
...
def tableshow2(self):
self.table5 = CustomTableSearch(self)
self.table5.show()
...
One way to do this is to create the dialog only on demand, rather than creating it straight away when the module is loaded.
class ProfilePage(QtGui.QMainWindow):
def __init__(self):
super(ProfilePage, self).__init__()
self.table5 = None
self.initUI()
def initUI(self):
...
btns.clicked.connect(self.tableshow2)
def tableshow2(self):
if self.table5 is None:
self.table5 = CustomTableSearch()
self.table5.show()
Related
i have a main GUI-Window from which i open a new Window (FCT-popup) with a buttonclick:
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow() # sets ui = to the main window from the ui-file
self.ui.setupUi(self)
[...]
def enter_fct_results(self):
self.FCTpopup = FCT_Window()
self.FCTpopup.show()
In the Window i have a QTable to fill and a button to submit the data and close the window:
class FCT_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_FCT_Window()
self.ui.setupUi(self)
[...]
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
def on_submit(self): # event when user clicks
fct_nparray = np.zeros((self.ui.tableFCTinputs.rowCount(), self.ui.tableFCTinputs.columnCount()))
for j in range(self.ui.tableFCTinputs.columnCount()):
for i in range(self.ui.tableFCTinputs.rowCount()):
fct_nparray[i, j] = float(self.ui.tableFCTinputs.item(i, j).text())
return fct_nparray, lambda: self.close()
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
The receiving function iin the main window looks like ths:
def store_fct_data(self, data):
self.fct_data = data
Now i just want to understand how i can make either the mainwindow or the pushbutton which opens the 2nd window disabled. Disabling inside enter_fct_results() works, but if i want to enable it again with either store_fct_data or on_submit provides errors like this:
self.ui.pushButton_FCTresults.setEnabled(1)
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
AttributeError: type object 'MainWindow' has no attribute 'ui'
I dont think i have understood it here how to deal with multiple windows and stuff. For example how would i change a the color of a button in the main window by using a button in window2. How do i access the widgets? if i am inside the same Window i do that easily by
self.ui.pushbutton.setText("New Text")
I dont get how to access items and attributes across Windows :( Can you help me?
Access to attributes of another instance
There is a fundamental difference between disabling the button of the second window in enter_fct_results and what you tried in the lambda: in the first case, you're accessing an instance attribute (for instance, self.FCTpopup.ui.pushButton), while in the second you're trying to access a class attribute.
The self.ui object is created in the __init__ (when the class instance is created), so the instance will have an ui attribute, not the class:
class Test:
def __init__(self):
self.value = True
test = Test()
print(test.value)
>>> True
print(Test.value)
>>> AttributeError: type object 'Test' has no attribute 'value'
Provide a reference
The simple solution is to create a reference of the instance of the first window for the second:
def enter_fct_results(self):
self.FCTpopup = FCT_Window(self)
self.FCTpopup.show()
class FCT_Window(QMainWindow):
def __init__(self, mainWindow):
QMainWindow.__init__(self)
self.mainWindow = mainWindow
self.ui.pushButton_submitFCT.clicked.connect(self.doSomething)
def doSomething(self):
# ...
self.mainWindow.ui.pushButton.setEnabled(True)
Using modal windows (aka, dialogs)
Whenever a window is required for some temporary interaction (data input, document preview, etc), a dialog is preferred: the main benefit of using dialogs is that they are modal to the parent, preventing interaction on that parent until the dialog is closed; another benefit is that (at least on Qt) they also have a blocking event loop within their exec() function, which will only return as soon as the dialog is closed. Both of these aspects also make unnecessary disabling any button in the parent window. Another important reason is that QMainWindow is not intended for this kind of operation, also because it has features that generally unnecessary for that (toolbars, statusbars, menus, etc).
def enter_fct_results(self):
self.FCTpopup = FCT_Window(self)
self.FCTpopup.exec_()
class FCT_Window(QDialog):
def __init__(self, parent):
QMainWindow.__init__(self, parent)
self.ui.pushButton_submitFCT.clicked.connect(self.doSomething)
def doSomething(self):
# ...
self.accept()
The above makes mandatory to recreate the ui in designer using a QDialog (and not a QMainWindow) instead. You can just create a new one and drag&drop widgets from the original one.
i finally found my mistake: It was the place of the signal connection. It has to be right before the 2nd window is opened:
def enter_fct_results(self):
self.FCTpopup = Dialog(self.fct_data)
self.FCTpopup.submitted.connect(self.store_fct_data)
self.FCTpopup.exec_()
With this now i can send the stored data from the mainwindow to the opened window, import into the table, edit it and send it back to the main window on submit.
Guys!
I'm working with QTDesigner and PyQT5, I've created two screens using QTDesi. A Login form and a Main form. I'm trying to call the main screen after the login screen. But it didn't work. I've looked up to many tutorials, but non of them, worked for me.
Here's some code:
To call the Login Screen, I've used this class (On Controller):
class LoginController(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent=parent)
super().__init__(parent)
self.setupUi(self)
self.txtLogo.setPixmap(QtGui.QPixmap('../gui/img/icons/aperam.png'))
self.action_performed()
def action_performed(self):
self.pushButton.clicked.connect(self.valid_login)
def valid_login(self):
user = self.txtUser.text()
password = self.txtPassword.text()
if model.validate_login(user, password):
self.close()
main = HomeScreen()
Then, to call the Main Screen, I'm using this:
class HomeScreen(Ui_Model):
def __init__(self):
super(HomeScreen, self).__init__()
self.ui = Ui_Model()
self.main = QtWidgets.QMainWindow()
self.login_home_screen()
def login_home_screen(self):
self.ui.setupUi(self.main)
self.main.show()
self.ui.actionNovo.triggered.connect(self.user_screen_show)
self.main.close()
But It didn't work for me. It only shows up a black screen then closes.
The "Start" from the system is this code (Where I call the LoginScreen):
cd = LoginController()
if __name__ == "__main__":
import sys
ui = LoginController()
cd.show()
sys.exit(app.exec_())
Can you help me? I've tried to many tutorials and articles, but both them didn't work. I want to call another form after the login is sucessufuly.
Thanks
There are various problems with your code.
HomeScreen should inherit from QMainWindow too, not only from Ui_Model;
you should avoid mixing QWidget/uic creation styles;
you should not call the base class __init__ if you're also calling super().__init__ afterwards;
you create a HomeScreen instance (main), but then the function returns, which means that that instance will be instantly garbage collected;
two instances of LoginController are being created, but you only need one;
the home screen is shown and closed afterwards, which doesn't make much sense;
there is usually no need to create other functions if you just run them once (I'm referring to action_performed and login_home_screen), especially if they only do small tasks that can be included in the __init__;
The simplest solution for your case is to create a custom signal for the login screen, and connect it to the show function of the home screen window.
Note that I don't know what user_screen_show does; if it's used to show again the login, you should use a similar system to show the dialog again.
class LoginController(QtWidgets.QDialog, Ui_Dialog):
loginSuccessful = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(LoginController, self).__init__(parent)
self.setupUi(self)
self.txtLogo.setPixmap(QtGui.QPixmap('../gui/img/icons/aperam.png'))
self.pushButton.clicked.connect(self.valid_login)
def valid_login(self):
user = self.txtUser.text()
password = self.txtPassword.text()
if model.validate_login(user, password):
# login is valid, emit the signal
self.loginSuccessful.emit()
self.hide()
class HomeScreen(QtWidgets.QMainWindow, Ui_Model):
newLogin = QtCore.pyqtSignal()
def __init__(self):
super(HomeScreen, self).__init__()
self.setupUi(self)
self.actionNovo.triggered.connect(self.user_screen_show)
def user_screen_show(self):
self.newLogin.emit()
self.hide()
if __name__ == "__main__":
import sys
cd = LoginController()
cd.show()
home = HomeScreen()
cd.loginSuccessful.connect(home.show)
home.newLogin.connect(cd.show)
sys.exit(app.exec_())
The most obvious approach for me is to declare each window (window, dialog or widget) in the constructor and call the show() method when needed. Something like this:
class MultiWindowApp():
def __init__(self):
self.window_1 = self.init_window_1()
self.window_2 = self.init_window_2()
def init_window_1(self):
gui = uic.loadUi(...)
# other settings
return gui
def init_window_2(self):
gui = uic.loadUi(...)
# other settings
return gui
def show_window_1(self):
self.window_1.show()
def show_window_2(self):
self.window_2.show()
Nevertheless, it does not seem to be memory efficient, because I store the windows in the memory all the time, even when I am not showing it.
Alternative solution that comes to my mind is to create a separate class for each window (or other widget) and have one placeholder for all in the main class. Assign an instance of the respective class and delete on closing the window. A minimal example below:
class Window_1(QWidget):
def __init__(self):
QWidget.__init__(self)
uic.loadUi(...)
# other settings
self.show()
class Window_2(QWidget):
def __init__(self):
QWidget.__init__(self)
uic.loadUi(...)
# other settings
self.show()
class MultiWindowApp():
def __init__(self):
self.widget_placeholder = None
def show_window_1(self):
self.widget_placeholder = Window_1()
def show_window_2(self):
self.widget_placeholder = Window_1()
This would be a bit slower, but I would avoid keeping in memory unnecessary stuff. Nevertheless, I still have a feeling that there is a better way. What is the proper way of designing such an application?
I didn't run the examples above, so there can be some errors, but I think that the concepts behind them are clear.
I am designing an application using PyQt that will manage multiple instances of Selenium. Each instance has a QFrame with unique information and controls and can be tabbed through from the main window.
class Instance(QFrame):
def __init__(self):
super().__init__()
self.username = "whatever"
...
self.startButton = QPushButton('Start')
self.startButton.clicked.connect(lambda: self.engineStart())
self.exitButton = QPushButton('Exit')
self.exitButton.clicked.connect(lambda: self.engineExit())
...
How it looks
Users should be able to create and delete instances at will.
Creating a tab is no problem. I have a "+" button set as the QTabWidget's cornerWidget. It is connected to a simple method to add the tab.
class App(QFrame):
def __init__(self):
...
def addNewTab(self):
t = Instance()
self.tabs.addTab(t, t.username)
The problem is, how can I use the "Exit" button from the "inside" Instance class to remove the tabs that are managed from the main window's "outside" class? I need some way to call removeTab()
To do what you want you must create a slot in the main window, and connect it to the clicked signal of the button as shown below:
class App(QFrame):
def __init__(self):
...
def addNewTab(self):
t = Instance()
self.tabs.addTab(t, t.username)
t.exitButton.clicked.connect(self.slot)
def slot(self):
self.tabs.removeTab(your_index)
I setup a Main window with below codes :
import Main_BookSpace
class MainWindowClass(QtWidgets.QMainWindow, Ui_MainWindow, QtWidgets.QAction):
def __init__(self):
super(MainWindowClass, self).__init__()
self.setupUi(self)
# SQL Database relate
self.database = Main_database.DataBaseRelate()
# init space booking sheet
self.space_booking_class = Main_BookSpace.BookingSpaceClass(self.tableWidget, self.database)
self.tableWidget.itemClicked.connect(self.space_booking_class.table_click_action)
In another python file , there is a class: BookingSpaceClass.
In this class I will get some data from database , and put these data into the table widget in the Main window :
class BookingSpaceClass:
def __init__(self, theTable, database):
self.theTable = theTable
self.database = database
def table_click_action(self):
indexes = self.theTable.selectionModel().selectedIndexes()
data_id = self.theTable.item(indexes[0].row(), 23).text()
return_data = self.database.read_data_with_id("spacebooking", data_id)
print(return_data)
How can I transfer the data which get from table_click_action to Main window's widget?
It's not clear from your example why the BookingSpaceClass needs to exist at all. It's just storing references to the exact same gui widgets that the main class does. The table_click_action method could just as easily be on the main class. You should probably connect the itemClicked signal to a slot on the main class and move everything from BookingSpaceClass into the main class, or at least call the BookingSpaceClass helper methods from the main class instead of using signals.
self.tableWidget.itemClicked.connect(self.on_itemClicked)
#QtCore.pyqtSlot()
def on_itemClicked(self)
return_data = self.space_booking_class.table_click_action()