How to stop my program in raspberry with thread? - python

I have a project to build GUI for raspberry project thermocycler. I have some error in pushbutton GUI. I can start the thermocycler from pushbutton GUI, but can't to stop. When thermocycler is on, my GUI freeze. I will write my code below. If anyone have a solution for my error please. what is need use a threading?
my code
import sys
from PySide2 import *
from PySide2.QtGui import QPainter
from PySide2.QtWidgets import (QMainWindow, QApplication)
from PySide2.QtCharts import QtCharts
from PySide2.QtGui import QPainter
from PySide2.QtWidgets import (QMainWindow, QApplication)
from PySide2.QtCharts import QtCharts
from random import randrange
from functools import partial
import csv
import time
import math
import RPi.GPIO as GPIO
import Adafruit_ADS1x15
from datetime import datetime
file_date = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
from ui_interface4 import 4
shadow_elements = {
"left_menu_frame",
"frame_3",
"frame_5",
"header_frame",
"frame_9"
}
temp_denat = 95
temp_anel = 55
temp_exten = 72
temp_refri = 4
heat_pin = 19 #pwm > GPIO 19 > LPWM
cool_pin = 18 #pwm > GPIO 13 > RPWM
pwm_fan = 12 #pwm > GPIO 12 > fan cooling
low_volt = 21 #digital > GPIO 21 > 3v3
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(20)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.ui.centralwidget.setGraphicsEffect(self.shadow)
self.setWindowIcon(QtGui.QIcon(":/icons/icons/pie-chart.svg"))
self.setWindowTitle("PCR_2")
QSizeGrip(self.ui.size_grip)
self.ui.minimize_window_button.clicked.connect(lambda: self.showMinimized())
self.ui.close_window_button.clicked.connect(lambda: self.close())
self.ui.restore_window_button.clicked.connect(lambda: self.restore_or_maximize_window())
switch = False
def Program_Run (switch):
def suhu_c(R, Ro=100000.0, To=25.0, beta=3950.0):
suhu = math.log(R / Ro)
suhu /= beta
suhu += 1.0 / (To + 273.15)
suhu = (1.0 / suhu)
suhu -=273.15
return suhu
GPIO.setwarnings(False) #disable warning
GPIO.setmode(GPIO.BCM) #board GPIO
GPIO.setup(heat_pin,GPIO.OUT)
GPIO.setup(cool_pin,GPIO.OUT)
GPIO.setup(pwm_fan,GPIO.OUT)
GPIO.setup(low_volt,GPIO.OUT)
GPIO.output(low_volt,GPIO.HIGH)
adc = Adafruit_ADS1x15.ADS1115(address=0x49,busnum=1)
GAIN = 2/3
t = 0
rn = 100000
pwm_val = 0
print('channel-0 value')
told = time.time()
cool = GPIO.PWM(heat_pin,490) #490Hz
heat = GPIO.PWM(cool_pin,490) #490Hz
fan = GPIO.PWM(pwm_fan,490) #490Hz
heat.start(0)
cool.start(0)
fan.start(0)
while switch == True:
range = time.time() - told
bit = adc.read_adc_difference(0,gain=GAIN)
voltage = (bit*6.144)/32768
R = rn*((5*rn)-(voltage*2*rn))/((5*rn)+(voltage*2*rn))
suhu = suhu_c(R)
if suhu <= temp_denat:
pwm = 96
cool.ChangeDutyCycle(0)
heat.ChangeDutyCycle(pwm)
fan.ChangeDutyCycle(0)
if suhu >= temp_denat:
pwm = 80
cool.ChangeDutyCycle(pwm)
heat.ChangeDutyCycle(0)
fan.ChangeDutyCycle(100)
if range >= 1.00:
print(t,' ADC bit:',round(bit),' bit', ',voltage: ',round(voltage,3),' volt','suhu', round(suhu,3), ' C','R:',round(R,3),'ohm')
t += 1
told = time.time()
while switch == False:
break
def Start_Program():
switch = True
Program_Run (switch)
def Stop_Program():
switch = False
Program_Run (switch)
def moveWindow(e):
if self.isMaximized() == False: #Not maximized
if e.buttons() == Qt.LeftButton:
self.move(self.pos() + e.globalPos() - self.clickPosition)
self.clickPosition = e.globalPos()
e.accept()
self.ui.header_frame.mouseMoveEvent = moveWindow
self.show()
for x in shadow_elements:
## # Shadow effect style
effect = QtWidgets.QGraphicsDropShadowEffect(self)
effect.setBlurRadius(18)
effect.setXOffset(0)
effect.setYOffset(0)
effect.setColor(QColor(0, 0, 0, 255))
getattr(self.ui, x).setGraphicsEffect(effect)
self.ui.START_btn.clicked.connect(Start_Program)
self.ui.CANCEL_btn.clicked.connect(Stop_Program)
def restore_or_maximize_window(self):
if self.isMaximized():
self.showNormal()
self.ui.restore_window_button.setIcon(QtGui.QIcon(u":/icons/icons/maximize-2.svg"))
else:
self.showMaximized()
self.ui.restore_window_button.setIcon(QtGui.QIcon(u":/icons/icons/minimize-2.svg"))
def mousePressEvent(self, event):
self.clickPosition = event.globalPos()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

