class Window(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super().__init__(parent) # load config self.data = yaml_loader() # load ui self.setupUi(self) # load icons self.setWindowTitle("Sputofy") self.setWindowIcon(QIcon(os.path.join(RES_PATH, "logo.svg"))) loopIcon = QIcon() loopIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.loopBtn.setIcon(loopIcon) prevIcon = QIcon() prevIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "backwardIcon.svg"))) self.prevBtn.setIcon(prevIcon) playIcon = QIcon() playIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "playIcon.svg"))) self.playBtn.setIcon(playIcon) nextIcon = QIcon() nextIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "forwardIcon.svg"))) self.nextBtn.setIcon(nextIcon) randomIcon = QIcon() randomIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "randomIconOFF.svg"))) self.randomBtn.setIcon(randomIcon) volumeIcon = QIcon() volumeIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "volumeIcon.svg"))) self.volumeBtn.setIcon(volumeIcon) # window's settings self.xCor = self.data['last_position']['xPos'] self.yCor = self.data['last_position']['yPos'] self.widthSize = self.data['last_window_size']['width'] self.heightSize = self.data['last_window_size']['height'] self.setGeometry(self.xCor, self.yCor, self.widthSize, self.heightSize) # load YouTubeToMP3 self.YouTubeToMP3 = YouTubeToMP3Window() # open YouTubeToMP3 using button self.actionYT_MP3.triggered.connect(self.YouTubeToMP3.show_window) # info action self.actionInfo.triggered.connect(self.info_handle) #=========================== mediaplayer ============================== # create media player object self.mediaPlayer = QMediaPlayer(None) # open button self.actionOpen_Song.triggered.connect(self.open_song) self.actionOpen_Folder.triggered.connect(self.open_folder) # play button self.playBtn.setEnabled(False) self.playBtn.clicked.connect( self.play_video ) # when btn is pressed: if it is playing it pause, if it is paused it plays # QShortcut(QKeySequence("Space"), self).activated.connect(self.play_video)metodo da ricordare in caso di problemi #TODO # duration slider self.durationSlider.setEnabled(False) self.durationSliderMaxValue = 0 self.durationSlider.valueChanged.connect( self.mediaPlayer.setPosition ) # set mediaPlayer position using the value took from the slider QShortcut('Right', self, lambda: self.durationSlider.setValue( self.durationSlider.value() + 10000)) # 1s = 1000ms QShortcut('Left', self, lambda: self.durationSlider.setValue( self.durationSlider.value() - 10000)) # 1s = 1000ms QShortcut('Shift+Right', self, lambda: self.durationSlider.setValue( self.durationSliderMaxValue - 1000)) # jump to the end-1s of song QShortcut('Shift+Left', self, lambda: self.durationSlider.setValue(0)) # restart song # volumeSlider self.volumeSlider.setProperty("value", 100) self.volumeSlider.setRange(0, 100) self.volumeSlider.setValue( self.data['volume'] if self.data['volume'] != 0 else self.data['volume'] + 1 ) # set slider value | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.mediaPlayer.setVolume( self.data['volume'] if self.data['volume'] != 0 else self.data['volume'] + 1 ) # set mediaPlayer volume | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.volumeLabel.setText( f"{self.data['volume']}%" if self.data['volume'] != 0 else f"{self.data['volume']+1}%" ) # set volume label text | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.volumeSlider.valueChanged.connect( self.mediaPlayer.setVolume ) # set mediaPlayer volume using the value took from the slider QShortcut('Up', self, lambda: self.volumeSlider.setValue( self.volumeSlider.value() + 1)) # volume + 1 QShortcut('Down', self, lambda: self.volumeSlider.setValue( self.volumeSlider.value() - 1)) # volume - 1 QShortcut( 'Shift+Up', self, lambda: self.volumeSlider.setValue(100)) # set maximum volume QShortcut( 'Shift+Down', self, lambda: self.volumeSlider.setValue(0)) # set minimun volume(mute) # volumeBtn self.volumeBtn.clicked.connect( self.volume_toggle) # mute/unmute volume pressing btn self.isMuted = False # starting with a non-muted volume self.previousVolume = self.data[ 'volume'] # loading last registered volume # media player signals self.mediaPlayer.durationChanged.connect( self.duration_changed) # set range of duration slider self.mediaPlayer.positionChanged.connect( self.position_changed) # duration slider progress self.mediaPlayer.stateChanged.connect( self.player_state) # see when it's playing or in pause self.mediaPlayer.volumeChanged.connect( self.volume_icon) # change volumebtn icon #=========================== playlist ============================== # create the playlist self.playlist = QMediaPlaylist() self.playlist.setPlaybackMode(2) self.mediaPlayer.setPlaylist(self.playlist) # clear the playlist self.playlistIsEmpty = True # playlistList model self.model = PlaylistModel(self.playlist) self.playlistView.setModel(self.model) self.playlist.currentIndexChanged.connect( self.playlist_position_changed) selection_model = self.playlistView.selectionModel() selection_model.selectionChanged.connect( self.playlist_selection_changed) #=========================== playlist function ============================== self.mediaList = [] # array of loaded songs self.currentPlaylist = "" # current loaded playlist name self.isCustomPlaylist = False # add song name on title self.playlist.currentMediaChanged.connect(self.set_title) # playlist buttons self.nextBtn.clicked.connect(self.next_song) # seek track forward self.prevBtn.clicked.connect(self.prev_song) # seek track backward self.mediaPlayer.mediaStatusChanged.connect( self.auto_next_track ) # once song is ended seek track forward and play it self.actionLoopIt.triggered.connect( self.loop_song) # (1) loop the same song self.actionShuffle.triggered.connect( self.shuffle_playlist) # change song's order self.loopBtn.clicked.connect(self.loop) # (3) loop the playlist self.randomBtn.clicked.connect( self.random) # (4) play random song without end # create new playlist self.actionCreatePlaylist.triggered.connect(self.custom_playlist) # delete current playlist self.actionDeletePlaylist.triggered.connect(self.delete_playlist) # remove all songs self.actionClearQueue.triggered.connect(self.clear_queue) # load playlist self.actionDict = {} # dictionary of action Objects for action in self.data['playlistList']: self.actionDict[action] = self.menuPlaylist.addAction( action, partial(self.load_playlist, action)) if len(self.data['playlistList']) == 0: self.menuPlaylist.menuAction().setVisible(False) #================== Songs opening ==================# def open_folder(self): foldername = QFileDialog.getExistingDirectory(self, "Open folder", "c:\\") if foldername: self.playlist.clear() self.mediaList.clear() for song in os.listdir(foldername): media = f"{foldername}/{song}" self.playlist.addMedia(QMediaContent(QUrl(media))) self.mediaList.append(media) self.playlist.setCurrentIndex(0) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.playlistIsEmpty = False self.isCustomPlaylist = False self.model.layoutChanged.emit() # load songs in list view self.set_title() self.mediaPlayer.pause() # adjust play/pause icon def open_song(self): filename, _ = QFileDialog.getOpenFileName(self, "Open Song", "c:\\") if filename: if self.playlistIsEmpty == False: self.playlist.clear() self.mediaList.clear() self.playlistIsEmpty = True self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.mediaList.append(filename) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.isCustomPlaylist = False self.model.layoutChanged.emit() # load song in list view self.set_title() # adjust play/pause icon if self.playlist.mediaCount( ) == 1: # if there is 1 song and you add another self.playlist.setCurrentIndex(0) self.mediaPlayer.pause() def load_playlist(self, playlistName): self.playlist.clear() self.mediaList.clear() # reload config self.data = yaml_loader() for song in self.data['playlistList'][playlistName]: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(song))) self.mediaList.append(song) self.playlist.setCurrentIndex(0) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.playlistIsEmpty = False self.isCustomPlaylist = True self.model.layoutChanged.emit() # load songs in list view self.currentPlaylist = playlistName # name of current loaded playlist self.set_title() self.statusbar.showMessage(f'Playlist "{playlistName}" loaded', 4000) self.menuPlaylist.menuAction().setVisible(True) # adjust play/pause icon self.mediaPlayer.pause() def set_title(self): if self.playlist.mediaCount() == 0: self.setWindowTitle("Sputofy") else: if self.isCustomPlaylist == False: self.setWindowTitle( f"Sputofy - {os.path.splitext(self.playlist.currentMedia().canonicalUrl().fileName())[0]} - {self.playlist.currentIndex()+1}/{self.playlist.mediaCount()}" ) else: self.setWindowTitle( f"Sputofy - {self.currentPlaylist} - {os.path.splitext(self.playlist.currentMedia().canonicalUrl().fileName())[0]} - {self.playlist.currentIndex()+1}/{self.playlist.mediaCount()}" ) #=======================================================# #================== Player Functions ==================# def play_video(self): if self.durationSlider.isEnabled(): # if slider was enabled if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def duration_changed(self, duration): self.durationSlider.setRange(0, duration) if duration > 0: self.totalTime_Label.setText(time_format(round( duration / 1000))) # duration is in ms self.durationSliderMaxValue = duration def position_changed(self, position): if position >= 0: self.elapsedTime_Label.setText(time_format( (position / 1000))) # position is in ms # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). self.durationSlider.blockSignals(True) self.durationSlider.setValue(position) self.durationSlider.blockSignals(False) #=======================================================# #================== Playlist Settings ==================# #TODO Work in progress def playlist_array(self): index = self.playlist.mediaCount() mediaList = [] for i in range(index): # songPath = (self.playlist.media(path).canonicalUrl().path())#.split("/",1)[1] # mediaList.append(songPath) # print(self.playlist.media(i).canonicalUrl().path()) mediaList.append(self.playlist.media(i).canonicalUrl().fileName()) return mediaList def custom_playlist(self): if not self.playlist.mediaCount() == 0: name, is_notEmpty = QInputDialog.getText(self, "playlist", "save playlist as:") if name: if name in self.data['playlistList']: self.statusbar.showMessage( "playlist not created (name is already used)", 4000) else: self.data['playlistList'][name] = self.mediaList yaml_dump(self.data) # add new action Object to dictionary self.actionDict[name] = self.menuPlaylist.addAction( name, partial(self.load_playlist, name)) self.load_playlist( name) # instantly loading the new playlist else: self.statusbar.showMessage( "playlist not created (you should give a name to your baby :/)", 4000) else: self.statusbar.showMessage("there are no songs to playlist", 4000) def delete_playlist(self): if self.isCustomPlaylist: if len(self.data['playlistList']) == 1: self.menuPlaylist.menuAction().setVisible(False) self.data['playlistList'].pop( self.currentPlaylist) # remove playlist from dictionary self.menuPlaylist.removeAction(self.actionDict[ self.currentPlaylist]) # remove relative action self.actionDict.pop( self.currentPlaylist) # remove relative action Object self.playlist.clear() self.model.layoutChanged.emit() self.setWindowTitle("Sputofy") yaml_dump(self.data) self.statusbar.showMessage( 'succesfully deleted "' + self.currentPlaylist + '" playlist', 4000) else: self.statusbar.showMessage("cannot delete a non custom playlist", 4000) def clear_queue(self): self.playlist.clear() self.mediaList.clear() self.playBtn.setEnabled(False) self.model.layoutChanged.emit() def playlist_position_changed(self, i): if i > -1: ix = self.model.index(i) self.playlistView.setCurrentIndex(ix) def playlist_selection_changed(self, ix): # We receive a QItemSelection from selectionChanged. i = ix.indexes()[0].row() self.posizione = i self.playlist.setCurrentIndex(i) self.mediaPlayer.play() #=======================================================# #================== Playback Settings ==================# def next_song(self): if self.playlist.currentIndex() == self.playlist.mediaCount() - 1: self.playlist.setCurrentIndex(0) else: self.playlist.next() def prev_song(self): if self.playlist.currentIndex() == 0: self.playlist.setCurrentIndex(self.playlist.mediaCount() - 1) else: self.playlist.previous() def loop_song(self): if self.playlist.playbackMode() != 1: self.playlist.setPlaybackMode(1) self.actionLoopIt.setText("Loop it: ON") self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) else: self.playlist.setPlaybackMode(2) self.actionLoopIt.setText("Loop it: OFF") def shuffle_playlist(self): if self.playlist.mediaCount(): self.playlist.shuffle() self.model.layoutChanged.emit() else: self.statusbar.showMessage("there are no songs to shuffle", 4000) def loop(self): if self.playlist.playbackMode() != 3: self.playlist.setPlaybackMode(3) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconON.svg"))) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) self.actionLoopIt.setText("Loop it: OFF") else: self.playlist.setPlaybackMode(2) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) def random(self): if self.playlist.playbackMode() != 4: self.playlist.setPlaybackMode(4) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconON.svg"))) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.actionLoopIt.setText("Loop it: OFF") else: self.playlist.setPlaybackMode(2) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) def auto_next_track(self): if self.mediaPlayer.mediaStatus() == QMediaPlayer.EndOfMedia: if self.playlist.playbackMode() == 2: # index starts from 0 mediacount starts from 1 if self.playlist.currentIndex( ) != self.playlist.mediaCount() - 1: self.playlist.next() self.mediaPlayer.play() else: # if ended song was the last one set the index to the first one and pause self.playlist.setCurrentIndex(0) self.mediaPlayer.pause() # loop playlist elif self.playlist.playbackMode() == 3: self.playlist.next() self.mediaPlayer.play() # random song elif self.playlist.playbackMode() == 4: while self.playlist.previousIndex( ) == self.playlist.currentIndex( ): # preventing repeating the same song self.playlist.setCurrentIndex( random.randint(0, self.playlist.mediaCount() - 1)) #=======================================================# #================== Volume Settings ==================# def volume_icon(self, volume): self.volumeLabel.setText(f"{volume}%") if volume: volumeIcon = QIcon() volumeIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "volumeIcon.svg")), QIcon.Normal, QIcon.Off) self.volumeBtn.setIcon(volumeIcon) self.previousVolume = self.volumeSlider.value() self.isMuted = False else: volumeMutedIcon = QIcon() volumeMutedIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "volumeMutedIcon.svg")), QIcon.Normal, QIcon.Off) self.volumeBtn.setIcon(volumeMutedIcon) self.isMuted = True def volume_toggle(self): if self.isMuted == False: self.volumeSlider.setValue(0) self.isMuted = True elif self.isMuted == True: if self.previousVolume == 0: self.volumeSlider.setValue(10) else: self.volumeSlider.setValue(self.previousVolume) self.isMuted = False #=======================================================# def mousePressEvent(self, event): ''' remove the border around the buttons created by using tab key ''' focused_widget = QtWidgets.QApplication.focusWidget() try: focused_widget.clearFocus() except: pass QMainWindow.mousePressEvent(self, event) def player_state(self, event): ''' event handler that adjust the play/pause icon ''' if event == QMediaPlayer.PlayingState: pauseIcon = QIcon() pauseIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "pauseIcon.svg")), QIcon.Normal, QIcon.Off) self.playBtn.setIcon(pauseIcon) elif event == QMediaPlayer.PausedState: playIcon = QIcon() playIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "playIcon.svg")), QIcon.Normal, QIcon.Off) self.playBtn.setIcon(playIcon) def closeEvent(self, event): ''' event handler that take window information and save it in config before the window close ''' # retrieve position xAxis = self.geometry().x() yAxis = self.geometry().y() self.data['last_position']['xPos'] = xAxis self.data['last_position']['yPos'] = yAxis # retrieve size width = self.width() height = self.height() self.data['last_window_size']['width'] = width self.data['last_window_size']['height'] = height # retrieve volume self.data['volume'] = self.mediaPlayer.volume() # retrieve user user = os.getlogin() self.data[ 'default_folder'] = f"C:\\Users\\{user}\\Desktop\\sputofy_songs" yaml_dump(self.data) def info_handle(self): info = "Sputofy\n1.0.0\n©2020 "+\ "Sputofy is a free audio player based on the converted youtube songs made by a_str0\n\n"+\ "Sputofy is written using python 3.x and PyQt5 modules" msg = QMessageBox.about(self, "About", info)
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.title = 'JPR Reader GUI version2' self.left = 50 self.top = 100 self.width = 1200 self.height = 800 self.fileReady = False self.tableRow = 5 self.tableCol = 15 self.row = 2 self.audiolist = [] self.configFile = ".//configurationFile.xlsx" self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # menu menubar = self.menuBar() filemenu = menubar.addMenu('File') fileAct = QAction('Open File', self) fileAct.setShortcut('Ctrl+O') filemenu.addAction(fileAct) fileAct.triggered.connect(self.openFileNameDialog) # status_bar self.statusbar = self.statusBar() # tabs self.tabs = QTabWidget() self.tab1 = QWidget(self) self.tab2 = QWidget(self) self.tabs.addTab(self.tab1, "조작") self.tabs.addTab(self.tab2, "설정") self.setCentralWidget(self.tabs) # tab1 gui self.ButtonGroupBox = self.createButtonGroupBox() self.createLogTable() self.tab1.layout = QVBoxLayout() self.tab1.layout.addWidget(self.ButtonGroupBox) self.tab1.layout.addWidget(self.logTable) self.tab1.setLayout(self.tab1.layout) # tab2 gui self.explanation = QLabel() self.explanation.setText("""<<< JPR reader 설정 테이블 >>> 이곳에서 JPR reader가 읽어주는 항목과 아이템을 설정할 수 있습니다. 항목과 아이템의 설정 방법은 셀을 더블 클릭한 후 이름을 입력하는 방식으로진행됩니다. 그 후 떠오른 파일 탐색기에서 지정할 mp3파일을 선택해 주세요. 설정의 확인은 셀의 색으로 확인 가능합니다. 지정이 완료된 항목은 노란색, 아이템은 하늘색으로 표시됩니다. 지정이 되지 않은 셀은 빨간색으로 표시됩니다. 행과 열 버튼으로 테이블의 크기를 조절할 수 있으며 초기화시 모든 설정은 사라지며 테이블은 5by5로 초기화 됩니다. <<<주의사항>>> 1. 첫번째 열은 반드시 항목을 입력해 주세요. 2. 입력되는 이름은 엑셀파일에서 표현된 명칭과 완벽히 일치해야 합니다.(엔터나 스페이스, 오타 주의!) 3. 초기화를 누르면 처음부터 모든 항목과 아이템을 지정해야 합니다. 4. 엑셀 파일의 ()나 /을 통해 구분한 아이템은 따로 입력해 주세요. 5. 사용하는 엑셀파일의 구조를 유지해 주세요. 변경시 프로그램의 수정이 필요할 수 있습니다.(예: '박스'항목은 항상 있을 것으로 간주됩니다.) 제작자 정보: 김현우([email protected])""") self.explanation.setAlignment(Qt.AlignCenter) self.createConfigTable() self.createHorizontalButtons2() self.tab2.layout = QVBoxLayout() self.tab2.layout.addWidget(self.explanation) self.tab2.layout.addWidget(self.configTable) self.tab2.layout.addWidget(self.horizontalButtons2) self.tab2.setLayout(self.tab2.layout) # Show widget self.show() def openFileNameDialog(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") if not fileName: self.statusbar.showMessage('Fail to load file...') else: self.wb = load_workbook(fileName.strip()) self.ws = self.wb.active self.fileReady = True self.row = 2 self.statusbar.showMessage('succeed to load file...') self.logTable.clear() # set logTable's horizontal header self.logTable.setHorizontalHeaderItem(0, QTableWidgetItem("포장순번")) self.logTable.setHorizontalHeaderItem(1, QTableWidgetItem("거래처명")) self.logTable.setHorizontalHeaderItem(2, QTableWidgetItem("배송센터")) self.logTable.setHorizontalHeaderItem(3, QTableWidgetItem("특이사항")) self.logTable.setHorizontalHeaderItem(4, QTableWidgetItem("박스")) # initialize dictionary self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } col = 6 num = 0 header = str(self.ws.cell(row=1, column=col + 1).value).strip() while header != 'None': self.dict[header] = None col = col + 1 num = num + 1 header = str(self.ws.cell(row=1, column=col + 1).value).strip() self.tableCol = 5 + num self.logTable.setColumnCount(self.tableCol) for c in range(5, self.tableCol): self.logTable.setHorizontalHeaderItem( c, QTableWidgetItem(list(self.dict.keys())[c])) def createButtonGroupBox(self): buttonGroupBox = QGroupBox("Controller") vLayout = QVBoxLayout() pre_button = QPushButton('w', self) pre_button.clicked.connect(self.pre_click) pre_button.setIcon(QIcon('.\\img\\up-arrow.png')) pre_button.setIconSize(QSize(600, 100)) pre_button.setShortcut('w') vLayout.addWidget(pre_button) hBottensWidget = self.createHButtons() vLayout.addWidget(hBottensWidget) next_button = QPushButton('x', self) next_button.clicked.connect(self.next_click) next_button.setIcon(QIcon('.\\img\\down-arrow.png')) next_button.setIconSize(QSize(600, 100)) next_button.setShortcut('x') vLayout.addWidget(next_button) buttonGroupBox.setLayout(vLayout) return buttonGroupBox def createHButtons(self): hBottensWidget = QWidget() hLayout = QHBoxLayout() back_button = QPushButton('a', self) back_button.clicked.connect(self.back_click) back_button.setIcon(QIcon('.\\img\\left-arrow.png')) back_button.setIconSize(QSize(200, 150)) back_button.setShortcut('a') hLayout.addWidget(back_button) cur_button = QPushButton('s', self) cur_button.clicked.connect(self.cur_click) cur_button.setIcon(QIcon('.\\img\\reload.png')) cur_button.setIconSize(QSize(200, 150)) cur_button.setShortcut('s') hLayout.addWidget(cur_button) forward_button = QPushButton('d', self) forward_button.clicked.connect(self.forward_click) forward_button.setIcon(QIcon('.\\img\\right-arrow.png')) forward_button.setIconSize(QSize(200, 150)) forward_button.setShortcut('d') hLayout.addWidget(forward_button) hBottensWidget.setLayout(hLayout) return hBottensWidget def createHorizontalButtons2(self): self.horizontalButtons2 = QGroupBox("설정 변경") layout = QHBoxLayout() plusRowButton = QPushButton('행+', self) plusRowButton.clicked.connect(self.plus_row) layout.addWidget(plusRowButton) plusColButton = QPushButton('열+', self) plusColButton.clicked.connect(self.plus_col) layout.addWidget(plusColButton) init_button = QPushButton('초기화', self) init_button.clicked.connect(self.initialize) layout.addWidget(init_button) self.horizontalButtons2.setLayout(layout) def createLogTable(self): # Create table self.logTable = QTableWidget() self.logTable.setRowCount(self.tableRow) self.logTable.setColumnCount(self.tableCol) self.logTable.move(0, 0) def createConfigTable(self): self.configTable = QTableWidget() try: # load configurationFile cwb = load_workbook(self.configFile.strip()) except: # message box string = "설정파일을 불러올 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: # Get matadata cws = cwb.active self.crow = cws.cell(row=1, column=1).value self.ccol = cws.cell(row=1, column=2).value # Configure table self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) self.configTable.move(0, 0) # Load data from configFile for i in range(self.crow): for j in range(self.ccol): item = str(cws.cell(row=i + 2, column=j + 1).value).strip() if item == 'None': item = '' self.configTable.setItem(i, j, QTableWidgetItem(item)) # check if files exist arr = listdir('./audio_clips') for row in range(self.crow): for col in range(self.ccol): # if 박스(0,0) if row == 0 and col == 0: continue # reset backgound color self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) # if file exist, change background color fname = str(row) + '_' + str(col) + '.mp3' if fname in arr: if row == 0: self.configTable.item(row, col).setBackground( QColor(255, 255, 0)) else: self.configTable.item(row, col).setBackground( QColor(0, 255, 255)) # 박스(0,0) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) # link the callback function self.configTable.itemDoubleClicked.connect(self.item_doubleClicked) self.configTable.itemChanged.connect(self.item_changed) def setLogTable(self): # shifting other rows for r in range(1, self.tableRow): for c in range(self.tableCol): try: self.logTable.item(r, c).setBackground(QColor(255, 255, 255)) self.logTable.setItem(r - 1, c, self.logTable.item(r, c).clone()) except: pass # set current row for idx, key in enumerate(list(self.dict.keys())): if type(self.dict[key]) is list: self.logTable.setItem( self.tableRow - 1, idx, QTableWidgetItem(' '.join(self.dict[key]))) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) else: self.logTable.setItem(self.tableRow - 1, idx, QTableWidgetItem(self.dict[key])) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) def read(self): # 포장 순번 self.dict['num'] = str(self.ws.cell(row=self.row, column=3).value[2:]).strip() # 거래처명 self.dict['partner'] = str(self.ws.cell(row=self.row, column=5).value).strip() # 배송센터 self.dict['parcel'] = str(self.ws.cell(row=self.row, column=4).value).strip() # 특이사항 self.dict['exception'] = str( self.ws.cell(row=self.row, column=6).value).strip() # 박스 self.dict['box'] = str(self.ws.cell(row=self.row, column=2).value).strip() # left things print(len(self.dict)) for i in range(5, len(self.dict)): header = str(self.ws.cell(row=1, column=i + 2).value).strip() self.dict[header] = str( self.ws.cell(row=self.row, column=i + 2).value).strip() self.parsing(header, self.dict[header]) print(self.dict) self.setLogTable() def parsing(self, key, val): if val == 'None' or val == '': self.dict[key] = None else: if '(' in val or '/' in val: arr = re.split('[(/]', val) for i in range(len(arr)): if ')' in arr[i]: arr[i] = arr[i][:arr[i].index(')')] arr[i] = arr[i].strip() self.dict[key] = arr else: self.dict[key] = val def itemFromKeyVal(self, key, val): items = self.configTable.findItems(val, Qt.MatchExactly) if len(items) <= 0: # Error string = '(' + key + ', ' + val + ') ' + '아이템을 찾을 수 없습니다.' QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: for item in items: if self.configTable.item(0, item.column()).data(0) == key: return item def load_audiolist(self): for key, val in self.dict.items(): # 포장순번 if key == 'num': for i in range(len(self.dict['num'])): self.audiolist.append('_' + val[i]) # beep self.audiolist.append('_beep') # 택배발송 elif key == 'parcel': if val == '택배발송': self.audiolist.append('_택배발송') # beep self.audiolist.append('_beep') # 박스 elif key == 'box': item = self.itemFromKeyVal('박스', val) if item: self.audiolist.append( str(item.row()) + '_' + str(item.column())) # beep self.audiolist.append('_beep') elif key in ['partner', 'exception']: pass # general case else: # The case(val == None) will be ignored if val == None: pass # when val is list elif type(val) == list: for idx, eachVal in enumerate(val): item = self.itemFromKeyVal(key, eachVal) if item: if idx == 0: self.audiolist.append( '0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') # when val is not list else: item = self.itemFromKeyVal(key, val) if item: if val == '1' or key == val: self.audiolist.append('0_' + str(item.column())) # key else: self.audiolist.append('0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') print(self.audiolist) def speak(self): self.playlist.clear() for clip in self.audiolist: url = QUrl.fromLocalFile('./audio_clips/' + clip + '.mp3') #print(url) self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() #----------------------- Control button callback -----------------------# @pyqtSlot() def pre_click(self): if not self.fileReady: self.cur_click() else: if self.row == 2: self.statusbar.showMessage("Can't be previous.") else: self.row -= 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def cur_click(self): if not self.fileReady: string = '파일이 준비되어 있지 않습니다.' QMessageBox.question(self, '경고', string, QMessageBox.Ok, QMessageBox.Ok) self.openFileNameDialog() else: self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def next_click(self): if not self.fileReady: self.cur_click() else: if self.row == self.ws.max_row: self.statusbar.showMessage("It's over.") else: self.row += 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def back_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') cnt = 0 for i in range(self.playlist.mediaCount()): # if it's start point, start at here if self.playlist.currentIndex() == 0: break # go backward self.playlist.setCurrentIndex(self.playlist.previousIndex(1)) # start at previous beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): cnt = cnt + 1 if cnt == 2: print(self.playlist.currentIndex()) if self.player.state() == QMediaPlayer.StoppedState: self.player.play() break @pyqtSlot() def forward_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') for i in range(self.playlist.mediaCount()): # don't go further from end point if self.playlist.currentIndex() < 0: break # go forward self.playlist.setCurrentIndex(self.playlist.nextIndex(1)) # start at next beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): print(self.playlist.currentIndex()) break #----------------------- Configuration button callback -----------------------# @pyqtSlot() def plus_row(self): # change configTable self.crow = self.crow + 1 self.configTable.setRowCount(self.crow) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for col in range(self.ccol): self.configTable.setItem(self.crow - 1, col, QTableWidgetItem('')) self.configTable.item(self.crow - 1, col).setBackground(QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def plus_col(self): # change configTable self.ccol = self.ccol + 1 self.configTable.setColumnCount(self.ccol) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for row in range(self.crow): self.configTable.setItem(row, self.ccol - 1, QTableWidgetItem('')) self.configTable.item(row, self.ccol - 1).setBackground( QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def initialize(self): # remove configurable audio files arr = listdir('./audio_clips') p = re.compile('_.+') for file in arr: if p.match(file): continue remove('./audio_clips/' + file) # init configTable self.configTable.itemChanged.disconnect(self.item_changed) # lock self.configTable.clear() self.crow = 5 self.ccol = 5 self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) # reset configTable item for row in range(self.crow): for col in range(self.ccol): self.configTable.setItem(row, col, QTableWidgetItem('')) self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) self.configTable.itemChanged.connect(self.item_changed) # unlock # init configFile self.update_configFile() #---------------------- ConfigTable signal callback ------------------------# def item_doubleClicked(self, item): self.previousItem = item.data(0) print(self.previousItem) def update_configFile(self): cwb_w = Workbook(write_only=True) cws_w = cwb_w.create_sheet() cws_w.append([self.crow, self.ccol]) for row in range(self.crow): itemList = [] for col in range(self.ccol): itemList.append(self.configTable.item(row, col).data(0)) cws_w.append(itemList) try: cwb_w.save(self.configFile) except: string = """설정파일이 열려있을 수 있습니다. 설정파일을 닫은 후에 다시 시도하세요.""" QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) def item_changed(self, item): self.configTable.itemChanged.disconnect(self.item_changed) # lock if item.row() == 0 and item.column() == 0: string = "이 항목은 바꿀 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) else: # get file name fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") # count the number of key that has same name keys = self.configTable.findItems(item.data(0), Qt.MatchExactly) kcnt = 0 for key in keys: if key.row() == 0: kcnt = kcnt + 1 # count the number of atribute that has same name atributes = self.configTable.findItems(item.data(0), Qt.MatchExactly) acnt = 0 for atribute in atributes: if atribute.row() == 0: pass elif atribute.column() == item.column(): acnt = acnt + 1 # change is accepted only in case of uniqueness and existence and it is not 박스(0,0) if kcnt >= 2: string = "항목명이 같을 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif acnt >= 2: string = "같은 항목에 같은 이름의 아이템을 둘 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif fileName: # copy file to local dir dst = "./audio_clips./" + str(item.row()) + '_' + str( item.column()) + '.mp3' copyfile(fileName, dst) # change cell color if item.row() == 0: self.configTable.item(item.row(), item.column()).setBackground( QColor(255, 255, 0)) else: self.configTable.item(item.row(), item.column()).setBackground( QColor(0, 255, 255)) # update configFile self.update_configFile() else: string = "선택이 취소됨." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) self.configTable.itemChanged.connect(self.item_changed) # unlock