how to call a python script on button click using PyQt - python

I have created a form using PyQt4 which has a push button. On this push button I want to call another python script which looks like this:
File1.py:
import sys
from PyQt4 import QtCore, QtGui
from file1_ui import Ui_Form
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
File1_ui.py
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8("Form"))
Form.resize(400, 300)
self.pushButton = QtGui.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(120, 200, 95, 20))
self.pushButton.setObjectName(_fromUtf8("pushButton"))
self.retranslateUi(Form)
QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("clicked()")), Form.close)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("Form", "Close", None, QtGui.QApplication.UnicodeUTF8))
File2.py
import sys
from PyQt4 import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
panel = Qt.QWidget()
layout = Qt.QHBoxLayout()
panel.setLayout(layout)
from taurus.qt.qtgui.panel import TaurusForm
panel = TaurusForm()
model = [ 'test/i1/1/%s' % p for p in props ]
panel.setModel(model)
panel.show()
sys.exit(app.exec_())
File1_ui.py is created from the Qtdesigner and then I am using File1.py to execute it.So File2.py when executed alone opens up a panel and displays few attributes.I want this script to be called on the button click in the first form(file1.py) which I created using Qtdesigner.Could you let me know how I could achieve this functionality.Thanks.

You will need to make some modifications to File2.py to make the appropriate calls depending on whether it is running standalone or not. When you are launching the script via File1.py there will already be a QApplication instance with event loop running, so trying to create another and run its event loop will cause problems.
Firstly, move the core part of your script into its own function. This will allow you to easily call it from File1.py. You can then handle the case where the script is running standalone and needs to create a QApplication instance and start its event loop. (I am not familiar the the taurus library you are using, but you can probably substitute TaurusApplication for QtGui.QApplication)
File2.py:
import sys
from PyQt4 import QtCore, QtGui
def runscript():
panel = QtGui.QWidget()
layout = QtGui.QHBoxLayout(panel)
return panel # Must return reference or panel will be deleted upon return
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
panel = runscript()
panel.show()
sys.exit(app.exec_())
Assuming your files are in the same directory you can simply write import File2 and use File2.runscript() to run your code. You then just need to connect the function to your pushbuttons clicked() signal to run it. The only problem here is that the reference to the QWidget returned from the runscript() function will be lost (and the object deleted) if you connect directly to runscript(). For this reason I created a method launch_script() which saves a reference in MyForm.
File1.py:
import sys
from PyQt4 import QtCore, QtGui
from file1_ui import Ui_Form
import File2
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
# This is a bit of a hack.
self.ui.pushButton.clicked.connect(self.launch_script)
def launch_script(self):
self.panel = File2.runscript()
self.panel.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
I don't use Qt Designer, so I don't know the correct way to go about connecting the signal to launch_script(). The code I have written should work, but obviously violates OOP principles and is dependent on the name of the pushbutton widget assigned by the software.

Related

(PyQt) QVBoxLayout shrink upon multiple loading of addWidget

