Function is not run when connecting Button to it in PyQt4 - python-3.x

I m new to PyQt4 and having problems when trying to connect a button to a function.
I m using PyQt 4.11.4.
Neither the clicked.connect method nor the line in the comment below seemed to work.
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
def setupUi(self, Window):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(200, 200)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setGeometry(0,0,100,100)
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton = QtGui.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(100, 50, 55, 20))
self.pushButton.setText("Run")
self.pushButton.clicked.connect(self.run)
#OR QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL('clicked()'),self.run)
def run(self):
print("ok")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
Window().setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

First of all, by placing statements directly after the __name__ == "__main__" test you introduce global variables. For instance the MainWindow variable is global and is subsequently altered in the Window.setupUi method. This is undesirable. It's better to create a main function and call only that. For example by changing those lines into this...
def main():
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
Window().setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
you will get a run-time error and thus notice something's wrong.
By the way, it is the convention in Python to start class names with an upper case character and objects with a lower case. Therefore, at first glance I thought that MainWindow was a class. It is, however, an object and should better be renamed to mainWindow. Notice how the StackOverflow syntax highlighting colors all identifiers that start with an capital letter light blue?
So, the issue with the signal is that you create two main window objects: first by MainWindow = QtGui.QMainWindow() and then on the next line by Window().setupUi(MainWindow). On that second line you call setupUi with the self parameter set to a temporary Window object, and the Window parameter set to a QtGui.QMainWindow object (MainWindow). It's not surprising that this doesn't work, it is actually a bit surprising it does something at all.
Finally, it is strongly recommended to use layouts in Qt to arrange the widgets. This makes the calls to setGeometry() superfluous. Adding widgets to a layout also set's their parent, so you don't need to do this separately then. I think you definitely should read the docs on layout management (except the last section on writing your own layout manager).
Apply all the advice above and you get something like this...
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self._setupUi()
def _setupUi(self):
self.resize(200, 200)
# the central widget is called my widget to prevent clashes with
# the centralWidget() function.
self.mainWidget = QtGui.QWidget()
self.setCentralWidget(self.mainWidget)
self.mainLayout = QtGui.QVBoxLayout()
self.mainWidget.setLayout(self.mainLayout)
self.pushButton = QtGui.QPushButton()
self.pushButton.setText("Run")
self.mainLayout.addWidget(self.pushButton)
self.pushButton.clicked.connect(self.run)
def run(self):
print("ok")
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = Window()
mainWindow.setObjectName("mainWindow") # can be omitted.
mainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Related

Start the script in PyQt5 and python3 [duplicate]

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.

PYQT: mainwindow closes while having second and third window open

