def QImage_to_compressed_bytes(qimage, format): """ Compress QImage or QPixmap into bytes corresponding to a image file format (BMP,PNG,JPG) To reconstruct the QImage or QPixamp : image = QImage.fromData(data) image = QImage() ; image.loadFromData(data) image = QPixmap(); image.loadFromData(data) The QImage.format will be preserved when loading only if in format in : Format_Mono Format_Indexed8 Format_RGB32 Format_ARGB32 Format_Grayscale8 """ # https://stackoverflow.com/questions/24965646/convert-pyqt4-qtgui-qimage-object-to-base64-png-data # https://stackoverflow.com/questions/57404778/how-to-convert-a-qpixmaps-image-into-a-bytes qbytearray = QByteArray() qbuffer = QBuffer(qbytearray) qbuffer.open(QIODevice.WriteOnly) ok = qimage.save(qbuffer, format) assert ok return qbytearray.data() # fait une copie ?
def save(self, fname, format, draft): if is_text_string(fname): if format == "pdf": self.app = guidata.qapplication() if draft: mode = QPrinter.ScreenResolution else: mode = QPrinter.HighResolution printer = QPrinter(mode) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOrientation(QPrinter.Landscape) printer.setOutputFileName(fname) printer.setCreator("guiqwt.pyplot") self.print_(printer) else: if self.win is None: self.show() if PYQT5: pixmap = self.win.centralWidget().grab() else: pixmap = QPixmap.grabWidget(self.win.centralWidget()) pixmap.save(fname, format.upper()) else: # Buffer fd = fname assert hasattr(fd, "write"), "object is not file-like as expected" if self.win is None: self.show() pixmap = QPixmap.grabWidget(self.win.centralWidget()) buff = QBuffer() buff.open(QIODevice.ReadWrite) pixmap.save(buff, format.upper()) fd.write(buff.data()) buff.close() fd.seek(0)
def pngbinary2Qlabel(databinary): buff = QBuffer() buff.open(QIODevice.WriteOnly) buff.write(databinary) dat = buff.data() pixmap = QtGui.QPixmap() pixmap.loadFromData(dat, 'PNG') label = QtWidgets.QLabel() label.setPixmap(pixmap) return label
def qt_to_pil_image(qimg): buffer = QBuffer() buffer.open(QIODevice.ReadWrite) # preserve alha channel with png # otherwise ppm is more friendly with Image.open if qimg.hasAlphaChannel(): qimg.save(buffer, 'png') else: qimg.save(buffer, 'ppm') b = BytesIO() try: b.write(buffer.data()) except TypeError: # the types seemed to change between versions of qtpy b.write(buffer.data().data()) buffer.close() b.seek(0) pil_img = Image.open(b) return pil_img
def _download( self, url, path=None, force=False, verify=True, chunked=True, ): """Callback for download.""" if path is None: path = url.split('/')[-1] # Make dir if non existent folder = os.path.dirname(os.path.abspath(path)) if not os.path.isdir(folder): os.makedirs(folder) # Get headers try: r = requests.head( url, proxies=self.proxy_servers, verify=verify, timeout=self.DEFAULT_TIMEOUT, ) status_code = r.status_code except Exception as error: status_code = -1 logger.error(str(error)) logger.debug('Status code {0} - url'.format(status_code, url)) if status_code != 200: logger.error('Invalid url {0}'.format(url)) return path total_size = int(r.headers.get('Content-Length', 0)) # Check if file exists if os.path.isfile(path) and not force: file_size = os.path.getsize(path) else: file_size = -1 # print(path, total_size, file_size) # Check if existing file matches size of requested file if file_size == total_size: self._sig_download_finished.emit(url, path) return path else: try: r = requests.get( url, stream=chunked, proxies=self.proxy_servers, verify=verify, timeout=self.DEFAULT_TIMEOUT, ) status_code = r.status_code except Exception as error: status_code = -1 logger.error(str(error)) # File not found or file size did not match. Download file. progress_size = 0 bytes_stream = QBuffer() # BytesIO was segfaulting for big files bytes_stream.open(QBuffer.ReadWrite) # For some chunked content the app segfaults (with big files) # so now chunked is a kwarg for this method if chunked: for chunk in r.iter_content(chunk_size=self._chunk_size): # print(url, progress_size, total_size) if chunk: bytes_stream.write(chunk) progress_size += len(chunk) self._sig_download_progress.emit( url, path, progress_size, total_size, ) else: bytes_stream.write(r.content) bytes_stream.seek(0) data = bytes_stream.data() with open(path, 'wb') as f: f.write(data) bytes_stream.close() self._sig_download_finished.emit(url, path) return path
class MainWindow(QMainWindow): max_recent_files = 10 def __init__(self): super().__init__() self.setWindowTitle("OkPlayer") icon = QIcon() icon.addPixmap(QPixmap("ok_64x64.ico"), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) self.recent_file_acts = [] self.init_menu() self.now = datetime.now() # Setting self.setting = {} self.load_setting() # Status bar self.learning_time_ms = 0 self.learning_time_ms_total = self.setting.get( "learning_time_ms_total", 0) self.status_bar = self.statusBar() self.label_learning_time = QLabel(self) self.label_learning_time.setAlignment(Qt.AlignRight) self.status_bar.addPermanentWidget(self.label_learning_time) self.label_learning_time.setText( f"Learning time: 00:00" f" / total {ms2min_sec(self.learning_time_ms_total)}") # Timer for learning time self.timer_learning_time = QTimer(self) self.timer_learning_time.timeout.connect(self.update_learning_time) self.timer_learning_time.setInterval(1000) # Player self.player = QMediaPlayer(self) self.player.mediaStatusChanged.connect(self.qmp_status_changed) self.player.positionChanged.connect(self.qmp_position_changed) self.player.setNotifyInterval(50) self.player.setVolume(50) self.player_buf = QBuffer() self.path_media = "" self.music_data = None self.duration_ms = 0 self.duration_str = "" # A/B Loop self.pos_loop_a = None self.pos_loop_b = None # Layout self.label_music = QLabel("No music", self) self.ico_play = qta.icon("fa.play") self.ico_pause = qta.icon("fa.pause") layout = QVBoxLayout() layout_volume = QHBoxLayout() layout_btn_progress = QVBoxLayout() layout_music_btns = QHBoxLayout() self.btn_rewind = QPushButton(qta.icon("fa.backward"), "", self) self.btn_rewind.clicked.connect(self.rewind) self.btn_play = QPushButton(self.ico_play, "", self) self.btn_play.clicked.connect(self.play) self.btn_fastforward = QPushButton(qta.icon("fa.forward"), "", self) self.btn_fastforward.clicked.connect(self.fastforward) self.btn_rewind.setFocusPolicy(Qt.NoFocus) self.btn_play.setFocusPolicy(Qt.NoFocus) self.btn_fastforward.setFocusPolicy(Qt.NoFocus) layout_music_btns.addWidget(self.btn_rewind) layout_music_btns.addWidget(self.btn_play) layout_music_btns.addWidget(self.btn_fastforward) layout_progress = QHBoxLayout() self.progressbar = MusicProgressBar(self) self.progressbar.sig_pb_pos.connect(self.set_media_position) self.elapsed_time = QLineEdit(f"00:00 / 00:00", self) self.elapsed_time.setReadOnly(True) self.elapsed_time.setAlignment(Qt.AlignHCenter) layout_progress.addWidget(self.progressbar) layout_progress.addWidget(self.elapsed_time) layout_btn_progress.addWidget(self.label_music) layout_btn_progress.addLayout(layout_music_btns) layout_btn_progress.addLayout(layout_progress) # Volume self.qdial_volume = QDial(self) self.qdial_volume.setMinimumWidth(110) self.qdial_volume.setWrapping(False) self.qdial_volume.setNotchesVisible(True) self.qdial_volume.setMinimum(0) self.qdial_volume.setMaximum(100) self.qdial_volume.setValue(self.player.volume()) self.qdial_volume.valueChanged.connect(self.qdial_changed) layout_volume.addLayout(layout_btn_progress) layout_volume.addWidget(self.qdial_volume) # Lyrics self.display_lyrics = LyricsDisplay(self) layout.addLayout(layout_volume) layout.addWidget(self.display_lyrics) central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) # Auto Play self.update_recent_file_action() path = self.setting.get("LastPlayedPath", "") if osp.isfile(path): self.load_music_file(path) self.setFocus() def init_menu(self): """Init menu.""" color_icon = "#87939A" menu_bar = self.menuBar() menu_bar.setNativeMenuBar(False) # Don't use mac native menu bar # File file_menu = menu_bar.addMenu("&File") # Open open_action = QAction(qta.icon("ei.folder-open", color=color_icon), "&Open", self) open_action.setShortcut("Ctrl+O") open_action.setStatusTip("Open file") open_action.triggered.connect(self.open_music_file) file_menu.addAction(open_action) file_menu.addSeparator() # Recent Files for i in range(MainWindow.max_recent_files): self.recent_file_acts.append( QAction(self, visible=False, triggered=self.load_recent_music)) for i in range(MainWindow.max_recent_files): file_menu.addAction(self.recent_file_acts[i]) file_menu.addSeparator() # Exit exit_action = QAction(qta.icon("mdi.exit-run", color=color_icon), "&Exit", self) exit_action.setShortcut("Ctrl+Q") exit_action.setStatusTip("Exit App") exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) # Help help_menu = menu_bar.addMenu("&Help") about_action = QAction( "&About", self, statusTip="Show the application's About box", triggered=self.about, ) help_menu.addAction(about_action) def about(self): """Show messagebox for about.""" QMessageBox.about( self, "About music player a/b loop", "The music player a/b loop is made by <b>ok97465</b>", ) def update_recent_file_action(self): """Update recent file action.""" files = self.setting.get("recent_files", []) num_recent_files = min(len(files), MainWindow.max_recent_files) for i in range(num_recent_files): text = osp.splitext(osp.basename(files[i]))[0] self.recent_file_acts[i].setText(text) self.recent_file_acts[i].setData(files[i]) self.recent_file_acts[i].setVisible(True) for j in range(num_recent_files, MainWindow.max_recent_files): self.recent_file_acts[j].setVisible(False) def open_music_file(self): """Open music file.""" self.stop() fname = QFileDialog.getOpenFileName( self, "Open music file", "/home/ok97465", filter="Music Files (*.mp3, *.m4a)", ) self.load_music_file(fname[0]) def load_music_file(self, path: str): """Load music file""" if not osp.isfile(path): return self.path_media = path path_lyrics = path[:-3] + "vtt" self.display_lyrics.read_vtt(path_lyrics) fp = io.BytesIO() self.music_data = AudioSegment.from_file(path) self.music_data.export(fp, format="wav") self.player_buf.setData(fp.getvalue()) self.player_buf.open(QIODevice.ReadOnly) self.player.setMedia(QMediaContent(), self.player_buf) def load_recent_music(self): """Load recent music.""" action = self.sender() if action: self.stop() self.load_music_file(action.data()) def load_setting(self): """Load setting file.""" try: with open("setting.json", "r") as fp: self.setting = json.load(fp) except FileNotFoundError: pass def keyPressEvent(self, event): key = event.key() shift = event.modifiers() & Qt.ShiftModifier if shift: if key == Qt.Key_O: self.adjust_ab_loop(-100) else: if key in [Qt.Key_H, Qt.Key_Left, Qt.Key_A]: self.rewind(ms=5000) elif key in [Qt.Key_L, Qt.Key_Right, Qt.Key_D]: self.fastforward(ms=5000) elif key in [Qt.Key_J]: self.rewind(ms=1000 * 38) elif key in [Qt.Key_K, Qt.Key_F]: self.fastforward(ms=1000 * 38) elif key == Qt.Key_Up: self.control_volume(5) elif key == Qt.Key_Down: self.control_volume(-5) elif key in [Qt.Key_I, Qt.Key_W, Qt.Key_Menu]: self.set_ab_loop() elif key == Qt.Key_O: self.adjust_ab_loop(500) elif key in [Qt.Key_Space, Qt.Key_Hangul_Hanja]: self.play() elif key in [Qt.Key_S]: self.save_ab_loop() elif key in [Qt.Key_Q, Qt.Key_U, Qt.Key_Slash]: self.send_AB_loop_lyrics_to_papago() super().keyPressEvent(event) def set_ab_loop(self): """Set A/B loop.""" if self.pos_loop_b: self.pos_loop_b = None self.pos_loop_a = None elif self.pos_loop_a: self.pos_loop_b = self.player.position() self.player.setPosition(self.pos_loop_a) else: self.pos_loop_a = self.player.position() self.progressbar.pos_loop_a = self.pos_loop_a self.progressbar.pos_loop_b = self.pos_loop_b self.progressbar.repaint() def adjust_ab_loop(self, offset_ms): """Adjust A/B loop.""" if self.pos_loop_b: self.pos_loop_b += offset_ms self.pos_loop_a += offset_ms def save_ab_loop(self): """Save A/B loop""" if self.pos_loop_b is None: return is_playing = False if self.player.state() == QMediaPlayer.PlayingState: is_playing = True if is_playing: self.player.pause() path_new = (self.path_media[:-4] + f"{self.pos_loop_a}_{self.pos_loop_b}" + self.path_media[-4:]) seg = self.music_data[self.pos_loop_a:self.pos_loop_b] seg.export(path_new, format="mp3") if is_playing: self.player.play() def play(self): """Play music file.""" if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() self.btn_play.setIcon(self.ico_play) self.timer_learning_time.stop() else: self.player.play() self.btn_play.setIcon(self.ico_pause) self.timer_learning_time.start() def stop(self): """Stop.""" self.save_current_media_info() self.player.stop() self.player_buf.close() self.path_media = "" self.pos_loop_b = None self.pos_loop_a = None self.timer_learning_time.stop() self.label_music.setText("No music") self.btn_play.setIcon(self.ico_play) def control_volume(self, step: int): """Control volume.""" volume = self.player.volume() if step < 0: new_volume = max([0, volume + step]) else: new_volume = min([100, volume + step]) self.qdial_volume.setValue(new_volume) def navigate_media(self, ms: int): """Navigate the position of media.""" position_ms = self.player.position() if ms < 0: new_position_ms = max([0, position_ms + ms]) else: new_position_ms = min([self.duration_ms, position_ms + ms]) self.player.setPosition(new_position_ms) def rewind(self, ms: int = 5000): """Re-wind media of QMediaPlayer.""" self.navigate_media(ms * -1) def fastforward(self, ms: int = 5000): """fastfoward media of QMediaPlayer.""" self.navigate_media(ms) def qmp_status_changed(self): """Handle status of QMediaPlayer if the status is changed.""" status = self.player.mediaStatus() if status == QMediaPlayer.LoadedMedia and self.path_media: duration_ms = self.player.duration() self.duration_ms = duration_ms self.duration_str = ms2min_sec(duration_ms) self.elapsed_time.setText(f"00:00 / {self.duration_str}") self.progressbar.setMaximum(duration_ms) music_basename = osp.splitext(osp.basename(self.path_media))[0] self.label_music.setText(music_basename) self.player.play() # read previous position path = self.path_media position = self.setting.get(path, 0) self.player.setPosition(position) # update recent files files = self.setting.get("recent_files", []) try: files.remove(path) except ValueError: pass files.insert(0, path) del files[MainWindow.max_recent_files:] self.setting["recent_files"] = files self.update_recent_file_action() # Player state state = self.player.state() if state in [QMediaPlayer.PausedState, QMediaPlayer.StoppedState]: self.btn_play.setIcon(self.ico_play) self.timer_learning_time.stop() elif state == QMediaPlayer.PlayingState: self.btn_play.setIcon(self.ico_pause) self.timer_learning_time.start() def qmp_position_changed(self, position_ms: int): """Handle position of qmedia if the position is changed.""" if self.pos_loop_b: if (position_ms == self.duration_ms) or (self.pos_loop_b < position_ms): self.player.setPosition(self.pos_loop_a) self.progressbar.setValue(position_ms) self.elapsed_time.setText( f"{ms2min_sec(position_ms)} / {self.duration_str}") self.display_lyrics.update_media_pos(position_ms) def qdial_changed(self, pos: int): """Handle Qdial position.""" self.player.setVolume(pos) def send_AB_loop_lyrics_to_papago(self): """Send AB loop lyrics to papago.""" if not self.pos_loop_b: return lyrics = self.display_lyrics.get_lyrics_in_range( self.pos_loop_a, self.pos_loop_b) lyrics = lyrics.replace("\n", "") webbrowser.open(f"https://papago.naver.com/?sk=en&tk=ko&st={lyrics}", autoraise=False) @Slot(int) def set_media_position(self, position_ms: int): """Set the position of Qmedia.""" self.player.setPosition(position_ms) def save_current_media_info(self): """Save current media info to setting file.""" if not osp.isfile(self.path_media): return if self.path_media: position = self.player.position() self.setting[self.path_media] = position self.setting["LastPlayedPath"] = self.path_media def update_learning_time(self): """Update learning time.""" self.learning_time_ms += 1000 self.learning_time_ms_total += 1000 self.label_learning_time.setText( f"Learning time : {ms2min_sec(self.learning_time_ms)}" f" / total : {ms2min_sec(self.learning_time_ms_total)}") def closeEvent(self, event): """Save setting.""" self.stop() self.setting["learning_time_ms_total"] = self.learning_time_ms_total with open("setting.json", "w") as fp: json.dump(self.setting, fp, indent=2) now = self.now cur = sqlite3.connect("history.db") cur.execute("CREATE TABLE IF NOT EXISTS LearningTimeData(" "DayOfWeek INTEGER, " "month INTEGER, " "day INTEGER, " "timestamp REAL, " "LearningTime_ms INTEGER)") cur.execute( "insert into LearningTimeData Values (?,?,?,?,?)", (now.weekday(), now.month, now.day, now.timestamp(), self.learning_time_ms), ) cur.commit() cur.close()