Why is the layout shrinking like this and other times going back to normal?
I've created several separate UI files in QtDesigner, one is the MainWindow and the other is a widget for Loading Data.
In order to work with these files, I've created separate child classes of each UI file. In order to add a new widget to the MainWindow I've created a addWidget() function; it works by adding a particular widget to the scrollarea layout. You can see this function in MainWindow.py
Here is the code for __main__.py
import multiprocessing as mp
import os.path
import sys
import time
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from point_spectra_gui.future_.functions import *
from point_spectra_gui.future_.util import delete
from point_spectra_gui.future_.util.excepthook import my_exception_hook
def new():
p = mp.Process(target=main, args=())
p.start()
def connectWidgets(ui):
ui.actionLoad_Data.triggered.connect(lambda: ui.addWidget(LoadData.Ui_Form))
def main():
sys._excepthook = sys.excepthook
sys.excepthook = my_exception_hook
app = QtWidgets.QApplication(sys.argv)
mainWindow = QtWidgets.QMainWindow()
ui = MainWindow.Ui_MainWindow()
ui.setupUi(mainWindow)
connectWidgets(ui)
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is the code for MainWindow.py
from PyQt5 import QtWidgets
from point_spectra_gui.future_.functions import *
from point_spectra_gui.future_.util import *
from point_spectra_gui.ui import MainWindow
class Ui_MainWindow(MainWindow.Ui_MainWindow):
def setupUi(self, MainWindow):
self.MainWindow = MainWindow
super().setupUi(MainWindow) # Run the basic window UI
self.menu_item_shortcuts() # set up the shortcuts
def addWidget(self, object):
widget = object()
widget.setupUi(self.scrollArea)
self.widgetLayout = QtWidgets.QVBoxLayout()
self.widgetLayout.setObjectName("widgetLayout")
self.verticalLayout_3.addLayout(self.widgetLayout)
self.widgetLayout.addWidget(widget.get_widget())
def menu_item_shortcuts(self):
self.actionExit.setShortcut("ctrl+Q")
self.actionCreate_New_Workflow.setShortcut("ctrl+N")
self.actionOpen_Workflow.setShortcut("ctrl+O")
self.actionRestore_Workflow.setShortcut("ctrl+R")
self.actionSave_Current_Workflow.setShortcut("ctrl+S")
Here is the code of the child class LoadData.py
from PyQt5 import QtWidgets
from point_spectra_gui.ui.LoadData import Ui_loadData
class Ui_Form(Ui_loadData):
def setupUi(self, Form):
super().setupUi(Form)
self.connectWidgets()
def get_widget(self):
return self.groupBox
def connectWidgets(self):
self.newFilePushButton.clicked.connect(lambda: self.on_getDataButton_clicked())
# self.get_data_line_edit.textChanged.connect(lambda: self.get_data_params())
# self.dataname.textChanged.connect(lambda: self.get_data_params())
def on_getDataButton_clicked(self):
filename, _filter = QtWidgets.QFileDialog.getOpenFileName(None, "Open Data File", '.', "(*.csv)")
self.fileNameLineEdit.setText(filename)
if self.fileNameLineEdit.text() == "":
self.fileNameLineEdit.setText("*.csv")
**Edit
Upon trying this again and then shrinking the window. The layout goes back to normal.
This to me tells me it's not a problem with my code, it's the way the Qt handles the adding of widgets. I still do not understand why this is happening though. So any insight into how this is happening is very much appreciated.
This problem is with the Form.resize() inside the generated code.
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
To fix this you'll need to go into QtDesigner and set the geometry back to it's default the Layout size by clicking the red circled item.
This, in essence, deletes the resize method call
You can then convert again with pyuic

Use separate interface file with PyQt

