I am trying to copy a file with a progress bar included in PyQt5. I can see in a console with print("Copied: {}".format(percent)) that 100% is copied.
However, my progress bar does not update my copy process. How can I check that the signal was sent? Where did I make mistake?
import os, sys
from PyQt5 import QtWidgets, QtCore
class Extended(QtCore.QThread):
def __init__(self):
super().__init__()
self.src_file = 'test.txt'
self.dst_file = 'test_copy.txt'
self.copyfileobj(self.src_file, self.dst_file, self.cb)
copied_percent = QtCore.pyqtSignal(int)
def cb(self, temp_file_size):
self.file_size = os.stat(self.src_file).st_size
percent = int(temp_file_size/self.file_size*100)
print("Copied: {}".format(percent))
self.copied_percent.emit(percent)
def copyfileobj(self, fsrc, fdst, callback, length=16*1024):
copied = 0
with open(fsrc, "rb") as fr, open(fdst, "wb") as fw:
while True:
buff = fr.read(length)
if not buff:
break
fw.write(buff)
copied += len(buff)
callback(copied)
class MyApp(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.w = QtWidgets.QWidget()
self.w.setWindowTitle("Progress bar copy test")
# Add widgets on the window
self.copy_button = QtWidgets.QPushButton("Copy", self)
self.copy_button.sizeHint()
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setGeometry(0, 40, 300, 25)
self.progress_bar.setMaximum(100)
self.copy_button.clicked.connect(self.on_button_click)
def on_button_click(self):
self.copy_button.setDisabled(True)
self.ext = Extended()
self.ext.copied_percent.connect(self.on_count_change)
self.ext.start()
def on_count_change(self, value):
self.progress_bar.setValue(value)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
prog = MyApp()
prog.show()
sys.exit(app.exec_())
Ok, I figured it out. There has to be a run method in a thread-supported class which is executed when you call a start() method on a instance object self.ext. I also added self.ext.join() method to not end a program before the thread is executed and finished.
import os, sys
from PyQt5 import QtWidgets, QtCore
class Extended(QtCore.QThread):
"""
For running copy operation
"""
copied_percent_signal= QtCore.pyqtSignal(int)
def __init__(self):
super().__init__()
self.src_file = 'test.txt'
self.dst_file = 'test_copy.txt'
self.file_size = os.stat(self.src_file).st_size
def run(self):
self.copyfileobj(self.src_file, self.dst_file, self.my_callback)
def my_callback(self, temp_file_size):
percent = int(temp_file_size/self.file_size*100)
print("Copiedd: {}".format(percent))
self.copied_percent_signal.emit(percent)
def copyfileobj(self, fsrc, fdst, callback, length=16*1024):
copied = 0
with open(fsrc, "rb") as fr, open(fdst, "wb") as fw:
while True:
buff = fr.read(length)
if not buff:
break
fw.write(buff)
copied += len(buff)
callback(copied)
class MyApp(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# Instance attribute defined later in on_button_click()
self.ext = None
self.w = QtWidgets.QWidget()
self.w.setWindowTitle("Progress bar copy test")
# Add widgets on the window
self.copy_button = QtWidgets.QPushButton("Copy", self)
self.copy_button.sizeHint()
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setGeometry(0, 40, 300, 25)
self.progress_bar.setMaximum(100)
self.copy_button.clicked.connect(self.on_button_click)
def on_button_click(self):
self.copy_button.setDisabled(True)
self.ext = Extended()
self.ext.copied_percent_signal.connect(self.on_count_change)
self.ext.start()
self.ext.join()
def on_count_change(self, value):
self.progress_bar.setValue(value)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
prog = MyApp()
prog.show()
sys.exit(app.exec_())
My file is being copied and the progress bar is working.
Related
I can simply play a wav file from a file with the code below:
media = vlc.MediaPlayer('c.wav')
media.audio_set_volume(50)
media.play()
How can I do the same with qrc resource file? I tried this code but doesn't seem to work:
mediafile = QFile(':/sounds/c.wav')
media = vlc.Instance().media_player_new()
media.set_media(mediafile)
media.audio_set_volume(50)
media.play()
You need to use vlc.media_new_callbacks for this so that you can wrap the QFile and use its methods. Below is a demo script that shows how to implement that:
import sys, vlc
from PyQt5.QtCore import QFile, QCoreApplication
#vlc.CallbackDecorators.MediaOpenCb
def open_cb(opaque, data, size):
data.contents.value = opaque
size.value = sys.maxsize
return 0
#vlc.CallbackDecorators.MediaReadCb
def read_cb(opaque, buffer, length):
data = qfile.read(length)
for index, char in enumerate(data):
buffer[index] = char
return len(data)
#vlc.CallbackDecorators.MediaSeekCb
def seek_cb(opaque, offset):
qfile.seek(offset)
return 0
#vlc.CallbackDecorators.MediaCloseCb
def close_cb(opaque):
qfile.close()
if __name__ == '__main__':
import time, signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
print('Press Ctrl+C to Quit')
player = vlc.Instance().media_player_new()
qfile = QFile(sys.argv[1])
qfile.open(QFile.ReadOnly)
player.set_media(vlc.Instance().media_new_callbacks(
open_cb, read_cb, seek_cb, close_cb, None))
player.play()
app = QCoreApplication(sys.argv)
app.exec_()
UPDATE:
It's somewhat tricky to get this working correctly in a more typical PyQt application, because the callbacks have to be static functions. Here is an extended example that shows how it can be done:
import sys, ctypes, vlc
from PyQt5 import QtCore, QtWidgets
class VLCPlayer(QtCore.QObject):
def __init__(self, parent=None):
super().__init__()
self._player = vlc.Instance().media_player_new()
#staticmethod
#vlc.CallbackDecorators.MediaOpenCb
def _open_cb(voidptr, data, size):
data.contents.value = voidptr
size.value = sys.maxsize
return 0
#staticmethod
#vlc.CallbackDecorators.MediaReadCb
def _read_cb(voidptr, buffer, length):
stream = ctypes.cast(
voidptr, ctypes.POINTER(ctypes.py_object)).contents.value
data = stream.read(length)
for index, char in enumerate(data):
buffer[index] = char
return len(data)
#staticmethod
#vlc.CallbackDecorators.MediaSeekCb
def _seek_cb(voidptr, offset):
stream = ctypes.cast(
voidptr, ctypes.POINTER(ctypes.py_object)).contents.value
stream.seek(offset)
return 0
#staticmethod
#vlc.CallbackDecorators.MediaCloseCb
def _close_cb(voidptr):
stream = ctypes.cast(
voidptr, ctypes.POINTER(ctypes.py_object)).contents.value
stream.close()
def play(self):
self._player.play()
def stop(self):
self._player.stop()
def load(self, path):
file = QtCore.QFile(path)
file.open(QtCore.QIODevice.ReadOnly)
voidptr = ctypes.cast(ctypes.pointer(
ctypes.py_object(file)), ctypes.c_void_p)
self._player.set_media(vlc.Instance().media_new_callbacks(
self._open_cb, self._read_cb,
self._seek_cb, self._close_cb, voidptr))
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.buttonPlay = QtWidgets.QPushButton('Play')
self.buttonPlay.clicked.connect(self.handlePlay)
self.buttonStop = QtWidgets.QPushButton('Stop')
self.buttonStop.clicked.connect(self.handleStop)
self.buttonOpen = QtWidgets.QPushButton('Open')
self.buttonOpen.clicked.connect(self.handleOpen)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(self.buttonOpen)
layout.addWidget(self.buttonPlay)
layout.addWidget(self.buttonStop)
self.player = VLCPlayer(self)
def handlePlay(self):
self.player.play()
def handleStop(self):
self.player.stop()
def handleOpen(self):
path, ok = QtWidgets.QFileDialog.getOpenFileName(
self, filter='Audio Files (*.wav)')
if ok:
self.player.load(path)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setWindowTitle('VLC Player')
window.setGeometry(600, 100, 200, 80)
window.show()
sys.exit(app.exec_())
I am creating a program that uploads a folder into a bucket. Right now I have the program and UI all set I would just like to add a progress bar showing which file is being uploaded.
I am wondering if there is a way to use the
s3.upload_file(fileName, bucketName, objectName, Callback=ProgressPercentage(path.text())) to get a progress bar on my QMainWindow. Or if I need to go about this a different way.
class ProgressPercentage(object):
def __init__(self, filename):
self._filename = filename
self._size = float(os.path.getsize(filename))
self._seen_so_far = 0
self._lock = threading.Lock()
def __call__(self, bytes_amount):
# To simplify, assume this is hooked up to a single filename
with self._lock:
self._seen_so_far += bytes_amount
percentage = (self._seen_so_far / self._size) * 100
sys.stdout.write(
"\r%s %s / %s (%.2f%%)" % (
self._filename, self._seen_so_far, self._size,
percentage))
sys.stdout.flush()
The logic in this case is to create a QObject that has a signal that indicates the progress, in addition the upload task must be executed in a secondary thread so that the GUI does not freeze:
import math
import os
import sys
import threading
import boto3
from PyQt5 import QtCore, QtWidgets
class S3Worker(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
percentageChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self._s3 = boto3.client("s3")
#property
def s3(self):
return self._s3
def upload(self, filename, bucketname, objectname):
self._size = float(os.path.getsize(filename))
self._seen_so_far = 0
threading.Thread(
target=self._execute, args=(filename, bucketname, objectname), daemon=True
).start()
def _execute(self, fileName, bucketName, objectName):
self.started.emit()
self.s3.upload_file(fileName, bucketName, objectName, Callback=self._callback)
self.finished.emit()
def _callback(self, bytes_amount):
self._seen_so_far += bytes_amount
percentage = (self._seen_so_far / self._size) * 100
self.percentageChanged.emit(math.floor(percentage))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.filename_le = QtWidgets.QLineEdit()
self.upload_btn = QtWidgets.QPushButton("Upload")
self.percentage_pb = QtWidgets.QProgressBar()
lay = QtWidgets.QGridLayout(self)
lay.addWidget(QtWidgets.QLabel("filename:"))
lay.addWidget(self.filename_le, 0, 1)
lay.addWidget(self.upload_btn, 0, 2)
lay.addWidget(self.percentage_pb, 1, 0, 1, 3)
self.qs3 = S3Worker()
self.upload_btn.clicked.connect(self.start_upload)
self.qs3.started.connect(lambda: self.upload_btn.setEnabled(False))
self.qs3.finished.connect(lambda: self.upload_btn.setEnabled(True))
self.qs3.percentageChanged.connect(self.percentage_pb.setValue)
def start_upload(self):
filename = self.filename_le.text()
if os.path.exists(filename):
self.qs3.upload(filename, "mybucket", "foobject")
def main():
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
The simple idea is that user inputs duration in seconds, and presses a PyQt button, that calls a function that creates a python subprocess and runs windump via it. Then time sleep is used to wait for user defined duration and then process.terminate(), terminates it (code below)
def windump_exec(duration):
p = s.Popen(['windump', '-i', '3', '-w', 'packets.pcap'], stdout=s.PIPE)
time.sleep(duration)
p.terminate()
Now once this is done, scapy reads .pcap file and I show stuff on the screen in short. While this is happening QWaitingSpinner is running, and to handle this I run the above logic (including scapy) using QRunnable (code below)
class ThreadRunnable(QRunnable):
def __init__(self, _time, filler):
QRunnable.__init__(self)
self.time = _time
self.filler = filler
self.signal = RunnableSignal()
def run(self):
windump_exec(self.time)
packets = parse_data()
self.filler(packets)
self.signal.result.emit()
The Problem is that the windump code works fine on it's own, but inside the QThread it doesn't create an output file and hence scapy has nothing to read (open), and it gives error.
Instead of using Popen with QThread you can use QProcess, in my test I have used tcpdump but I suppose that changing to windump should have the same behavior:
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from scapy.all import rdpcap
import psutil
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class DumpProcesor(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self._process = QtCore.QProcess()
self._timer = QtCore.QTimer(singleShot=True)
self._timer.timeout.connect(self.handle_timeout)
self._pid = -1
#property
def process(self):
return self._process
#property
def timer(self):
return self._timer
#QtCore.pyqtSlot()
def start(self):
self.started.emit()
status, self._pid = self._process.startDetached()
if status:
self._timer.start()
else:
self.finished.emit()
#QtCore.pyqtSlot()
def handle_timeout(self):
if self._pid > 0:
p = psutil.Process(self._pid)
p.terminate()
QtCore.QTimer.singleShot(100, self.finished.emit)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.log_te = QtWidgets.QTextEdit(readOnly=True)
self.time_sb = QtWidgets.QSpinBox(minimum=1)
self.start_btn = QtWidgets.QPushButton(self.tr("Start"))
grid_layout = QtWidgets.QGridLayout(self)
grid_layout.addWidget(self.log_te, 0, 0, 1, 3)
grid_layout.addWidget(QtWidgets.QLabel("Time (seg):"), 1, 0)
grid_layout.addWidget(self.time_sb, 1, 1)
grid_layout.addWidget(self.start_btn, 1, 2)
self.dump_procesor = DumpProcesor(self)
self.dump_procesor.process.setProgram("tcpdump")
filename = os.path.join(CURRENT_DIR, "packets.pcap")
self.dump_procesor.process.setArguments(["-i", "3", "-w", filename])
self.start_btn.clicked.connect(self.start)
self.dump_procesor.finished.connect(self.on_finished)
#QtCore.pyqtSlot()
def start(self):
self.log_te.clear()
self.start_btn.setDisabled(True)
self.dump_procesor.timer.setInterval(self.time_sb.value() * 1000)
self.dump_procesor.start()
#QtCore.pyqtSlot()
def on_finished(self):
self.start_btn.setDisabled(False)
filename = os.path.join(CURRENT_DIR, "packets.pcap")
packets = rdpcap(filename)
for packet in packets:
t = packet.show(dump=True)
self.log_te.append(t)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I need open other window (configuration windows) in my app.
Each window is a different class. I would like to know if I can group everything in the same class and open the window through a function.
from PyQt5 import QtWidgets, uic
import sys
class OpenMyApp(QtWidgets.QMainWindow):
# My App
pathUi = 'ui/interface.ui'
pathStylesheet = 'ui/stylesheet.qss'
def __init__(self):
super(OpenMyApp, self).__init__()
self.init_interface()
def init_interface(self):
self.ui = uic.loadUi(self.pathUi, self)
style_app = self.pathStylesheet
with open(style_app, "r") as fh:
self.setStyleSheet(fh.read())
self.ui.btn_openConfigApp.clicked.connect(self.open_config)
def open_config(self):
pass
class OpenMyConfigApp(QtWidgets.QMainWindow):
# My Config App
pathUi = 'ui/config-interface.ui'
pathStylesheet = 'ui/stylesheet.qss'
def __init__(self):
super(OpenMyConfigApp, self).__init__()
self.init_interface()
def init_interface(self):
self.ui = uic.loadUi(self.pathUi, self)
style_app = self.pathStylesheet
with open(style_app, "r") as fh:
self.setStyleSheet(fh.read())
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = OpenMyApp()
window.show()
sys.exit(app.exec_())
Here it is sample program i want to update the my progress bar using with pyqt4.and i want to show th3 30% data saving and another 60% data processing.I am executing the my program it is aborting.Can any one please help me how to update the my progress bar.Thank you in advance.
Given below is my code:
import sys
import time
from pyface.qt import QtGui, QtCore
global X,Y
X= 5
Y= 4
import threading
class SaveWorker(QtCore.QObject):
progress_update = QtCore.Signal(int)
def save_file(self):
while True:
MyCustomWidget().updateProgressBar()
class Dialog(QtGui.QDialog):
def __init__(self, parent = None):
super(Dialog, self).__init__(parent)
self.setStyleSheet("QDialog {background-color:black; color:white }")
self.label1 = QtGui.QLabel(
text="Please Wait...",
font=QtGui.QFont("Times", 20,weight=QtGui.QFont.Bold)
)
self.progress = QtGui.QProgressBar()
self.box = QtGui.QVBoxLayout()
self.label2 = QtGui.QLabel()
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(self.label1)
vbox.addLayout(self.box)
self.show_gif()
def show_gif(self):
self.progress = QtGui.QProgressBar()
self.progress.setRange(0,100)
self.box.addWidget(self.progress)
self.show()
class MyCustomWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyCustomWidget, self).__init__(parent)
self.worker = SaveWorker()
self.gif_dialog = Dialog()
self.worker.progress_update.connect(self.gif_dialog.show_gif)
thread = threading.Thread(target=self.worker.save_file)
thread.daemon = True
thread.start()
self.progressPer = 0
fileList = []
processes = []
_dataSavingPer = 30.0/(X*Y)
for i in range(X*Y):
name = 'file'+str(i+1) + ".txt"
fileList.append(name)
self.progressPer += _dataSavingPer
self.updateProgressBar(self.progressPer)
#updating the progress bar
_dataProcessPer = 60.0/(X*Y)
for file in fileList:
process = 'fileProcess'+str(i+1) + ".txt"
processes.append(process)
self.progressPer += _dataProcessPer
self.updateProgressBar(self.progressPer)
#Updating the progressPer
#how can i update these two values in to progressbar
def updateProgressBar(self,value):
self.gif_dialog.progress.setValue(value)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyCustomWidget()
sys.exit(app.exec_())
I do not understand what you tried when writing the following:
class SaveWorker(QtCore.QObject):
progress_update = QtCore.Signal(int)
def save_file(self):
while True:
MyCustomWidget().updateProgressBar()
updateProgressBar requires a value what value are you going through?, on the other hand when using MyCustomWidget() you are creating an object different from the one shown, and no MyCustomWidget object should be created in another thread.
What you have to do is move the heavy task to the save_file method since it will be executed in another thread:
import sys
import threading
from pyface.qt import QtGui, QtCore
X, Y = 5, 4
class SaveWorker(QtCore.QObject):
progressChanged = QtCore.Signal(int)
def save_file(self):
fileList = []
processes = []
_dataSavingPer = 30.0/(X*Y)
progress = 0
for i in range(X*Y):
name = 'file'+str(i+1) + ".txt"
fileList.append(name)
progress += _dataSavingPer
self.progressChanged.emit(progress)
_dataProcessPer = 60.0/(X*Y)
for file in fileList:
process = 'fileProcess'+str(i+1) + ".txt"
processes.append(process)
progress += _dataProcessPer
self.progressChanged.emit(progress)
class Dialog(QtGui.QDialog):
def __init__(self, parent = None):
super(Dialog, self).__init__(parent)
self.setStyleSheet("QDialog {background-color:black; color:white }")
self.label1 = QtGui.QLabel(
text="Please Wait...",
font=QtGui.QFont("Times", 20,weight=QtGui.QFont.Bold)
)
self.progress = QtGui.QProgressBar()
self.box = QtGui.QVBoxLayout()
self.label2 = QtGui.QLabel()
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(self.label1)
vbox.addLayout(self.box)
self.show_gif()
def show_gif(self):
self.progress = QtGui.QProgressBar()
self.progress.setRange(0,100)
self.box.addWidget(self.progress)
self.show()
class MyCustomWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyCustomWidget, self).__init__(parent)
self.worker = SaveWorker()
self.gif_dialog = Dialog()
self.worker.progressChanged.connect(self.gif_dialog.progress.setValue)
thread = threading.Thread(target=self.worker.save_file)
thread.daemon = True
thread.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyCustomWidget()
sys.exit(app.exec_())