In the code below, when I load the GridscanCanvas' pixmap with the function get_core_data it works perfectly fine, no crashes, and the image is then displayed in my app.
However, when I want to update that same pixmap in the function get_square_data, it crashes every time.
Both times, the original image is an np.array and the only difference between them is the size of the array. I've commented on the code where it crashes.
I'm very lost, since both functions are nearly identical. Any help would be greatly appreciated!
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os
import numpy as np
from mdoc_class import Gridscan_mdoc
from mrc_class import Gridscan_mrc
from em_ss_find_gridsquares import find_gridsquares
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setGeometry(100,100,1000,1000)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.gridscan_canvas = GridscanCanvas(self)
self.init_menu()
self.pixel_size = 92.7
self.gridsquare_length = 85000
self.spacing_length = 40000
self.show()
def init_menu(self):
bar = self.menuBar()
self.file = bar.addMenu('File')
load_mdoc_action = QAction('Load mdoc', self)
self.file.addAction(load_mdoc_action)
self.file.triggered.connect(self.file_respond)
find_squares_action = QAction('Find squares', self)
self.file.addAction(find_squares_action)
def file_respond(self, q):
signal = q.text()
if signal == 'Load mdoc':
mdoc_file = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('Home'),
"MDOC(*.mdoc);;AllFiles(*.*)")[0]
if mdoc_file:
try:
self.load_core_data = LoadCoreData(mdoc_file)
except Exception as e:
print(e)
return
try:
self.load_core_data.new_output.connect(self.get_core_data)
except Exception as e:
print(e)
return
try:
self.load_core_data.start()
except Exception as e:
print(e)
return
elif signal == 'Find squares':
try:
self.find_squares = FindGridsquares(self.mrc.gridscan, self.pixel_size, self.gridsquare_length,
self.spacing_length)
except Exception as e:
print(e)
return
try:
self.find_squares.new_output.connect(self.get_square_data)
except Exception as e:
print(e)
return
try:
self.find_squares.start()
except Exception as e:
print(e)
return
else:
return
def get_core_data(self, core_data):
self.mrc = core_data['mrc']
self.mdoc = core_data['mdoc']
# This here works fine
self.gridscan_canvas.pixmap = QPixmap(QImage(self.mrc.gridscan, self.mrc.gridscan.shape[1],
self.mrc.gridscan.shape[0], QImage.Format_Grayscale8))
self.gridscan_canvas.update()
def get_square_data(self, square_data):
self.centres = square_data['centres']
self.aligned_centres = square_data['aligned_centres']
self.aligned_gridscan = square_data['aligned_gridscan']
self.rot_angle = square_data['rot_angle']
self.sqrd_centres = square_data['sqrd_centres']
# This here crashes is where it crashes
self.gridscan_canvas.pixmap = QPixmap(QImage(self.aligned_gridscan, self.aligned_gridscan.shape[1],
self.aligned_gridscan.shape[0], QImage.Format_Grayscale8))
self.gridscan_canvas.update()
class GridscanCanvas(QWidget):
DELTA = 10
def __init__(self, *args, **kwargs):
super(GridscanCanvas, self).__init__(*args, **kwargs)
self.draggin_idx = -1
self.points_old = np.array([[v * 5, v * 5] for v in range(75)], dtype=np.float)
self.width = 700
self.height = 700
self.setFixedSize(self.width, self.height)
self.pixmap = None
self.points = None
self.scaling_factor = [1, 1]
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
if self.pixmap is not None:
self.pixmap = self.pixmap.scaled(self.width, self.height, transformMode=Qt.SmoothTransformation)
qp.drawPixmap(0, 0, self.width, self.height, self.pixmap)
else:
self.scaling_factor = [1, 1]
if self.points is not None:
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
pen = QPen()
pen.setWidth(5)
pen.setColor(Qt.red)
qp.setPen(pen)
for x,y in self.points:
qp.drawPoint(x,y)
class LoadCoreData(QThread):
new_output = pyqtSignal(object)
def __init__(self, mdoc_filepath):
QThread.__init__(self)
self.isRunning = True
self.mdoc_filepath = mdoc_filepath
def __del__(self):
self.wait()
def run(self):
mdoc = Gridscan_mdoc(self.mdoc_filepath)
mrc = Gridscan_mrc(mdoc)
output = {'mdoc': mdoc, 'mrc': mrc}
self.new_output.emit(output)
class FindGridsquares(QThread):
new_output = pyqtSignal(object)
def __init__(self, gridscan, pixel_size, gridsquare_length, spacing_length):
QThread.__init__(self)
self.isRunning = True
self.gridscan = gridscan
self.pixel_size = pixel_size
self.gridsquare_length = gridsquare_length
self.spacing_length = spacing_length
def __del__(self):
self.wait()
def run(self):
centres, aligned_centres, aligned_gridscan, rot_angle, sqrd_centres =find_gridsquares(self.gridscan, self.pixel_size, self.gridsquare_length, self.spacing_length)
output = {'centres': centres, 'aligned_centres': aligned_centres, 'aligned_gridscan': aligned_gridscan, 'rot_angle': rot_angle, 'sqrd_centres': sqrd_centres}
self.new_output.emit(output)
app = QApplication([])
window = MainWindow()
app.exec_()
Related
So , I am trying to blink the push button with the color that I pass and between white. And it only seems to blink it so many times after that code would crash.
I have tried it to implement the blinking with different blink rates and it
would it still break at some time.
in 'a' I have the string like "Background-color: rgb(255,0,0)".
in 'timings' i have a list like[208, 280]or it could be [48,32,48,32,100,280], this represents the on and off timings 'zeroth index' represents on and 'index 1' represents off time and follows the pattern so on.
while True:
i = 0
while i < len(timings):
if self.p[2] == 1:
self.b.setStyleSheet("{}".format(a))
self.b.update()
time.sleep(timings[i]/1000)
self.b.setStyleSheet("Background-color: rgb(255,255,255)")
self.b.update()
time.sleep(timings[i+1]/1000)
i = i + 2
self.head1, self.head2, self.head3 all have list with some thing like this ["Background-color:rgb(255,0,0)",list of on and off patterns, head number#]
right now, I am working with three heads.
def flash(self):
obj1 = threads(self.head1, self.head1_pb)
obj2 = threads(self.head2, self.head2_pb)
obj3 = threads(self.head3, self.head3_pb)
obj1.start()
time.sleep(.02)
obj2.start()
time.sleep(.02)
obj3.start()
class threads(Thread):
def __init__(self, a, pb):
Thread.__init__(self)
self.p = a
self.b = pb
def run(self):
a = self.p[0]
timings = self.p[1]
print(timings[0])
while True:
i = 0
while i < len(timings):
if self.p[2] == 1:
self.b.setStyleSheet("{}".format(a))
self.b.update()
time.sleep(timings[i]/1000)
self.b.setStyleSheet("Background-color: rgb(255,255,255)")
self.b.update()
time.sleep(timings[i+1]/1000)
i = i + 2
elif self.p[2] == 2:
self.b.setStyleSheet("{}".format(a))
self.b.update()
time.sleep(timings[i]/1000)
self.b.setStyleSheet("Background-color: rgb(255,255,255)")
self.b.update()
time.sleep(timings[i+1]/1000)
i = i + 2
else:
self.b.setStyleSheet("{}".format(a))
self.b.update()
time.sleep(timings[i]/1000)
self.b.setStyleSheet("Background-color: rgb(255,255,255)")
self.b.update()
time.sleep(timings[i+1]/1000)
i = i + 2
You can change the color as many times as you want, the problem is that you should not use a time consuming loop or use time.sleep() because they block the eventloop generating the GUI to freeze. Instead, use a QTimer to call the task that changes color every so often.
In the following example, create a custom button that implements what you want:
from PySide2 import QtCore, QtGui, QtWidgets
class PushButton(QtWidgets.QPushButton):
def __init__(self, *args, **kwargs):
super(PushButton, self).__init__(*args, **kwargs)
self._color = QtGui.QColor("white")
timer_on = QtCore.QTimer(singleShot=True, timeout=self.on_timeout)
timer_off = QtCore.QTimer(singleShot=True, timeout=self.on_timeout)
self._timers = (timer_on, timer_off)
for timer, function in zip(self._timers, (self.on, self.off)):
timer.timeout.connect(function)
def setTime(self, on_time, off_time):
for t, timer in zip((on_time, off_time), self._timers):
timer.setInterval(t)
#QtCore.Slot()
def on_timeout(self):
timer = self.sender()
if timer not in self._timers:
return
timer_on, timer_off = self._timers
another_timer = timer_off if timer is timer_on else timer_on
another_timer.start()
def start(self):
timer_on, _ = self._timers
timer_on.start()
def stop(self):
for timer in self._timers:
timer.stop()
self.off()
def color(self):
return self._color
def setColor(self, color):
if self.color() == color:
return
self._color = color
def on(self):
self.setStyleSheet(
"""PushButton{ background-color: %s}""" % (self.color().name(),)
)
def off(self):
self.setStyleSheet(
"""PushButton{ background-color: rgb(255,255,255)}"""
)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button = PushButton()
button.setColor(QtGui.QColor("salmon"))
button.setTime(208, 280)
button.start()
# stop blink in 30 seconds
# QtCore.QTimer.singleShot(30 * 1000, button.stop)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
w = Widget()
w.show()
sys.exit(app.exec_())
Plus:
As you note that you have a lot of data, it is best to create an iterator to save memory. Considering the above I have created a QPushButton that obtains the data from the iterator.
import random
from functools import partial
from PySide2 import QtCore, QtGui, QtWidgets
def generate_data():
i = 0
while i < 1000000:
color_on = random.randint(10, 500)
color_off = random.randint(10, 500)
color = QtGui.QColor(*random.sample(range(255), 3))
yield color_on, color_off, color
i += 1
class PushButton(QtWidgets.QPushButton):
def __init__(self, *args, **kwargs):
super(PushButton, self).__init__(*args, **kwargs)
self._color = QtGui.QColor("white")
self._generator = None
self.m_timer = QtCore.QTimer(
self, timeout=self.on_timeout, singleShot=True
)
def setGenerator(self, generator):
self._generator = generator
def start(self):
self.on_timeout()
#QtCore.Slot()
def on_timeout(self):
try:
time_on, time_off, color = next(self._generator)
self.setColor(color)
self.m_timer.start(time_on + time_off)
QtCore.QTimer.singleShot(
time_on, partial(self.setColor, QtGui.QColor("white"))
)
except StopIteration:
self.m_timer.stop()
def setColor(self, color):
self.setStyleSheet(
"""PushButton{ background-color: %s}""" % (color.name(),)
)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
for _ in range(6):
button = PushButton()
button.setGenerator(generate_data())
button.start()
lay.addWidget(button)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
w = Widget()
w.resize(320, 240)
w.show()
sys.exit(app.exec_())
I want to show a spinner while a task is being performed in my pyqt5 application. I found this nice implementation of a spinner, so I tried it: https://github.com/z3ntu/QtWaitingSpinner
The demo works ok, but in the demo the spinner is shown into an empty area of the dialog. I would like it to be an overlay which covers the whole dialog.
The author of QtWaitingSpinner suggests that "As an alternative example, the code below will create a spinner that (1) blocks all user input to the main application for as long as the spinner is active, (2) automatically centers itself on its parent widget every time "start" is called and (3) makes use of the default shape, size and color settings." with the following code:
spinner = QtWaitingSpinner(self, True, True, Qt.ApplicationModal)
spinner.start() # starts spinning
But I tried this implementation as an example, and it didn't work:
from PyQt5.QtWidgets import QApplication, QDialog, QTabWidget, QWidget, QGroupBox, QPushButton, QVBoxLayout
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
import requests
import urllib
from waitingspinnerwidget import QtWaitingSpinner
class DownloadDataDialog(QDialog):
def __init__(self, parent=None):
super(DownloadDataDialog, self).__init__(parent)
self.spinner = QtWaitingSpinner(self, True, True, Qt.ApplicationModal)
tabWidget = QTabWidget(self)
tabWidget.addTab(MyTab(tabWidget), "MyTab")
mainLayout = QVBoxLayout()
mainLayout.addWidget(tabWidget)
self.setLayout(mainLayout)
self.setWindowTitle("Download option chain data from web")
class MyTab(QWidget):
def __init__(self, parent=None):
super(MyTab, self).__init__(parent)
dataGroup = QGroupBox('Data')
getButton = QPushButton('Download')
getButton.clicked.connect(self.download_data)
dataLayout = QVBoxLayout()
dataLayout.addWidget(getButton)
dataGroup.setLayout(dataLayout)
mainLayout = QVBoxLayout()
mainLayout.addWidget(dataGroup)
mainLayout.addStretch(1)
self.setLayout(mainLayout)
def download_data(self):
self.parent().parent().parent().spinner.start()
url = 'http://www.meff.es/docs/Ficheros/Descarga/dRV/RV180912.zip'
filepath = None
try:
filepath = self.download_data_file(url)
except Exception as e:
print(e)
self.parent().parent().parent().spinner.stop()
if filepath:
#TODO doing stuff here
self.parent().parent().parent().close()
else:
pass #TODO show error dialog
def download_data_file(self, download_url):
# Build request URL and download the file
destination = 'test.zip'
urllib.request.urlretrieve(download_url, destination)
return destination
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
tabdialog = DownloadDataDialog()
tabdialog.show()
sys.exit(app.exec_())
So my intention is creating an invisible layer, setting the spinner as its only widget, and showing the translucid layer over the whole dialog window.
Any idea of how I should do that?
Once I also had that problem so I modified the library, first activate the flags: QtCore.Qt.Dialog | QtCore.Qt.FramelessWindowHint, and the other change must be done in updatePosition() method:
def updatePosition(self):
if self.parentWidget() and self._centerOnParent:
parentRect = QtCore.QRect(self.parentWidget().mapToGlobal(QtCore.QPoint(0, 0)), self.parentWidget().size())
self.move(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, self.size(), parentRect).topLeft())
The result is as follows:
waitingspinnerwidget.py
import math
from PyQt5 import QtCore, QtGui, QtWidgets
class QtWaitingSpinner(QtWidgets.QWidget):
def __init__(self, parent=None, centerOnParent=True, disableParentWhenSpinning=False, modality=QtCore.Qt.NonModal):
super().__init__(parent, flags=QtCore.Qt.Dialog | QtCore.Qt.FramelessWindowHint)
self._centerOnParent = centerOnParent
self._disableParentWhenSpinning = disableParentWhenSpinning
# WAS IN initialize()
self._color = QtGui.QColor(QtCore.Qt.black)
self._roundness = 100.0
self._minimumTrailOpacity = 3.14159265358979323846
self._trailFadePercentage = 80.0
self._revolutionsPerSecond = 1.57079632679489661923
self._numberOfLines = 20
self._lineLength = 10
self._lineWidth = 2
self._innerRadius = 10
self._currentCounter = 0
self._isSpinning = False
self._timer = QtCore.QTimer(self)
self._timer.timeout.connect(self.rotate)
self.updateSize()
self.updateTimer()
self.hide()
# END initialize()
self.setWindowModality(modality)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
def paintEvent(self, QPaintEvent):
self.updatePosition()
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), QtCore.Qt.transparent)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
if self._currentCounter >= self._numberOfLines:
self._currentCounter = 0
painter.setPen(QtCore.Qt.NoPen)
for i in range(0, self._numberOfLines):
painter.save()
painter.translate(self._innerRadius + self._lineLength, self._innerRadius + self._lineLength)
rotateAngle = float(360 * i) / float(self._numberOfLines)
painter.rotate(rotateAngle)
painter.translate(self._innerRadius, 0)
distance = self.lineCountDistanceFromPrimary(i, self._currentCounter, self._numberOfLines)
color = self.currentLineColor(distance, self._numberOfLines, self._trailFadePercentage,
self._minimumTrailOpacity, self._color)
painter.setBrush(color)
painter.drawRoundedRect(QtCore.QRect(0, -self._lineWidth / 2, self._lineLength, self._lineWidth), self._roundness,
self._roundness, QtCore.Qt.RelativeSize)
painter.restore()
def start(self):
self.updatePosition()
self._isSpinning = True
self.show()
if self.parentWidget and self._disableParentWhenSpinning:
self.parentWidget().setEnabled(False)
if not self._timer.isActive():
self._timer.start()
self._currentCounter = 0
def stop(self):
self._isSpinning = False
self.hide()
if self.parentWidget() and self._disableParentWhenSpinning:
self.parentWidget().setEnabled(True)
if self._timer.isActive():
self._timer.stop()
self._currentCounter = 0
def setNumberOfLines(self, lines):
self._numberOfLines = lines
self._currentCounter = 0
self.updateTimer()
def setLineLength(self, length):
self._lineLength = length
self.updateSize()
def setLineWidth(self, width):
self._lineWidth = width
self.updateSize()
def setInnerRadius(self, radius):
self._innerRadius = radius
self.updateSize()
def color(self):
return self._color
def roundness(self):
return self._roundness
def minimumTrailOpacity(self):
return self._minimumTrailOpacity
def trailFadePercentage(self):
return self._trailFadePercentage
def revolutionsPersSecond(self):
return self._revolutionsPerSecond
def numberOfLines(self):
return self._numberOfLines
def lineLength(self):
return self._lineLength
def lineWidth(self):
return self._lineWidth
def innerRadius(self):
return self._innerRadius
def isSpinning(self):
return self._isSpinning
def setRoundness(self, roundness):
self._roundness = max(0.0, min(100.0, roundness))
def setColor(self, color=QtCore.Qt.black):
self._color = QColor(color)
def setRevolutionsPerSecond(self, revolutionsPerSecond):
self._revolutionsPerSecond = revolutionsPerSecond
self.updateTimer()
def setTrailFadePercentage(self, trail):
self._trailFadePercentage = trail
def setMinimumTrailOpacity(self, minimumTrailOpacity):
self._minimumTrailOpacity = minimumTrailOpacity
def rotate(self):
self._currentCounter += 1
if self._currentCounter >= self._numberOfLines:
self._currentCounter = 0
self.update()
def updateSize(self):
size = (self._innerRadius + self._lineLength) * 2
self.setFixedSize(size, size)
def updateTimer(self):
self._timer.setInterval(1000 / (self._numberOfLines * self._revolutionsPerSecond))
def updatePosition(self):
if self.parentWidget() and self._centerOnParent:
parentRect = QtCore.QRect(self.parentWidget().mapToGlobal(QtCore.QPoint(0, 0)), self.parentWidget().size())
self.move(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, self.size(), parentRect).topLeft())
def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines):
distance = primary - current
if distance < 0:
distance += totalNrOfLines
return distance
def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, colorinput):
color = QtGui.QColor(colorinput)
if countDistance == 0:
return color
minAlphaF = minOpacity / 100.0
distanceThreshold = int(math.ceil((totalNrOfLines - 1) * trailFadePerc / 100.0))
if countDistance > distanceThreshold:
color.setAlphaF(minAlphaF)
else:
alphaDiff = color.alphaF() - minAlphaF
gradient = alphaDiff / float(distanceThreshold + 1)
resultAlpha = color.alphaF() - gradient * countDistance
# If alpha is out of bounds, clip it.
resultAlpha = min(1.0, max(0.0, resultAlpha))
color.setAlphaF(resultAlpha)
return color
With the above we solve one of those problems, the other problem is that urllib.request.urlretrieve() is blocking so it will cause the GUI to freeze, so the solution is to move it to another thread, using a previous response we can do it in the following way:
from PyQt5 import QtCore, QtGui, QtWidgets
import urllib.request
from waitingspinnerwidget import QtWaitingSpinner
class RequestRunnable(QtCore.QRunnable):
def __init__(self, url, destination, dialog):
super(RequestRunnable, self).__init__()
self._url = url
self._destination = destination
self.w = dialog
def run(self):
urllib.request.urlretrieve(self._url, self._destination)
QMetaObject.invokeMethod(self.w, "FinishedDownload", QtCore.Qt.QueuedConnection)
class DownloadDataDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(DownloadDataDialog, self).__init__(parent)
self.spinner = QtWaitingSpinner(self, True, True, QtCore.Qt.ApplicationModal)
tabWidget = QtWidgets.QTabWidget(self)
tabWidget.addTab(MyTab(), "MyTab")
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addWidget(tabWidget)
self.setWindowTitle("Download option chain data from web")
class MyTab(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyTab, self).__init__(parent)
dataGroup = QtWidgets.QGroupBox('Data')
getButton = QtWidgets.QPushButton('Download')
getButton.clicked.connect(self.download_data)
dataLayout = QtWidgets.QVBoxLayout(self)
dataLayout.addWidget(getButton)
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addWidget(dataGroup)
mainLayout.addStretch(1)
def download_data(self):
self.parentWidget().window().spinner.start()
url = 'http://www.meff.es/docs/Ficheros/Descarga/dRV/RV180912.zip'
destination = 'test.zip'
runnable = RequestRunnable(url, destination, self)
QtCore.QThreadPool.globalInstance().start(runnable)
#QtCore.pyqtSlot()
def FinishedDownload(self):
self.parentWidget().window().spinner.stop()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
tabdialog = DownloadDataDialog()
tabdialog.show()
sys.exit(app.exec_())
I have a situation where a Gui is active and a QThread.
The QThread is getting and saving data in the background all the time, it should not stop doing this.
Now I want to process the latest data from the QThread without interrupting the QThread or freezing the Gui.
So I think I need another thread to do this?!
How can I pass its data to another thread and do something with it?
import sys, random, time
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
self.setWindowTitle('Window')
#getData
self.myGetData = getData()
self.myGetData.start()
self.show()
class getData(QThread):
#This Thread should run all the time
def run(self):
while True:
myNumber = random.randint(0, 100)
#He 'processData Thread'!! Do Something with mynumber!!
time.sleep(1)
class processData(QThread):
def processNumber(self, myNumber):
#Extremly complex code will execute while 'getData' is doing its thing.
newNumber = myNumber * 10
return newNumber
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Ui()
sys.exit(app.exec_())
Found a lot of examples on how to pass data from a QThread to the interface (signals).. But not the other way around.
The signals are confusing me a bit in this case..
I would like to propose do next:
import sys, random, time
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5 import Qt #+
class getData(QThread):
#This Thread should run all the time
threadSignalGetData = pyqtSignal(int)
def __init__(self, myNumber):
super().__init__()
self.myNumber = myNumber
def run(self):
#myNumber = 0
while True:
#He 'processData Thread'!! Do Something with mynumber!!
Qt.QThread.msleep(1000)
self.myNumber += 1
self.threadSignalGetData.emit(self.myNumber)
class MsgBoxGetData(Qt.QDialog):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.label = Qt.QLabel("")
layout.addWidget(self.label)
close_btn = Qt.QPushButton("Close window GetData")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 65, 400, 80)
self.setWindowTitle('MsgBox GetData')
self.setStyleSheet("""QLabel{
font-family:'Consolas';
color: green;
font-size: 16px;}""")
class processData(QThread):
threadSignalProcessData = pyqtSignal(int, int)
def __init__(self, myNumber):
super().__init__()
self.newNumber = myNumber
def run(self):
flagProcessData = True
while flagProcessData:
#Extremly complex code will execute while 'getData' is doing its thing.
newNumber = self.newNumber * 10
self.threadSignalProcessData.emit(self.newNumber, newNumber)
flagProcessData = False
class MsgBoxProcessData(Qt.QDialog):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.label = Qt.QLabel("")
layout.addWidget(self.label)
close_btn = Qt.QPushButton("Close window ProcessData")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 200, 400, 80)
self.setWindowTitle('MsgBox ProcessData')
self.setStyleSheet("""QLabel{
font-family:'Consolas';
color: red;
font-size: 24px;}""")
class Ui(Qt.QWidget):
def __init__(self, parent=None):
super(Ui, self).__init__(parent)
layout = Qt.QVBoxLayout(self)
self.lbl = Qt.QLabel("process GUI")
layout.addWidget(self.lbl)
self.btnA = Qt.QPushButton("Start getData")
layout.addWidget(self.btnA)
self.btnB = Qt.QPushButton("Start processData")
layout.addWidget(self.btnB)
self.setGeometry(550, 65, 300, 300)
self.setWindowTitle('Window')
self.btnA.clicked.connect(self.usingGetData)
self.btnB.clicked.connect(self.usingProcessData)
self.myNumber = 0
self.newNumber = None
self.msgGetData = MsgBoxGetData()
self.threadGetData = None
self.msgProcessData = MsgBoxProcessData()
self.threadProcessData = None
self.counter = 0
self.timer = Qt.QTimer()
self.timer.setInterval(1000)
# -------- timeout -------> def recurring_timer(self):
self.timer.timeout.connect(self.recurring_timer)
self.timer.start()
self.setStyleSheet("""QLabel{
font-family:'Consolas';
color: blue;
font-size: 20px;}""")
self.show()
def recurring_timer(self):
self.counter += 1
self.lbl.setText("process GUI: %d" % self.counter)
# ---- getData(QThread) -----------------------------------------------------#
def usingGetData(self):
if self.threadGetData is None:
self.threadGetData = getData(self.myNumber)
self.threadGetData.threadSignalGetData.connect(self.on_threadSignalGetData)
self.threadGetData.finished.connect(self.finishedGetData)
self.threadGetData.start()
self.btnA.setText("Stop getData(QThread)")
else:
self.threadGetData.terminate()
self.threadGetData = None
self.btnA.setText("Start getData(QThread)")
def finishedGetData(self):
self.threadGetData = None
self.btnA.setText("Start getData(QThread)")
def on_threadSignalGetData(self, value):
self.myNumber = value
self.msgGetData.label.setText(str(self.myNumber))
if not self.msgGetData.isVisible():
self.msgGetData.show()
# --END-- getData(QThread) -------------------#
# ---- processData(QThread) -----------------------------------------------------#
def usingProcessData(self):
if self.threadProcessData is None:
self.threadProcessData = processData(self.myNumber)
self.threadProcessData.threadSignalProcessData.connect(self.on_threadSignalProcessData)
self.threadProcessData.finished.connect(self.finishedProcessData)
self.threadProcessData.start()
self.btnB.setText("Stop processData(QThread)")
else:
self.threadProcessData.terminate()
self.threadProcessData = None
self.btnB.setText("Start processData(QThread)")
def finishedProcessData(self):
self.threadProcessData = None
self.btnB.setText("Start processData(QThread)")
def on_threadSignalProcessData(self, value1, value2):
self.newNumber = value2
self.msgProcessData.label.setText(str(value1)+" * 10 = "+str(self.newNumber))
if not self.msgProcessData.isVisible():
self.msgProcessData.show()
# --END-- processData(QThread) -------------------#
# -------------------- closeEvent --------------------------------------- #
def closeEvent(self, event):
reply = Qt.QMessageBox.question\
(self, 'Question',
"QUIT ?",
Qt.QMessageBox.Yes,
Qt.QMessageBox.No)
if reply == Qt.QMessageBox.Yes:
# close getData(QThread)
if self.threadGetData:
self.threadGetData.terminate()
self.msgGetData.close()
# close processData(QThread)
if self.threadProcessData:
self.threadProcessData.terminate()
self.msgProcessData.close()
#event.accept()
super().closeEvent(event)
else:
event.ignore()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Ui()
sys.exit(app.exec_())
I'm trying to create small GUI application, where user will be able to draw points and curves. I implemented scale option.
I would like to scale only paths, not graphic representations of the points (RBNode class). Is there way to scale QGraphicsView with exception of RBNode class?
My current approach is to pass factor attribute created in wheelEvent method inside RBGraphicView class (which inherits from QGraphicsView) into the instances of RBNode and use it to redraw RBNode.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import math
from PySide.QtGui import *
from PySide.QtCore import *
class RBNode(QGraphicsItem):
def __init__(self, factorView = 1):
super(RBNode, self).__init__()
self.factor = factorView
self.pressed = False
self.x = self.pos().x()
self.y = self.pos().y()
self.setFlag(QGraphicsItem.ItemIsMovable)
def boundingRect(self):
self.update()
return QRectF(-5*self.factor,-5*self.factor,10*self.factor,10*self.factor)
def paint(self, painter, option, widget):
self.update()
rect = QRectF(-5*self.factor,-5*self.factor,10*self.factor,10*self.factor)
if self.pressed:
painter.setBrush(Qt.red)
else:
painter.setBrush(Qt.darkGray)
painter.drawEllipse(rect)
def mousePressEvent(self, event):
self.pressed = True
self.update()
QGraphicsItem.mousePressEvent(self,event)
def mouseReleaseEvent(self, event):
self.pressed = False
self.update()
QGraphicsItem.mouseReleaseEvent(self,event)
class RBGraphicView(QGraphicsView):
def __init__(self):
super(RBGraphicView, self).__init__()
self.factorView = 1
self.initScene()
self.initGui()
def initGui(self):
self.setWindowTitle("A Simple Animation")
self.show()
def initScene(self):
self.rbScene = QGraphicsScene(self)
self.rbAddItem(self.rbScene)
self.setScene(self.rbScene)
def wheelEvent(self, event):
factor = math.pow(2.0, -event.delta() / 240.0)
self.scaleView(factor)
def scaleView(self, scaleFactor):
factor = self.matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0,0,1,1)).width()
if factor < 0.001 or factor > 1000:
return
self.scale(scaleFactor, scaleFactor)
self.factorView = factor
def rbAddItem(self, scene):
rbNode1 = RBNode(self.factorView)
rbNode1.setPos(100,100)
rbNode2 = RBNode()
rbNode2.setPos(100,100)
rbP2 = QPointF(20.0, 10.0)
rbP3 = QPointF(80.0, 30.0)
rbP4 = QPointF(90.0, 70.0)
bezierPath = QPainterPath()
bezierPath.moveTo(rbNode1.x, rbNode1.y)
bezierPath.cubicTo(rbP2.x(),rbP2.y(),rbP3.x(),rbP3.y(),rbP4.x(),rbP4.y())
myItem = QGraphicsPathItem(bezierPath)
scene.addItem(rbNode1)
scene.addItem(rbNode2)
scene.addItem(myItem)
if __name__ == '__main__':
try:
myApp = QApplication(sys.argv)
myView = RBGraphicView()
myApp.exec_()
sys.exit(0)
except NameError:
print("Name Error:", sys.exc_info()[1])
except SystemExit:
print("Closing Window...")
except Exception:
print(sys.exc_info()[1])
How can a QThread get text from a QLineEdit?
i tried self.t.updateSignal.connect(self.inputedittext.text) to get the QLineEdit value, but I get an error:
TypeError: unsupported operand type(s) for +=:
PyQt4.QtCore.pyqtBoundSignal' and 'int'
or I get the message:
bound signal updateSignal of xxxxxx at 0x02624580
Code:
import sys
import time
from PyQt4 import QtGui, QtCore
class mc(QtGui.QWidget):
def __init__(self):
super(mc,self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QThread')
self.inputedittext = QtGui.QLineEdit()
self.startbutton = QtGui.QPushButton('start')
self.stopbutton = QtGui.QPushButton('stop')
self.textlable = QtGui.QLabel('0')
lv1 = QtGui.QVBoxLayout()
lb1 = QtGui.QHBoxLayout()
lb1.addWidget(self.inputedittext)
lb1.addWidget(self.startbutton)
lb1.addWidget(self.stopbutton)
lb2 = QtGui.QHBoxLayout()
lb2.addWidget(self.textlable)
lv1.addLayout(lb1)
lv1.addLayout(lb2)
self.setLayout(lv1)
self.t = test_QThread()
self.t.updateSignal.connect(self.inputedittext.text)
self.startbutton.clicked.connect(self.start_t)
self.connect(self.t,QtCore.SIGNAL('ri'),self.setlable)
def setlable(self,i):
self.textlable.setText(i)
def start_t(self):
self.t.start()
# print(self.inputedittext.text())
class test_QThread(QtCore.QThread):
updateSignal = QtCore.pyqtSignal(QtCore.QString)
def __init__(self):
QtCore.QThread.__init__(self)
def run(self):
i = self.updateSignal
# i=0
go = True
while go:
i+=1
time.sleep(1)
self.emit(QtCore.SIGNAL('ri'),str(i))
print('run...')
def main():
app = QtGui.QApplication(sys.argv)
mw = mc()
mw.show()
app.exec_()
if __name__ == '__main__':
main()
Use signals to communicate from thread to gui, and from gui to thread:
class mc(QtGui.QWidget):
...
def initUI(self):
...
self.t = test_QThread()
self.t.progressSignal.connect(self.setlable)
self.t.requestSignal.connect(self.sendText)
self.startbutton.clicked.connect(self.start_t)
self.stopbutton.clicked.connect(self.stop_t)
def sendText(self):
self.t.updateSignal.emit(self.inputedittext.text())
def setlable(self, i):
self.textlable.setText(str(i))
def start_t(self):
self.t.start()
def stop_t(self):
self.t.stop()
class test_QThread(QtCore.QThread):
requestSignal = QtCore.pyqtSignal()
updateSignal = QtCore.pyqtSignal(str)
progressSignal = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(test_QThread, self).__init__(parent)
self.updateSignal.connect(self.updateSlot)
self._running = False
self._count = 0
def updateSlot(self, text):
print 'received: "%s", count: %d' % (text, self._count)
def stop(self):
self._running = False
def run(self):
self._count = 0
self._running = True
while self._running:
self._count += 1
time.sleep(1)
self.progressSignal.emit(self._count)
if self._count % 3 == 0:
self.requestSignal.emit()