I need some help in my current problem.
I have a window with three buttons. When pressing two of them ("GraphicButton" and "QuestionButton") a second and third window shall open. This works fine. The third button "ImportButton" is for populating a table widget in the mainwindow with a dataframe from excel.
When I press the "ImportButton" first everything works fine.
BUT when I press one of the other buttons first to open the other windows and THEN press the "ImportButton", the mainwindow and the other ones will close.
I could use some advice here.
Here is my code for main.py:
import sys
import pandas as pd
import os
from qtpy import QtWidgets, QtCore, QtGui
from ui.mainwindow import Ui_MainWindow
from ui.graphwindow import Ui_GraphWindow
from ui.questionwindow import Ui_QuestionWindow
app = QtWidgets.QApplication(sys.argv)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowTitle("Test")
self.ui.ImportButton.clicked.connect(self.import_clicked)
self.ui.GraphicButton.clicked.connect(self.graphic_clicked)
self.ui.QuestionButton.clicked.connect(self.question_clicked)
def import_clicked(self):
self.ui.RefWidget.setSortingEnabled(False)
self.ui.LubWidget.setSortingEnabled(False)
self.ui.MixWidget.setSortingEnabled(False)
self.ui.RefWidget.setRowCount(0)
self.ui.LubWidget.setRowCount(0)
self.ui.MixWidget.setRowCount(0)
if self.ui.RefBoxC.currentIndex() == 0:
A = os.path.join(os.path.dirname(__file__),"./Datenbank/CO2.xlsx")
df = pd.read_excel(A)
self.ui.RefWidget.setColumnCount(len(df.columns))
self.ui.RefWidget.setRowCount(len(df.index))
for i in range(len(df.index)):
for j in range(len(df.columns)):
self.ui.RefWidget.setItem(i, j, QtWidgets.QTableWidgetItem(str(df.iat[i, j])))
self.ui.RefWidget.resizeColumnsToContents()
self.ui.RefWidget.resizeRowsToContents()
else:
A = os.path.join(os.path.dirname(__file__), "./Datenbank/PlatzhalterRef.xlsx")
df = pd.read_excel(A)
self.ui.RefWidget.setColumnCount(len(df.columns))
self.ui.RefWidget.setRowCount(len(df.index))
for i in range(len(df.index)):
for j in range(len(df.columns)):
self.ui.RefWidget.setItem(i, j, QtWidgets.QTableWidgetItem(str(df.iat[i, j])))
self.ui.RefWidget.resizeColumnsToContents()
self.ui.RefWidget.resizeRowsToContents()
self.ui.RefWidget.setSortingEnabled(True)
self.ui.LubWidget.setSortingEnabled(True)
self.ui.MixWidget.setSortingEnabled(True)
def graphic_clicked(self):
self.Gwindow = QtWidgets.QMainWindow()
self.ui = Ui_GraphWindow()
self.ui.setupUi(self.Gwindow)
self.Gwindow.show()
def question_clicked(self):
self.Questwindow = QtWidgets.QMainWindow()
self.ui = Ui_QuestionWindow()
self.ui.setupUi(self.Questwindow)
self.Questwindow.show()
window = MainWindow()
window.show()
sys.exit(app.exec_())
Disclaimer: the solution provided by this answer has not been tested and may be incomplete.
When you click either the GraphicButton or QuestionButton,
self.ui = Ui_GraphWindow()
or
self.ui = Ui_QuestionWindow()
This will modify self.ui. When the ImportButton is clicked, self.ui will refer to the other windows. This, I believe, is unintentional.
When the commands in import_clicked() are run, there may be references to undefined classes, widgets, and whatnot.
self.ui.RefWidget.setSortingEnabled(False)
I'm guessing RefWidget is not defined in Ui_QuestionWindow or Ui_GraphWindow.
To solve this, try adding
def import_clicked(self):
self.ui = Ui_MainWindow() # set self.ui to refer to a MainWindow
# ...
and see if it works. (Warning: a new MainWindow may be created and the old MainWindow be left forsaken. Again, I haven't tested this to see if it does create a new MainWindow but my intuition tells me that it does.)
EDIT: Above suggestion in strikethrough did not work for OP.
If further trouble and chaos ensues, consider having three separate member variables instead of a single self.ui.
def __init__(self, parent = None):
super().__init__(parent)
self.uiMain = Ui_MainWindow() # new member variable: self.uiMain
# ...
def import_clicked(self):
self.uiMain.RefWidget.setSortingEnabled(False)
self.uiMain.LubWidget.setSortingEnabled(False)
self.uiMain.MixWidget.setSortingEnabled(False)
# ...
def graphic_clicked(self):
self.Gwindow = QtWidgets.QMainWindow()
self.uiGraphic = Ui_GraphWindow() # new member variable: self.uiGraphic
# ...
def question_clicked(self):
self.Questwindow = QtWidgets.QMainWindow()
self.uiQuestion = Ui_QuestionWindow() # new member variable: self.uiQuestion
# ...

PyQt5 - How to draw a dot on mouse click position?

I am trying to draw a dot on my main window, but the dot is not shown.
I've tried bounding mousePressEvent to paintEvent, but it didn't work as well. Here's current version of my code(which is not working too). Also I tried place a point with drawPoint method and it didn't work too.
import sys
from PyQt5 import QtWidgets, QtGui, QtCore, uic
class GUI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('gui.ui', self)
self.setFixedSize(self.size())
self.show()
def mousePressEvent(self, e):
print(e.pos())
qp = QtGui.QPainter()
qp.begin(self)
qp.setPen(QtCore.Qt.red)
qp.drawEllipse(e.pos().x(), e.pos().y(), 10, 10)
qp.end()
self.update()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = GUI()
sys.exit(app.exec_())
I know that mousePressEvent is working since I get coords of the click.
I am okay to change methods of dot-placing or type of dots to place, but it should have customizable color and size.
You should only draw within the paintEvent method, and this paint does not save memory so if you want to graph several points you must store them in some container, for example using QPolygon.
paintEvent() is called every time you call update() or repaint(), for example it is called every time it is resized, the window is moved, etc.
import sys
from PyQt5 import QtWidgets, QtGui, QtCore, uic
class GUI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('gui.ui', self)
self.setFixedSize(self.size())
self.show()
self.points = QtGui.QPolygon()
def mousePressEvent(self, e):
self.points << e.pos()
self.update()
def paintEvent(self, ev):
qp = QtGui.QPainter(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing)
pen = QtGui.QPen(QtCore.Qt.red, 5)
brush = QtGui.QBrush(QtCore.Qt.red)
qp.setPen(pen)
qp.setBrush(brush)
for i in range(self.points.count()):
qp.drawEllipse(self.points.point(i), 5, 5)
# or
# qp.drawPoints(self.points)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = GUI()
sys.exit(app.exec_())

populating combo box with folders on disk using QFileSystemModel

Hi I have written this basic code trying to populate folders underneath the /Users/ directory, but I don't know what I am missing its not populating.
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am getting a / in the comobobox instead of the whole list of folders under /Users/ directory.
I think its better to use QFileSystemModel instead of using os.listdir interms of efficiency and will update the view if somebody updates folder or adds folder in the /Users/ directory !
Remember that QFileSystemModel is a hierarchical model, so you need to let the QComboBox know which QModelIndex represents the children you want to display. You do that with QComboBox.setRootModelIndex()
QFileSystemModel.setRootPath() conveniently returns the QModelIndex of the path you set.
So a small change is all you need (tested on Windows) -
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
index = fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
combo.setRootModelIndex(index)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

KeypressEvent not working, How to correct it?

I am trying to get keypressevent work with the following code
import sys,
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(QtGui.QWidget):
def __init__(self):
super(Ui_MainWindow, self).__init__()
def keyPressEvent(self, event):
print 'a'
def setupUi(self, MainWindow):
MainWindow.setObjectName(("MainWindow"))
MainWindow.resize(371, 345)
MainWindow.setMaximumSize(QtCore.QSize(401, 600))
MainWindow.setWindowIcon(QtGui.QIcon('icons/icon.png'))
screen = QtGui.QDesktopWidget().screenGeometry()
mysize = MainWindow.geometry()
hpos = ( screen.width() - mysize.width() ) / 2
vpos = ( screen.height() - mysize.height() ) / 2
MainWindow.move(hpos, vpos)
#some GUI
MainWindow.setCentralWidget(self.centralwidget)
cd=MainWindow.centralWidget()
cd.setFocusPolicy(QtCore.Qt.StrongFocus)
cd.setFocus()
self.actionHardware = QtGui.QAction(MainWindow)
self.actionHardware.setObjectName(("actionHardware"))
self.retranslateUi(MainWindow)
#COnnect odes
def retranslateUi(self, MainWindow):
#sime button text codes
if __name__=="__main__" :
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
The code was partially generated using QTDesigner. I noticed that replacing Mainwindow.show() by ui.show() enables keypressevent but at the cost of not showing any buttons i create in the Mainwindow central widget
It looks like the problem is in the way you are re-using the code output by Designer. You defined Ui_MainWindow.keyPressEvent, and created an instance "ui" of the class. However: "ui" is never directly incorporated into the GUI anywhere (ui.setupUi adds other widgets, but not itself, to MainWindow) and thus events are never delivered to ui.
My approach would look more like this:
class Ui_MainWindow(object): ## note this does not need to inherit QWidget
... ## and ideally, this code should not be changed
... ## after designer generates it
...
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def keyPressEvent(self, ev):
print "key press"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
MainWindow = Window()
MainWindow.show()
sys.exit(app.exec_())

Resources