im trying to make an application in python 2.7 with QtDesigner and PyQT 4.11.
In the application I want to have two QPushButtons that change the value of a QSpinBox when clicked. I've read several pages of StackOverflow answers concerning problems with the Signal Slot mechanic, but couldnt find an answer to my problem.
Im using pyuic4 to generate the Ui_W_Setup.py, here the rellevant part:
class Ui_W_Setup(object):
def setupUi(self, W_Setup):
W_Setup.setObjectName(_fromUtf8("W_Setup"))
self.Motorsteuerung = QtGui.QDockWidget(W_Setup)
self.Motorsteuerung.setEnabled(True)
self.Motorsteuerung.setMinimumSize(QtCore.QSize(140, 365))
self.Motorsteuerung.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
self.Motorsteuerung.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea)
self.Motorsteuerung.setObjectName(_fromUtf8("Motorsteuerung"))
self.dockWidgetContents = QtGui.QWidget()
self.dockWidgetContents.setObjectName(_fromUtf8("dockWidgetContents"))
self.B_ZuNehm = QtGui.QPushButton(self.dockWidgetContents)
self.B_ZuNehm.setGeometry(QtCore.QRect(40, 250, 81, 71))
self.B_ZuNehm.setObjectName(_fromUtf8("B_ZuNehm"))
self.B_ZuTast = QtGui.QPushButton(self.dockWidgetContents)
self.B_ZuTast.setGeometry(QtCore.QRect(40, 180, 81, 71))
self.B_ZuTast.setObjectName(_fromUtf8("B_ZuTast"))
self.SB_Position = QtGui.QDoubleSpinBox(self.dockWidgetContents)
self.SB_Position.setGeometry(QtCore.QRect(40, 21, 81, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.SB_Position.setFont(font)
self.SB_Position.setFrame(True)
self.SB_Position.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.SB_Position.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
self.SB_Position.setSpecialValueText(_fromUtf8(""))
self.SB_Position.setCorrectionMode(QtGui.QAbstractSpinBox.CorrectToNearestValue)
self.SB_Position.setDecimals(1)
self.SB_Position.setMaximum(50.0)
self.SB_Position.setSingleStep(0.2)
self.SB_Position.setProperty("value", 0.0)
self.SB_Position.setObjectName(_fromUtf8("SB_Position"))
Then I have a main python file to load the ui.py and execute the application:
import sys
from PyQt4 import QtGui,QtCore
from Setup import Ui_W_Setup
app = QtGui.QApplication(sys.argv)
class MainWindow(QtGui.QMainWindow, Ui_W_Setup):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
motor = MotorClass()
self.B_ZuNehm.clicked.connect(motor.zuNehm)
self.B_ZuTast.clicked.connect(motor.zuTast)
class MotorClass(QtCore.QObject):
def __init__(self):
super(MotorClass, self).__init__()
global window
confirm = QtGui.QMessageBox()
confirm.setText('Verfahrweg frei?')
confirm.setStandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)
confirm.setDefaultButton(QtGui.QMessageBox.Yes)
#QtCore.pyqtSlot() #shouldnt be needed with my current knowledge
def zuNehm(self):
print("TestNehm")
response = self.confirm.exec_()
if response == 16384: #seems to be the return value for "Yes"
window.SB_Position.setValue(float(1))
#QtCore.pyqtSlot() #shouldnt be needed aswell with my current knowledge
def zuTast(self):
print("TestTast")
response = self.confirm.exec_()
if response == 16384:
window.SB_Position.setValue(float(2))
def main():
global window #I dont like declaring it globally , is there a better way
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
There is no exeption thrown and the prints don't show up in the console aswell.
The signal connections don't work because you are not keeping a reference to the instance of MotorClass you created. When it gets garbage-collected, the signal connections are automatically removed.
The code below fixes this problem, as well as some other problems with the MotorClass (e.g. global references to the window variable).
class MainWindow(QtGui.QMainWindow, Ui_W_Setup):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
self.motor = MotorClass(self)
self.B_ZuNehm.clicked.connect(self.motor.zuNehm)
self.B_ZuTast.clicked.connect(self.motor.zuTast)
class MotorClass(QtCore.QObject):
def __init__(self, parent):
super(MotorClass, self).__init__(parent)
self.confirm = QtGui.QMessageBox()
self.confirm.setText('Verfahrweg frei?')
self.confirm.setStandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)
self.confirm.setDefaultButton(QtGui.QMessageBox.Yes)
def zuNehm(self):
print("TestNehm")
response = self.confirm.exec_()
if response == QtGui.QMessageBox.Yes:
self.parent().SB_Position.setValue(float(1))
def zuTast(self):
print("TestTast")
response = self.confirm.exec_()
if response == QtGui.QMessageBox.Yes:
self.parent().SB_Position.setValue(float(2))
def main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
For example this line>>
self.B_FileOpen.clicked.connect(fileSelect)
when you make this connection your function "fileSelect" still doesn't exists.
Try make your "connections" after you have all declared.
If it's not the "fileSelect" specifically it's some of the others. But this behavior is usually when you try to connect something you haven't still declared.
Related
Hy guys,
in my executable program there is a toolbar. Well, the user decides to move the toolbar. Now the toolbar is floating. I know I have to conntect the floating-signals that is emittted when the toolbar ist arranged by the user. How can I save the new position of the toolbar? I know the method of adding the toolbar to the main window with a position:self.addToolBar( Qt.LeftToolBarArea , toolbar_name). In the handle_floating()-method you see what I want: There I want to get the position currently, but how? You also see I have just added one member variable, named self.toolbar_pos, to hold the position of the toolbar. My idea is, when application is terminated I want to serialize this value to a file, and later, when application is ran again its will read that file and set the toolbar accordingly. But this is no problem. Currently I don't have no idea to get the position of the toolbar.
I need your help :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.toolbar_pos = None
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.addToolBar(self.toolbar )
self.toolbar.addAction(exitAction)
self.toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
| QtCore.Qt.BottomToolBarArea
| QtCore.Qt.LeftToolBarArea
| QtCore.Qt.RightToolBarArea)
self.addToolBar( QtCore.Qt.LeftToolBarArea , self.toolbar )
self.toolbar.topLevelChanged.connect(self.handle_floating)
def handle_floating(self, event):
# The topLevel parameter is true
# if the toolbar is now floating
if not event:
# If the toolbar no longer floats,
# then calculate the position where the
# toolbar is located currently.
self.toolbar_pos = None
print "Get position: ?"
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.setGeometry(300, 300, 300, 200)
ex.setWindowTitle('Toolbar example')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The QMainWindow class already has APIs for this: i.e. saveState and restoreState. These can be used to save and restore the state of all the toolbars and dock-widgets in your application.
To use them, you first need to make sure that all your toolbars and dock-widgets are given a unique object-name when they are created:
class Example(QtGui.QMainWindow):
...
def initUI(self):
...
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName('foobar')
Then you can override closeEvent to save the state:
class Example(QtGui.QMainWindow):
...
def closeEvent(self, event):
with open('/tmp/test.conf', 'wb') as stream:
stream.write(self.saveState().data())
(NB: I've just used a temporary file here for testing, but it would obviously be much better to use something like QSettings in your real application).
Finally, you can restore the state that was saved previously:
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.initUI()
try:
with open('/tmp/test.conf', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
except IOError:
pass
I have a PyQT gui that has one gui thread, and 24 "tester threads." They work fine, but still seem to stay up when I close the gui. How can I gracefully close the threads to avoid python crashing?
#!/usr/bin/python
# Standard Lib
from datetime import datetime
import logging
import os
import random
import sys
import time
# Third Party
from PyQt4 import QtGui, QtCore
# Local Kung Fu
stuff
class SSITesterThread(QtCore.QThread):
# vars for updating gui using signals
updateText = QtCore.pyqtSignal(str)
updateColor = QtCore.pyqtSignal(str)
updateSN = QtCore.pyqtSignal(str)
def __init__(self, thread_number, port, path, parent=None):
super(SSITesterThread, self).__init__(parent)
self.delay = random.random()
def run(self):
self.ssitester()
def ssitester(self):
# stuff
class SSITestSuiteGUI(QtGui.QMainWindow):
def __init__(self, parent=None):
self._threads = []
QtGui.QWidget.__init__(self, parent)
# Init class from template and paths
self.launch_tester_threads()
def init_gui_nodes(self, com_ports_list):
for num, port, in zip(range(1, 25), range(0, 24)):
label = getattr(self.ui, 'com_{}'.format(num))
label.setText("COM Port: {}".format(com_ports_list[port]["COM"]))
def launch_tester_threads(self):
logging.info("Spinning up threads...")
for num, com_port_chunk in zip(range(1, 25), self.com_ports_list):
tester_thread = SSITesterThread(thread_number=num, port=com_port_chunk["COM"], path=self.vc_test_path)
# get a reference to the associated textbox somehow...
status_box = getattr(self.ui, 'status_{}'.format(num))
tester_thread.updateText.connect(status_box.setText)
status_box = getattr(self.ui, 'status_{}'.format(num))
tester_thread.updateColor.connect(status_box.setStyleSheet)
sn_label = getattr(self.ui, 'sn_{}'.format(num))
tester_thread.updateSN.connect(sn_label.setText)
sn_label.setText("S/N: None")
tester_thread.start()
self._threads.append(tester_thread)
logging.info("Ready for tests.")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
test_suite = SSITestSuiteGUI()
test_suite.show()
# Close app only when window is closed.
sys.exit(app.exec_())
I tried implementing this solution: https://gist.githubusercontent.com/metalman/10721983/raw/15c6f115f9918fee7c1b88d0a549d4cc59a5b346/qapplication_about_to_quit_signal.py
But got an error:
attributeerror: 'function' object has no attribute '__pyqtSignature__'
Thanks for your time.
UPDATE:
Added the suggestion below as:
#QtCore.pyqtSlot()
def stop(self):
return
in my SSITesterThread Class, and it errors out when various "emits" I've used as singals in the thread sudden try to access NoneType objects:
File in gui.py, line 75 in tester,
self.updateColor.emit("{}".format(thread_colors.green_alert)
AttributeError: "NoneType" object has no attribute 'green alert'
Did the fix work, and this is a new problem? Because it seems like things still aren't shutting down gracefully.
It looks like you're importing your GUI from a form generated by QTDesigner. Try this:
self.ui.closeEvent = self.closeEvent
I believe your problem is that you're editing the wrong QMainWindow instance in the wrong place.
You probably just need to decorate the stop method as a slot.
#QtCore.pyqtSlot()
def stop(self):
# do thread cleanup, stop thread
I'm learning about GUI python using pyQt4. I have function A in another file python. and I want to run in GUI file python that I extracted from file .ui (output of designer pyQt4). How to create activity indicator which is active when the function A is running? can I use progress bar (in pyQt4 designer) without know how many time for my function A running?
Thank you.
this is the function to call A in GUI .py:
def RunFunction():
import Kdtree
_dir = kdTreeOk.getNeighbor(float(radius)) #function 'A'
file = file_open('Summary.txt',_dir) # ignore, just file to save result of `A`
with file:
textOutput=file.read()
ui.result.setPlainText(textOutput)
#### button to run RunFunction in file GUI .py
ui._run.clicked.connect(RunFunction)
QProgressDialog is made for this purpose and generally called via QThread. Here's a (messy) basic example to show how this can work (without any threading). If you are calling this dialog from another window, just set parent as the calling window and you can read attributes in this dialog by calling self.parent.some_variable.
EDITED to work properly ;).
from PyQt4 import QtCore, QtGui
from time import sleep
import sys
class ProgressBarWidget(QtGui.QProgressDialog):
def __init__(self, parent=None, app=None):
super(ProgressBarWidget, self).__init__(parent)
self.app=app
self._allow_close = True
layout = QtGui.QVBoxLayout(self)
# Create a progress bar and a button and add them to the main layout
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setRange(0,100)
layout.addWidget(self.progressBar)
self.button = QtGui.QPushButton("Start", self)
layout.addWidget(self.button)
self.button.clicked.connect(self.onStart)
self.upload_count = 10
def onStart(self):
self.progressBar.setValue(0)
self.button.setText("Uploading...")
self.run()
def makeProgress(self, current_num, total_num, message = ''):
if total_num == current_num:
self.onFinished()
elif current_num == 0:
self.progressBar.setValue(0)
else:
multiplier = int(float(float(100) / float(total_num)))
c_times_m = current_num * multiplier
for i in xrange(c_times_m - int(self.progressBar.value())):
new_val = int(self.progressBar.value()) + 1
self.progressBar.setValue(new_val)
sleep(.01)
def onFinished(self):
# progress complete
self.progressBar.setRange(0,100)
for i in xrange(int(self.progressBar.value()),101):
self.progressBar.setValue(i)
self.button.setEnabled(True)
self.button.setText('Exit')
self.button.clicked.disconnect(self.onStart)
self.button.clicked.connect(self.close)
def run(self):
self._allow_close = False
self.button.setDisabled(True)
total = self.upload_count * 2
progress_meter = 0
downloaded = []
tests_to_upload = 10
for each in xrange(tests_to_upload):
sleep(0.15)
progress_meter += 1
self.makeProgress(progress_meter,total)
sleep(0.2)
progress_meter += 1
self.makeProgress(progress_meter, total)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = ProgressBarWidget(app=app)
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
I am attempting to start a wxPython application but I want a banner to be displayed before it is started.
One way to do this is to start a wxPython application which in turn starts another wxPython application, the reason for doing it this way is since the App part of the second wxPython application needs to do some processing before starting and may take some time.
The issue is how do I start the other application and know that it has started?
Currently I do this which blocks for the entire GUI session:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
I have attempted to do the following but the frame of the first application does not seem to close:
loadCompleted, EVT_LOAD_COMPLETED = wx.lib.newevent.NewEvent()
class MyRegion(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="My Region")
self.label = wx.StaticText(self, label="Hello, World!")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.label, 0, wx.ALL, 5)
self.SetSizer(sizer)
self.startThread = Thread(target=self.Start)
self.startThread.start()
self.Bind(EVT_LOAD_COMPLETED, self.OnClose)
def OnClose(self, result):
self.Close()
def Start(self):
try:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
except:
pass
wx.PostEvent(self, loadCompleted(result=(None)))
if __name__ == "__main__":
app = wx.App()
frame = MyRegion(None)
frame.Show()
app.MainLoop()
I don't think starting a second wxPython application is the way to go. Instead, I would just load the banner inside your frame's __init__ method, then do your processing. When the processing finishes, you can destroy the banner and show your main app. Here's some psuedo-code:
#
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
banner = MyBanner()
# do a bunch of stuff
banner.Destroy() # or banner.Close()
self.Show()
Now if the processing takes a really long time, you can put that into a thread and have the thread send a message back to the UI that tells it that the thread is finished. When the app receives the message, it can close the banner in the handler and show the App at that point. Please note that you need to use a thread-safe method, such as wx.CallAfter or wx.PostEvent.
Check out the following articles for ideas:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
I'm using Python 2.7 with PyQt 4.0.
I'm trying to make a QGraphicsRectItem move 10 px up in a animation. I have read the documentation and several tutorials but I can't get it to work. What is wrong with my code?
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import random
class TestWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene)
self.button1 = QPushButton("Do test")
self.button2 = QPushButton("Move forward 10")
layout = QVBoxLayout()
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(self.button1)
buttonLayout.addWidget(self.button2)
buttonLayout.addStretch()
layout.addWidget(self.view)
layout.addLayout(buttonLayout)
self.setLayout(layout)
self.button1.clicked.connect(self.do_test)
self.button2.clicked.connect(self.move_forward)
def do_test(self):
self.turtle = self.scene.addRect(0,0,10,20)
def move_forward(self):
animation = QGraphicsItemAnimation()
timeline = QTimeLine(1000)
timeline.setFrameRange(0,100)
animation.setTimeLine(timeline)
animation.setItem(self.turtle)
animation.setPosAt(1.0, QPointF(self.turtle.x(),self.turtle.y()+10))
timeline.start()
Thanks for the help!
The reason why your example doesn't work, is that you are not keeping a reference to the QGraphicsItemAnimation created in the move_forward method, and so it gets garbage-collected before it has a chance to do anything.
I would suggest you create the animation in __init__ so that you can access it later as an instance attribute:
def __init__(self, parent=None):
...
self.animation = QGraphicsItemAnimation()
def move_forward(self):
timeline = QTimeLine(1000)
timeline.setFrameRange(0, 100)
self.animation.setTimeLine(timeline)
self.animation.setItem(self.turtle)
self.animation.setPosAt(
1.0, QPointF(self.turtle.x(), self.turtle.y() + 10))
timeline.start()
try this small change (in function move_forward).
replace
animation = QGraphicsItemAnimation()
with
animation = QGraphicsItemAnimation(self)
that changes the behaviour for me.