Related

How to move a pixel column in PyQt5 Pixmap?

I am making a python project,I am using pyqt. The aim is to show a picture (I am using Pixmap) and I want that any column of pixel of this picture go down randomly according to the time. The aim is to create an effect of 'dissolve' the screen.
Here is my code :
#-*- coding: utf-8 -*-
# ---------- Imports ----------
import sys, time, os, random
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# ---------- Main ----------
class Ui (QMainWindow):
def __init__(self):
QWidget.__init__(self)
# ----- Fichiers -----
folder_path = os.path.dirname(os.path.abspath(__file__))
self.picture_path = str(folder_path + '\\solar_wallpaper.jpg')
self.icon_path = str(folder_path + '\\solar_icon.ico')
# ----- Configuration de la fenĂȘtre -----
self.setWindowFlags(self.windowFlags() &~ Qt.WindowCloseButtonHint)
self.setWindowFlags(self.windowFlags() &~ Qt.WindowMinimizeButtonHint)
self.setWindowFlags(self.windowFlags() &~ Qt.WindowMaximizeButtonHint)
self.setWindowTitle('Solar')
self.setWindowIcon(QIcon(self.icon_path))
self.setStyleSheet("background-color: black;")
# ----- Appel des méthodes -----
self.init_components()
self.init_layout()
self.showFullScreen()
def init_components (self):
self.setCentralWidget(QGroupBox())
self.picture = QLabel(self)
self.picture.setScaledContents(True)
self.picture.setPixmap(QPixmap(self.picture_path))
self.picture.setAlignment(Qt.AlignCenter)
colonne = self.picture.width()
for i in range (colonne):
None
def init_layout (self):
self.layout = QVBoxLayout()
self.layout.addWidget(self.picture)
self.centralWidget().setLayout(self.layout)
# ---------- Launcher ----------
app = QApplication.instance()
if not app :
app = QApplication(sys.argv)
ui = Ui()
app.exec()
A possible solution is to copy and paste pieces of rectangles but with a vertical distance:
import random
import sys
from PyQt5.QtCore import QRect, QTimer
from PyQt5.QtGui import QColor, QPainter, QPixmap
from PyQt5.QtWidgets import QApplication, QLabel
COLUMN_WIDTH = 10
DELTA = 10
def build_pixmap():
delta = 20
pixmap = QPixmap(512, 512)
pixmap.fill(QColor("blue"))
painter = QPainter(pixmap)
for i in range(5, 15):
dt = i * delta
r = QRect(pixmap.rect().adjusted(0, dt, 0, -dt))
color = QColor(*random.sample(range(255), 3))
painter.fillRect(r, color)
painter.end()
return pixmap
def aply_effect(pixmap, number_of_columns):
i = round(pixmap.width() / COLUMN_WIDTH)
for _ in range(number_of_columns):
j = random.randint(0, i)
rect = QRect(j * COLUMN_WIDTH, 0, COLUMN_WIDTH, pixmap.height())
pix = pixmap.copy(rect)
painter = QPainter(pixmap)
painter.drawPixmap(rect.translated(0, DELTA), pix)
painter.end()
return pixmap
def main():
app = QApplication(sys.argv)
pixmap_demo = build_pixmap()
label = QLabel(pixmap=pixmap_demo)
label.show()
def on_timeout():
out_pixmap = aply_effect(label.pixmap(), 40)
label.setPixmap(out_pixmap)
timer = QTimer(interval=40, timeout=on_timeout)
timer.start()
app.exec_()
if __name__ == "__main__":
main()

