class PROCESS(QObject): queue_finished = pyqtSignal(int) prog_finished = pyqtSignal(tuple) stoped = pyqtSignal(int, int) # 1: start, 0: finished, 2: error | queue state = pyqtSignal(int, str) def __init__(self, parent=None): QObject.__init__(self, parent) self.process = QProcess() self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.finished.connect(self.emit_finished) self.process.stateChanged.connect(self.emit_state) self.process_type = 0 # 0 process, 1 runnable self.threadpool = QThreadPool() self.worker = Worker() self.worker.signals.state.connect(self.emit_state) self.queue = None def emit_state(self, state): self.state.emit(state, self.prog_name) def emit_finished(self, exitcode, exitstatus): self.prog_finished.emit((self.prog_name, exitcode, exitstatus)) def set_queue(self, queue): self.queue = queue def start_process(self): try: obj = self.queue.get(False) self.prog_name = list(obj.keys())[0] if callable(list(obj.values())[0][0]): self.process_type = 1 funct = list(obj.values())[0][0] args = list(obj.values())[0][1] self.worker.insert_function(funct, args, self.prog_name) self.threadpool.start(self.worker) else: self.process_type = 0 self.process.start( list(obj.values())[0][0], list(obj.values())[0][1]) except Empty: self.queue_finished.emit(self.queue.name) def force_finished(self): # for process (programs) self.stoped.emit(1, self.queue.name) if self.process_type == 0: self.process.terminate() if not self.process.waitForFinished(1000): self.process.kill() else: if self.threadpool.activeThreadCount(): self.threadpool.clear() self.threadpool.waitForDone() with self.queue.mutex: self.queue.queue.clear() self.stoped.emit(0, self.queue.name)
class TaskConsumer(metaclass=SingletonMeta): def __init__(self) -> None: self.lock = threading.Lock() self.q = list() self.thread_pool = QThreadPool() self.thread_pool.setMaxThreadCount(1) def set_thread_pool(self, tp): self.thread_pool = tp def insert_task(self, task: Task) -> None: """ Insert task if whether its not in queue or its status is either done or failed. :param task: :return: """ with self.lock: self.__remove_finished_task() if not any(task.task_type == each.task_type for each in self.q): self.q.append(task) def get_task(self) -> Task: with self.lock: self.__remove_finished_task() if self.q: for each in self.q: if each.task_type == TaskTypes.SERIAL_OPEN: return each return self.q[0] else: return None def consume_task(self) -> None: task = self.get_task() if task and self.thread_pool.activeThreadCount() == 0: self.thread_pool.start(task) def clear_task_queue(self) -> None: self.q.clear() self.thread_pool.clear() def __remove_finished_task(self) -> None: clr_lst = [ index for index, task in enumerate(self.q) if task.status > TaskStatus.RUNNING ] for index in clr_lst: del self.q[index]
class BackgroundWorker(QObject): def __init__(self, parent=None): super().__init__(parent=parent) self._threads = QThreadPool() def runTask(self, fn=None, fn_started=None, fn_finished=None, fn_failed=None, token=None, fn_progress=None, **kwargs): self._threads.start( Task( fn=fn, fn_started=fn_started, fn_finished=fn_finished, fn_failed=fn_failed, cancel_token=token, fn_progress=fn_progress, **kwargs, ) ) def isRunning(self): return self._threads.activeThreadCount() > 0
class Fuzzer(QObject): communication = None def __init__(self, communication, max_thread_number, inputs, wordlist): super(Fuzzer, self).__init__() self.communication = communication self.max_thread_number = max_thread_number self.pool = QThreadPool() self.pool.globalInstance() self.inputs = inputs self.wordlist = wordlist self.socket_data = [inputs[0], inputs[1], inputs[2]] def start(self): num_of_chunks = len(self.wordlist) // int(self.max_thread_number) new_fuzz_list = list(self.listChunks(self.wordlist, num_of_chunks)) self.pool.setMaxThreadCount(self.max_thread_number) for sub_list in new_fuzz_list: QCoreApplication.processEvents() for word in sub_list: QCoreApplication.processEvents() if self.pool.activeThreadCount() < self.pool.maxThreadCount(): fuzz_thread = Fuzz(self.communication, self.socket_data, self.inputs[3], str(word), self.inputs[5]) fuzz_thread.setAutoDelete(True) self.pool.start(fuzz_thread) else: self.pool.waitForDone() def __del__(self): pass def listChunks(self, myList, numOfChunks): for i in range(0, len(myList), numOfChunks): yield myList[i:i + numOfChunks]
class PackageMonitor(QThread): signal = pyqtSignal() def __init__(self): super().__init__() self.pool = QThreadPool() self.pool.globalInstance() self.pool.setMaxThreadCount(3) def run(self): flag = True while flag: count = self.pool.activeThreadCount() if count <= 0: # 打包完成,发完成信号,重置flag,终止线程 self.signal.emit() flag = False else: # 打包进行中,线程休眠 QThread.sleep(3) def add_runnable(self, runnable): self.pool.start(runnable) def clear(self): self.pool.clear()
class MainPage(QMainWindow): def __init__(self): super(MainPage, self).__init__() loadUi('./ui/ui_mainwindow.ui', self) self.mainapp = MainApp() # main.py 에서 연결 self.mainapp.gui_framework = self self.threadpool = QThreadPool() self._stopflag = False # main app 정지 신호 self._exitflag = False # main app 종료 신호 self.EXIT_CODE_REBOOT = -123456789 # 초기 출력 self.print_setting() # mainwindow 의 textBrowser_setting 에 현재 설정 출력 self.textBrowser.append("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) # 시그널/슬롯 생성 self.actionSimulationSetting.triggered.connect( self.executeConfigPage) # 페이지(윈도우) 연결 self.actionRun.triggered.connect(self.run_program) # Run mainapp self.actionStop.triggered.connect(self.stop_program) # self.actionSaveAgent.triggered.connect(self.save_agent) self.actionLoadAgent.triggered.connect(self.load_agent) self.actionLoadAni.triggered.connect(self.load_animation) self.actionReboot.triggered.connect(self.reboot_program) def closeEvent(self, event): reply = QMessageBox.question( self, "Message", "Are you sure you want to quit? Any unsaved work will be lost.", QMessageBox.Close | QMessageBox.Cancel, QMessageBox.Cancel) if reply == QMessageBox.Close: self._stopflag = True self._exitflag = True event.accept() else: event.ignore() def run_program(self): worker = Worker(self.mainapp) # 정지설정 False / run & 설정버튼 비활성화 self._stopflag = False self.actionRun.setEnabled(False) self.actionSimulationSetting.setEnabled(False) # Execute self.threadpool.start(worker) self.write_console("Multithreading with %d of %d threads" % (self.threadpool.activeThreadCount(), self.threadpool.maxThreadCount())) # Alternative: def run_program2(self): # 외부에서 self.actionRun.triggered.connect(self.run_program2) # # self.thread = Thread(target=self.mainapp.run_main) # self.thread.daemon = True # self.thread.start() def stop_program(self): self._stopflag = True self.actionRun.setEnabled(True) self.actionSimulationSetting.setEnabled(True) def reboot_program(self): reply = QMessageBox.question( self, "Message", "Are you sure you want to reboot? Any unsaved work will be lost.", QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) if reply == QMessageBox.Ok: self._stopflag = True self._exitflag = True qApp.exit(self.EXIT_CODE_REBOOT) # def save_agent(self): # if not self.mainapp.env: # QMessageBox.warning(self, "Message", "Any agent/environment isn't loaded.") # elif self.mainapp.agent.name == 'rl': # self.mainapp.agent.save_file(self.mainapp.log_dir, self.mainapp.iter) # QMessageBox.information(self, "Message", "RL agent file is saved") # elif self.mainapp.agent.name == 'greedy': # QMessageBox.warning(self, "Message", "Greedy agent file CANNOT be saved") def load_agent(self): options = QFileDialog.Options() # filter: "All Files (*)", "Python Files (*.py)", "PKL Files (*.pkl)" fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "PKL Files (*.pkl)", options=options) if fileName: QMessageBox.information(self, "Message", "agent file is loaded \n %s" % fileName) self.mainapp.agent_name = fileName self.print_setting() def load_animation(self): options = QFileDialog.Options() # filter: "All Files (*)", "Python Files (*.py)", "PKL Files (*.pkl)" fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "PKL Files (*.pkl)", options=options) if fileName: QMessageBox.information( self, "Message", "animation file is loaded \n %s" % fileName) with open(fileName, 'rb') as file: # james.p 파일을 바이너리 읽기 모드(rb)로 열기 ani_data = pickle.load(file) temp_ani = GraphicDisplay(ani_data['width'], ani_data['height'], unit_pixel=ani_data['unit']) temp_ani.data = ani_data['data'] temp_ani.mainloop() def print_setting(self): self.textBrowser_setting.setText("[ Current Setting ]\n") if self.mainapp.agent_name.lower() in ('rl', 'reinforcement learning'): self.textBrowser_setting.append("Agent: Reinforcement Learning") elif self.mainapp.agent_name.lower() == 'greedy': self.textBrowser_setting.append("Agent: %s" % self.mainapp.agent_name) else: self.textBrowser_setting.append("Agent: Loaded %s" % self.mainapp.agent_name) self.textBrowser_setting.append("Task: %s" % self.mainapp.task) self.textBrowser_setting.append("Num Battery: %d" % self.mainapp.b_num) self.textBrowser_setting.append("Num Flight: %d" % self.mainapp.f_num) self.textBrowser_setting.append("Flight Time Interval: %.2f" % self.mainapp.f_interval) self.textBrowser_setting.append("Defense range (map width): %.2f" % self.mainapp.map_width) self.textBrowser_setting.append("Termination Type: %s" % str(self.mainapp.termination[0])) self.textBrowser_setting.append("Termination Criteria: %s" % str(self.mainapp.termination[1])) self.textBrowser_setting.append("Autosave cycle: %d iter" % self.mainapp.autosave_iter) def executeConfigPage(self): simconfig_page = SimconfigPage(self, self.mainapp) simconfig_page.exec_() def write_console(self, text, box='textBrowser'): if box == 'textBrowser_setting': self.textBrowser_setting.append(str(text)) self.textBrowser_setting.moveCursor(QtGui.QTextCursor.End) else: self.textBrowser.append(str(text)) self.textBrowser.moveCursor(QtGui.QTextCursor.End)
class Main_Window_class(QDialog): PLOTS_NUMBER = 3 EFFECTS_NUMBER = 4 def __init__(self): super().__init__() self.nlabels = 6 self.slider_step = 1 self.slider_default = 0 self.slider_max = 50 self.slider_min = -50 # Коэффициент ослабления эхо эффекта self.echo_coefficient = 1 # Задержка в фреймах self.echo_shift = 1000 # "Жесткий" предел овердрайва за которым волна обрезается self.soft_clipping_hard_limit = 14000 # "Мягкий" предел овердрайва за которым волна сглаживается self.soft_clipping_linear_limit = 12000 # -3 dB self.music_is_playing = False self.threadpool = QThreadPool() self.nchannels = None # number of channels self.sampwidth = None # number of bytes per sample self.framerate = None # number of frames per second self.nframes = None # total number of frames self.comptype = None # compression type self.compname = None # compression type name self.elem_per_hertz = None self.coefficient = 1000 # коэффициент прореживания, для уменьшения частоты дискретизации self.buffer_size = None self.buffer_cnt = 0 self.music_worker = None self.min_freq = 0 self.max_freq = None self.channels = [] self.spectrum = None self.spectrum_original = None self.channels_original = [] self.spectrum_hard_clipping = None self.channels_hard_clipping = [] self.spectrum_soft_clipping = None self.channels_soft_clipping = None self.spectrum_envelop = None self.channels_envelop = [] self.spectrum_echo = None self.channels_echo = [] self.app_name = 'Эквалайзер' self.buttons_labels = ['Воспроизвести', 'Пауза', 'Остановить'] self.bands = [[], []] self.labels = [] self.ui_labels = [] self.sliders = [] self.sliders_workers = [None for _ in range(self.nlabels)] self.sliders_old_values = [ self.slider_default for _ in range(self.nlabels) ] self.LCD_numbers = [] self.canvases = [] self.effects = [ self.doing_hard_clipping, self.doing_envelop, self.doing_echo, self.doing_soft_clipping ] self.effects_checkboxes_labels = [ 'Жесткий клиппинг (дисторшн)', 'Энвелоп', 'Эхо', 'Мягкий клиппинг (овердрайв)' ] self.effects_checkboxes = [] self.effects_checkboxes_workers = [] self.play_button, self.stop_button, self.pause_button = None, None, None self.redraw_mutex = threading.Lock() self.run_ui() def run_ui(self): self.pull_music() self.create_interface() def pull_music(self): path_to_pull = QFileDialog.getOpenFileName(self, 'Выберите .wav файл')[0] wav = wave.open(path_to_pull, mode='r') # nchannels - число каналов .wav файла # sampwidth - число байт на сэмпл # framerate - число фреймов в секунду # nframes - общее число фреймов # comptype - тип сжатия # compname - имя типа сжатия (self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname) = wav.getparams() # Теорема Котельникова, в дискретном сигнале представлены частоты от нуля до framerate // 2 self.max_freq = self.framerate // 2 self.buffer_size = self.framerate # считываем все фреймы content = wav.readframes(self.nframes) # и сохраняем в массив с нужным типом элемента samples = np.fromstring(content, dtype=IntTypes.types[self.sampwidth]) # разбиваем на каналы, например, \xe2\xff\xe3\xfа — это фрейм 16-битного wav-файла. Значит, \xe2\xff — сэмпл # первого (левого) канала, а \xe3\xfа for i in range(self.nchannels): self.channels.append(samples[i::self.nchannels]) # сохраняем оригинальные каналы self.channels_original = self.channels.copy() # создаем worker'ов, который создадут измененные дорожки с примененными эффектами for i in range(self.EFFECTS_NUMBER): worker = Worker(self.effects[i], self.channels) self.effects_checkboxes_workers.append(worker) self.threadpool.start(worker) # Выполняем быстрое дискретное преобразование Фурье, для получения спектра self.spectrum = np.fft.rfft(self.channels_original) self.spectrum_original = self.spectrum.copy() # запускаем pygame pygame.mixer.pre_init(frequency=self.framerate, size=-8 * self.sampwidth, channels=self.nchannels) pygame.init() def create_bands(self): """Создает подписи к слайдерам""" step = (self.max_freq - self.min_freq) // 2**self.nlabels self.bands[0].append(self.min_freq) self.bands[1].append(self.min_freq + step) for i in range(1, self.nlabels - 1): self.bands[0].append(self.bands[1][i - 1]) self.bands[1].append(self.bands[0][i] + 2**i * step) self.bands[0].append(self.bands[1][self.nlabels - 2]) self.bands[1].append(self.max_freq) for i in range(self.nlabels): self.labels.append(f'{self.bands[0][i]} - {str(self.bands[1][i])}') def create_labels(self): for label in self.labels: self.ui_labels.append(QLabel(label, self)) def create_lcd_numbers(self): for _ in range(self.nlabels): self.LCD_numbers.append(QLCDNumber(self)) def create_sliders(self): for i in range(self.nlabels): slider = QSlider(Qt.Vertical, self) slider.setMinimum(self.slider_min) slider.setMaximum(self.slider_max) slider.setValue(self.slider_default) slider.setFocusPolicy(Qt.StrongFocus) slider.setTickPosition(QSlider.TicksBothSides) slider.setSingleStep(self.slider_step) slider.valueChanged[int].connect(self.slider_change_value) slider.valueChanged[int].connect(self.LCD_numbers[i].display) self.sliders.append(slider) def create_checkboxes(self): for i in range(self.EFFECTS_NUMBER): checkbox = QCheckBox(self.effects_checkboxes_labels[i], self) checkbox.setChecked(False) checkbox.stateChanged.connect(self.checkbox_clicked_listener) self.effects_checkboxes.append(checkbox) def create_buttons(self): self.play_button = QPushButton(self.buttons_labels[0], self) self.pause_button = QPushButton(self.buttons_labels[1], self) self.stop_button = QPushButton(self.buttons_labels[2], self) self.play_button.clicked.connect(self.button_clicked_listener) self.pause_button.clicked.connect(self.button_clicked_listener) self.stop_button.clicked.connect(self.button_clicked_listener) def create_graphics(self): self.create_lcd_numbers() self.create_sliders() self.create_bands() self.create_labels() self.create_checkboxes() self.create_buttons() self.elem_per_hertz = self.spectrum.shape[1] // (self.max_freq - self.min_freq) plots_labels = [('Частота, Гц', 'Амплитуда'), ('Частота, Гц', 'Амплитуда'), ('Время, с', 'Амплитуда')] plots_sources = [ (self.channels[0][::self.coefficient], ), (np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.coefficient], np.abs(self.spectrum[0][::self.coefficient]) / self.nframes), (np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.coefficient], np.abs(self.spectrum[0][::self.coefficient]) / self.nframes) ] for i in range(self.PLOTS_NUMBER): figure = plt.figure() subplot = figure.add_subplot(1, 1, 1) subplot.plot(*plots_sources[i]) subplot.set_xlabel(plots_labels[i][0]) subplot.set_ylabel(plots_labels[i][1]) figure.align_xlabels() figure.align_ylabels() canvas = FigureCanvas(figure) navigation_tool = NavigationToolbar(canvas, self) canvas = { 'figure': figure, 'canvas': canvas, 'navigation_tool': navigation_tool, 'axes_labels': plots_labels[i], } self.canvases.append(canvas) canvas['canvas'].draw() def create_interface(self): self.create_graphics() labels_box = QHBoxLayout() for label in self.ui_labels: labels_box.addWidget(label) nums_box = QHBoxLayout() for number in self.LCD_numbers: nums_box.addWidget(number) sliders_box = QHBoxLayout() for slider in self.sliders: sliders_box.addWidget(slider) graph_box = QVBoxLayout() graph_box.addWidget(self.canvases[0]['navigation_tool']) graph_box.addWidget(self.canvases[0]['canvas']) left_box = QVBoxLayout() left_box.addLayout(labels_box) left_box.addLayout(sliders_box) left_box.addLayout(nums_box) left_box.addLayout(graph_box) checkbox_and_button_layout = QHBoxLayout() for i in range(self.EFFECTS_NUMBER): checkbox_and_button_layout.addWidget(self.effects_checkboxes[i]) checkbox_and_button_layout.addWidget(self.play_button) checkbox_and_button_layout.addWidget(self.pause_button) checkbox_and_button_layout.addWidget(self.stop_button) graph_box = QVBoxLayout() graph_box.addWidget(self.canvases[1]['navigation_tool']) graph_box.addWidget(self.canvases[1]['canvas']) graph_box.addWidget(self.canvases[2]['navigation_tool']) graph_box.addWidget(self.canvases[2]['canvas']) right_box = QVBoxLayout() right_box.addLayout(checkbox_and_button_layout) right_box.addLayout(graph_box) all_box = QHBoxLayout() all_box.addLayout(left_box) all_box.addLayout(right_box) self.setLayout(all_box) self.setWindowTitle(self.app_name) self.showMaximized() def slider_change_value(self, value): for i, slider in enumerate(self.sliders): if self.sender() == slider: self.sliders_workers[i] = Worker(self.music_edit, i, value) self.threadpool.start(self.sliders_workers[i]) def checkbox_clicked_listener(self, state): if self.sender() == self.effects_checkboxes[0]: if state == Qt.Checked: self.effects_checkboxes[1].setChecked(False) self.effects_checkboxes[2].setChecked(False) self.effects_checkboxes[3].setChecked(False) self.channels = self.channels_hard_clipping.copy() self.spectrum = self.spectrum_hard_clipping.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() elif self.sender() == self.effects_checkboxes[1]: if state == Qt.Checked: self.effects_checkboxes[0].setChecked(False) self.effects_checkboxes[2].setChecked(False) self.effects_checkboxes[3].setChecked(False) self.channels = self.channels_envelop.copy() self.spectrum = self.spectrum_envelop.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() elif self.sender() == self.effects_checkboxes[2]: if state == Qt.Checked: self.effects_checkboxes[0].setChecked(False) self.effects_checkboxes[1].setChecked(False) self.effects_checkboxes[3].setChecked(False) self.channels = self.channels_echo.copy() self.spectrum = self.spectrum_echo.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() elif self.sender() == self.effects_checkboxes[3]: if state == Qt.Checked: self.effects_checkboxes[0].setChecked(False) self.effects_checkboxes[1].setChecked(False) self.effects_checkboxes[2].setChecked(False) self.channels = self.channels_soft_clipping.copy() self.spectrum = self.spectrum_soft_clipping.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() for slider in self.sliders: slider.setValue(self.slider_default) draw_1 = Worker(self.draw_array, self.spectrum, 0) self.threadpool.start(draw_1) draw_2 = Worker(self.draw_array, self.channels, 1) self.threadpool.start(draw_2) def button_clicked_listener(self): if self.sender() == self.play_button: # Запустить if not self.music_is_playing: self.music_is_playing = True self.music_worker = Worker(self.start_music) self.threadpool.start(self.music_worker) elif self.sender() == self.pause_button: # Пауза if self.music_is_playing: self.music_is_playing = False elif self.sender() == self.stop_button: # Остановить if self.music_is_playing: self.music_is_playing = False self.threadpool.clear() for slider in self.sliders: worker = Worker(self.music_edit, self.sliders.index(slider), self.slider_default) self.sliders_workers.append(worker) self.threadpool.start(worker) self.threadpool.start(Worker(self.wait_until_sliders_launched)) for slider in self.sliders: slider.setValue(self.slider_default) self.buffer_cnt = 0 for i in range(self.EFFECTS_NUMBER): self.effects_checkboxes[i].setChecked(False) def wait_until_sliders_launched(self): while self.threadpool.activeThreadCount() != len(self.sliders): sleep(0.1) self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() def start_music(self): tmp_channels = [ self.channels[0][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:], self.channels[1][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:] ] tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) sound = tmp_sound if not self.music_is_playing: return pygame.mixer.Sound.play(sound) start_pos = self.buffer_cnt for self.buffer_cnt in range(start_pos + 1, self.nframes // self.buffer_size): tmp_channels = [ self.channels[0][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:], self.channels[1][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:] ] tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) while pygame.mixer.get_busy(): sleep(0.01) sound = tmp_sound if not self.music_is_playing: return pygame.mixer.Sound.play(sound) tmp_channels = [ self.channels[0][self.buffer_cnt * self.buffer_size::], self.channels[1][self.buffer_cnt * self.buffer_size::] ] tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) while pygame.mixer.get_busy(): sleep(0.01) sound = tmp_sound if not self.music_is_playing: return pygame.mixer.Sound.play(sound) self.buffer_cnt = 0 self.music_is_playing = False def music_edit(self, pos, value): old_value = self.sliders_old_values[pos] self.sliders_old_values[pos] = value if old_value == value: return if pos == 0: for i in range(self.nchannels): self.spectrum[i][:self.elem_per_hertz * self.bands[1][pos] + 1] *= 10**((value - old_value) / 20) elif pos == 5: for i in range(self.nchannels): self.spectrum[i][self.elem_per_hertz * self.bands[0][pos]:] *= 10**( (value - old_value) / 20) else: for i in range(self.nchannels): self.spectrum[i][self.elem_per_hertz * self.bands[0][pos]:self.elem_per_hertz * self.bands[1][pos] + 1] *= 10**((value - old_value) / 20) self.channels = (np.fft.irfft(self.spectrum)).astype( IntTypes.types[self.sampwidth]) draw_1 = Worker(self.draw_array, self.spectrum, 0) self.threadpool.start(draw_1) draw_2 = Worker(self.draw_array, self.channels, 1) self.threadpool.start(draw_2) def redraw_subplot(self, canvas: dict, left, right=None): # потоконебезопасная функция, нужен mutex self.redraw_mutex.acquire() canvas['figure'].clear() subplot = canvas['figure'].add_subplot(1, 1, 1) subplot.set_xlabel(canvas['axes_labels'][0]) subplot.set_ylabel(canvas['axes_labels'][1]) canvas['figure'].align_xlabels() canvas['figure'].align_ylabels() if right is not None: subplot.plot(left, right) else: subplot.plot(left) canvas['canvas'].draw() self.redraw_mutex.release() def draw_array(self, arr, spectrum_or_channel): if spectrum_or_channel == 0: self.redraw_subplot( self.canvases[1], np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.coefficient], np.abs(arr[0][::self.coefficient]) / self.nframes) else: self.redraw_subplot(self.canvases[0], arr[0][::self.coefficient]) def doing_hard_clipping(self, channels): threshold_max = int(0.6 * np.max(channels[0])) threshold_min = int(0.6 * np.min(channels[0])) self.channels_hard_clipping = np.maximum( np.minimum(channels, threshold_max), threshold_min).astype(IntTypes.types[self.sampwidth]) self.spectrum_hard_clipping = np.fft.rfft(self.channels_hard_clipping) def doing_soft_clipping(self, channels): clip_limit = self.soft_clipping_linear_limit + int( pi / 2 * (self.soft_clipping_hard_limit - self.soft_clipping_linear_limit)) scale = self.soft_clipping_hard_limit - self.soft_clipping_linear_limit tmp_channels = np.array(channels, copy=True) for i in range(len(tmp_channels)): for j in range(len(tmp_channels[i])): n = tmp_channels[i][j] amplitude, sign = abs(n), 1 if n >= 0 else -1 if amplitude <= self.soft_clipping_linear_limit: tmp_channels[i][j] = n continue if amplitude >= clip_limit: tmp_channels[i][j] = self.soft_clipping_hard_limit * sign continue compression = scale * sin( float(amplitude - self.soft_clipping_linear_limit) / scale) tmp_channels[i][j] = (self.soft_clipping_linear_limit + int(compression)) * sign self.channels_soft_clipping = tmp_channels self.spectrum_soft_clipping = np.fft.rfft(self.channels_soft_clipping) def doing_envelop(self, channels): frequency = 1 / 15 envelope_sig = np.array([ abs(sin(2 * pi * frequency * t / self.framerate)) for t in range(self.nframes) ]) tmp_channels = np.array(channels, copy=True) for i in range(self.nchannels): tmp_channels[i] = (tmp_channels[i] * envelope_sig).astype( IntTypes.types[self.sampwidth]) self.channels_envelop = tmp_channels self.spectrum_envelop = np.fft.rfft(self.channels_envelop) def doing_echo(self, channels): tmp_channels = np.array(channels, copy=True) echo_channels = tmp_channels.copy() for i in range(len(tmp_channels)): echo_channels[i] = (echo_channels[i] * self.echo_coefficient).astype( IntTypes.types[self.sampwidth]) echo_channels[i] = np.append(echo_channels[i][self.echo_shift:], np.zeros(self.echo_shift)).astype( IntTypes.types[self.sampwidth]) tmp_channels[i] += echo_channels[i] self.channels_echo = tmp_channels self.spectrum_echo = np.fft.rfft(self.channels_echo)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.mode = ['lineart', 'gray', 'color'] self.resolution = ['75', '100', '150', '200', '300', '600', '1200'] self.compression = ['None', 'JPEG'] self.scanFolder = os.getcwd() self.ver = sane.init() self.ui = uic.loadUi("mainwindow.ui", self) #self.ui = Ui_MainWindow() #self.ui.setupUi(self) self.dialog = QDialog() self.message = QMessageBox() self.threadpool = QThreadPool() self.settings = QSettings("bibuweb.de", "Scan2Folder") self.progressBar = None self.configWin = ConfigWindow(self) self.configWin.ui.scanButton.clicked.connect(self.configScan) self.ui.resolutions.addItems(self.resolution) self.ui.resolutions.setCurrentIndex( self.ui.resolutions.findText('300')) self.ui.btnOpenDir.clicked.connect(self.openDir) self.ui.btnStartscan.clicked.connect(self.startScanJob) self.ui.btnOcr.clicked.connect(self.ocr_startProcess) self.ui.actionCalibrate.triggered.connect(self.configureWindow) self.configWin.ui.saveButton.clicked.connect(self.saveConfig) # Change Color back after error self.ui.filename.cursorPositionChanged.connect(self.leditcolor) self.ui.scanpath.cursorPositionChanged.connect(self.leditcolor) self.ui.scanpath.textChanged.connect(self.scanPathCanged) self.is_dev = True self.dev_available = False self.dev_connected = False self.adf = False self.dev = None self.devices = [] self.scanStatus = False self.btnStyle = "" self.contrast = 1 self.brightness = 1 self.color = 1 self.sharpness = 1 self.scanPath = "" self.ocr = False self.crop = False self.cropSize = {'left': 1, 'top': 1, 'width': 1, 'height': 1} self.ocrFiles = [] self.tempocr = None if self.settings.contains("ocr"): print("Load: ", self.settings.value('ocr')) if self.settings.value('ocr') == 'true': self.ocr = True self.ui.actionEnable_OCR.setChecked(True) self.configWin.ui.OCR_Enabled.setChecked(True) self.configWin.ui.OCR_Box.setEnabled(True) else: self.ui.actionEnable_OCR.setChecked(False) self.configWin.ui.OCR_Enabled.setChecked(False) self.configWin.ui.OCR_Box.setEnabled(False) ## connect Signal here and not before loading settings ## if not, you never will get the stored value because QAction is triggered when ever the value changed self.configWin.ui.OCR_Enabled.stateChanged.connect(self.ocrConfig) if self.settings.contains('crop'): if self.settings.value('crop') == 'true': self.crop = True self.configWin.ui.checkCrop.setChecked(self.crop) if self.settings.contains('cropSize'): #print(self.settings.value('cropSize')) self.cropSize = self.settings.value('cropSize') self.configWin.ui.cropX.setValue(self.cropSize['left']) self.configWin.ui.cropY.setValue(self.cropSize['top']) self.configWin.ui.cropW.setValue(self.cropSize['width']) self.configWin.ui.cropH.setValue(self.cropSize['height']) if self.settings.contains("path"): self.ui.scanpath.setText(self.settings.value("path")) self.scanPath = self.settings.value("path") self.createCompleter() if self.settings.contains('contrast'): self.brightness = self.settings.value('brightness') self.contrast = self.settings.value('contrast') self.configWin.ui.brigthnessLcd.setValue(float(self.brightness)) self.configWin.ui.brigthnesSlider.setValue( int(float(self.brightness) * 10)) self.configWin.ui.contrastLcd.setValue(float(self.contrast)) self.configWin.ui.contrastSlider.setValue( int(float(self.contrast) * 10)) if self.settings.contains('color'): self.color = self.settings.value('color') self.configWin.ui.colorLcd.setValue(float(self.color)) self.configWin.ui.colorSlider.setValue(int(float(self.color) * 10)) if self.settings.contains("sharpness"): self.sharpness = self.settings.value('sharpness') self.configWin.ui.sharpnessLcd.setValue(float(self.sharpness)) self.configWin.ui.sharpnessSlider.setValue( int(float(self.sharpness) * 10)) def closeEvent(self, event): #if not set, process keeps running in background self.scanStatus = False def openDir(self): fileDlg = QFileDialog() self.scanFolder = fileDlg.getExistingDirectory( self, 'Scan Folder', self.scanFolder, QFileDialog.DontUseNativeDialog) self.ui.scanpath.setText(self.scanFolder) self.settings.setValue("path", self.scanFolder) self.settings.sync() def startThread(self, fn, resultFn=None, complete=None): worker = Worker( fn) # Any other args, kwargs are passed to the run function if resultFn is not None: worker.signals.result.connect(resultFn) if complete is not None: worker.signals.finished.connect(complete) #worker.signals.progress.connect(self.scannerProgress) self.threadpool.start(worker) def thread_complete(self): self.scannerProgress(100) time.sleep(1) self.dialog.close() if self.dev_available: self.show() else: #TODO: put error dlg here self.message.setText( "No scanner found\n Check your Configuration!") self.message.exec() print("Error: No Devices found") print("THREAD COMPLETE! ", self.threadpool.activeThreadCount()) @pyqtSlot(str) def scanPathCanged(self, path): self.scanPath = path self.createCompleter() def createCompleter(self): ff = glob.glob(self.scanPath + "/*.pdf") files = [] for f in ff: files.append(os.path.basename(f).split('.')[0]) completer = QCompleter(files) completer.setCaseSensitivity(Qt.CaseInsensitive) self.ui.filename.setCompleter(completer) def scannerLookup(self): #self.dialog.setModal(True) self.uidlg = Ui_Dialog() self.uidlg.setupUi(self.dialog) self.dialog.show() def checkScanMode(self): mode = "" if self.ui.btnBuW.isChecked(): mode = self.ui.btnBuW.text() elif self.ui.btnGray.isChecked(): mode = self.ui.btnGray.text() elif self.ui.btnColor.isChecked(): mode = self.ui.btnColor.text() print(mode) return mode def scannerAddToDlg(self, result): self.devices = result if len(self.devices) > 0: self.dev_available = True for i, dev in enumerate(result): self.ui.comboBox.addItem(dev[i]) else: return def scannerProgress(self, val): self.uidlg.progressBar.setValue(val) def scanners(self): self.scannerLookup() self.startThread(sane.get_devices, self.scannerAddToDlg, self.thread_complete) def scannerCheck(self): self.statusBar().showMessage("looking up for scanner ....") print(self.devices[0]) count = 0 while self.is_dev: try: #ToDo: check index from self.dev = sane.open(self.devices[0][0]) except: print("no scanner connected, waiting...", self.dev) if self.dev is not None: self.is_dev = False self.dev_connected = True print("scanner connected") time.sleep(3) ## Stop process after 3 times to avoid endless loop if no device is available ## due started as thread count += 1 if count > 2: self.is_dev = False self.statusBar().showMessage("No Scanner connected!") def commonThreadEnd(self): print("Thread ended") def scanDocThreadEnded(self): self.statusBar().showMessage("Job stopped") self.scanStatus = False self.setLedStatus() def scannerCheckThreadEnd(self): print("Lookup Thread ended") if self.dev_connected: self.startThread(self.scanDocuments, None, self.scanDocThreadEnded) def setScannerStatus(self): if self.dev_connected: self.setLedStatus() self.statusBar().showMessage("Scanner connected", 10) self.setScanButton("running") else: self.setScanButton('stopped') def setLedStatus(self): if self.scanStatus: pix = QPixmap(":/images/square_green.svg") else: pix = QPixmap(":/images/square_red.svg") self.ui.statusLed.setPixmap(pix) def scanDocuments(self): ip = self.devices[0][0].split('=')[1] print(ip) url = 'http://' + ip + XML_PATH self.dev.mode = self.checkScanMode() self.dev.resolution = int(self.ui.resolutions.currentText()) imgNr = 0 savePath = self.ui.scanpath.text() + "/" imgPrefix = self.ui.filename.text() + "_" #self.dev.contrast = 900 #self.dev.brightness = self.brightness while self.scanStatus: btnreq = urlopen(url) soup = bs4.BeautifulSoup(str(btnreq.read()), 'lxml') if soup.startscan.string == str(1): #print("Pressed") if soup.adfloaded.string == str(1): self.adf = True print("ADF Source") self.dev.source = 'ADF' imIter = self.dev.multi_scan() while self.adf: try: im = imIter.next() imgNr = imgNr + 1 img = imgPrefix + str(imgNr) + ".png" im.save(savePath + img) if self.ocr: self.ocrFiles.append(savePath + img) except: self.adf = False break else: self.adf = False imgNr = imgNr + 1 img = imgPrefix + str(imgNr) + ".png" self.dev.start() im = self.dev.snap() self.enhanceImage(im, savePath, img) if self.ocr: self.ocrFiles.append(savePath + img) time.sleep(3) def enhanceImage(self, image, path, pf): brightness = ImageEnhance.Brightness(image) image = brightness.enhance(float(self.brightness)) contrast = ImageEnhance.Contrast(image) image = contrast.enhance(float(self.contrast)) colour = ImageEnhance.Color(image) image = colour.enhance(float(self.color)) sharpness = ImageEnhance.Sharpness(image) image = sharpness.enhance(float(self.sharpness)) print("image saved ", self.ui.scanpath.text() + "/" + pf) image.save(path + pf) @pyqtSlot() def leditcolor(self): self.ui.scanpath.setStyleSheet("background-color:rgb(255, 255, 255)") self.ui.filename.setStyleSheet("background-color:rgb(255, 255, 255)") def startScanJob(self): # print("Path: ",self.scanpath.text()) # print("Mode: ",mode) # print("Resolution: ", self.resolutions.currentText()) # print("File: ", self.filename.text()) if len(self.ui.scanpath.text()) == 0: msg = QMessageBox() msg.setText("Please enter file path!") msg.exec() self.ui.scanpath.setStyleSheet( "background-color:rgb(255, 170, 127)") return if len(self.ui.filename.text()) == 0: msg = QMessageBox() msg.setText("Please enter file name prefix!") msg.exec() self.ui.filename.setStyleSheet( "background-color:rgb(255, 170, 127)") return if not self.scanStatus: self.setScanButton("starting") self.startThread(self.scannerCheck, self.setScannerStatus, self.scannerCheckThreadEnd) self.scanStatus = True self.ui.scanpath.setEnabled(False) self.ui.filename.setEnabled(False) else: self.scanStatus = False self.setScanButton("stopped") self.ui.scanpath.setEnabled(True) self.ui.filename.setEnabled(True) self.ui.filename.clear() if self.ocr and len(self.ocrFiles) > 0: self.ui.btnOcr.setEnabled(True) self.tempocr = tempfile.NamedTemporaryFile(delete=False) for f in self.ocrFiles: self.tempocr.write(str(f + "\n").encode()) self.tempocr.close() def setScanButton(self, status): if status == "starting": self.btnStyle = self.ui.btnStartscan.styleSheet() self.ui.btnStartscan.setStyleSheet("background-color: yellow") self.ui.btnStartscan.setText("Starting...") if status == "running": self.ui.btnStartscan.setStyleSheet("background-color: red") self.ui.btnStartscan.setText("Stop") self.statusBar().showMessage("Scan job is running..") if status == "stopped": self.ui.btnStartscan.setText("Sart Scan") self.ui.btnStartscan.setStyleSheet(self.btnStyle) def configureWindow(self): if self.dev is not None: self.configWin.ui.scanButton.setEnabled(True) self.configWin.ui.scanButton.setText("Start Scan") else: self.configWin.ui.scanButton.setEnabled(False) self.configWin.ui.scanButton.setText("Sart Scan Service first") self.configWin.show() def ocr_startProcess(self): self.progressDlg = QProgressDialog(self) self.progressDlg.setWindowTitle("OCR Process") self.progressDlg.setLabelText("OCR Process in Progress ...") self.progressDlg.setAutoClose(False) self.progressDlg.setAutoReset(False) self.progressDlg.setModal(True) self.startThread(self.ocr_process, None, self.ocr_stopped) def ocr_process(self): if len(self.ocrFiles) > 0: val = 0 ### add one more for pdf create process max = len(self.ocrFiles) + 1 self.progressDlg.setRange(0, max) for f in self.ocrFiles: val += 1 self.progressDlg.setValue(val) #TODO: if is checked ocrt.deskew(f) #TODO: if is checked #NOTE: this is crop and resize in one step # size and dpi are predifined to A4 300 print(self.cropSize) if self.cropSize['width'] > 1: ocrt.crop_resize(f, self.cropSize["left"], self.cropSize["top"], self.cropSize["width"], self.cropSize["height"]) #TODO: if is checked ocrt.check_orientation(f) print(self.tempocr.name) ## works in python 3.9+ #pdfname = self.ocrFiles[0].removesuffix("_1.png") pdfname, suff = self.ocrFiles[0].rsplit("_1.png") print(pdfname) ### this runs in its own process self.progressDlg.setLabelText("OCR Process finishing ...") ocrt.create_pdf(self.tempocr.name, pdfname) #### ## Workaround to get a correct finished process ## while pytesseract uses subprocces which can not be handled in this thread #### while not os.path.isfile(pdfname + ".pdf"): time.sleep(3) self.progressDlg.setValue(val + 1) os.unlink(self.tempocr.name) self.ocrFiles.clear() else: return def ocr_stopped(self): print("OCR finished") self.progressDlg.close() def configScan(self): self.dev.resolution = int(self.ui.resolutions.currentText()) self.dev.mode = self.checkScanMode() self.dev.start() im = self.dev.snap() pix = ImageQt.ImageQt(im.convert('RGBA')) #self.configWin.im = im #self.configWin.ui.view.setPixmap(self.configWin.pixmap.fromImage(pix)) self.configWin.pixmapItem.setPixmap(QPixmap.fromImage(pix)) self.configWin.ui.view.fitInView(self.configWin.pixmapItem, Qt.KeepAspectRatio) self.configWin.pixmapItem.grabMouse() self.configWin.setBufferImage() self.configWin.enhanceImage() #im = None @pyqtSlot() def saveConfig(self): self.brightness = self.configWin.ui.brigthnessLcd.value() self.contrast = self.configWin.ui.contrastLcd.value() self.color = self.configWin.ui.colorLcd.value() self.sharpness = self.configWin.ui.sharpnessLcd.value() self.crop = self.configWin.ui.checkCrop.isChecked() self.settings.setValue('brightness', self.brightness) self.settings.setValue('contrast', self.contrast) self.settings.setValue('color', self.color) self.settings.setValue('sharpness', self.sharpness) self.settings.setValue('ocr', self.ocr) self.settings.setValue('crop', self.crop) self.settings.setValue('cropSize', self.cropSize) self.settings.sync() self.configWin.close() @pyqtSlot(int) def ocrConfig(self, state): if state == Qt.Checked: self.ocr = True self.configWin.ui.OCR_Box.setEnabled(True) else: self.ocr = False self.configWin.ui.OCR_Box.setEnabled(False) def closeEvent(self, e): if self.tempocr is not None: if os.path.exists(self.tempocr.name): os.unlink(self.tempocr.name) if self.configWin.isVisible(): self.configWin.close() e.accept()
class DuplicateImageFinderRuntime(QThread): dupes_found_event = pyqtSignal(int, list) scan_status_event = pyqtSignal(str, int, int, int, int, int, int) scan_finish_event = pyqtSignal() def __init__(self, files_to_scan, hash_size, h_dist_max, algorithm): super().__init__() self.files_to_scan = files_to_scan self.scan_size = len(files_to_scan) self.hash_size = hash_size self.h_dist_max = h_dist_max self.algorithm = algorithm self.stop = False self.status_counter = random.randint(5, 15) self.thread_pool = QThreadPool() self.connection, self.cursor = self._setup_database() self.cohort_counter = 0 self.file_counter = 0 def stop_scan(self): self.stop = True def run(self): self._start_duplicate_search2() def _process_file(self, hash_data): self.status_counter -= 1 self.file_counter += 1 cohort = None self.cursor.execute( '''SELECT file_name, cohort_id from file_details f WHERE hamming_dist(f.hash_code, ?) <= ?''', ( hash_data.image_hash, self.h_dist_max, )) row = self.cursor.fetchone() if row is not None: found_file = row[0] cohort_id = row[1] if cohort_id is None: cohort = self.cohort_counter self.cohort_counter += 1 # Update existing file self.cursor.execute( '''UPDATE file_details SET cohort_id=? WHERE file_name=?''', ( cohort, found_file, )) else: cohort = cohort_id # Insert new file self.cursor.execute( '''INSERT into file_details VALUES (?, ?, ?)''', (hash_data.file_name, hash_data.image_hash, cohort)) # Raise an event if required if cohort is not None: self.cursor.execute( '''SELECT file_name from file_details WHERE cohort_id = ?''', (cohort, )) files = [] for row in self.cursor: files.append(row[0]) self.dupes_found_event.emit(cohort, files) if self.status_counter == 0: self.status_counter = random.randint(5, 15) self.scan_status_event.emit(hash_data.file_name, self.file_counter, self.scan_size, self.thread_pool.activeThreadCount(), self.thread_pool.maxThreadCount(), 0, 0) if self.stop: self._close_database() self.scan_finish_event.emit() return def _start_duplicate_search2(self): while len(self.files_to_scan) > 0: if self.stop: self._close_database() self.scan_finish_event.emit() return file = self.files_to_scan.pop() worker = HashWorker(file, self.hash_size, self.algorithm) worker.signals.result.connect(self._process_file) worker.setAutoDelete(True) # Execute self.thread_pool.start(worker) self.thread_pool.waitForDone() self.scan_status_event.emit("", self.file_counter, self.scan_size, self.thread_pool.activeThreadCount(), self.thread_pool.maxThreadCount(), 0, 0) self.scan_finish_event.emit() def _setup_database(self): connection = sqlite3.connect(':memory:') cursor = connection.cursor() connection.create_function("hamming_dist", 2, self._hamming_distance) connection.row_factory = sqlite3.Row sqlite3.enable_callback_tracebacks(True) # Create table cursor.execute('''CREATE TABLE IF NOT EXISTS file_details ( file_name text, hash_code text, cohort_id integer)''') cursor.execute( '''CREATE INDEX IF NOT EXISTS idx_hash on file_details(hash_code)''' ) return connection, cursor def _close_database(self): self.cursor.close() self.connection.close() @staticmethod def _hamming_distance(s1, s2): """Return the Hamming distance between equal-length sequences""" if len(s1) != len(s2): raise ValueError("Undefined for sequences of unequal length") return sum(el1 != el2 for el1, el2 in zip(s1, s2)) @staticmethod def _calculate_hash(file, hash_size): image = Image.open(file) return imagehash.average_hash(image, hash_size=hash_size) @staticmethod def _calculate_hash2(file, hash_size): image = Image.open(file) return HashedImage(file, imagehash.average_hash(image, hash_size=hash_size))
class MainUI(MainWindow.Ui_MainWindow): def __init__(self, parent_window: QtWidgets.QMainWindow): self.parent_window = parent_window self.setupUi(self.parent_window) self.directoryListWidget = DirectoryListWidget() self.directoryListWidget.signals.dropped.connect( self.dropped_onto_list) self.startPushButton.clicked.connect(self.start) self.processing_thread_pool = QThreadPool() self.processing_thread_pool.setMaxThreadCount(1) self.setup_additional() self.stream = Stream() self.stream.newText.connect(self.add_line_to_stdout) sys.stdout = self.stream @property def reference_ending(self): return self.referenceEndingLineEdit.text( ) if self.referenceEndingLineEdit.text() != "" else None @property def filter(self): return self.filterCheckBox.checkState() @property def merge(self): return self.mergeCheckBox.checkState() @property def check(self): return self.checkCheckBox.checkState() @property def paths(self): paths = {} for i in range(self.directoryListWidget.count()): path = self.directoryListWidget.item(i).data(8) dir_name = os.path.dirname(path) try: paths[dir_name].append(path) except KeyError: paths[dir_name] = [path] return paths def __del__(self): try: sys.stdout = sys.__stdout__ except AttributeError: pass def dropped_onto_list(self, paths): paths = self.get_paths(paths) for paths_list in paths: for path in paths_list: item = QtWidgets.QListWidgetItem(os.path.basename(path)) item.setData(8, path) self.directoryListWidget.addItem(item) def get_paths(self, paths): path_list = [[]] for path in paths: if os.path.isdir(path): path_list.append(self.get_paths_from_directory(path)) else: path_list[0].append(path) return path_list @staticmethod def get_paths_from_directory(directory): return [ os.path.join(directory, f) for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f)) ] def setup_additional(self): self.parent_window.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) self.centralwidget.layout().addWidget(self.directoryListWidget, 1, 0) self.directoryListWidget.setAcceptDrops(True) def add_line_to_stdout(self, string): string = string.replace("\n", '') self.stdoutTextBrowser.append(string) def processing_finished(self): if self.processing_thread_pool.activeThreadCount() <= 0: self.startPushButton.setEnabled(True) def start(self): self.startPushButton.setEnabled(False) for path_dir, paths in self.paths.items(): worker = Worker(self.start_processing_dir, path_dir, paths) worker.signals.finished.connect(self.processing_finished) self.processing_thread_pool.start(worker) def start_processing_dir(self, path_dir, paths): string_path_dir = path_dir.replace('\\\\', '/').replace('\\', '/') print(f"Started processing sounds from {string_path_dir}") process_sounds(paths, self.reference_ending, self.filter, self.check, self.merge)
class MainWindow(QMainWindow): """ You can use @pyqtSlot(int) syntax (with parameters), or you can pass this, but it make code more readable. Вы можете использовать синтаксис объявления слотов @pyqtSlot(int) с указанием типа передаваемых значений, или опустить его вовсе, однако это делает код нагляднее и позволяет быстро понять, что слот, а что - функция. """ def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._counter = 0 self.init_ui() self._threadpool = QThreadPool() #self._threadpool = QtCore.QThreadPool.globalInstance() #self._threadpool.setMaxThreadCount(2) print("Multithreading with maximum {} threads" .format(self._threadpool.maxThreadCount())) self._timer = QTimer() self._timer.setInterval(1000) self._timer.timeout.connect(self.recurring_timer) self._timer.start() def init_ui(self): layout = QVBoxLayout() self._label = QLabel("Start") b = QPushButton("Start QRunnable") b.pressed.connect(self.start_new_runnable) layout.addWidget(self._label) layout.addWidget(b) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) @pyqtSlot(int) def thread_progress_fn(self, n): print("{}% done".format(n)) @pyqtSlot(object) def thread_print_output(self, s): print('Result: {}'.format(s)) @pyqtSlot() def thread_complete(self): print("QRunnable worker COMPLETE!") @pyqtSlot(tuple) def thread_error(self, err): QMessageBox.warning(self, "Warning!", err[1], QMessageBox.Ok) print('Error {}\n{}'.format(err[1], err[2])) @pyqtSlot() def start_new_runnable(self): # Pass the function to execute worker = Worker(1, debug=True) # Any other args, kwargs are passed to the run function worker.signals.result.connect(self.thread_print_output) worker.signals.finished.connect(self.thread_complete) worker.signals.progress.connect(self.thread_progress_fn) worker.signals.error.connect(self.thread_error) worker.setAutoDelete(True) # Execute (tryStart() better than start() ) if self._threadpool.tryStart(worker) is False: print("Can't create worker!") QMessageBox.warning(self, "Warning!", "Can't create worker!", QMessageBox.Ok) @pyqtSlot() def recurring_timer(self): self._counter += 1 self._label.setText("Counter: {}".format(self._counter)) print('Active thread count: {}'.format(self._threadpool.activeThreadCount())) def closeEvent(self, event): """Main window closed, override PyQt5 widget function""" print('Try to exit, active thread count: {}'.format(self._threadpool.activeThreadCount())) reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self._threadpool.waitForDone() self._timer.stop() event.accept() else: event.ignore()
class Main_Window_class(QDialog): def __init__(self): super().__init__() self.initUI() def initUI(self): ### Hyperparameters ### self.nlabels = 6 self.checkboxes_lables = ['Клиппинг', 'Энвелоп'] self.btns_lables = ['Воспроизвести', 'Пауза', 'Остановить'] self.app_name = 'Эквалайзер' self.sld_min = -50 self.sld_max = 50 self.sld_def = 0 self.sld_interval = 10 self.sld_step = 1 ####################### ### Global variables ### self.path_to_pull = None self.nchannels = None # number of channels self.sampwidth = None # number of bytes per sample self.framerate = None # number of frames per second self.nframes = None # total number of frames self.comptype = None # compression type self.compname = None # compression type name self.elem_per_herz = None self.koeff = 1000 # коэффициент прореживания self.types = {} self.buffer_size = None self.buffer_cnt = 0 self.music_is_playing = False self.threadpool = QThreadPool() print(self.threadpool.maxThreadCount()) self.music_worker = None self.sld1_worker = None self.sld2_worker = None self.sld3_worker = None self.sld4_worker = None self.sld5_worker = None self.sld6_worker = None self.checkbox1_worker = None self.checkbox2_worker = None self.min_freq = 0 self.max_freq = None self.channels = [] self.spectrum = None self.spectrum_original = None self.spectrum_kliping = None self.spectrum_envelop = None self.channels_original = [] self.channels_kliping = [] self.channels_envelop = [] self.bands = [[], []] self.labels = [] ######################## ### Links ### # https://habrahabr.ru/post/113239/ # http://old.pynsk.ru/posts/2015/Nov/09/matematika-v-python-preobrazovanie-fure/ # http://www.7not.ru/articles/klip_cool.phtml # https://martinfitzpatrick.name/article/multithreading-pyqt-applications-with-qthreadpool/ # https://www.tutorialspoint.com/pyqt/pyqt_qlineedit_widget.htm # https://stackoverflow.com/questions/21887862/python-chorus-effect-and-meaning-of-audio-data # http://websound.ru/articles/sound-processing/effects.htm # http://www.auditionrich.com/lessons/kak-v-adobe-audition-ispolzovat-effekt-envelope-follower.html # https://works.doklad.ru/view/M_cHUqES_HE/2.html ############# self.path_to_pull = QFileDialog.getOpenFileName( self, 'Выберите .wav файл')[0] self.pull_music() self.create_bands() self.create_lables() self.create_LCD_numbers() self.create_sliders() self.create_checkboxes() self.create_buttons() self.create_graphics() self.create_interface() def pull_music(self): wav = wave.open(self.path_to_pull, mode='r') self.types = {1: np.int8, 2: np.int16, 4: np.int32} (self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname) = wav.getparams() self.max_freq = self.framerate // 2 self.buffer_size = self.framerate content = wav.readframes(self.nframes) samples = np.fromstring(content, dtype=self.types[self.sampwidth]) for i in range(self.nchannels): self.channels.append(samples[i::self.nchannels]) self.channels = np.array(self.channels) self.channels_original = self.channels.copy() self.checkbox1_worker = Worker(self.doing_kliping, self.channels) self.threadpool.start(self.checkbox1_worker) self.checkbox2_worker = Worker(self.doing_envelop, self.channels) self.threadpool.start(self.checkbox2_worker) self.spectrum = np.fft.rfft(self.channels_original) self.spectrum_original = self.spectrum.copy() pygame.mixer.pre_init(frequency=self.framerate, size=-self.sampwidth * 8, channels=self.nchannels) pygame.init() def create_bands(self): step = (self.max_freq - self.min_freq) // 2**self.nlabels self.bands[0].append(self.min_freq) self.bands[1].append(self.min_freq + step) for i in range(1, self.nlabels - 1): self.bands[0].append(self.bands[1][i - 1]) self.bands[1].append(self.bands[0][i] + 2**i * step) self.bands[0].append(self.bands[1][self.nlabels - 2]) self.bands[1].append(self.max_freq) for i in range(self.nlabels): self.labels.append( str(self.bands[0][i]) + ' - ' + str(self.bands[1][i])) def create_lables(self): self.label_1 = QLabel(self.labels[0], self) self.label_2 = QLabel(self.labels[1], self) self.label_3 = QLabel(self.labels[2], self) self.label_4 = QLabel(self.labels[3], self) self.label_5 = QLabel(self.labels[4], self) self.label_6 = QLabel(self.labels[5], self) def create_LCD_numbers(self): self.num_1 = QLCDNumber(self) self.num_2 = QLCDNumber(self) self.num_3 = QLCDNumber(self) self.num_4 = QLCDNumber(self) self.num_5 = QLCDNumber(self) self.num_6 = QLCDNumber(self) def create_sliders(self): self.sld_1 = QSlider(Qt.Vertical, self) self.sld_2 = QSlider(Qt.Vertical, self) self.sld_3 = QSlider(Qt.Vertical, self) self.sld_4 = QSlider(Qt.Vertical, self) self.sld_5 = QSlider(Qt.Vertical, self) self.sld_6 = QSlider(Qt.Vertical, self) self.sld_1.setMinimum(self.sld_min) self.sld_2.setMinimum(self.sld_min) self.sld_3.setMinimum(self.sld_min) self.sld_4.setMinimum(self.sld_min) self.sld_5.setMinimum(self.sld_min) self.sld_6.setMinimum(self.sld_min) self.sld_1.setMaximum(self.sld_max) self.sld_2.setMaximum(self.sld_max) self.sld_3.setMaximum(self.sld_max) self.sld_4.setMaximum(self.sld_max) self.sld_5.setMaximum(self.sld_max) self.sld_6.setMaximum(self.sld_max) self.sld_1.setValue(self.sld_def) self.sld_2.setValue(self.sld_def) self.sld_3.setValue(self.sld_def) self.sld_4.setValue(self.sld_def) self.sld_5.setValue(self.sld_def) self.sld_6.setValue(self.sld_def) self.sld_1.setFocusPolicy(Qt.StrongFocus) self.sld_2.setFocusPolicy(Qt.StrongFocus) self.sld_3.setFocusPolicy(Qt.StrongFocus) self.sld_4.setFocusPolicy(Qt.StrongFocus) self.sld_5.setFocusPolicy(Qt.StrongFocus) self.sld_6.setFocusPolicy(Qt.StrongFocus) self.sld_1.setTickPosition(QSlider.TicksBothSides) self.sld_2.setTickPosition(QSlider.TicksBothSides) self.sld_3.setTickPosition(QSlider.TicksBothSides) self.sld_4.setTickPosition(QSlider.TicksBothSides) self.sld_5.setTickPosition(QSlider.TicksBothSides) self.sld_6.setTickPosition(QSlider.TicksBothSides) self.sld_1.setTickInterval(self.sld_interval) self.sld_2.setTickInterval(self.sld_interval) self.sld_3.setTickInterval(self.sld_interval) self.sld_4.setTickInterval(self.sld_interval) self.sld_5.setTickInterval(self.sld_interval) self.sld_6.setTickInterval(self.sld_interval) self.sld_1.setSingleStep(self.sld_step) self.sld_2.setSingleStep(self.sld_step) self.sld_3.setSingleStep(self.sld_step) self.sld_4.setSingleStep(self.sld_step) self.sld_5.setSingleStep(self.sld_step) self.sld_6.setSingleStep(self.sld_step) self.sld_1.valueChanged[int].connect(self.sliderChangeValue) self.sld_2.valueChanged[int].connect(self.sliderChangeValue) self.sld_3.valueChanged[int].connect(self.sliderChangeValue) self.sld_4.valueChanged[int].connect(self.sliderChangeValue) self.sld_5.valueChanged[int].connect(self.sliderChangeValue) self.sld_6.valueChanged[int].connect(self.sliderChangeValue) self.sld_1.valueChanged[int].connect(self.num_1.display) self.sld_2.valueChanged[int].connect(self.num_2.display) self.sld_3.valueChanged[int].connect(self.num_3.display) self.sld_4.valueChanged[int].connect(self.num_4.display) self.sld_5.valueChanged[int].connect(self.num_5.display) self.sld_6.valueChanged[int].connect(self.num_6.display) self.old_value_sld1 = self.sld_def self.old_value_sld2 = self.sld_def self.old_value_sld3 = self.sld_def self.old_value_sld4 = self.sld_def self.old_value_sld5 = self.sld_def self.old_value_sld6 = self.sld_def def create_checkboxes(self): self.checkbox_1 = QCheckBox(self.checkboxes_lables[0], self) self.checkbox_2 = QCheckBox(self.checkboxes_lables[1], self) self.checkbox_1.setChecked(False) self.checkbox_2.setChecked(False) self.checkbox_1.stateChanged.connect(self.checkboxClicked) self.checkbox_2.stateChanged.connect(self.checkboxClicked) def create_buttons(self): self.btn_1 = QPushButton(self.btns_lables[0], self) self.btn_2 = QPushButton(self.btns_lables[1], self) self.btn_3 = QPushButton(self.btns_lables[2], self) self.btn_1.clicked.connect(self.buttonClicked) self.btn_2.clicked.connect(self.buttonClicked) self.btn_3.clicked.connect(self.buttonClicked) def create_graphics(self): figure_1 = plt.figure() self.figure_2 = plt.figure() self.figure_4 = plt.figure() self.canvas_1 = FigureCanvas(figure_1) self.canvas_2 = FigureCanvas(self.figure_2) self.canvas_4 = FigureCanvas(self.figure_4) self.toolbar_1 = NavigationToolbar(self.canvas_1, self) self.toolbar_2 = NavigationToolbar(self.canvas_2, self) self.toolbar_4 = NavigationToolbar(self.canvas_4, self) figure_1.clear() self.figure_2.clear() self.figure_4.clear() ax_1 = figure_1.add_subplot(1, 1, 1) self.ax_2 = self.figure_2.add_subplot(1, 1, 1) self.ax_4 = self.figure_4.add_subplot(1, 1, 1) ax_1.set_xlabel('Частота, Гц') self.ax_2.set_xlabel('Частота, Гц') self.ax_4.set_xlabel('Время, с') figure_1.align_xlabels() self.figure_2.align_xlabels() self.figure_4.align_xlabels() ax_1.set_ylabel('Амплитуда') self.ax_2.set_ylabel('Амплитуда') self.ax_4.set_ylabel('Амплитуда') figure_1.align_ylabels() self.figure_2.align_ylabels() self.figure_4.align_ylabels() self.elem_per_herz = self.spectrum.shape[1] // (self.max_freq - self.min_freq) ax_1.plot( np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.koeff], np.abs(self.spectrum[0][::self.koeff]) / self.nframes) self.ax_2.plot( np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.koeff], np.abs(self.spectrum[0][::self.koeff]) / self.nframes) self.ax_4.plot(self.channels[0][::self.koeff]) self.canvas_1.draw() self.canvas_2.draw() self.canvas_4.draw() def create_interface(self): self.labels_box = QHBoxLayout() self.labels_box.addWidget(self.label_1) self.labels_box.addWidget(self.label_2) self.labels_box.addWidget(self.label_3) self.labels_box.addWidget(self.label_4) self.labels_box.addWidget(self.label_5) self.labels_box.addWidget(self.label_6) self.nums_box = QHBoxLayout() self.nums_box.addWidget(self.num_1) self.nums_box.addWidget(self.num_2) self.nums_box.addWidget(self.num_3) self.nums_box.addWidget(self.num_4) self.nums_box.addWidget(self.num_5) self.nums_box.addWidget(self.num_6) self.slds_box = QHBoxLayout() self.slds_box.addWidget(self.sld_1) self.slds_box.addWidget(self.sld_2) self.slds_box.addWidget(self.sld_3) self.slds_box.addWidget(self.sld_4) self.slds_box.addWidget(self.sld_5) self.slds_box.addWidget(self.sld_6) self.graphs_box_1 = QVBoxLayout() self.graphs_box_1.addWidget(self.toolbar_4) self.graphs_box_1.addWidget(self.canvas_4) self.checks_and_btns_box = QHBoxLayout() self.checks_and_btns_box.addWidget(self.checkbox_1) self.checks_and_btns_box.addWidget(self.checkbox_2) self.checks_and_btns_box.addWidget(self.btn_1) self.checks_and_btns_box.addWidget(self.btn_2) self.checks_and_btns_box.addWidget(self.btn_3) self.graphs_box_2 = QVBoxLayout() self.graphs_box_2.addWidget(self.toolbar_1) self.graphs_box_2.addWidget(self.canvas_1) self.graphs_box_2.addWidget(self.toolbar_2) self.graphs_box_2.addWidget(self.canvas_2) self.left_box = QVBoxLayout() self.left_box.addLayout(self.labels_box) self.left_box.addLayout(self.slds_box) self.left_box.addLayout(self.nums_box) self.left_box.addLayout(self.graphs_box_1) self.right_box = QVBoxLayout() self.right_box.addLayout(self.checks_and_btns_box) self.right_box.addLayout(self.graphs_box_2) self.all_box = QHBoxLayout() self.all_box.addLayout(self.left_box) self.all_box.addLayout(self.right_box) self.setLayout(self.all_box) self.setWindowTitle(self.app_name) self.showMaximized() def sliderChangeValue(self, value): if (self.sender() == self.sld_1): self.sld1_worker = Worker(self.music_edit, 0, value) self.threadpool.start(self.sld1_worker) elif (self.sender() == self.sld_2): self.sld2_worker = Worker(self.music_edit, 1, value) self.threadpool.start(self.sld2_worker) elif (self.sender() == self.sld_3): self.sld3_worker = Worker(self.music_edit, 2, value) self.threadpool.start(self.sld3_worker) elif (self.sender() == self.sld_4): self.sld4_worker = Worker(self.music_edit, 3, value) self.threadpool.start(self.sld4_worker) elif (self.sender() == self.sld_5): self.sld5_worker = Worker(self.music_edit, 4, value) self.threadpool.start(self.sld5_worker) else: self.sld6_worker = Worker(self.music_edit, 5, value) self.threadpool.start(self.sld6_worker) def checkboxClicked(self, state): if (self.sender() == self.checkbox_1): if (state == Qt.Checked): self.checkbox_2.setChecked(False) self.channels = self.channels_kliping.copy() self.spectrum = self.spectrum_kliping.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() else: if (state == Qt.Checked): self.checkbox_1.setChecked(False) self.channels = self.channels_envelop.copy() self.spectrum = self.spectrum_envelop.copy() else: self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() self.sld_1.setValue(self.sld_def) self.sld_2.setValue(self.sld_def) self.sld_3.setValue(self.sld_def) self.sld_4.setValue(self.sld_def) self.sld_5.setValue(self.sld_def) self.sld_6.setValue(self.sld_def) draw_1 = Worker(self.draw_array, self.spectrum, 0) self.threadpool.start(draw_1) draw_2 = Worker(self.draw_array, self.channels, 1) self.threadpool.start(draw_2) def buttonClicked(self): if (self.sender() == self.btn_1): if (self.music_is_playing == False): self.music_is_playing = True self.music_worker = Worker(self.start_music) self.threadpool.start(self.music_worker) elif (self.sender() == self.btn_2): if (self.music_is_playing == True): self.music_is_playing = False else: if (self.music_is_playing == True): self.music_is_playing = False self.threadpool.clear() sliders = [ self.sld1_worker, self.sld2_worker, self.sld3_worker, self.sld4_worker, self.sld5_worker, self.sld6_worker ] for slider in sliders: self.sld_stop(slider) self.buffer_cnt = 0 self.sld_1.setValue(self.sld_def) self.sld_2.setValue(self.sld_def) self.sld_3.setValue(self.sld_def) self.sld_4.setValue(self.sld_def) self.sld_5.setValue(self.sld_def) self.sld_6.setValue(self.sld_def) self.checkbox_1.setChecked(False) self.checkbox_2.setChecked(False) tmp_worker = Worker(self.tmp_func) self.threadpool.start(tmp_worker) def sld_stop(self, slider): ids = { self.sld1_worker: 0, self.sld2_worker: 1, self.sld3_worker: 2, self.sld4_worker: 3, self.sld5_worker: 4, self.sld6_worker: 5 } slider = Worker(self.music_edit, ids[slider], self.sld_def) self.threadpool.start(slider) def tmp_func(self): while (self.threadpool.activeThreadCount() != 1): sleep(0.1) self.channels = self.channels_original.copy() self.spectrum = self.spectrum_original.copy() print('music stopped') def start_music(self): tmp_channels = [] tmp_channels.append( self.channels[0][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:]) tmp_channels.append( self.channels[1][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:]) tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) sound = tmp_sound if (self.music_is_playing == False): return pygame.mixer.Sound.play(sound) start_pos = self.buffer_cnt for self.buffer_cnt in range(start_pos + 1, self.nframes // self.buffer_size): tmp_channels = [] tmp_channels.append( self.channels[0][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:]) tmp_channels.append( self.channels[1][self.buffer_cnt * self.buffer_size:(self.buffer_cnt + 1) * self.buffer_size + 1:]) tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) while (pygame.mixer.get_busy()): sleep(0.01) sound = tmp_sound if (self.music_is_playing == False): return pygame.mixer.Sound.play(sound) tmp_channels = [] tmp_channels.append(self.channels[0][self.buffer_cnt * self.buffer_size::]) tmp_channels.append(self.channels[1][self.buffer_cnt * self.buffer_size::]) tmp_channels = np.array(tmp_channels) tmp_channels = np.ascontiguousarray(tmp_channels.T) tmp_sound = pygame.sndarray.make_sound(tmp_channels) while (pygame.mixer.get_busy()): sleep(0.01) sound = tmp_sound if (self.music_is_playing == False): return pygame.mixer.Sound.play(sound) self.buffer_cnt = 0 self.music_is_playing = False def music_edit(self, pos, value): old_values = { 0: self.old_value_sld1, 1: self.old_value_sld2, 2: self.old_value_sld3, 3: self.old_value_sld4, 4: self.old_value_sld5, 5: self.old_value_sld6 } old_value = old_values[pos] if pos == 0: self.old_value_sld1 = value elif pos == 1: self.old_value_sld2 = value elif pos == 2: self.old_value_sld3 = value elif pos == 3: self.old_value_sld4 = value elif pos == 4: self.old_value_sld5 = value else: self.old_value_sld6 = value if (old_value == value): return if (pos == 0): for i in range(self.nchannels): self.spectrum[i][:self.elem_per_herz * self.bands[1][pos] + 1] *= 10**((value - old_value) / 20) elif (pos == 5): for i in range(self.nchannels): self.spectrum[i][self.elem_per_herz * self.bands[0][pos]:] *= 10**( (value - old_value) / 20) else: for i in range(self.nchannels): self.spectrum[i][self.elem_per_herz * self.bands[0][pos]:self.elem_per_herz * self.bands[1][pos] + 1] *= 10**((value - old_value) / 20) self.channels = (np.fft.irfft(self.spectrum)).astype( self.types[self.sampwidth]) draw_1 = Worker(self.draw_array, self.spectrum, 0) self.threadpool.start(draw_1) draw_2 = Worker(self.draw_array, self.channels, 1) self.threadpool.start(draw_2) def draw_array(self, arr, spectrum_or_channel): if (spectrum_or_channel == 0): self.figure_2.clear() self.ax_2 = self.figure_2.add_subplot(1, 1, 1) self.ax_2.set_xlabel('Частота, Гц') self.figure_2.align_xlabels() self.ax_2.set_ylabel('Амплитуда') self.figure_2.align_ylabels() self.ax_2.plot( np.fft.rfftfreq(self.nframes, 1. / self.framerate)[::self.koeff], np.abs(arr[0][::self.koeff]) / self.nframes) self.canvas_2.draw() else: self.figure_4.clear() self.ax_4 = self.figure_4.add_subplot(1, 1, 1) self.ax_4.set_xlabel('Время, с') self.figure_4.align_xlabels() self.ax_4.set_ylabel('Амплитуда') self.figure_4.align_ylabels() self.ax_4.plot(arr[0][::self.koeff]) self.canvas_4.draw() def doing_kliping(self, channels): print('kliping start') start_time = time() threshold_max = int(0.6 * np.max(channels[0])) threshold_min = int(0.6 * np.min(channels[0])) self.channels_kliping = np.maximum(np.minimum(channels, threshold_max), threshold_min).astype( self.types[self.sampwidth]) self.spectrum_kliping = np.fft.rfft(self.channels_kliping) print('kliping end: ' + str(time() - start_time)) def doing_envelop(self, channels): print('envelop start') start_time = time() frequency = 1 / 15 envelope_sig = np.array([ abs(sin(2 * pi * frequency * t / self.framerate)) for t in range(self.nframes) ]) tmp_channels = channels.copy() for i in range(self.nchannels): tmp_channels[i] = (tmp_channels[i] * envelope_sig).astype( self.types[self.sampwidth]) self.channels_envelop = tmp_channels.copy() self.spectrum_envelop = np.fft.rfft(self.channels_envelop) print('envelop end: ' + str(time() - start_time))
class BackupManager(): #A hub to manage all Backups def __init__(self, dir): self.threadpool = QThreadPool() self.subthreadpool = QThreadPool() self.threads = 0 self.BackupDirectory = dir self.BackupStorage = {} self.load_from_txt() self.load_prev_from_json() print("Backup Manager initiated: " + dir) self.print() def getAmountOfThreads(self): return self.threadpool.activeThreadCount() def close(self): try: self.save_prev_to_json() self.save_to_txt() self.cleanup() except: print("closing failed") def addBackup(self, b): assert isinstance(b, Backup), 'Not a Backup!' name = b.getName() if name not in self.BackupStorage: self.BackupStorage[name] = [b] print("New Backup Entry Created: " + name) return True else: temp = self.BackupStorage[name] for i in temp: if (b.getDirectory() == i.getDirectory()): return False temp.append(b) self.BackupStorage[name] = temp print("Added Entry to existing Backup: " + name) return True def newBackup(self, name, dir, date = datetime.now(). strftime("%d_%m_%Y-%H_%M_%S")): try: b = Backup(name, dir, date) except(AssertionError): print("Backup initiation failed due to invalid inputs") return False self.addBackup(b) return True def moveBackupManager(self, newdir): tempbdir = self.BackupDirectory self.BackupDirectory = newdir self.threads += 1 print(tempbdir) print(newdir) def backupALL(self): for b in self.BackupStorage.keys(): self.back_up_threaded(b) def back_up_threaded(self, b): self.threads += 1 worker = CopyWorker(self.back_up, b, self.BackupDirectory) worker.signals.finished.connect(self.thread_complete) self.threadpool.start(worker) def back_up(self, b, bdir): self.link_Backup(b, datetime.now(). strftime("%d_%m_%Y-%H_%M_%S")) self.save_Backup(b, bdir) def link_Backup(self, key, date, mode=0): dir = key + r'/' if (self.BackupStorage[key].__len__() == 1 or mode==1): for b in self.BackupStorage[key]: b.setDate(date) folder = os.path.basename(b.getDirectory()) name = folder + "_" + date b.setBackupLocation(dir+name) else: i = 0 for b in self.BackupStorage[key]: b.setDate(date) folder = os.path.basename(b.getDirectory()) i+=1 name = folder + "_" + date b.setBackupLocation(dir+ str(i) + "_" +name) def save_Backup(self, key, bdir): for b in self.BackupStorage[key]: b.savetoBackupLocation (bdir) def getAll(self): return self.BackupStorage.keys() def getinfo(self, key): l = [] for i in self.BackupStorage[key]: for p in i.returnData(): l.append(p) return l def thread_complete(self): self.threads -=1 print(self.threads) if self.threads == 0: print("All Done") def print(self): for b in self.BackupStorage.keys(): print("-----" + b + "-----") for i in self.BackupStorage[b]: i.printData() def save_prev_to_json(self): for b in self.BackupStorage.keys(): c = 0 for i in self.BackupStorage[b]: c+=1 try: f = open(self.BackupDirectory + "//" + b +"_"+ str(c) + "_prev.json", 'w') f.write(json.dumps(i.getPrevious())) f.close() except: print("couldn't save previous for: " + b+ "_" + str(i)) def load_prev_from_json(self): for b in self.BackupStorage.keys(): c = 0 for i in self.BackupStorage[b]: c += 1 try: with open(self.BackupDirectory + "\\" + b +"_"+ str(c) + "_prev.json", 'r') as json_file: data = json.load(json_file) i.setPrevious(data) except: print("couldn't load json file: " + self.BackupDirectory + "\\" + b +"_"+ str(c) + "_prev.json") def save_to_txt(self, dir = ""): f = open(self.BackupDirectory + r"/backup.txt", 'w') for b in self.BackupStorage.keys(): f.write("Name: " + b ) for i in self.BackupStorage[b]: f.write("Date: " + i.getDate()) f.write("Directory: " + i.getDirectory()) f.write("Backupdirectory: " + i.getBackupLocation()) f.write("\n") f.close() def load_from_txt(self): try: f = open(self.BackupDirectory + "\\backup.txt", 'r') for l in f.readlines(): temp = re.split('Name: |Date: |Directory: |Backupdirectory: ', l.strip("\n")) temp.pop(0) self.load_entry(temp) f.close() print("Successfully loaded backup.txt") except: print("Couldn't load backup.txt") def load_entry(self, l): name = l.pop(0) while(l!=[]): date = l.pop(0) dir = l.pop(0) bdir = l.pop(0) b = Backup(name, dir, date = date) b.setBackupLocation(bdir) self.addBackup(b) def clear_all(self): self.BackupStorage = {} def cleanup(self): #deletes empty directories in Backup Folder print("-Cleanup-") count = 0 for i in os.listdir(self.BackupDirectory): try: os.rmdir(os.path.join(self.BackupDirectory, i)) count+=1 except: print("Didn't delete: " + i) print("Deleted " + str(count) + " folders") def deletePrevious(self): for b in self.BackupStorage.keys(): for i in self.BackupStorage[b]: self.threads +=1 worker = CopyWorker(i.deletePrevious, self.BackupDirectory) worker.signals.finished.connect(self.thread_complete) self.threadpool.start(worker) def deleteCurrent(self): for b in self.BackupStorage.keys(): for i in self.BackupStorage[b]: i.deleteCurrent(self.BackupDirectory) def open_in_explorer(self, key): for b in self.BackupStorage[key]: b.open_in_explorer() def open_Backup_in_explorer(self, key): for b in self.BackupStorage[key]: try: b.open_Backup_in_explorer(self.BackupDirectory) except(AssertionError): print("Directory not valid") def deleteSpecific(self, b, str): print(self.BackupStorage[b]) for i in self.BackupStorage[b]: if i.deleteFromString(str, self.BackupDirectory): self.BackupStorage[b].remove(i) if self.BackupStorage[b]== []: del self.BackupStorage[b] def setEF(self, key, eFlag, str = ""): if str=="": for i in self.BackupStorage[key]: i.setEnabledFlag(eFlag) else: for i in self.BackupStorage[key]: if i.checkifstringiscurrent(str): i.setEnabledFlag(eFlag)
class ProgressWindow(QMainWindow, Ui_ProgressWindow): """Window that displays a list of jobs that are being performed. """ def __init__(self, main_window: 'MainWindow', *args, **kwargs): """Constructor for ProgressWindow. Parameter --------- main_window : MainWindow - Send in the main window, so it can be hidden and shown. """ super().__init__(*args, **kwargs) self.setupUi(self) self.main_window = main_window self.job_index = 0 self.jobs_threadpool = QThreadPool() self.actionShow_Main_Window.toggled.connect(self.toggle_main_window) self.original_close_event = self.closeEvent # type: ignore self.closeEvent = self._window_closed # pylint: disable=invalid-name def toggle_main_window(self, toggled: bool): """Either hides or shows the main window. Parameters ---------- toggled : bool - Whether the window should be shown. """ if self.main_window is None: return self.main_window.setVisible(toggled) def _window_closed(self, event: QCloseEvent): """Reimplement the closeEvent method of this window. Toggle the action linked to this window in the main window to off. Parameters ---------- event : QCloseEvent - The event that is sent when the window is closed. """ self.main_window.actionCurrent_Jobs.setChecked(False) self.original_close_event(event) def create_new_job(self, created_actions: List[ActionToCreate]): """Creates a job that will be performed on the songs """ created_job: List[Action] = [] for index, created_action in enumerate(created_actions): action = Action(created_action, self.job_index, index) action.signals.action_started.connect(self.set_action_length) action.signals.action_progress\ .connect(self.update_action_progress) action.signals.action_finished\ .connect(self.update_action_complete) if action.subaction_signals is not None: action.subaction_signals.subaction_started\ .connect(self.set_subaction_length) action.subaction_signals.subaction_progress\ .connect(self.update_subaction_progress) action.subaction_signals.subaction_finished\ .connect(self.update_subaction_complete) created_job.append(action) self.add_to_current_jobs(created_job) def add_to_current_jobs(self, created_job: List[Action]): """Add the list of actions sent to the jobs window, and then perform the actions in the order created. Parameters ---------- created_job : List[Action] - List of actions that should be performed. """ job = Job(created_job, self.job_index) scroll_area_widget = self.jobsScrollAreaWidgetContents job_progress_widget = JobProgressWidget(scroll_area_widget, created_job, self.job_index, self) scroll_layout = self.jobsScrollAreaWidgetContents.layout() index_to_add = scroll_layout.count() - 1 scroll_layout.insertWidget(index_to_add, job_progress_widget) self.jobsShownCount.setText(f"{index_to_add}") job.signals.job_progress.connect(self.update_job_progress) job.signals.job_finished.connect(self.update_job_complete) self.job_index = self.job_index + 1 self.add_new_job(job) def add_new_job(self, job: Job): """Add a new job to the jobs being displayed, and perform the job. Parameters ---------- job : Job - The Job that should be performed and displayed. """ self.jobs_threadpool.start(job) thread_count = self.jobs_threadpool.activeThreadCount() self.activeJobsCount.setText(f"{thread_count}") # region PyQt Slots for action progress def _get_progress_widget(self, job_index: int, action_index: Optional[int] = None, subaction_index: Optional[int] = None)\ -> Union[JobProgressWidget, ActionProgressWidget, SubactionProgressWidget]: """Get the widget whose progress should be updated, based on the indexes sent. If an action index or subaction index is not sent, sends the main job progress widget. Parameters ---------- job_index : int - The index of the job progress widget whose progress should be updated.\n action_index : Optional[int], optional - The index of the action progress widget whose progress should be updated. By default None.\n subaction_index : Optional[int], optional - The index of the subaction progress widget whose progress should be updated. By default None.\n Returns ------- Union[JobProgressWidget, ActionProgressWidget, SubactionProgressWidget] - The Widget whose progress should be updated, based on the indexes sent. """ job_widget_name = f"JobProgressWidget_{job_index}" job_progress_widget = self\ .jobsScrollAreaWidgetContents.findChild(JobProgressWidget, job_widget_name) job_progress_widget = cast(JobProgressWidget, job_progress_widget) if action_index is None: return job_progress_widget action_progress_widget = job_progress_widget\ .actionsScrollAreaWidgetContents.layout()\ .itemAt(action_index).widget() action_progress_widget = cast(ActionProgressWidget, action_progress_widget) if subaction_index is None: return action_progress_widget subaction_progress_widget = action_progress_widget\ .subactionsScrollAreaWidgetContents.layout()\ .itemAt(subaction_index).widget() subaction_progress_widget = cast(SubactionProgressWidget, subaction_progress_widget) return subaction_progress_widget @pyqtSlot(int, int) def update_job_progress(self, job_index: int, progress: int): """Update the progress bar of the job that is associated with this job index. Parameters ---------- job_index : int - The index of the job that should be updated.\n progress : int - The value the progress bar should be updated to. """ job_progress_widget = self._get_progress_widget(job_index) job_progress_widget = cast(JobProgressWidget, job_progress_widget) job_progress_widget.jobProgressBar.setValue(progress) @pyqtSlot(int) def update_job_complete(self, job_index: int): """Update the progress bar of the job progress widget to be complete, and allow a user to remove this job from the list. Parameters ---------- job_index : int - The index of the job that should be updated. """ job_progress_widget = self._get_progress_widget(job_index) job_progress_widget.jobProgressBar\ .setValue(job_progress_widget.jobProgressBar.maximum()) job_progress_widget.removeJobButton.setEnabled(True) job_progress_widget.removeJobButton.show() job_count = self.jobs_threadpool.activeThreadCount() self.activeJobsCount.setText(f"{job_count}") @pyqtSlot(int, int, int) def set_action_length(self, job_index: int, action_index: int, length: int): """Set the maximum of the progress bar associated with this action to length. Parameters ---------- job_index : int - The index of the job the action is associated with.\n action_index : int - The index of this action in the job list.\n length : int - The value the progress bar should set as the maximum.\n """ action_progress_widget = self._get_progress_widget( job_index, action_index) action_progress_widget = cast(ActionProgressWidget, action_progress_widget) action_progress_widget.actionProgressBar.setMaximum(length) @pyqtSlot(int, int, int) def update_action_progress(self, job_index: int, action_index: int, progress: int): """Update the progress bar associated with this action with the progress sent. Parameters ---------- job_index : int - The index of the job the action is associated with.\n action_index : int - The index of the action whose progress bar should be updated.\n progress : int - The value that the progress bar should be set to. """ action_progress_widget = self._get_progress_widget( job_index, action_index) action_progress_widget = cast(ActionProgressWidget, action_progress_widget) action_progress_widget.actionProgressBar.setValue(progress) @pyqtSlot(int, int) def update_action_complete(self, job_index: int, action_index: int): """Update the progress bar of the action widget to be complete. Parameters ---------- job_index : int - The index of the job the action is associated with.\n action_index : int - The index of the action that should be updated. """ action_progress_widget = self._get_progress_widget( job_index, action_index) action_progress_widget.actionProgressBar\ .setValue(action_progress_widget.actionProgressBar.maximum()) @pyqtSlot(int, int, int, int) def set_subaction_length(self, job_index: int, action_index: int, subaction_index: int, length: int): """Set the max value of the progress bar linked to this subaction. Parameters ---------- job_index : int - The index of the job linked to this subaction.\n action_index : int - The index of the action linked to this subaction.\n subaction_index : int - The index of this subaction in the subactions list.\n length : int - The value that should be set as the max value.\n """ subaction_progress_widget = self._get_progress_widget( job_index, action_index, subaction_index) subaction_progress_widget = cast(SubactionProgressWidget, subaction_progress_widget) subaction_progress_widget.subactionProgressBar.setMaximum(length) @pyqtSlot(int, int, int, int) def update_subaction_progress(self, job_index: int, action_index: int, subaction_index: int, progress: int): """Update the progress bar linked to this subaction with the progress value sent. Parameters ---------- job_index : int - The index of the job linked to this subaction.\n action_index : int - The index of the action linked to this subaction.\n subaction_index : int - The index of this subaction in the subactions list.\n progress : int - The value that the progress bar should be upated to. """ subaction_progress_widget = self._get_progress_widget( job_index, action_index, subaction_index) subaction_progress_widget = cast(SubactionProgressWidget, subaction_progress_widget) subaction_progress_widget.subactionProgressBar.setValue(progress) @pyqtSlot(int, int, int) def update_subaction_complete(self, job_index: int, action_index: int, subaction_index: int): """Update the progress bar of this subaction to the max value. Parameters ---------- job_index : int - The index of the job linked to this subaction.\n action_index : int - The index of the action linked to this subaction.\n subaction_index : int - The index of this subaction in the subactions list. """ subaction_progress_widget = self._get_progress_widget( job_index, action_index, subaction_index) subaction_progress_widget = cast(SubactionProgressWidget, subaction_progress_widget) subaction_progress_widget.subactionProgressBar.setValue( subaction_progress_widget.subactionProgressBar.maximum())
class CommandExecutionFactory(QThread): finish_event = pyqtSignal('PyQt_PyObject', int) result_event = pyqtSignal('PyQt_PyObject') def __init__(self, runnable_commands, logger=None, max_threads=None): super().__init__() self.pending_jobs = runnable_commands self.total_jobs = len(self.pending_jobs) self.completed = [] self.thread_pool = QThreadPool() self.stop = False self.logger = logger if max_threads: self.max_threads = min(max_threads, self.thread_pool.maxThreadCount()) else: self.max_threads = self.thread_pool.maxThreadCount() self.log(f"Multi-threading with maximum of {self.max_threads} threads") def add_task(self, task): self.pending_jobs.append(task) self.total_jobs += 1 self.run() def get_max_threads(self): return self.max_threads def get_active_thread_count(self): return self.thread_pool.activeThreadCount() def stop_scan(self): self.stop = True def run(self): self.do_work() def thread_complete(self, result): self.completed.append(result) self.do_work() def is_running(self): return self.thread_pool.activeThreadCount() > 0 def do_work(self): if len(self.pending_jobs) > 0: if self.stop: self.log( "Stop has been received. Waiting for threads to finish before exiting..." ) self.thread_pool.waitForDone() self.log( f"Waiting for {self.total_jobs - len(self.pending_jobs) - len(self.completed)} " f"threads to finish") if (self.total_jobs - len(self.pending_jobs) - len(self.completed)) == 0: self.finish_event.emit(self.completed, sum(self.completed)) return active_threads = self.thread_pool.activeThreadCount() available_threads = self.max_threads - active_threads for i in range(0, min(available_threads, len(self.pending_jobs))): self.log("Dispatching thread on empty slot...") runnable = self.pending_jobs.pop() runnable.signals.__complete__.connect(self.thread_complete) runnable.setAutoDelete(True) # Execute self.thread_pool.start(runnable) else: self.log("Waiting for threads to finish") if len(self.completed) == self.total_jobs: total_time = sum(self.completed) self.log(f"Done. Total work completed in...{total_time}") self.finish_event.emit(self.completed, total_time) def log(self, message): if self.logger: self.logger.info(message)