realtime plot using PyQt5 - python

I want to build a program where we get updated data from interactive brokers (a popular online broker) and I want to update a plot in a simple pyqt5 interface.
The program sequence is the following:
We start the Ui_MainWindow interface. If we click on pushbutton2 we initiate a thread with the function GetDataBackground. This function uses the class GetDataand every time there is an update of the data to plot, the function tickPrice and tickSize is executed. In this step, I want to update the plot (for the moment it can be with random numbers like codded in the update_graph).
When I run the program it returns the error:
AttributeError: 'GetData' object has no attribute 'MplWidget'
Which seams that I do not have access to the main window.
The code is the following:
from PyQt5 import QtCore, QtGui, QtWidgets
from mplwidget import MplWidget
import threading
import sys
sys.path.insert(0, "/Users/nuno/Desktop/IB_API/source/pythonclient/")
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.ticktype import TickTypeEnum
import numpy as np
import sys
class GetData(EWrapper, EClient):
outrights = []
calendars = []
flies = []
def __init__(self):
EClient.__init__(self, self)
def error(self, reqId, errorCode, errorString):
print("Error: ", reqId, " ", errorCode, " ", errorString)
def tickPrice(self, reqId , tickType, price, attrib):
print("Tick Price. Ticker Id: ", reqId, "tickType: ", TickTypeEnum.to_str(tickType), " Price: ", price, end='\n')
self.outrights[reqId][TickTypeEnum.to_str(tickType)] = price
price = [x['DELAYED_LAST'] for x in self.outrights]
print('Push button')
self.testplot()
self.update_graph()
print(price)
def tickSize(self, reqId, tickType, size):
print("Tick Size. Ticker Id: ", reqId, "tickType: ", TickTypeEnum.to_str(tickType), 'Size: ', size)
self.outrights[reqId][TickTypeEnum.to_str(tickType)] = size
self.testplot()
def testplot(self):
print('Function correctly called')
def update_graph(self):
print('Push button')
x = np.random.rand(30)
y = np.random.rand(30)
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(x, y)
self.MplWidget.canvas.axes.legend(('random'), loc='upper right')
self.MplWidget.canvas.draw()
def GetDataBackground():
"""
This function gets the data in the background.
:param PlotData:
:return:
"""
print('Interactive Brokers get data has started.')
app = GetData()
app.connect("127.0.0.1", 4002, 0)
print("serverVersion: {} connectionTime: {}".format(app.serverVersion(), app.twsConnectionTime()))
contract_list = ['GEZ0', 'GEH1', 'GEM1', 'GEU1', 'GEZ1', 'GEH2', 'GEM2', 'GEU2', 'GEZ2', 'GEH3', 'GEM3', 'GEU3',
'GEZ3', 'GEH4', 'GEM4', 'GEU4', 'GEZ4']
# This for loop generates a list of dictionaries that live inside the class TestApp
for i, contract in enumerate(contract_list):
temp_dict = {}
print(contract)
temp_dict['localSymbol'] = contract
temp_dict['tickerID'] = i
temp_dict['DELAYED_ASK'] = None
temp_dict['DELAYED_BID'] = None
temp_dict['DELAYED_OPEN'] = None
temp_dict['DELAYED_HIGH'] = None
temp_dict['DELAYED_LOW'] = None
temp_dict['DELAYED_CLOSE'] = None
temp_dict['DELAYED_ASK_SIZE'] = None
temp_dict['DELAYED_BID_SIZE'] = None
temp_dict['DELAYED_VOLUME'] = None
temp_dict['DELAYED_LAST'] = None
temp_dict['DELAYED_LAST_SIZE'] = None
app.outrights.append(temp_dict)
contracts = [Contract() for x in contract_list] # _
for i in range(len(contract_list)):
contracts[i].secType = 'FUT'
contracts[i].exchange = 'GLOBEX'
contracts[i].currency = 'USD'
contracts[i].localSymbol = contract_list[i]
app.reqMarketDataType(3)
for i in range(len(contract_list)):
print(contracts[i].localSymbol)
app.reqMktData(i, contracts[i], "", False, False, [])
app.run()
class Ui_MainWindow(GetData):
def __init__(self):
super().__init__()
def start_download(self):
download_info = threading.Thread(target = GetDataBackground)
download_info.start()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1280, 1024)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(880, 80, 221, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton2.setGeometry(QtCore.QRect(880, 45, 221, 32))
self.pushButton2.setObjectName("Get data")
self.MplWidget = MplWidget(self.centralwidget)
self.MplWidget.setGeometry(QtCore.QRect(49, 39, 771, 551))
self.MplWidget.setObjectName("MplWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1148, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionOpen = QtWidgets.QAction(MainWindow)
self.actionOpen.setObjectName("actionOpen")
self.actionSave = QtWidgets.QAction(MainWindow)
self.actionSave.setObjectName("actionSave")
self.actionClose = QtWidgets.QAction(MainWindow)
self.actionClose.setObjectName("actionClose")
self.actionSave_as = QtWidgets.QAction(MainWindow)
self.actionSave_as.setObjectName("actionSave_as")
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionOpen)
self.menuFile.addAction(self.actionSave)
self.menuFile.addAction(self.actionSave_as)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionClose)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton2.setText(_translate("MainWindow", "GetData"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionOpen.setText(_translate("MainWindow", "Open..."))
self.actionSave.setText(_translate("MainWindow", "Save"))
self.actionClose.setText(_translate("MainWindow", "Close"))
self.actionSave_as.setText(_translate("MainWindow", "Save As..."))
self.pushButton.clicked.connect(self.update_graph)
self.pushButton2.clicked.connect(self.start_download)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

Please refer to:
https://eli.thegreenplace.net/2009/01/20/matplotlib-with-pyqt-guis
This is an excellent example for matplotlib with PyQt GUIs
Refer here too for more detailed example:
https://github.com/eliben/code-for-blog/blob/master/2008/wx_mpl_dynamic_graph.py

Related

Why can't My GUI program built by PyQt5 show?

I refer to the article1 to build my GUI by PyQt5,The difference between the program of the article and mine is the module <img_controller.py>. When I initilize my img_controller instance,I only need the parameter ui(the class I got from Qtdesigner)and my program ,img_controller. will revise the attributes of ui. Initialize the parameters of img_controller.py according to 1 are directed inputted attributes of ui.
When I run the program got from 1, it can work normally; but I run my program, I can't get the mainwindow and the wrong message hints that "AttributeError: 'Img_controller' object has no attribute 'ui'".I don't know where is my problem, because in the function __ init __ of Img_controller(class), I state that "self.ui = ui",anyone can tell me the problem, thank you very much.
The following is my program:
UI.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1085, 857)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(110, 20, 861, 491))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.scrollArea = QtWidgets.QScrollArea(self.verticalLayoutWidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
# self.scrollAreaWidgetContents = QtWidgets.QWidget()
# self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 857, 487))
# self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.image_label = QtWidgets.QLabel(self.scrollArea) #此處有更動
self.image_label.setGeometry(QtCore.QRect(10, 10, 841, 471))
self.image_label.setObjectName("image_label")
self.scrollArea.setWidget(self.image_label) #此處有更動
self.verticalLayout.addWidget(self.scrollArea)
self.btn_zoomin = QtWidgets.QPushButton(self.centralwidget)
self.btn_zoomin.setGeometry(QtCore.QRect(330, 530, 75, 23))
self.btn_zoomin.setObjectName("btn_zoomin")
self.btn_zoomout = QtWidgets.QPushButton(self.centralwidget)
self.btn_zoomout.setGeometry(QtCore.QRect(640, 530, 75, 23))
self.btn_zoomout.setObjectName("btn_zoomout")
self.slider = QtWidgets.QSlider(self.centralwidget)
self.slider.setGeometry(QtCore.QRect(440, 530, 160, 22))
self.slider.setOrientation(QtCore.Qt.Horizontal)
self.slider.setObjectName("slider")
self.btn_open = QtWidgets.QPushButton(self.centralwidget)
self.btn_open.setGeometry(QtCore.QRect(140, 530, 75, 23))
self.btn_open.setObjectName("btn_open")
self.label_resolution = QtWidgets.QLabel(self.centralwidget)
self.label_resolution.setGeometry(QtCore.QRect(770, 530, 75, 15))
self.label_resolution.setObjectName("label_resolution")
self.label_filename = QtWidgets.QLabel(self.centralwidget)
self.label_filename.setGeometry(QtCore.QRect(130, 660, 111, 41))
self.label_filename.setObjectName("label_filename")
self.label_img_shape = QtWidgets.QLabel(self.centralwidget)
self.label_img_shape.setGeometry(QtCore.QRect(540, 620, 411, 51))
self.label_img_shape.setObjectName("label_img_shape")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1085, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.image_label.setText(_translate("MainWindow", "image"))
self.btn_zoomin.setText(_translate("MainWindow", "zoom_in"))
self.btn_zoomout.setText(_translate("MainWindow", "zoom_out"))
self.btn_open.setText(_translate("MainWindow", "open file"))
self.label_resolution.setText(_translate("MainWindow", "TextLabel"))
self.label_filename.setText(_translate("MainWindow", "file_name"))
self.label_img_shape.setText(_translate("MainWindow", "TextLabel"))
img_controller.py
from PyQt5 import QtCore, QtGui
import cv2
from UI import Ui_MainWindow
class Img_controller(object):
def __init__(self, ui:Ui_MainWindow, img_ratio:int = 50):
super(Img_controller, self).__init__()
self.img_path = 'sad.jpg'
self.img_ratio = img_ratio
self.read_img(self.img_path)
self.ui = ui
def read_img(self,img_path):
try:
self.img = cv2.imread(img_path)
self.orig_h, self.orig_w, self.orig_c = self.img.shape
self.img_path = img_path
except:
self.img = cv2.imread(self.img_path)
self.orig_h, self.orig_w, self.orig_c = self.img.shape
bytesPerline = self.orig_h*self.orig_c
self.qimg = QtGui.QImage(self.img, self.orig_w, self.orig_h, bytesPerline, QtGui.QImage.Format_RGB888).rgbSwapped()
self.origin_qpixmap = QtGui.QPixmap.fromImage(self.qimg)
self.img_ratio = 50
self.set_img_ratio()
def set_img_ratio(self):
self.img_ratio = pow(10, (self.img_ratio - 50)/50)
qpixmap_height = self.orig_h * self.img_ratio
self.qpixmap = self.origin_qpixmap.scaledToHeight(qpixmap_height)
#更新UI介面上的顯示
self.__update_img()
self.__update_text_ratio()
self.__update_text_img_shape()
self.__update_text_file_path()
def __update_img(self):
self.ui.image_label.setPixmap(self.qpixmap)
self.ui.image_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
def __update_text_file_path(self):
self.ui.label_filename.setText(f"File path = {self.img_path}")
def __update_text_ratio(self):
self.ui.label_resolution.setText(f"{int(100*self.img_ratio)} %")
def __update_text_img_shape(self):
current_text = f"Current img shape = ({self.qpixmap.width()}, {self.qpixmap.height()})"
origin_text = f"Origin img shape = ({self.origin_width}, {self.origin_height})"
self.ui.label_img_shape.setText(current_text+"\t"+origin_text)
def set_zoom_in(self):
self.img_ratio = max(0, self.img_ratio - 1)
self.set_img_ratio()
def set_zoom_out(self):
self.img_ratio = min(100, self.img_ratio + 1)
self.set_img_ratio()
def set_slider_value(self, value):
self.img_ratio = value
self.set_img_ratio()
controller.py
from PyQt5 import QtCore,QtWidgets,QtGui
from PyQt5.QtWidgets import QMainWindow,QFileDialog
from img_controller import Img_controller
from UI import Ui_MainWindow
class Ui_controller(QMainWindow):
def __init__(self):
super(Ui_controller,self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setup_control()
def setup_control(self):
self.img_controller = Img_controller(ui = self.ui)
self.ui.btn_open.clicked.connect(self.open_file)
self.ui.btn_zoomin.clicked.connect(self.img_controller.set_zoom_in)
self.ui.btn_zoomout.clicked.connect(self.img_controller.set_zoom_out)
self.ui.slider.valueChanged.connect(self.getslidervalue)
def open_file(self):
filename, filetype = QFileDialog.getOpenFileName(self, "Open file", "./") # start path
self.init_new_picture(filename)
def init_new_picture(self, filename):
self.ui.slider.setProperty("value", 50)
self.img_controller.read_img(filename)
def getslidervalue(self):
self.img_controller.set_slider_value(self.ui.slider.value()+1)
What you DIDN'T say was the key piece of information -- the rest of the traceback. Notice that Img_controller.__init__ calls self.read_img, which calls self.set_img_ratio, which calls self.__update_img, which uses self.ui, and that all happens BEFORE you set self.ui. You need to swap the order of that initialization.

Test class methods using Pytest

I am taking a programming course now, CSE 111 programming with functions, and my last assignment is to find an interesting code and write some test functions, to test the correct working of the code. This is the code:
#Imported modules
import sys
import wave, contextlib, math, time
import speech_recognition as sr
from moviepy.editor import AudioFileClip
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QThread, pyqtSignal
class Ui_MainWindow(object):
"""Main window GUI."""
def __init__(self):
"""Initialisation function."""
self.mp4_file_name = ""
self.output_file = ""
self.audio_file = "speech.wav"
def setupUi(self, MainWindow):
"""Define visual components and positions."""
# Main window
MainWindow.setObjectName("MainWindow")
MainWindow.resize(653, 836)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 20, 161, 41))
# Selected video file label
font = QtGui.QFont()
font.setPointSize(14)
self.label.setFont(font)
self.label.setObjectName("label")
self.selected_video_label = QtWidgets.QLabel(self.centralwidget)
self.selected_video_label.setGeometry(QtCore.QRect(230, 20, 371, 41))
font = QtGui.QFont()
font.setPointSize(8)
self.selected_video_label.setFont(font)
self.selected_video_label.setFrameShape(QtWidgets.QFrame.Box)
self.selected_video_label.setText("")
self.selected_video_label.setObjectName("selected_video_label")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(50, 90, 161, 41))
# Transcribed text box
font = QtGui.QFont()
font.setPointSize(14)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.transcribed_text = QtWidgets.QTextBrowser(self.centralwidget)
self.transcribed_text.setGeometry(QtCore.QRect(230, 320, 381, 431))
self.transcribed_text.setObjectName("transcribed_text")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(230, 280, 161, 41))
font = QtGui.QFont()
font.setPointSize(14)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.transcribe_button = QtWidgets.QPushButton(self.centralwidget)
self.transcribe_button.setEnabled(False)
self.transcribe_button.setGeometry(QtCore.QRect(230, 150, 221, 81))
# Transcribe button
font = QtGui.QFont()
font.setPointSize(14)
self.transcribe_button.setFont(font)
self.transcribe_button.setObjectName("transcribe_button")
self.transcribe_button.clicked.connect(self.process_and_transcribe_audio)
# progeress bar
self.progress_bar = QtWidgets.QProgressBar(self.centralwidget)
self.progress_bar.setGeometry(QtCore.QRect(230, 250, 381, 23))
self.progress_bar.setProperty("value", 0)
self.progress_bar.setObjectName("progress_bar")
self.message_label = QtWidgets.QLabel(self.centralwidget)
self.message_label.setGeometry(QtCore.QRect(0, 760, 651, 21))
# Message label (for errors and warnings)
font = QtGui.QFont()
font.setPointSize(8)
self.message_label.setFont(font)
self.message_label.setFrameShape(QtWidgets.QFrame.Box)
self.message_label.setText("")
self.message_label.setObjectName("message_label")
self.output_file_name = QtWidgets.QPlainTextEdit(self.centralwidget)
self.output_file_name.setGeometry(QtCore.QRect(230, 90, 371, 41))
# Output file name
font = QtGui.QFont()
font.setPointSize(14)
self.output_file_name.setFont(font)
self.output_file_name.setObjectName("output_file_name")
# Menubar options
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 653, 21))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuAbout = QtWidgets.QMenu(self.menubar)
self.menuAbout.setObjectName("menuAbout")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionOpen_mp4_video_recording = QtWidgets.QAction(MainWindow)
self.actionOpen_mp4_video_recording.setObjectName("actionOpen_mp4_video_recording")
self.actionOpen_mp4_video_recording.triggered.connect(self.open_audio_file)
self.actionAbout_vid2text = QtWidgets.QAction(MainWindow)
self.actionAbout_vid2text.setObjectName("actionAbout_vid2text")
self.actionAbout_vid2text.triggered.connect(self.show_about)
self.actionNew = QtWidgets.QAction(MainWindow)
self.actionNew.setObjectName("actionNew")
self.actionNew.triggered.connect(self.new_project)
self.menuFile.addAction(self.actionOpen_mp4_video_recording)
self.menuFile.addAction(self.actionNew)
self.menuAbout.addAction(self.actionAbout_vid2text)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuAbout.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
"""Translate UI method."""
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "Selected video file:"))
self.label_3.setText(_translate("MainWindow", "Output file name:"))
self.label_5.setText(_translate("MainWindow", "Transcribed text:"))
self.transcribe_button.setText(_translate("MainWindow", "Transcribe"))
self.output_file_name.setPlaceholderText(_translate("MainWindow", "interview1.txt"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuAbout.setTitle(_translate("MainWindow", "About"))
self.actionOpen_mp4_video_recording.setText(_translate("MainWindow", "Open mp4 video recording"))
self.actionAbout_vid2text.setText(_translate("MainWindow", "About video to speech"))
self.actionNew.setText(_translate("MainWindow", "New"))
def open_audio_file(self):
"""Open the audio (*.mp4) file."""
file_name = QFileDialog.getOpenFileName()
if file_name[0][-3:] == "mp4":
self.transcribe_button.setEnabled(True)
self.mp4_file_name = file_name[0]
self.selected_video_label.setText(file_name[0])
else:
self.message_label.setText("Please select an *.mp4 file")
def convert_mp4_to_wav(self):
"""Convert the mp4 video file into an audio file."""
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_thread = convertVideoToAudioThread(self.mp4_file_name, self.audio_file)
self.convert_thread.finished.connect(self.finished_converting)
self.convert_thread.start()
def get_audio_duration(self, audio_file):
"""Determine the length of the audio file."""
with contextlib.closing(wave.open(audio_file,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames / float(rate)
return duration
def transcribe_audio(self, audio_file):
"""Transcribe the audio file."""
total_duration = self.get_audio_duration(audio_file) / 10
total_duration = math.ceil(total_duration)
self.td = total_duration
if len(self.output_file_name.toPlainText()) > 0:
self.output_file = self.output_file_name.toPlainText()
else:
self.output_file = "my_speech_file.txt"
# Use thread to process in the background and avoid freezing the GUI
self.thread = transcriptionThread(total_duration, audio_file, self.output_file)
self.thread.finished.connect(self.finished_transcribing)
self.thread.change_value.connect(self.set_progress_value)
self.thread.start()
def finished_converting(self):
"""Reset message text when conversion is finished."""
self.message_label.setText("Transcribing file...")
self.transcribe_audio(self.audio_file)
def finished_transcribing(self):
"""This run when transcription finished to tidy up UI."""
self.progress_bar.setValue(100)
self.transcribe_button.setEnabled(True)
self.message_label.setText("")
self.update_text_output()
def set_progress_value(self, val):
"""Update progress bar value."""
increment = int(math.floor(100*(float(val)/self.td)))
self.progress_bar.setValue(increment)
def process_and_transcribe_audio(self):
"""Process the audio into a textual transcription."""
self.transcribe_button.setEnabled(False)
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_mp4_to_wav()
def update_text_output(self):
"""Update the text box with the transcribed file."""
f = open(self.output_file, "r")
self.transcribed_text.setText(f.read())
f.close()
def new_project(self):
"""Clear existing fields of data."""
self.message_label.setText("")
self.transcribed_text.setText("")
self.selected_video_label.setText("")
self.output_file_name.document().setPlainText("")
self.progress_bar.setValue(0)
def show_about(self):
"""Show about message box."""
msg = QMessageBox()
msg.setWindowTitle("About video to Speech")
msg.setText("Nelson Petro - CSE 111")
msg.setIcon(QMessageBox.Information)
msg.exec_()
class convertVideoToAudioThread(QThread):
"""Thread to convert mp4 video file to wav file."""
def __init__(self, mp4_file_name, audio_file):
"""Initialization function."""
QThread.__init__(self)
self.mp4_file_name = mp4_file_name
self.audio_file = audio_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run video conversion task."""
audio_clip = AudioFileClip(self.mp4_file_name)
audio_clip.write_audiofile(self.audio_file)
class transcriptionThread(QThread):
"""Thread to transcribe file from audio to text."""
change_value = pyqtSignal(int)
def __init__(self, total_duration, audio_file, output_file):
"""Initialization function."""
QThread.__init__(self)
self.total_duration = total_duration
self.audio_file = audio_file
self.output_file = output_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run transcription, audio to text."""
r = sr.Recognizer()
for i in range(0, self.total_duration):
try:
with sr.AudioFile(self.audio_file) as source:
audio = r.record(source, offset=i*10, duration=10)
f = open(self.output_file, "a")
f.write(r.recognize_google(audio))
f.write(" ")
self.change_value.emit(i)
except:
print("Unknown word detected...")
continue
f.close()
def main():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
And I have written these two test functions. The first one to test the default values of the MainWindow constructor and the second one to test the correct functionality of the
open_audio_file() function (using the directions provided in a previous question). I am not pretty familiar with OOP, so I would really appreciate if you can help me getting the second function working correct.
These are the two test functions:
def test_MainWindowConstructor():
ui = Ui_MainWindow()
assert ui.mp4_file_name == ""
assert ui.output_file == ""
assert ui.audio_file == "speech.wav"
def test_that_providing_a_raw_file_is_rejected():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
ui.actionOpen_mp4_video_recording.triggered
QFileDialog.getOpenFileName = "video.raw"
ui.open_audio_file
assert ui.message_label.text == "Please select an *.mp4 file"
Thank you so much before hand.
You wrote:
ui.actionOpen_mp4_video_recording.triggered
...
ui.open_audio_file
As written, that doesn't make sense.
You probably want to invoke a function,
rather than evaluate an expression for
side effects and then discard it.
There's a big difference between fn and fn() --
the latter actually calls the function.
You are successfully verifying that the triggered
attribute exists, and that can be helpful in a unit test.
To verify, try some typo, such as "fliggered",
and note the AttributeError that elicits.
Similarly you are verifying the open_audio_file
attribute exists. That's a simpler situation so
it is probably less useful for a unit test to do that.
Better to call the function, and verify it behaves
as intended.
Try running your tests in this way:
$ pytest --cov --cov-report=term-missing
That will help you to notice that there's two
kinds of audio files you should test:
with & without an ".mp4" suffix.
Note that the target code sometimes mentions
a function without calling it, without
tacking on (). What's going on there?
We name that situation a "callback".
That is, the target code's API accepts
a pointer (ok, a reference) to a function.
It remembers the function, and subsequently calls
it at the appropriate time, after some UI event
has happened.
That syntax was likely confusing you -- it explains
the absence of () in the target code.
Academic integrity
You are clearly learning some things
about unit tests in this assignment,
and have commendable curiosity.
Be sure to include a reference to
this SO question when you hand in your work.
Most schools are happy with use of external
resources as long as you cite them.

All threads are not running with QThread along with QtSerialPort (for Arduino) and Matplotlib

I'm trying to get an output from an arduino to be updated in two pyqt windows, one of which is a matplotlib plot. If I comment out the non-plot thread window, the plot works. But when I try to run both at the same time, I get the message
3 Device is already open
3 Device is already open
but nothing shows up in the plot, though the window is there. Whereas the non-plot window is updated with values. I'm not sure if I should use QThreadpool instead of QThread. On a side note, if I try to rerun the code, I get the message
2 Access is denied.
2 Access is denied.
2 Access is denied.
so I have to unplug and replug in the arduino, uploading its code again, before running the python script again, and neither window is updated with values.
The arduino code I use, to just give signals over 1 second intervals, to test it out, is
int PinOutput = 13;
int PinInput = A0;
int inph;
int inpl;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(PinInput, INPUT);
pinMode(PinOutput, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
inpl = analogRead(PinInput)/4;
Serial.println(inpl);
analogWrite(PinOutput,255);
delay(1000);
inph = analogRead(PinInput)/4;
Serial.println(inph);
analogWrite(PinOutput,0);
delay(1000);
}
Where pin 13 is connected to A0 on the arduino.
The python code is
import sys
from PyQt5 import QtCore, QtWidgets, QtSerialPort
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
seril_port_connection = QtSerialPort.QSerialPort("COM3")
seril_port_connection.setBaudRate(QtSerialPort.QSerialPort.Baud9600)
class MyThread2(QtCore.QThread):
ard_signal = QtCore.pyqtSignal(str)
def __init__(self):
QtCore.QThread.__init__(self)
self.serial_port = seril_port_connection
self.serial_port.errorOccurred.connect(self.handle_error)
self.serial_port.readyRead.connect(self.run)
self.serial_port.open(QtCore.QIODevice.ReadWrite)
def run(self):
while self.serial_port.canReadLine():
line = self.serial_port.readLine().data().decode().strip().strip('\x00')
try:
self.ard_signal.emit(str(line))
except ValueError as e:
print("error", e)
def handle_error(self, error):
if error == QtSerialPort.QSerialPort.NoError:
return
print(error, self.serial_port.errorString())
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(325, 237)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(110, 20, 61, 16))
self.label.setObjectName("label")
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(90, 60, 104, 71))
self.textEdit.setObjectName("textEdit")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(100, 150, 75, 23))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 325, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "test window"))
self.label.setText(_translate("MainWindow", "pyqt5 tests"))
self.pushButton.setText(_translate("MainWindow", "test button"))
self.pushButton.clicked.connect(self.label_change)
self.thread_start = MyThread()
self.thread_start.ard_signal.connect(self.label.setText)
self.thread_start.start()
def label_change(self):
self.pushButton.setText('Button Clicked!')
self.textEdit.setText('taco')
class MainWindowm(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindowm, self).__init__(*args, **kwargs)
self.canvas = FigureCanvasQTAgg(Figure(figsize=(5, 4), dpi=100))
self.setCentralWidget(self.canvas)
self.axes = self.canvas.figure.subplots()
n_data = 10
self.xdata = list(range(n_data))
self.ydata = [0 for i in range(n_data)]
self.thread_start = MyThread2()
self.thread_start.ard_signal.connect(self.update_plot)
self.thread_start.start()
def handle_error(self, error):
if error == QtSerialPort.QSerialPort.NoError:
return
print(error, self.serial_port.errorString())
def update_plot(self, value):
self.ydata = self.ydata[1:] + [float(value)]
self.axes.cla()
self.axes.plot(self.xdata, self.ydata, "r")
self.canvas.draw()
class MyThread(QtCore.QThread):
ard_signal = QtCore.pyqtSignal(str)
def __init__(self):
QtCore.QThread.__init__(self)
self.serial_port = seril_port_connection
self.serial_port.errorOccurred.connect(self.handle_error)
self.serial_port.readyRead.connect(self.run)
self.serial_port.open(QtCore.QIODevice.ReadWrite)
def run(self):
while self.serial_port.canReadLine():
line = self.serial_port.readLine().data().decode().strip().strip('\x00')
try:
self.ard_signal.emit(str(line))
except ValueError as e:
print("error", e)
def handle_error(self, error):
if error == QtSerialPort.QSerialPort.NoError:
return
print(error, self.serial_port.errorString())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
w = MainWindowm()
w.show()
sys.exit(app.exec_())
So I'm trying to make the arduino output available globally, to then be used by different threads at the same time, being updated in two windows, one with a plot. Any info on how to do this would help, thanks.
I assume that the OP is using my old answer, and in that solution indicate that the use of QSerialPort avoids the unnecessary use of threads, and this also applies in this case. On the other hand, a serial port cannot be handled by several classes. In this case, you just have to create a class that manages the serial port and distributes the information to the other elements.
import sys
from PyQt5 import QtCore, QtWidgets, QtSerialPort
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(325, 237)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(110, 20, 61, 16))
self.label.setObjectName("label")
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(90, 60, 104, 71))
self.textEdit.setObjectName("textEdit")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(100, 150, 75, 23))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 325, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "test window"))
self.label.setText(_translate("MainWindow", "pyqt5 tests"))
self.pushButton.setText(_translate("MainWindow", "test button"))
class MainWindowm(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindowm, self).__init__(*args, **kwargs)
self.canvas = FigureCanvasQTAgg(Figure(figsize=(5, 4), dpi=100))
self.setCentralWidget(self.canvas)
self.axes = self.canvas.figure.subplots()
n_data = 10
self.xdata = list(range(n_data))
self.ydata = [0 for i in range(n_data)]
def update_plot(self, value):
self.ydata = self.ydata[1:] + [value]
self.axes.cla()
self.axes.plot(self.xdata, self.ydata, "r")
self.canvas.draw()
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
def update_text(self, value):
self.label.setNum(value)
self.label.adjustSize()
class SerialManager(QtCore.QObject):
valueChanged = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super().__init__(parent)
self.serial_port = QtSerialPort.QSerialPort("COM3")
self.serial_port.setBaudRate(QtSerialPort.QSerialPort.Baud9600)
self.serial_port.errorOccurred.connect(self.handle_error)
self.serial_port.readyRead.connect(self.handle_ready_read)
self.serial_port.open(QtCore.QIODevice.ReadWrite)
def handle_ready_read(self):
while self.serial_port.canReadLine():
codec = QtCore.QTextCodec.codecForName("UTF-8")
line = codec.toUnicode(self.serial_port.readLine()).strip().strip("\x00")
try:
print(line)
value = float(line)
except ValueError as e:
print("error", e)
else:
self.valueChanged.emit(value)
def handle_error(self, error):
if error == QtSerialPort.QSerialPort.NoError:
return
print(error, self.serial_port.errorString())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w1 = MainWindow()
w1.show()
w = MainWindowm()
w.show()
manager = SerialManager()
manager.valueChanged.connect(w1.update_text)
manager.valueChanged.connect(w.update_plot)
sys.exit(app.exec_())

