I made an application that operates with GUI with python. I will briefly summarize that time.
For example, it is an image that opens a Python file under a specific folder, processes it one by one, and displays that XX out of XX is being executed.
First, create a class `` `FileList``` that collects files with a specific extension and creates a list.
filelist.py
import os
class FileList():
''' store file list'''
def __init__(self, root_dir, ext):
self.root_dir = root_dir
self.ext = ext
self.files = []
def retrieve(self):
for rd, _, fl in os.walk(self.root_dir):
for f in fl:
_, fext = os.path.splitext(f)
if fext == self.ext:
self.files.append(os.path.join(rd, f))
def print(self):
for f in self.files:
print(f)
It's a disappointingly easy class.
I will try it.
Yeah, maybe right.
Design the GUI. The required elements are as follows.
The parts for entering the target folder should have a "Browse for Folder" button and a text box that displays the specified folder.
The part for instructing execution should have a "execute" button.
For the part that displays the progress, the name of the file being processed is displayed in the text box, and the overall progress rate is displayed in the progress bar.
I think that this part placement can be easily done by using an appropriate GUI design tool, but for the time being, I will write it by hand.
Basically, plagiarize from First programs in PyQt5.
guimain.py
import sys
import os
from PyQt5.QtWidgets import (QWidget, QApplication,
QPushButton, QLineEdit,
QHBoxLayout, QVBoxLayout,
QTextEdit, QProgressBar,
QFileDialog)
from PyQt5.QtCore import Qt
class MainWidget(QWidget):
dirname = ''
step = 0
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.initUI()
def initUI(self):
self.resize(480, 360)
self.txtFolder = QLineEdit()
self.btnFolder = QPushButton('reference...')
hb1 = QHBoxLayout()
hb1.addWidget(self.txtFolder)
hb1.addWidget(self.btnFolder)
self.btnExec = QPushButton('Run')
self.btnExec.setEnabled(False)
hb2 = QHBoxLayout()
hb2.addWidget(self.btnExec)
self.txtLog = QTextEdit()
self.pbar = QProgressBar()
self.pbar.setTextVisible(False)
layout = QVBoxLayout()
layout.addLayout(hb1)
layout.addLayout(hb2)
layout.addWidget(self.txtLog)
layout.addWidget(self.pbar)
self.setLayout(layout)
self.setWindowTitle('PyQt5 Sample')
def main(args):
app = QApplication(args)
dialog = MainWidget()
dialog.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main(sys.argv)
It's almost as expected.
Define it as a clicked event in btnFolder.
self.btnFolder.clicked.connect(self.show_folder_dialog)
def show_folder_dialog(self):
''' open dialog and set to foldername '''
dirname = QFileDialog.getExistingDirectory(self,
'open folder',
os.path.expanduser('.'),
QFileDialog.ShowDirsOnly)
if dirname:
self.dirname = dirname.replace('/', os.sep)
self.txtFolder.setText(self.dirname)
self.btnExec.setEnabled(True)
self.step = 0
Open the dialog to specify the folder and enable the "Run" button.
Collect ".py" files under the specified folder and display the file name in txtLog. In addition, the progress during execution is represented by a progress bar.
guimain.py
from filelist import FileList
Modify the FileList class slightly from the original version.
filelist.py
class FileList():
''' store file list'''
def __init__(self):
self.root_dir = ''
self.ext = ''
self.files = []
def setup(self, root_dir, ext):
self.root_dir = root_dir
self.ext = ext
self.retrieve()
def retrieve(self):
self.files = []
for rd, _, fl in os.walk(self.root_dir):
for f in fl:
_, fext = os.path.splitext(f)
if fext == self.ext:
self.files.append(os.path.join(rd, f))
def print(self):
for f in self.files:
print(f)
init()
so,root_dir
Whenext
Was specified, but it was newly definedsetup()
Move to the method.
In the GUI program, since the GUI array is operated by mutti-thread, the FileList class is inherited from QThread so that the file array operation itself also operates by multi-thread.
In addition, we will implement a function to send a signal so that the progress bar can be moved for each process.
Originally, in process_file (), for example, if it is an image file, some editing is done, but in this sample, it is not the main point, so I will not do anything for the time being.
filelist.py
import os
import sys
from PyQt5.QtCore import pyqtSignal, QMutexLocker, QMutex, QThread
class FileList(QThread):
''' store file list'''
sig_file = pyqtSignal(str)
def __init__(self, parent=None):
super(FileList, self).__init__(parent)
self.stopped = False
self.mutex = QMutex()
def setup(self, root_dir, ext):
self.root_dir = root_dir
self.ext = ext
self.retrieve()
self.stopped = False
def stop(self):
with QMutexLocker(self.mutex):
self.stopped = True
def run(self):
for f in self.files:
fname = f
self.process_file(fname)
self.sig_file.emit(fname) #signal transmission
self.stop()
self.finished.emit() #signal transmission
def retrieve(self):
''' root_Get a file with the ext extension from dir'''
self.files = []
for rd, _, fl in os.walk(self.root_dir):
for f in fl:
_, fext = os.path.splitext(f)
if fext == self.ext:
self.files.append(os.path.join(rd, f))
self.length = len(self.files)
def process_file(self, path):
'''Do nothing for the time being'''
cnt = 0
if os.path.exists(path):
cnt += 1
else:
cnt = 0
def print(self):
for f in self.files:
print(f)
def main(args):
root_dir = '.'
ext = '.py'
if len(args) == 3:
root_dir = args[1]
ext = args[2]
fileList = FileList()
fileList.setup(root_dir, ext)
fileList.print()
if __name__ == '__main__':
main(sys.argv)
If you refer to this FileList class and press the button, guimain.py that scans the specified folder and displays the process will be as follows.
guimain.py
import sys
import os
from PyQt5.QtWidgets import (QWidget, QApplication, QPushButton, QLineEdit,
QHBoxLayout, QVBoxLayout, QTextEdit, QProgressBar,
QFileDialog)
from PyQt5.QtCore import pyqtSlot, Qt
from filelist import FileList
class MainWidget(QWidget):
dirname = ''
step = 0
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.initUI()
self.fileList = FileList()
self.fileList.sig_file.connect(self.update_status)
self.fileList.finished.connect(self.finish_process)
def initUI(self):
self.resize(480, 360)
self.txtFolder = QLineEdit()
self.txtFolder.setReadOnly(True)
self.btnFolder = QPushButton('reference...')
self.btnFolder.clicked.connect(self.show_folder_dialog)
hb1 = QHBoxLayout()
hb1.addWidget(self.txtFolder)
hb1.addWidget(self.btnFolder)
self.btnExec = QPushButton('Run')
self.btnExec.clicked.connect(self.exec_process)
self.btnExec.setEnabled(False)
self.btnExec.setVisible(True)
self.btnExit = QPushButton('End')
self.btnExit.setVisible(False) #Invalidation
self.btnExit.setEnabled(False) #Do not show
self.btnExit.clicked.connect(self.close)
hb2 = QHBoxLayout()
hb2.addWidget(self.btnExec)
hb2.addWidget(self.btnExit) #Added an invisible button in the initial state
self.txtLog = QTextEdit()
self.txtLog.setReadOnly(True)
self.pbar = QProgressBar()
self.pbar.setTextVisible(False)
layout = QVBoxLayout()
layout.addLayout(hb1)
layout.addLayout(hb2)
layout.addWidget(self.txtLog)
layout.addWidget(self.pbar)
self.setLayout(layout)
self.setWindowTitle('PyQt5 Sample')
def show_folder_dialog(self):
''' open dialog and set to foldername '''
dirname = QFileDialog.getExistingDirectory(self,
'open folder',
os.path.expanduser('.'),
QFileDialog.ShowDirsOnly)
if dirname:
self.dirname = dirname.replace('/', os.sep) #Convert directory delimiters according to the OS
self.txtFolder.setText(self.dirname)
self.btnExec.setEnabled(True)
self.step = 0
def print_log(self, logstr):
self.txtLog.append(logstr)
@pyqtSlot()
def exec_process(self):
if os.path.exists(self.dirname):
try:
QApplication.setOverrideCursor(Qt.WaitCursor)
self.fileList.setup(self.dirname, '.py')
maxCnt = self.fileList.length
self.pbar.setValue(0)
self.pbar.setMinimum(0)
self.pbar.setMaximum(maxCnt)
self.fileList.start()
except Exception as e:
self.print_log(str(e))
finally:
QApplication.restoreOverrideCursor()
else:
self.print_log('{0} is not exists'.format(self.dirname))
@pyqtSlot(str)
def update_status(self, filename):
self.txtLog.append(filename)
self.step += 1
self.pbar.setValue(self.step) #progress Bar
@pyqtSlot()
def finish_process(self):
self.fileList.wait()
#Hide the run button
self.btnExec.setEnabled(False)
self.btnExec.setVisible(False)
#Show the end button
self.btnExit.setEnabled(True)
self.btnExit.setVisible(True)
def main(args):
app = QApplication(args)
dialog = MainWidget()
dialog.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main(sys.argv)
It's getting annoying, so if you write it like the above, it will work for the time being.
In FileList.run (), by doing `self.sig_file.emit (fname)`
one by one, a signal is sent, and by receiving it with guimain.update_status (), the progress bar is displayed. You can proceed one by one.
I wrote a GUI app in Python. The key is to inherit QThread.
Recommended Posts