Id like to ask how to connect 2 different classes. I have the following codes in 2 classes (2 classes because i have created 2 different interface. 1 is the QMainwindow and the other 1 is QWidget).
Here's the code:
class MainWindow(QMainWindow, Ui_MainWindow):
def open_inv_search_form(self):
self.window = QtWidgets.QWidget()
self.ui = Ui_Inv_Search()
self.ui.setupUi(self.window)
self.window.show()
MainWindow.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
Inv_Search.hide()
MainWindow.setEnabled(True)
The idea is when SEARCH button is clicked in MainWindow, Inv_Search will pop up while MainWindow will be disabled. I have done this part correctly. When CLOSE button is clicked, Inv_Search will be hide and MainWindow will be enabled. However, when CLOSE button is clicked, nothing happened. And there is no error at all.
UPDATE
I was able to successfully do what I wanted. Here's the changes I did. Let me know if this is fine or can be better. Nevertheless, this code works.
class MainWindow(QMainWindow, Ui_MainWindow):
Inv_Search.show() #called to connect to the new window. This is the key since what i previously did only call the ui, but not connect it to the class itself.
MainWindow.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.setupUi(self)
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
Inv_Search.close() # I cant figure out how can I use the self but this works fine
MainWindow.setEnabled(True)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
MainWindow = MainWindow()
Inv_Search = Inv_Search() #added this one
#ui = Ui_MainWindow()
#ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
When you call Inv_Search.hide() and MainWindow.setEnabled(True) in close_inv_search_form, you call the methods on the class itself not on the instance, which is what you actually have to.
from PyQt5.QtCore import qApp
class MainWindow(QMainWindow, Ui_MainWindow):
def open_inv_search_form(self):
self.window = QtWidgets.QWidget()
self.window.ui = Ui_Inv_Search() # You have to set the attributes of the
self.window.ui.setupUi(self.window) # "window" not the instance of MainWindow
self.window.show()
self.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
self.hide() # Use the method "hide" on the instance
qApp.setEnabled(True) # Use the method "setEnabled" on the instance
if __name__ == "__main__" :
app = QApplication()
main = MainWindow() # This is the instance that can be referred to by qApp
main.exec_()
I am assuming that there is more code and a .ui file somewhere. It looks like this line
Inv_Search.hide()
should be changed to
self.hide()
Also, I think that because you need to call the method on the instance, not the class.
self.ui = Ui_Inv_Search()
should probably be
self.ui = Inv_Search()
You are doing a similar thing with MainWindow. It's a little more difficult here, because you will need to have an instance of MainWindow stored somewhere accessible. Although you may be able to access the MainWindow instance through QtWidget.parentWidget, in Python I prefer to just pass the instance to the constructor. So
def __init__(self, mainWindow):
self.mainWindow = mainWindow
# ... all the other stuff too
as your Inv_Search constructor, and
self.ui = Inv_Search(self)
# notice the new ^ argument
in your MainWindow constructor. And then
self.mainWindow.setEnabled(True)
in your class method. Also, your argument signature is wrong for the clicked signal. Use
def close_inv_search_form(self, checked=None):
# Need this ^ argument, even if you don't use it.
In reality, it seems like the functionality you are trying to accomplish is best suited for a modal dialog, such as that provided by QDialog, which will natively handle many of the effect I think you are looking for.
Related
I want to open another window upon a successful login but it continuously give this error.
QCoreApplication::exec: The event loop is already running
Here is the code.
LoginWindow - Login Page
MainWindow - Post login page
Is there anyway I can open another window upon a successful login and close the current one.
from PyQt5.QtWidgets import QMainWindow, QApplication
from login_ui import *
import sys
LOGGED_IN = False
TOKEN = ""
class LoginWindow(QMainWindow, Ui_LoginWindow):
GLOBAL_STATE = True
loginPortal = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.show()
self.handleButtons()
sys.exit(self.loginPortal.exec_())
def handleButtons(self):
self.CloseBtn.clicked.connect(self.close)
self.MinBtn.clicked.connect(self.showMinimized)
self.loginBtn.clicked.connect(self.login)
def login(self):
if self.username.text() == "admin" and self.password.text() == "password":
LOGGED_IN = True
TOKEN = "admin"
elif self.username.text() == "coord" and self.password.text() == "password":
LOGGED_IN = True
TOKEN = "coord"
else:
LOGGED_IN = False
Token = None
if LOGGED_IN:
MainWindow().show()
else:
self.close()
self.loginPortal.quit()
from main_ui import *
class MainWindow(QMainWindow, Ui_MainWindow):
app = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.showMaximized()
self.handleButtons()
sys.exit(self.app.exec_())
def handleButtons(self):
self.MinBtn.clicked.connect(self.showMinimized)
self.MaxBtn.clicked.connect(self.maximize)
self.CloseBtn.clicked.connect(self.close)
def maximize(self):
if self.GLOBAL_STATE:
self.showNormal()
self.GLOBAL_STATE = False
else:
self.showMaximized()
self.GLOBAL_STATE = True
if __name__ == "__main__":
LoginWindow()
There are two problems with your code:
Only one QApplication instance should exist.
You're creating a QApplication in the class definition.
The second problem is important, as QtWidgets can only be created when a QApplication is created. When python opens a script, it also initializes all the classes in its main indentation, which also means that every function in the base indentation of the class is executed.
In your case, it means that when each class is loaded, each one will try to create a QApplication, since app = QApplication([]) is in their main indentation. As explained in the first point, only one QApplication instance can only exist.
While technically your code could run fine when using a single class (the QApplication is created before actually creating the widget instance), this leads to problems exactly like in your case.
A possible workaround would be to check if a QApplication already exists:
# note: this is just for explanation purposes, do NOT do this unless you
# really know what you're doing
class LoginWindow(QMainWindow, Ui_MainWindow):
if not QApplication.instance():
loginPortal = QApplication([])
# ...
class MainWindow(QMainWindow, Ui_MainWindow):
if not QApplication.instance():
app = QApplication([])
But that's not a good approach anyway, and the reason is that the application should be also executed with exec_() (or exec()). While you're doing it within the __init__ of each widget, that's absolutely not the place for that: exec_() is blocking, which prevents returning the actual instance to the caller until the application quits. Even if you're not actually interested in that, it's not good practice, as the __init__ should always return as soon as possible. Finally, when you try to create the main window instance, it will try to exec the other QApplication instance, and this is the (final) source of your problem.
That's what the common if __name__ == '__main__': is important for: ensuring that only the main program executes what should be actually run, and in case of Qt, to create and run the only QApplication that should ever exist in the program lifetime.
A better approach when dealing with multiple windows is to create a signal and use that to interact with anything else.
This is a possible correct version of your code (be aware of the commented lines)
class LoginWindow(QMainWindow, Ui_LoginWindow):
loggedSignal = pyqtSignal()
GLOBAL_STATE = True
# loginPortal = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.show()
self.handleButtons()
# sys.exit(self.loginPortal.exec_())
# ...
def login(self):
# ...
if LOGGED_IN:
self.loggedSignal.emit()
else:
QApplication.quit()
self.close()
class MainWindow(QMainWindow, Ui_MainWindow):
# app = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
# self.showMaximized()
self.handleButtons()
# sys.exit(self.app.exec_())
if __name__ == '__main__':
app = QApplication(sys.argv)
login = LoginWindow()
mainWindow = MainWindow()
login.loggedSignal.connect(mainWindow.showMaximized)
sys.exit(app.exec_())
Note that you are using the GLOBAL_STATE in the main window, but you defined it in the login window only instead.
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_())
I am creating a small GUI program using PySide. I am having difficulty creating another object of same class. What exactly I am trying to do is that when clicked on a button on MainWindow it should create another independent window of same class.
import sys
from PySide import QtCore, QtGui
class Sticky(QtGui.QMainWindow):
def __init__(self,parent = None):
QtGui.QMainWindow.__init__(self,parent)
self.initUI()
def initUI(self):
....
self.addToolBarElements()
....
self.show()
def addToolBarElements(self):
....
self.newwindow = QtGui.QAction(QtGui.QIcon(os.path.join(os.path.dirname(__file__),'icons/new.png')),"New Note",self)
self.newwindow.setStatusTip("New")
self.newwindow.triggered.connect(newwindow)
self.toolBar.addAction(self.newwindow)
def newwindow(self):
#how to create new object of same class
def run():
app = QtGui.QApplication(sys.argv)
notes = Sticky()
sys.exit(app.exec_())
Here is what I have tried:
I have tried multiprocessing but I didn't understand much. I tried calling run() method again but it gives error.
Do not call with the same name 2 different elements, in your case self.newwindow refers to the QAction as the method of the class, avoid it, that is a type of error easy to commit but difficult to find.
going to the point, you just have to create a new object of the class, but the problem is that the garbage collector will eliminate it, to avoid it there are 2 possible options, the first is to make the new window member of the class, or second store it in a list, that's the one I choose because I think you want to have several windows.
import sys
import os
from PySide import QtCore, QtGui
class Sticky(QtGui.QMainWindow):
def __init__(self,parent = None):
QtGui.QMainWindow.__init__(self,parent)
self.others_windows = []
self.initUI()
def initUI(self):
self.addToolBarElements()
self.show()
def addToolBarElements(self):
self.toolBar = self.addToolBar("toolBar")
self.newwindow = QtGui.QAction(QtGui.QIcon(os.path.join(os.path.dirname(__file__),'icons/new.png')), "New Note",self)
self.newwindow.setStatusTip("New")
self.newwindow.triggered.connect(self.on_newwindow)
self.toolBar.addAction(self.newwindow)
def on_newwindow(self):
w = Sticky()
w.show()
self.others_windows.append(w)
def run():
app = QtGui.QApplication(sys.argv)
notes = Sticky()
sys.exit(app.exec_())
run()
How to receive close event in following code?
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.view = QUiLoader().load("sample.ui", self)
self.view.show()
def closeEvent(self, e):
print "close event recieved"
def main():
app = QApplication(sys.argv)
a=Main()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If I convert sample.ui to sample.py using pyside-uic and importing this into main.py then I was able to receive close event.
from sample import Ui_MainWindow
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
def closeEvent(self, e):
print "close event recieved"
app = QApplication(sys.argv)
a=Main()
a.show()
sys.exit(app.exec_())
The second example works because it effectively becomes a subclass of the top-level class from Qt Designer. By contrast, the first example uses composition rather than subclassing, which puts all the gui elements inside an internal namespace. The Main class is just a container that acts as the parent of the view widget, and is never actually shown (which in turn means it doesn't receive any close events).
In PyQt, the uic module has several funtions which allow you to work around these issues, but there is currently nothing like that in PySide. Instead, you have to roll your own function. See this answer for an explanation of how to do that.
Alternatively, you could change the top-level class in Qt Designer to a QWidget, and then make view the central widget of your Main class. This is a lot less flexible than the above method, though.
I am trying to write a python script to an application like matplotlib.
I need a function call to show the Qt main window. How do I do this?
class MainWindow(QtGui.QMainWindow):
def __init__(self,parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.numbers = 4
...
app = QtGui.QApplication(sys.argv)
dmw = DesignerMainWindow()
dmw.show()
sys.exit(app.exec_()) #this works, but pops the window right away
I want to be able to call the window when I wish instead. (Something like this)
def newWin():
app = QtGui.QApplication(sys.argv)
dwm = MainWindow()
sys.exit(app.exec_())
return dwn
a = newWin() # application is created now
a.numbers = 10 # do something
a.show() # should pop me the window now
EDIT : Pasting solution thanks to jadkik94
class App(QtGui.QApplication):
def __init__(self, args):
QtGui.QApplication.__init__(self,args)
self.window = MainWindow()
def doSomething(self, ii):
self.window.numbers = ii
def show(self):
self.window.show()
sys.exit(self.exec_())
a = App(sys.argv)
a.doSomething(12) #updates numbers alternately a.window.numbers = 12
a.show() #pops the window!
When used inside a function, the window does not show. The problem is simple: the window is garbage collected because it is defined inside the scope of the function, and then not used anymore, so Python sees it as "garbage" and deletes the object.
The best way I found of avoiding that is having an application class that will hold references to all the windows you want to show. So you can either have a regular class do that for you, or subclass the QtGui.QApplication if you can make use of it otherwise too. I would go for the second option.
Another option, if you really don't want to be using a class, is to set it to a global variable, and that will usually prevent it from being garbage-collected by Python.
Is this what you want:
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
a = main()
a.show()