Store audio from QAudioInput and pass it to SciPy FFT

how can I get audio input in real time from QAudioInput, store it in a NumPy array and pass it to SciPy FFT? What I have tried:
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
import sys
class Window(QMainWindow):
def __init__(self):
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
def read_audio(self):
data: QByteArray = self.ioDevice.readAll()
print(data.toUInt()) # Prints (0, False) which means error converting data
Inspired by the official example Audio Example I have created a QIODevice that allows obtaining the data. The following example takes the last N samples every T seconds by calculating its fft and displaying it using matplotlib.
import sys
import collections
from functools import cached_property
from PyQt5.QtCore import QIODevice, QObject, pyqtSignal, QTimer
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QApplication, QMainWindow
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from scipy.fft import fft, fftfreq
import numpy as np
FS = 44100
SAMPLE_COUNT = 2 * 1000
class AudioDevice(QIODevice):
data_changed = pyqtSignal(list, name="dataChanged")
def __init__(self, interval=1000, parent: QObject = None):
super().__init__(parent)
self.m_buffer = collections.deque(
[0 for _ in range(SAMPLE_COUNT)], maxlen=SAMPLE_COUNT
)
self.timer.timeout.connect(self.send_data)
self.timer.setInterval(interval)
self.timer.start()
#cached_property
def timer(self):
return QTimer()
def send_data(self):
self.data_changed.emit(list(self.m_buffer))
def readData(self, data, max_size):
return -1
def writeData(self, data):
max_size = len(data)
resolution = 4
start = 0
available_samples = int(max_size) // resolution
if available_samples < self.m_buffer.maxlen:
start = self.m_buffer.maxlen - available_samples
pos = 0
for _ in range(start, self.m_buffer.maxlen):
y = (1.0 * (data[pos] - 128)) / 128.0
self.m_buffer.append(y)
pos += resolution
return (self.m_buffer.maxlen - start) * resolution
class PlotWidget(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.setCentralWidget(self.canvas)
self.ax = self.canvas.figure.subplots()
self._line = None
def update_data(self, data):
T = 1 / FS
N = SAMPLE_COUNT
yf = fft(data)
xf = fftfreq(N, T)[: N // 2]
x = xf
y = 2.0 / N * np.abs(yf[0 : N // 2])
if self._line is None:
(self._line,) = self.ax.plot(x, y)
else:
self._line.set_data(x, y)
self.canvas.draw()
def main(args):
app = QApplication(args)
plot_widget = PlotWidget()
plot_widget.resize(640, 480)
plot_widget.show()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(FS)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
audio_input = QAudioInput(input_device, audio_format, None)
audio_device = AudioDevice(interval=100)
audio_device.data_changed.connect(plot_widget.update_data)
audio_device.open(QIODevice.WriteOnly)
audio_input.start(audio_device)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
data.toUInt() converts whole byte array to one uint value - not what you want. To get sample values you can use either numpy.frombuffer or struct.unpack.
import numpy
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
or
import struct
def read_audio(self):
data = self.ioDevice.readAll()
fmt = "#{}B".format(data.size())
values = struct.unpack(fmt, data.data())
I added widget that shows waveform to demonstrate that samples actually reflect signal from microphone - not random numbers.
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget
from PyQt5.QtGui import QPainter, QPolygonF
from PyQt5.QtCore import QPointF
import sys
import numpy
class WaveWidget(QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self._values = None
def setValues(self, values):
self._values = values
self.update()
def paintEvent(self, event):
if self._values is None:
return
painter = QPainter(self)
ys = self._values / 255 * self.height()
xs = numpy.linspace(0, self.width(), num = len(ys))
points = QPolygonF([QPointF(x,y) for x,y in zip(xs,ys)])
painter.drawPolyline(points)
class Window(QMainWindow):
def __init__(self):
super().__init__()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
widget = WaveWidget()
self._widget = widget
self.setCentralWidget(widget)
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
self._widget.setValues(values)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
app.exec()

PyQt5 The QLCNumber doesnt update

i want to make a program that count pulses then it go through some equation and display it in the gui .
This my main.py
import sys
import time
import RPi.GPIO as GPIO
import PyQt5
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from mainwindow import Ui_MainWindow
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP)
class MainWindow(QMainWindow):
# access variables inside of the UI's file
def __init__(self):
super().__init__()
self.mainwindow = Ui_MainWindow()
self.mainwindow.setupUi(self)
self.i=100
self.flow = 0
self.flowliter = 0
self.totalflow=0
self.mainwindow.lcdNumber.display(self.i)
self.mainwindow.lcdNumber_2.display(self.i)
self.show()
self.mainwindow.startbttn.clicked.connect(lambda: self.pressedstartButton())
self.mainwindow.stopbttn.clicked.connect(lambda: self.pressedstopButton())
def pressedstartButton(self):
print ("Pressed On!")
self.data()
def pressedstopButton(self):
print ("Pressed Off!")
def data(self) :
global count
count = 0
def countPulse(channel):
global count
if start_counter == 1:
count = count+1
GPIO.add_event_detect(FLOW_SENSOR, GPIO.FALLING, callback=countPulse)
while True:
try:
start_counter = 1
time.sleep(1)
start_counter = 4
self.flow = (10 * 60)
self.flowliter= (self.flow/60)
self.totalflow += self.flowliter
print("%d"% (count))
print ("The flow is: %.3f Liter/min" % (self.flow))
print ("The flowliter is: %.3f Liter" % (self.flowliter))
print ("The volume is: %.3f Liter" % (self.totalflow))
self.mainwindow.lcdNumber.display(self.flow)
self.mainwindow.lcdNumber_2.display(self.flowliter)
count = 0
time.sleep(1)
except KeyboardInterrupt:
print ('\ncaught keyboard interrupt!, bye')
GPIO.cleanup()
sys.exit()
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
the lcdnumber doesnt update in the gui but the self.flow update in the shell and i want display the value
in the gui but i dont know which one suitable qtablewidget or qtextbroswer
this code should count the pulse from the gpio 18 and show the flow in the gui.
You should not use time-consuming functions as they block the eventloop and the consequence is to freeze the GUI. In this case, you should not use an infinite loop or time.sleep but a QTimer is enough.
import sys
import RPi.GPIO as GPIO
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from mainwindow import Ui_MainWindow
FLOW_SENSOR = 18
class MainWindow(QMainWindow):
pinSignal = pyqtSignal()
def __init__(self):
super().__init__()
self.mainwindow = Ui_MainWindow()
self.mainwindow.setupUi(self)
self.i = 100
self.flow = 0
self.flowliter = 0
self.totalflow = 0
self.mainwindow.lcdNumber.display(self.i)
self.mainwindow.lcdNumber_2.display(self.i)
self.show()
self.mainwindow.startbttn.clicked.connect(self.pressedstartButton)
self.mainwindow.stopbttn.clicked.connect(self.pressedstopButton)
self.start_counter = False
self.count = 0
self.timer = QTimer(self, interval=1000, timeout=self.execute_every_second)
self.pinSignal.connect(self.handle_pin_signal)
def pressedstartButton(self):
print("Pressed On!")
GPIO.add_event_detect(FLOW_SENSOR, GPIO.FALLING, callback = lambda *args: self.pinSignal.emit())
self.execute_every_second()
self.timer.start()
def pressedstopButton(self):
print("Pressed Off!")
self.timer.stop()
GPIO.remove_event_detect(FLOW_SENSOR)
def handle_pin_signal(self):
if self.start_counter:
self.count += 1
def execute_every_second(self):
if not self.start_counter:
self.flow = 10 * 60
self.flowliter = self.flow / 60
self.totalflow += self.flowliter
print("%d" % (self.count))
print("The flow is: %.3f Liter/min" % (self.flow))
print("The flowliter is: %.3f Liter" % (self.flowliter))
print("The volume is: %.3f Liter" % (self.totalflow))
self.mainwindow.lcdNumber.display(self.flow)
self.mainwindow.lcdNumber_2.display(self.flowliter)
self.count = 0
self.start_counter = not self.start_counter
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setup(FLOW_SENSOR, GPIO.IN, pull_up_down=GPIO.PUD_UP)
app = QApplication(sys.argv)
form = MainWindow()
form.show()
ret = app.exec_()
GPIO.cleanup()
sys.exit(ret)
if __name__ == "__main__":
main()

Trying to open a PyQtGraph window in PyQt5

My OptionsViz class is working on its own. However, when I throw in the asyncio stuff it doesn't show anything updating. The loop is needed for code that I have removed for brevity, so please don't throw that away.
import sys
import asyncio
from qasync import QEventLoop
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget
class OptionViz:
def __init__(self, app):
self.app = app
self.p = pg.plot()
self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.p.setLabel("bottom", "Index", units="B")
self.curve = self.p.plot()
self.data = np.random.normal(size=(50,5000))
self.ptr = 0
self.lastTime = time()
self.fps = None
timer = QtCore.QTimer()
timer.timeout.connect(self.update)
timer.start(0)
def update(self):
self.curve.setData(self.data[self.ptr%10])
self.ptr += 1
now = time()
dt = now - self.lastTime
self.lastTime = now
if self.fps is None:
fps = 1.0/dt
else:
s = np.clip(dt*3., 0, 1)
self.fps = self.fps * (1-s) + (1.0/dt) * s
self.p.setTitle('%0.2f fps' % fps)
self.app.processEvents() ## force complete redraw for every plot
async def main(app):
# some await task here
viz = OptionViz(app)
# more await code here
if __name__ == '__main__':
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
loop.create_task(main(app))
loop.run_forever()
Qt is not compatible with asyncio so several libraries have been implemented such as quamash, asyncqt, qasync, etc. to make it compatible. In the case of quamash and asyncqt they have a bug that does not allow to execute but the qasync library so that it has solved it(execute pip install qasync to install the package).
On the other hand, the main method is not awaitable since it does not execute a time-consuming task but it is executed instantly so I have had to restructure your project:
import sys
import asyncio
from qasync import QEventLoop
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
class OptionViz:
def __init__(self, app):
self.app = app
p = pg.plot()
p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
p.setRange(QtCore.QRectF(0, -10, 5000, 20))
p.setLabel("bottom", "Index", units="B")
self.curve = p.plot()
async def main(viz):
data = np.random.normal(size=(50, 5000))
ptr = 0
while True:
viz.curve.setData(data[ptr % 10])
await asyncio.sleep(0.1)
ptr += 1
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
viz = OptionViz(app)
loop.create_task(main(viz))
loop.run_forever()
Again, the "main" function only creates an "OptionViz" object that does not take a long time is not awaitable so it is absurd to use async in that case. It seems that the OP does not understand the operation of asyncio.
By restructuring your code we can make the function awaitable so OptionViz must be a QObject to use the asyncSlot decorator, in addition the QTimer must be a child of the QObject so that its life cycle increases.
import sys
import asyncio
from qasync import QEventLoop, asyncSlot
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
from time import time
class OptionViz(QtCore.QObject):
def __init__(self, app):
super().__init__()
self.app = app
self.p = pg.plot()
self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.p.setLabel("bottom", "Index", units="B")
self.curve = self.p.plot()
self.data = np.random.normal(size=(50, 5000))
self.ptr = 0
self.lastTime = time()
self.fps = None
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update)
timer.start(0)
#asyncSlot()
async def update(self):
self.curve.setData(self.data[self.ptr % 10])
self.ptr += 1
now = time()
dt = now - self.lastTime
self.lastTime = now
if self.fps is None:
fps = 1.0 / dt
else:
s = np.clip(dt * 3.0, 0, 1)
self.fps = self.fps * (1 - s) + (1.0 / dt) * s
self.p.setTitle("%0.2f fps" % fps)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
viz = OptionViz(app)
loop.run_forever()

The button with transparent circular edge cannot be drawn correctly above the widget embedded with LibVLC

I have a program that plays video with LibVLC, and I want to put some circular buttons on top of the VLC Widget for a barrage effect.
This is my test code.
# -*- coding: utf-8 -*-
import sys
import os
import vlc
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
class demo_widget(QWidget):
def __init__(self):
super(demo_widget, self).__init__()
self.__ui__()
def __ui__(self):
self.resize(600, 400)
t_lay_parent = QHBoxLayout()
t_lay_parent.setContentsMargins(0, 0, 0, 0)
self.frame_media = QFrame(self)
t_lay_parent.addWidget(self.frame_media)
self.setLayout(t_lay_parent)
self.pushButton_test = QPushButton("Test", self)
self.pushButton_test.setFixedSize(70, 30)
self.pushButton_test.setStyleSheet("QPushButton{background-color:#36404A;border:1px solid #36404A;border-radius:10px;color:#98A6AD;}")
self.pushButton_test.move(265, 185)
self.pushButton_test.clicked.connect(self.slt_play)
self.instance = vlc.Instance("--network-caching=1000 --http-continuous")
self.player = self.instance.media_player_new()
if sys.platform.startswith('linux'): # for Linux using the X Server
self.player.set_xwindow(self.frame_media.winId())
elif sys.platform == "win32": # for Windows
self.player.set_hwnd(self.frame_media.winId())
elif sys.platform == "darwin": # for MacOS
self.player.set_nsobject(self.frame_media.winId())
def slt_play(self):
media = self.instance.media_new("1111.m4v")
self.player.set_media(media)
self.player.play()
app = QApplication(sys.argv)
demo = demo_widget()
demo.show()
sys.exit(app.exec_())
This is the ideal working screenshot.
But it actually produces white edges.
I tried to add the following Settings to the button, and it successfully worked as expected, but it ran outside the window.
self.pushButton_test.setWindowFlags(Qt.SplashScreen | Qt.FramelessWindowHint)
self.pushButton_test.setAttribute(Qt.WA_TranslucentBackground, True)
Is there a better way to draw a button with a transparent circular Angle?
A possible solution is to use QRegion with setMask():
# -*- coding: utf-8 -*-
import sys
import os
import vlc
from PySide2 import QtCore, QtGui, QtWidgets
class RoundButton(QtWidgets.QPushButton):
def __init__(self, radius, *args, **kwargs):
super(RoundButton, self).__init__(*args, **kwargs)
self._radius = radius
def resizeEvent(self, event):
r = self.rect()
rb = QtCore.QRect(0, 0, 2*self._radius, 2*self._radius)
reg = QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveRight(r.right())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveBottom(r.bottom())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
rb.moveLeft(r.left())
reg += QtGui.QRegion(rb, QtGui.QRegion.Ellipse)
reg += QtGui.QRegion(self._radius, 0, r.width() - 2*self._radius, r.height())
reg += QtGui.QRegion(0, self._radius, r.width(), r.height() - 2*self._radius)
self.setMask(reg)
super(RoundButton, self).resizeEvent(event)
class demo_widget(QtWidgets.QWidget):
def __init__(self):
super(demo_widget, self).__init__()
self.__ui__()
def __ui__(self):
self.resize(600, 400)
t_lay_parent = QtWidgets.QHBoxLayout(self)
t_lay_parent.setContentsMargins(0, 0, 0, 0)
self.frame_media = QtWidgets.QFrame()
t_lay_parent.addWidget(self.frame_media)
self.pushButton_test = RoundButton(10, "Test", self)
self.pushButton_test.setFixedSize(70, 30)
self.pushButton_test.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
self.pushButton_test.setStyleSheet("""QPushButton{
background-color:#36404A;
border:1px solid #36404A;
color:#98A6AD;
}""")
self.pushButton_test.move(265, 185)
self.pushButton_test.clicked.connect(self.slt_play)
self.instance = vlc.Instance("--network-caching=1000 --http-continuous")
self.player = self.instance.media_player_new()
if sys.platform.startswith('linux'): # for Linux using the X Server
self.player.set_xwindow(self.frame_media.winId())
elif sys.platform == "win32": # for Windows
self.player.set_hwnd(self.frame_media.winId())
elif sys.platform == "darwin": # for MacOS
self.player.set_nsobject(self.frame_media.winId())
def slt_play(self):
media = self.instance.media_new("1111.m4v")
self.player.set_media(media)
self.player.play()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
demo = demo_widget()
demo.show()
sys.exit(app.exec_())

Categories