I have an interface file generated from QtDesigner that I want to keep the same in case of changes.
Have a main file called application.py handle all the functions, and one file strictly for interface stuff.
I am using PyQt5.
I have not been able to find any tutorials on this specific question, any pointers would be helpful.
Code from YatsiInterface.py (shorten)
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_YatsiWindow(object):
def setupUi(self, YatsiWindow):
YatsiWindow.setObjectName("YatsiWindow")
YatsiWindow.resize(800, 516)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Terraria.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
YatsiWindow.setWindowIcon(icon)
self.windowLayout = QtWidgets.QWidget(YatsiWindow)
self.windowLayout.setObjectName("windowLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.windowLayout)
self.quitButton.setObjectName("quitButton")
self.buttonsLeftLayout.addWidget(self.quitButton)
YatsiWindow.setCentralWidget(self.windowLayout)
self.statusbar = QtWidgets.QStatusBar(YatsiWindow)
self.statusbar.setObjectName("statusbar")
YatsiWindow.setStatusBar(self.statusbar)
self.retranslateUi(YatsiWindow)
QtCore.QMetaObject.connectSlotsByName(YatsiWindow)
def retranslateUi(self, YatsiWindow):
_translate = QtCore.QCoreApplication.translate
YatsiWindow.setWindowTitle(_translate("YatsiWindow", "Yatsi - Server Interface"))
self.quitButton.setText(_translate("YatsiWindow", "Quit"))
How can I use self.quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit()) with the above code? I know to import the file with from YatsiInterface import Ui_YatsiWindow but I'm in the dark as how to create button functions without editing the interface file.
Edit:
I'll add my broken code below.
import sys
from YatsiInterface import Ui_YatsiWindow
from PyQt5 import QtCore, QtGui, QtWidgets
app = QtWidgets.QApplication([])
YatsiWindow = QtWidgets.QMainWindow()
ui = Ui_YatsiWindow()
ui.setupUi(YatsiWindow)
# Here's the bad part
ui.setupUi.btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
# Up there ^
YatsiWindow.show()
sys.exit(app.exec_())
Thanks for your help.
This would be the way to connect the signal
ui.quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
That being said, you probably don't want to do this. You should probably connect to the .close method of the window. When the window closes, by default, Qt will exit the event loop and quit.
ui.quitButton.clicked.connect(YatsiWindow.close)
from YatsiInterface import Ui_YatsiWindow
from PyQt5 import QtCore, QtGui
class my_application(QtGui.QWidget, Ui_YatsiWindow):
def __init__(self):
super(my_application, self).__init__()
self.setupUi(self)
self.quitButton.clicked.connect(self.my_mythod)
def my_method(self):
pass #all your code for the buttons clicked signal
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = my_application()
ui.show()
sys.exit(app.exec_())
This my_application class is inheriting your user interface class. Now, you can write your button related functions without editing the user interface file.
self.quitButton.clicked.connect(self.close)
this will close your user interface when button is clicked.

Ui_MainWindow has no attribute 'show'

I'm writing a program which will have multiple windows. I have a main program (attached) that calls the Ui files (that have been converted into .py). The main window and customise window open correctly (the first two listed) but neither the third or fourth windows open correctly, giving me the error
'Ui_MainWindow' object has no attribute 'show'
The main program;
from PyQt4 import QtCore, QtGui
import sys
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwin = main_menu_ui.Ui_MainWindow()
mainwin.show()
sys.exit(app.exec_())
def openCustomise(self):
customiseOpen = question_set_menu_ui.Ui_MainWindow()
customiseOpen.show()
sys.exit(app.exec_())
def openQuiz(self):
quizOpen = quiz_window_ui.Ui_MainWindow()
quizOpen.show()
sys.exit(app.exec_())
def addNewSet(self):
addNewOpen = question_set_edit_ui.Ui_MainWindow()
addNewOpen.show()
sys.exit(app.exec_())
Sorry if I'm missing something obvious, I'm learning Qt/Python for college.
The auto-generated UI class that you are importing extends object and doesn't have a show method (open up the .py file for yourself and verify this).
In general, you should structure your GUIs like this:
from PyQt4 import QtCore, QtGui
import sys
from layout_file import main_menu_ui
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = main_menu_ui()
self.ui.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwin = MyForm()
mainwin.show()
sys.exit(app.exec_())
You import your UI from your autogenerated UI file. You have a class that contains your GUI logic. It then sets up your UI layout from your imported UI in its __init__() method.

How to tune (Py-)Qt menus?