How can i change/update label

I am trying to build a simple internet speed test app using pyqt5
I am a beginner and trying to build small apps to learn GUI basics So here is the code whenever i try to change label text from my thread function is says that self has no member named label_7. ignore the print statements i was just using them for debugging
def Update_values(self,MainWindow):
_translate = QtCore.QCoreApplication.translate
self.label_7.setText(_translate("MainWindow", "Download Speed : " + str(down)))
at the upper part of code i got error
Thank you in advance
from PyQt5 import QtCore, QtGui, QtWidgets
import speedtest as st
speed_test = st.Speedtest()
download = speed_test.download()
val = 0
val1 = 0
val3 = 0
down = 0
up = 0
pin = 0
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(331, 307)
font = QtGui.QFont()
font.setFamily("Lucida Sans Unicode")
MainWindow.setFont(font)
MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
MainWindow.setMouseTracking(False)
MainWindow.setTabletTracking(False)
MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
MainWindow.setDocumentMode(False)
MainWindow.setTabShape(QtWidgets.QTabWidget.Triangular)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(self.centralwidget)
font = QtGui.QFont()
font.setFamily("Comic Sans MS")
font.setPointSize(16)
self.label.setFont(font)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 3)
self.line_2 = QtWidgets.QFrame(self.centralwidget)
self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line_2.setObjectName("line_2")
self.gridLayout.addWidget(self.line_2, 1, 0, 1, 3)
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 2)
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setFrameShape(QtWidgets.QFrame.NoFrame)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 2)
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 4, 0, 1, 2)
self.label_7 = QtWidgets.QLabel(self.centralwidget)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 5, 0, 1, 2)
self.label_8 = QtWidgets.QLabel(self.centralwidget)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1)
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setProperty("value", 24)
self.progressBar.setTextVisible(False)
self.progressBar.setObjectName("progressBar")
self.gridLayout.addWidget(self.progressBar, 7, 0, 1, 3)
self.check1 = QtWidgets.QCheckBox(self.centralwidget)
self.check1.setObjectName("check1")
self.check1.stateChanged.connect(self.check_1)
self.gridLayout.addWidget(self.check1, 8, 0, 1, 1)
self.check2 = QtWidgets.QCheckBox(self.centralwidget)
self.check2.setObjectName("check2")
self.check2.stateChanged.connect(self.check_2)
self.gridLayout.addWidget(self.check2, 8, 1, 1, 1)
self.check3 = QtWidgets.QCheckBox(self.centralwidget)
self.check3.setObjectName("check3")
self.check3.stateChanged.connect(self.check_3)
self.gridLayout.addWidget(self.check3, 8, 2, 1, 1)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.start_test)
self.gridLayout.addWidget(self.pushButton, 9, 0, 1, 3)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 331, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuAbout = QtWidgets.QMenu(self.menubar)
self.menuAbout.setObjectName("menuAbout")
MainWindow.setMenuBar(self.menubar)
self.actionSave_Results = QtWidgets.QAction(MainWindow)
self.actionSave_Results.setCheckable(False)
self.actionSave_Results.setChecked(False)
self.actionSave_Results.setObjectName("actionSave_Results")
self.actionChange_Save_Directory = QtWidgets.QAction(MainWindow)
self.actionChange_Save_Directory.setObjectName("actionChange_Save_Directory")
self.actionDeveloper = QtWidgets.QAction(MainWindow)
self.actionDeveloper.setObjectName("actionDeveloper")
self.menuFile.addAction(self.actionSave_Results)
self.menuFile.addAction(self.actionChange_Save_Directory)
self.menuAbout.addAction(self.actionDeveloper)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuAbout.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Internet Speed Test"))
self.label.setText(_translate("MainWindow", "Internet Speed Test By FATE"))
self.label_2.setText(_translate("MainWindow", "Status : Connected"))
self.label_4.setText(_translate("MainWindow", "Ping : --"))
self.label_3.setText(_translate("MainWindow", "Upload Speed : --"))
self.label_7.setText(_translate("MainWindow", "Download Speed : --"))
self.label_8.setText(_translate("MainWindow", "Progress :"))
self.check1.setText(_translate("MainWindow", "Download"))
self.check2.setText(_translate("MainWindow", "Upload"))
self.check3.setText(_translate("MainWindow", "Ping"))
self.pushButton.setText(_translate("MainWindow", "Start"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuAbout.setTitle(_translate("MainWindow", "About"))
self.actionSave_Results.setText(_translate("MainWindow", "Save Results"))
self.actionChange_Save_Directory.setText(_translate("MainWindow", "Change Save Directory"))
self.actionDeveloper.setText(_translate("MainWindow", "Developer"))
self.Update_values(MainWindow)
def Update_values(self,MainWindow):
_translate = QtCore.QCoreApplication.translate
self.label_7.setText(_translate("MainWindow", "Download Speed : " + str(down)))
# self.label_7.setText(_translate("MainWindow", "Download Speed : 10"))
print(down)
print(up)
print(pin)
def check_1(self, state):
global val
print(val)
if state != self.check1.isChecked():
val += 1
print("YESH "+ str(val))
else:
val -= 1
print("NO" + str(val))
def check_2(self, state):
global val1
print(val1)
if state != self.check1.isChecked():
val1 += 1
print("YESH "+ str(val1))
else:
val1 -= 1
print("NO" + str(val1))
def check_3(self, state):
global val3
print(val3)
if state != self.check1.isChecked():
val3 += 1
print("YESH "+ str(val3))
else:
val3 -= 1
print("NO" + str(val3))
def start_test(self):
self.threadclass = Thread()
self.threadclass.start()
class Thread(QtCore.QThread,Ui_MainWindow):
def __init__(self,parent = None):
super(Thread, self).__init__(parent)
def run(self):
if val == 1:
download = speed_test.download()
download_mbs = round(download / (10**6), 2)
global down
down = download_mbs
self.Update_values(Ui_MainWindow)
if val1 == 1:
upload = speed_test.upload()
upload_mbs = round(upload / (10**6), 2)
global up
up = upload_mbs
if val3 == 1:
ping = speed_test.results.ping
global pin
pin = ping
print("DONE")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
First of all, you should not modify the code generated by Qt Designer, so in my next solution you must regenerate the .py generated by pyuic and call it gui.py: python -m pyuic5 your_ui.ui -o gui.py.
Another error you have is that you should not update the GUI from another thread, nor use global variables since they are prone to generating silent errors, instead you should use the signals as shown below:
from enum import Flag, auto
import threading
from PyQt5 import QtCore, QtGui, QtWidgets
import speedtest
from gui import Ui_MainWindow
class Task(Flag):
NONE = auto()
DOWNLOAD = auto()
UPLOAD = auto()
PING = auto()
class SpeedtestWrapper(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
downloaded = QtCore.pyqtSignal(float)
uploaded = QtCore.pyqtSignal(float)
pinged = QtCore.pyqtSignal(float)
def start(self, flag):
threading.Thread(target=self._execute, args=(flag,), daemon=True).start()
def _execute(self, flag):
self.started.emit()
speed_test = speedtest.Speedtest()
if flag & Task.DOWNLOAD:
download = speed_test.download()
download_mbs = round(download / (10 ** 6), 2)
self.downloaded.emit(download_mbs)
if flag & Task.DOWNLOAD:
upload = speed_test.upload()
upload_mbs = round(upload / (10 ** 6), 2)
self.uploaded.emit(upload_mbs)
if flag & Task.DOWNLOAD:
ping = speed_test.results.ping
self.pinged.emit(ping)
self.finished.emit()
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self._flag = Task.NONE
self.speed_wrapper = SpeedtestWrapper()
self.speed_wrapper.started.connect(self.on_started)
self.speed_wrapper.finished.connect(self.on_finished)
self.speed_wrapper.downloaded.connect(self.on_downloaded)
self.speed_wrapper.uploaded.connect(self.on_uploaded)
self.speed_wrapper.pinged.connect(self.on_pinged)
#QtCore.pyqtSlot()
def start_test(self):
if self._flag != Task.NONE:
self.speed_wrapper.start(self._flag)
#QtCore.pyqtSlot(int)
def check_1(self, state):
if state:
self._flag |= Task.DOWNLOAD
else:
self._flag &= ~Task.DOWNLOAD
#QtCore.pyqtSlot(int)
def check_2(self, state):
if state:
self._flag |= Task.UPLOAD
else:
self._flag &= ~Task.UPLOAD
#QtCore.pyqtSlot(int)
def check_3(self, state):
if state:
self._flag |= Task.PING
else:
self._flag &= ~Task.PING
#QtCore.pyqtSlot()
def on_started(self):
self.label_7.setText(self.tr("Download Speed: --"))
self.label_3.setText(self.tr("Upload Speed: --"))
self.label_4.setText(self.tr("Ping Speed: --"))
self.pushButton.setEnabled(False)
#QtCore.pyqtSlot()
def on_finished(self):
self.pushButton.setEnabled(True)
#QtCore.pyqtSlot(float)
def on_downloaded(self, download):
self.label_7.setText(self.tr("Download Speed: %f" % download))
#QtCore.pyqtSlot(float)
def on_uploaded(self, upload):
self.label_3.setText(self.tr("Upload Speed: %f" % upload))
#QtCore.pyqtSlot(float)
def on_pinged(self, ping):
self.label_4.setText(self.tr("Ping Speed: %f" % ping))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

selection of several elements on qtreeview and their processing

I have a program with two QTreeView. When I press a button, I need to allow the user to select several elements, and when user press Escape, to transfer the selected elements to a waiting function, which will then pass to the handler function.
I've tried to use threads, gevent, and asyncio.
this function in main class, i run this function when i need get some files.
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import *
import ui
import exampleQTV
import asyncio
class PyMap(QMainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.select.clicked.connect(self.selectAction)
def selectAction(self):
self.getSomeFiles("Example", "Select some files, and press Escape", self.leftView)
def getSomeFiles(self, title, path, view):
# return QFileDialog.getOpenFileNames(self, title, path) ### older, and ugly variant
buttonReply = QMessageBox.information(self, "Information", "Select needed files",
QMessageBox.Ok)
loop = asyncio.get_event_loop()
tasks = [loop.create_task(view.getFiles())]
wait_tasks = asyncio.wait(tasks)
result = loop.run_until_complete(asyncio.gather(*tasks))
print (result)
# result = view.getFiles()
return result
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.rightView = exampleQTV.exampleQTV()
self.rightView.setObjectName("rightView")
self.gridLayout.addWidget(self.rightView, 1, 1, 1, 1)
self.leftView = exampleQTV.exampleQTV()
self.leftView.setObjectName("leftView")
self.gridLayout.addWidget(self.leftView, 1, 0, 1, 1)
self.select = QtWidgets.QPushButton(self.centralwidget)
self.select.setObjectName("select")
self.gridLayout.addWidget(self.select, 0, 0, 1, 2)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.select.setText(_translate("MainWindow", "Select"))
def main():
app = QtWidgets.QApplication(sys.argv)
window = PyMap()
window.show()
app.exec_()
if __name__ == '__main__':
main()
this functions in QTreeView's class
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from multiprocessing.pool import ThreadPool
import asyncio
class exampleQTV(QTreeView):
def __init__(self):
QTreeView.__init__(self)
self.model = QFileSystemModel()
self.model.setRootPath("/") # i'm on linux if you not change from / to for example D:\\
self.setModel(self.model)
self.eventCalled = False
self.requestForEscape = False
self.setColumnHidden(1, True)
self.setColumnHidden(2, True)
self.setColumnHidden(3, True)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
self.eventCalled = false
def getFiles_thread(self):
while True:
if self.requestForEscape == True:
if self.eventCalled == False:
return self.selectedIndexes()
async def getFiles(self):
self.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
self.eventCalled = True
self.requestForEscape = True
pool = ThreadPool(processes=1)
self.printMessage(1)
async_result = pool.apply_async(self.getFiles_thread)
self.printMessage(2)
result = await async_result.get()
self.printMessage(3)
# ### Sorry if it not corrent, i'm just copied from doc
# tasks = [self.getFiles_thread()]
# loop = asyncio.get_event_loop()
# result = loop.run_until_complete(asyncio.gather(*tasks))
# task = [gevent.spawn(self.getFiles_thread(), 2)]
# result = gevent.joinall(task)
# result = await self.getFiles_thread()
self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
return result
def printMessage(self, message):
print(message)
output:
1
2
The tasks of the GUI such as the selection of items, listening to keyboard events, etc. do not need to be executed in another thread, nor in another process.
Your way of programming is procedural but in the GUI the paradigm of Event-driven Programming is used, in the case of Qt it is implemented through the signals, slot and events. Only tasks that synchronously consume a lot of time must be executed in another thread, for example I emulated the task of copying with QtCore.QThread.sleep(...).
Considering the above I have implemented the logic of enabling the selection, listen to the keyPressEvent, emit a signal with the selected indexes, and call the heavy function with the data.
main.py
import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
import exampleQTV
class PyMap(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.select.clicked.connect(self.selectAction)
self.leftView.selectedIndexesSignal.connect(
self.on_selectedIndexesSignal
)
thread = QtCore.QThread(self)
thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(thread)
#QtCore.pyqtSlot()
def selectAction(self):
buttonReply = QtWidgets.QMessageBox.information(
self,
"Information",
"Select needed files",
QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel,
)
if buttonReply == QtWidgets.QMessageBox.Ok:
self.leftView.setEnableMultiSelection(True)
#QtCore.pyqtSlot(list)
def on_selectedIndexesSignal(self, indexes):
paths = []
for ix in indexes:
path = ix.data(QtWidgets.QFileSystemModel.FilePathRole)
paths.append(path)
print(paths)
wrapper = partial(self.m_worker.heavyTask, paths)
QtCore.QTimer.singleShot(0, wrapper)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.rightView = exampleQTV.ExampleQTV()
self.rightView.setObjectName("rightView")
self.gridLayout.addWidget(self.rightView, 1, 1, 1, 1)
self.leftView = exampleQTV.ExampleQTV()
self.leftView.setObjectName("leftView")
self.gridLayout.addWidget(self.leftView, 1, 0, 1, 1)
self.select = QtWidgets.QPushButton(self.centralwidget)
self.select.setObjectName("select")
self.gridLayout.addWidget(self.select, 0, 0, 1, 2)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.select.setText(_translate("MainWindow", "Select"))
class Worker(QtCore.QObject):
#QtCore.pyqtSlot(list)
def heavyTask(self, paths):
print("started")
# emulate heavy task
QtCore.QThread.sleep(5)
print(paths)
print("finished")
def main():
app = QtWidgets.QApplication(sys.argv)
window = PyMap()
window.show()
app.exec_()
if __name__ == "__main__":
main()
exampleQTV.py
from PyQt5 import QtCore, QtGui, QtWidgets
class ExampleQTV(QtWidgets.QTreeView):
selectedIndexesSignal = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super(ExampleQTV, self).__init__(parent)
self.model = QtWidgets.QFileSystemModel(self)
self.model.setRootPath(QtCore.QDir.rootPath())
self.setModel(self.model)
for i in (1, 2, 3):
self.setColumnHidden(i, True)
self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.expandAll()
def setEnableMultiSelection(self, enable):
self.setSelectionMode(
QtWidgets.QAbstractItemView.MultiSelection
if enable
else QtWidgets.QAbstractItemView.NoSelection
)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.selectedIndexesSignal.emit(self.selectedIndexes())
self.selectionModel().clearSelection()
self.setEnableMultiSelection(False)
super(ExampleQTV, self).keyPressEvent(event)

Categories