I would like to set two things for submenus in PyQt:
not open submenus when hovered
no delay when submenus get opened by a click
I suppose that I had to modify the hover behavior of the QAction object that is returned by the menuAction() method of the submenu object - but how to do this?
There is a QStyle::SH_Menu_SubMenuPopupDelay setting mentioned in the docs that is maybe what I need for the second but I also have no clue how to set this in PyQt.
My basic menu example:
#!/usr/bin/env python
from PyQt4 import QtGui
from PyQt4 import QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.menubar = QtGui.QMenuBar(self)
self.setMenuBar(self.menubar)
self.menuFile = QtGui.QMenu(self.menubar, title='File')
self.menubar.addAction(self.menuFile.menuAction())
self.submenu = QtGui.QMenu(self.menuFile, title='Submenu')
self.menuFile.addAction(QtGui.QAction(self, text="First"))
self.menuFile.addAction(self.submenu.menuAction())
self.menuFile.addAction(QtGui.QAction(self, text="Third"))
self.submenu.addAction(QtGui.QAction(self, text="First"))
self.submenu.addAction(QtGui.QAction(self, text="Second"))
self.submenu.addAction(QtGui.QAction(self, text="Third"))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())

Open a GUI file from another file PyQT

I've created many GUI interfaces in PyQT using QT Designer, but now I'm trying to open an interface from another one, and I don't know how to do it..
Start.py is the file which run the GUI Interface Authentification_1 and Acceuil_start.py is the file which run the GUI interface Acceuil_2.py, now I want from Start.py to lunch Acceuil_start.py.
Do you have any idea about that ? Thank you.
Here's my code :
Start.py :
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow #??? Acceuil_2.py is the file which I want to open
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Fenetre_auth()
self.ui.setupUi(self)
def authentifier(val): #Slot method
self.Acceuil = Acceuil() #???
self.Acceuil.show() #???
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
Acceuil_start.py
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
First, you should to name your GUI classes so they have a different name, and not the generic one, so you could distinct them.
Why you would need to do that? Well - simply, because it makes sense - if every class is representing different type of dialog, so it is the different type - and it should be named differently. Some of the names are/may be: QMessageBox, AboutBox, AddUserDialog, and so on.
In Acceuil_start.py (you should rename class in other module, too).
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow
class Acceuil(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Acceuil()
myapp.show()
sys.exit(app.exec_())
in the parent class, when you want to create the window, you are close (but it should work in any case):
def authentifier(val): #Slot method
self.Acceuil = Acceuil(self) # You should always pass the parent to the child control
self.Acceuil.show() #???
About parent issue: If your widget/window is creating another widget, setting creator object to be parent is always a good idea (apart from some singular cases), and you should read this to see why is that so:
QObjects organize themselves in object trees. When you create a QObject with another object as parent, it's added to the parent's children() list, and is deleted when the parent is. It turns out that this approach fits the needs of GUI objects very well. For example, a QShortcut (keyboard shortcut) is a child of the relevant window, so when the user closes that window, the shorcut is deleted too.
Edit - Minimal Working Sample
To see what I am trying to tell you, I've built a simple example. You have two classes - MainWindow and
ChildWindow. Every class can work without other class by creating separate QApplication objects. But, if you import ChildWindow in MainWindow, you will create ChildWindow in slot connected to singleshot timer which will trigger in 5 seconds.
MainWindow.py:
import sys
from PyQt4 import QtCore, QtGui
from ChildWindow import ChildWindow
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
QtCore.QTimer.singleShot(5000, self.showChildWindow)
def showChildWindow(self):
self.child_win = ChildWindow(self)
self.child_win.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MainWindow()
myapp.show()
sys.exit(app.exec_())
ChildWindow.py:
import sys
from PyQt4 import QtCore, QtGui
class ChildWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowTitle("Child Window!")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = ChildWindow()
myapp.show()
sys.exit(app.exec_())
To reference the other dialog from Start.py, you must prefix it by the module name, which in this case is Acceuil_start. As such, it is OK if there are duplicated function names in each module. So, you would have:
def authentifier(val): #Slot method
dlg = Acceuil_start.StartQT4()
dlg.exec_()
However, if you want these to run from the same process, keep in mind that you can't have two app objects. You would probably want to structure Acceuil_start.py to act like a dialog, rather than a main window. If these are two distinct main windows, you might find it easier to just invoke another Python interpreter with the Acceuil_start.py as a parameter.

Categories