--- Title: pythonでfoobarのalternativeを作る 30 (一旦完成) Author: yamasyuh68 Web: https://mimemo.io/m/JYpaMlMVe94yrdg --- 190618 現時点でのコードなり (・∀・)ノ 開発はひと段落 @[TOC](項目) # sqlview 本体 ``` import sqlite3 , re ,vlc ,time ,os ,subprocess # from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import ( QApplication,QWidget,QMainWindow , QAction , QListView,QVBoxLayout,QSlider,QLabel , QMenu ,QInputDialog) from PyQt5.QtGui import QStandardItemModel , QStandardItem , QPixmap , QCursor from PyQt5.QtCore import QTimer ,Qt ,QSettings,QRect import ui from sqlView_mylist import mylist ,taginfo from sqlView_save import listsaver ,settingsaver class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.ui = ui.Ui_MainWindow() self.ui.setupUi(self) self.db=os.path.join(os.path.dirname(__file__) , 'database.db') self.model=None self.dbreload() self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect(self.treemenu) self.ui.tabWidget.currentChanged.connect(self.tabchanged) self.ui.tabWidget.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tabWidget.tabBar().customContextMenuRequested.connect(self.tabmenu) self.model2 = QStandardItemModel() # tagview model self.ui.listView.setModel(self.model2) # list_tag self.current={'row':'' , 'dur':0 , 'list':None , 'p_list':None} self.autoplay=False self.sliderflag=True self.setaction() self.p = vlc.MediaPlayer() self.taginfo=taginfo(self) self.timer = QTimer(self) self.timer.setInterval(500) self.timer.timeout.connect(self.monitoring) r=settingsaver.load() if not r: r=[] r.append(QRect(0,650,600,400)) r.append((1,0)) self.setGeometry(r[0]) for l in range(r[1][0]): self.makenewtab('default') listsaver.load(self.ui.tabWidget) self.ui.tabWidget.setCurrentIndex (r[1][1]) p=r[1][2] if p=='': p=0 self.ui.tabWidget.widget(r[1][1]).setCurrentIndex( self.ui.tabWidget.widget(r[1][1]).model().index(p,0)) def setaction(self): # actionPlay = QAction('Play', self) # actionPlay.triggered.connect(self.actionplay) actionPause = QAction('PAUSE', self) actionPause.triggered.connect(self.actionpause) actionStop = QAction('STOP', self) actionStop.triggered.connect(self.actionstop) actionBye = QAction('BYE', self) actionBye.triggered.connect(self.actionbye) actionsave = QAction('--', self) actionsave.triggered.connect(self.actionsave) actiondb = QAction('DB', self) actiondb.triggered.connect(self.dbmenu) # self.ui.toolBar.addAction(actionPlay) self.ui.toolBar.addAction(actionPause) self.ui.toolBar.addAction(actionStop) self.ui.toolBar.addAction(actionsave) self.ui.toolBar.addAction(actiondb) self.ui.toolBar.addAction(actionBye) self.ui.toolBar.addSeparator() self.disptime=QLabel('00:00/00:00') self.ui.toolBar.addWidget(self.disptime) self.ui.toolBar.addSeparator() self.slider=QSlider(Qt.Horizontal) self.slider. sliderMoved[int].connect(self.slidetime) self.slider.sliderReleased.connect(self.settime) self.slider.setRange ( 0 , 100) # percent self.ui.toolBar.addWidget(self.slider) def slidetime(self, time): self.sliderflag=False timel=int( ((self.p.get_length()/1000) *time)/100) self.disptime.setText( '{:02}:{:02}/{}'.format(timel // 60 , timel % 60 , self.current['dur'])) self.sltime=time def settime(self) : if self.p.is_playing() : self.p.set_position( self.sltime/100 ) self.sliderflag=True def makenewtab(self,name): self.current['list'] = mylist() self.ui.tabWidget.addTab(self.current['list'], name) self.ui.tabWidget.setCurrentWidget(self.current['list']) lyt = QVBoxLayout() lyt.setContentsMargins(0, 0, 0, 0) lyt.setSpacing(0) self.current['list'].setLayout(lyt) self.current['list'].doubleClicked.connect(self.ListdoubleClicked) self.current['list'].clicked.connect(self.treeClicked) def tabchanged(self,e): self.current['list']=self.ui.tabWidget.widget(e) def ListdoubleClicked(self,e): row=self.current['list'].selectionModel().currentIndex().row() fname=e.siblingAtColumn(2).data() if not fname : # title-row --> return return if self.p.is_playing() : self.removeplay() self.p.stop() else: self.timer.start() self.current['p_list']=self.current['list'] self.actionplay(fname,row) def actionplay(self,f,row): self.current['row']=row self.p.set_mrl(f) self.p.play() self.taginfo.show(f) self.current['p_list'].model().setItem (row,1, QStandardItem('▶')) self.current['p_list'].setCurrentIndex(self.current['p_list'].model().index(row,0)) self.setWindowTitle(f) dur=int( self.p.get_length()/1000 ) self.current['dur']='{:02}:{:02}'.format( dur // 60 , dur % 60 ) def removeplay(self): delrow=self.current['p_list'].model().findItems('▶',Qt.MatchExactly,1)[0].row() self.current['p_list'].model().setItem(delrow,1,QStandardItem('')) return delrow def monitoring(self): if self.p.is_playing() : time=int(self.p.get_time()/1000) pos=int(self.p.get_position()*100) if self.sliderflag: self.disptime.setText( '{:02}:{:02}/{}'.format(time // 60 , time % 60 , self.current['dur'])) self.slider.setSliderPosition(pos) else: row=self.removeplay()+1 fname=self.current['p_list'].model().index(row,2).data() if fname=='tit': # album row+=1 fname=self.current['p_list'].model().index(row,2).data() if fname==None: # list-end fname=self.current['p_list'].model().index(1,2).data() row=1 self.actionplay(fname,row) def actionpause(self): self.p.pause() if self.timer.isActive() : self.timer.stop() else: self.timer.start() def actionstop(self): self.p.stop() self.timer.stop() self.removeplay() self.autoplay=False def dbmenu(self): menu=QMenu(self) action = QAction('DB_refresh', self) action.triggered.connect(self.dbrefresh) menu.addAction(action) action = QAction('DB_load', self) action.triggered.connect(self.dbreload) menu.addAction(action) menu.exec_(QCursor.pos ()) def dbrefresh(self): fname=os.path.join(os.path.dirname(__file__) , 'database.py') subprocess.Popen( ( r"C:\Python37\python.exe" , fname) ) def dbreload(self): if self.model : self.model.clear() del self.model line='select artist ,album,title ,path from musics order by artist ,album,title' self.model=self.sqlexecute(line) self.ui.treeView.setModel(self.model) # tree self.search(0) def tabmenu(self): menu=QMenu(self) action = QAction('addtab', self) action.triggered.connect(self.addtab) menu.addAction(action) action = QAction('renametab', self) action.triggered.connect(self.renametab) # not menu.addAction(action) action = QAction('showcurrenttab', self) action.triggered.connect(self.showcurrenttab) # not menu.addAction(action) menu.addSeparator() action = QAction('remove_tab', self) action.triggered.connect(self.removetab) menu.addAction(action) menu.exec_(QCursor.pos ()) def addtab(self): self.makenewtab('hoge') def renametab(self): text=QInputDialog.getText(self.ui.tabWidget,r'( ´∀`)','set New Name') if text[1]: self.ui.tabWidget.setTabText(self.ui.tabWidget.currentIndex() , text[0]) def showcurrenttab(self): self.ui.tabWidget.setCurrentIndex(self.getP_listIndexfromwidget()) def removetab(self): self.ui.tabWidget.removeTab(self.ui.tabWidget.currentIndex() ) def getP_listIndexfromwidget(self): for i in range(self.ui.tabWidget.count()): if self.ui.tabWidget.widget(i) is self.current['p_list'] : return i return 0 def actionsave(self): pass def sqlexecute( self, line ): conn = sqlite3.connect( self.db ) c = conn.cursor() item=['×','×'] tree=[None,None,None,None] model = QStandardItemModel() for l in c.execute(line) : # print(l) if item[0] .lower() != l[0].lower() : # 1 a-artist tree[1] = QStandardItem(l[0]) model.appendRow(tree[1]) item[0]=l[0] if item[1] != l[1] : # 1 date album tree[2] = QStandardItem( l[1] ) tree[1].appendRow(tree[2]) item[1]=l[1] tree[3]=QStandardItem(l[2]) , QStandardItem(l[3]) # tree[3].setData(l[3]) tree[2].appendRow(tree[3]) # l[3] path 入れてない c.close() conn.close() return model def actionbye(self): listsaver.save(self.ui.tabWidget) settingsaver.save(self) quit() def search(self,e): item=self.ui.lineEdit.text() temp=self.ui.treeView.model() if item=='' or (len(item)==1 and re.match('[a-zA-Z0-9_.,]',item) ) : # if item=='' : if temp == self.model : pass else: self.ui.treeView.setModel(self.model) del temp else : item="'%"+item+"%'" line='select artist ,album,title ,path from musics where \ artist like {} or path like {} or title like {}\ order by artist ,album,title'.format(item,item,item) self.ui.treeView.setModel(self.sqlexecute(line)) if temp != self.model: del temp def treeClicked(self,e): if self.sender() is self.ui.treeView : # case from tree path=( e.siblingAtColumn(1).data() ) else: # case from list row=self.current['list'].selectionModel().currentIndex().row() path=e.siblingAtColumn(2).data() if path=='tit': path=None if path: # dataがあればタイトル self.taginfo.show(path) def treemenu(self,e): menu=QMenu(self) action = QAction('Open Folder', self) action.triggered.connect(self.openfolder) menu.addAction(action) action = QAction('AddToList', self) action.triggered.connect(self.AddToCurrentList) menu.addAction(action) menu.exec_(QCursor.pos ()) def openfolder(self,e): path=self.ui.treeView.selectedIndexes()[0].siblingAtColumn(1).data() # path=( e.siblingAtColumn(1).data() ) subprocess.Popen(( 'explorer' , os.path.split(path)[0] )) subprocess.Popen(r'Mp3tag.exe') def AddToCurrentList(self,e): pass if __name__ == '__main__': import sys app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_()) ``` - メニューの追加でずいぶん長くなってしまった┐('д')┌ # sqlView_mylist タブのサブクラスとタグ情報表示 ``` import os ,mutagen from PyQt5.QtWidgets import QTreeView,QAbstractItemView from PyQt5.QtGui import QStandardItem ,QStandardItemModel,QPixmap from PyQt5.QtCore import Qt class mylist(QTreeView): def __init__(self): super().__init__() self.setAcceptDrops(True) # self.setDropIndicatorShown(0) self.setEditTriggers(QAbstractItemView.NoEditTriggers) model = QStandardItemModel(0,3) model.setHorizontalHeaderLabels(['track - Title', '▶', '']) self.setModel(model) self.header().setStretchLastSection(False) self.header().resizeSection(0, 300) self.header().resizeSection(1, 10) self.header().resizeSection(2, 100) self.setRootIsDecorated (0) def dragMoveEvent(self,e): self.setDropIndicatorShown(1) # to show indicator super().dragMoveEvent(e) def dropEvent(self, e): # ここで受け取る row=self.indexAt(e.pos()).row() # dropped row pos=self.dropIndicatorPosition() if pos==3: row=self.model().rowCount() if pos==2: row+=1 # print(row,pos ) # QAbstractItemView.OnItem 0 The item will be dropped on the index. # QAbstractItemView.AboveItem 1 The item will be dropped above the index. # QAbstractItemView.BelowItem 2 The item will be dropped below the index. # QAbstractItemView.OnViewport 3 if self.model().index(row,2).data() != 'tit' and pos!=3 : count=1 while self.model().index(row-count,2).data() != 'tit': count+=1 album=self.model().index(row-count,0).data() self.model().insertRow( row , \ (QStandardItem(album),QStandardItem(''),QStandardItem('tit')) ) self.setFirstColumnSpanned (row ,self.model().index(row,0).parent() ,1) # index の長さが1ならartistかアルバム、1以上ならタイトル index=e.source().selectedIndexes() if len(index)==1 : # album or artist if index[0].parent().data(): # album self.model().insertRow( row , QStandardItem(index[0].parent().data() +' : ' \ +index[0].data() + '──────────────────────')) self.model().setItem( row , 2 ,QStandardItem('tit')) # title flag self.setFirstColumnSpanned (row ,self.model().index(row,0).parent() ,1) i=0 row+=1 a,b=index[0].child(i, 0).data(),index[0].child(i, 1).data() while a: self.model().insertRow(row , (QStandardItem(a) ,QStandardItem('') ,QStandardItem(b) )) i+=1 row+=1 a,b=index[0].child(i, 0).data(),index[0].child(i, 1).data() else : # artist --ignore pass else: # title for l in range(0,len(index),2): self.model().insertRow( row , \ (QStandardItem(index[l].data()),QStandardItem(''),QStandardItem(index[l+1].data()) ) ) row+=1 self.setDropIndicatorShown(0) # to hide indicator # super().dropEvent(e) def keyPressEvent (self, e): # delet row if not self.model().rowCount(): return if e.key()==Qt.Key_Delete : sel=self.selectedIndexes() # indexのリスト delrow=sel[0].row() parent=sel[0].parent() if self.model().index(delrow,2).data()=='tit': # album-row count=1 check=self.model().index(delrow+count,2).data() while check != 'tit' and check !=None and check !='' : count+=1 check=self.model().index(delrow+count,2).data() self.model().removeRows( delrow, count ,parent) else: self.model().removeRow( delrow, parent) elif e.key()==16777350 : self.parent().parent().parent().parent().actionpause() class taginfo(): def __init__(self,parent): self.listmodel=parent.model2 self.panel=parent.ui.label def show(self,f): self.listmodel.clear() m = mutagen.File(f, easy=True) # m は辞書で要素はリスト、取り出し方が難しい tags=['artist','albumartist','album','title','tracknumber','date'] tagss=['Artist ','A-Artist ','Album ','Title ','Track ','Year '] for i,tag in enumerate(tags): try: t=m[tag][0] except: t='null' self.listmodel.appendRow(QStandardItem('{}{}'.format(tagss[i] , t ))) a,b=os.path.split(f) self.listmodel.appendRow(QStandardItem('file {}'.format(b))) self.listmodel.appendRow(QStandardItem('folda {}'.format(a))) l=m.info.pprint().split(',') self.listmodel.appendRow(QStandardItem('enc {}'.format(l[0]))) for n in l: if 'bps' in n: self.listmodel.appendRow(QStandardItem('bps {}'.format(n))) elif 'second' in n: self.listmodel.appendRow(QStandardItem('dur {}'.format(n))) try : album=m['album'][0]+'.jpg' except: album='' del m m = mutagen.File(f) flag=False try: artwork = m.tags["APIC:"].data # access APIC frame and grab the image pix=QPixmap() pix.loadFromData(artwork) # from memory rc=self.panel.geometry() self.panel.setPixmap(pix.scaled(rc.width(), rc.height())) flag=True except: flist=['cover.jpg','cover.jpeg','front.jpg','front.jpeg','folder.jpg',album] for fname in flist: name=os.path.join(a,fname) if os.path.exists(name): pix=QPixmap(name) rc=self.panel.geometry() self.panel.setPixmap(pix.scaled(rc.width(), rc.height())) flag=True break if not flag: self.panel.setText(r'( ´∀`)') del m ``` # sqlView_save SQLで設定とリストを保存 ``` import sqlite3 , os from PyQt5.QtGui import QStandardItem from PyQt5.QtCore import QRect class listsaver(): def save(p): fname=os.path.join(os.path.dirname(__file__) , 'listDB') conn = sqlite3.connect(fname) c = conn.cursor() c.execute('create table IF NOT EXISTS musiclists(name, title ,path)') c.execute('DELETE FROM musiclists') for i in range(p.count()):# タブの数だけ回す model=p.widget(i).model() for l in range(model.rowCount()): c.execute( "INSERT INTO musiclists (name, title ,path) VALUES (?,?,?)" , ( i , model.index(l,0).data() , model.index(l,2).data())) c.execute('create table IF NOT EXISTS tablist(name)') c.execute('DELETE FROM tablist') for i in range(p.count()) :# タブの数だけ回す c.execute( "INSERT INTO tablist (name) VALUES (?)" , ( p.tabText(i) ,) ) conn.commit() conn.close() def load(p): fname=os.path.join(os.path.dirname(__file__) , 'listDB') if not os.path.exists(fname): return conn = sqlite3.connect(fname) c = conn.cursor() for l in c.execute('select * from musiclists '): model=p.widget( l[0] ).model() # l[0]がタブインデクス model.appendRow([QStandardItem(l[1]), QStandardItem(''), QStandardItem(l[2]) ]) if l[2] =='tit' : ro=model.rowCount()-1 p.widget(l[0]).setFirstColumnSpanned (ro ,model.index(ro,0).parent() ,1) # del model for i,l in enumerate( c.execute('select * from tablist ')) : p.setTabText(i , l[0]) c.close() conn.close() class settingsaver(): def save(p): fname=os.path.join(os.path.dirname(__file__) , 'listDB') conn = sqlite3.connect(fname) c = conn.cursor() c.execute('create table IF NOT EXISTS general(top,left,width,height,tabcount,p_tab,row)') c.execute('DELETE FROM general') r=p.geometry() c.execute( "INSERT INTO general (top,left,width,height,tabcount,p_tab,row) VALUES (?,?,?,?,?,?,?)" , ( r.left() ,r.top() , r.width() , r.height() , p.ui.tabWidget.count() , p.getP_listIndexfromwidget() ,p.current['row'] ) ) conn.commit() conn.close() def load(): fname=os.path.join(os.path.dirname(__file__) , 'listDB') if not os.path.exists(fname): return None conn = sqlite3.connect(fname) c = conn.cursor() c.execute('select * from general ') l=c.fetchone() c.close() conn.close() return((QRect(l[0],l[1],l[2],l[3]) , (l[4],l[5],l[6]) )) ``` # database.py DB作成用の独立した py ``` import glob ,re , os ,mutagen ,datetime import sqlite3 import time # tag整形してからDB作成版 class makeDB(): def __init__( self): fold=[r'i:\Music\**'] # fold=[r'i:\Music\Pops_w\**'] self.db=os.path.join(os.path.dirname(__file__) , 'database.db') conn = sqlite3.connect(self.db) c = conn.cursor() c.execute('create table IF NOT EXISTS musics(path PRIMARY KEY,mdate,flag,artist,album,title)') # 1-------------- c.execute('UPDATE musics SET flag=4') # 2-------------- count={'all':0 , 'file':0 , 'music':0 , 'exist':0 , 'modified':0} start = time.time() for f in fold: print('first step....') self.first(f,c,count) # making log f=os.path.join(os.path.dirname(__file__) , 'log.log') fo=open(f, 'a' ) print( datetime.datetime.now() , sep="\n", end="\n", file=fo) print( count , sep="\n", end="\n", file=fo) print ("elapsed_time:{0}".format(time.time() - start) + "[sec]" ,sep="\n", end="\n", file=fo) print( '------------------------------------' , sep="\n", end="\n", file=fo) fo.close() # 3-------------- start = time.time() print('second step....') self.second(c) print ("elapsed_time:{0}".format(time.time() - start) + "[sec]") # 4-------------- print('del...') print(c.execute('DELETE FROM musics WHERE flag=4')) # c.execute('DELETE FROM musics WHERE artist is null') c.close() conn.commit() conn.close() def first(self,f,c ,count): #フルパスとmdate挿入、成功したらそのまま、失敗したらmdate比較して同じならそのまま、違ってたら # mdateをupdateしてflagをnullに戻す for p in glob.iglob(f , recursive=True): count['all']+=1 if os.path.isfile(p): count['file']+=1 if re.search('\.(mp3|ape|ogg|flac|aac|mp4|m4a)$', p): count['music']+=1 mtime=os.path.getmtime(p) try: c.execute( "INSERT INTO musics (path,mdate,flag) VALUES (?,?,?)" , (p,mtime,None )) except: count['exist']+=1 c.execute('select mdate from musics where path = ?',(p, )) res=c.fetchone() if res : if res[0]==mtime : c.execute('update musics set flag = ? where path = ? ',( 1, p )) else : count['modified']+=1 c.execute('update musics set mdate=?,flag = ? where path = ? ',( mtime,None, p )) def second(self,c): c.execute('select path from musics where flag IS NULL') for p in c.fetchall(): # print(p) try: m = mutagen.File(p[0], easy=True) # m は辞書で要素はリスト、取り出し方が難しい tup=1, except: print('error-- ' + p[0]) c.execute('update musics set flag = ? where path = ? ',( 4, p[0] )) continue try: tup += m['albumartist'][0], except: try : tup += m['artist'][0], except: tup += '?', try : date=m['date'][0] except: date='?' try : album=m['album'][0] except: album='?' if date=='?' and album=='?' : tup += '?', else: tup += '['+ date + ']' + album , title='' sl='' try : for i in m['discnumber'][0]: if i == '/': break else: sl += i title+='{:01}.'.format(int(sl)) except: pass ti='' try: for i in m['tracknumber'][0]: if i == '/': break else: ti += i title+='{:02} '.format(int(ti)) except: pass try: title += m['title'][0] except: title='?' tup +=title, tup += p[0], c.execute('update musics set flag=?,artist=?,album=?,title=? where path=?' , tup ) makeDB() ``` # ui.py designerで吐いたuiのpy ``` # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'ui.ui' # # Created by: PyQt5 UI code generator 5.12 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(598, 378) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_2.setSpacing(0) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.treeView = QtWidgets.QTreeView(self.centralwidget) self.treeView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.treeView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.treeView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.treeView.setDragEnabled(True) self.treeView.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.treeView.setObjectName("treeView") self.treeView.header().setVisible(False) self.verticalLayout.addWidget(self.treeView) self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit.setObjectName("lineEdit") self.verticalLayout.addWidget(self.lineEdit) self.horizontalLayout_2.addLayout(self.verticalLayout) self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName("verticalLayout_2") self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.listView = QtWidgets.QListView(self.centralwidget) self.listView.setMinimumSize(QtCore.QSize(0, 0)) self.listView.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.listView.setProperty("showDropIndicator", False) self.listView.setObjectName("listView") self.horizontalLayout.addWidget(self.listView) self.label = QtWidgets.QLabel(self.centralwidget) font = QtGui.QFont() font.setPointSize(22) self.label.setFont(font) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setObjectName("label") self.horizontalLayout.addWidget(self.label) self.horizontalLayout.setStretch(0, 2) self.horizontalLayout.setStretch(1, 1) self.verticalLayout_2.addLayout(self.horizontalLayout) self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setObjectName("tabWidget") self.verticalLayout_2.addWidget(self.tabWidget) self.verticalLayout_2.setStretch(0, 2) self.verticalLayout_2.setStretch(1, 3) self.horizontalLayout_2.addLayout(self.verticalLayout_2) self.horizontalLayout_2.setStretch(0, 1) self.horizontalLayout_2.setStretch(1, 2) MainWindow.setCentralWidget(self.centralwidget) self.toolBar = QtWidgets.QToolBar(MainWindow) self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) self.toolBar.setFloatable(False) self.toolBar.setObjectName("toolBar") MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) self.actionchange = QtWidgets.QAction(MainWindow) self.actionchange.setObjectName("actionchange") self.actionBye = QtWidgets.QAction(MainWindow) self.actionBye.setObjectName("actionBye") self.actionclear = QtWidgets.QAction(MainWindow) self.actionclear.setObjectName("actionclear") self.actionreload = QtWidgets.QAction(MainWindow) self.actionreload.setObjectName("actionreload") self.actionplay = QtWidgets.QAction(MainWindow) self.actionplay.setObjectName("actionplay") self.actionpause = QtWidgets.QAction(MainWindow) self.actionpause.setObjectName("actionpause") self.actionstop = QtWidgets.QAction(MainWindow) self.actionstop.setObjectName("actionstop") self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(-1) self.lineEdit.textChanged['QString'].connect(MainWindow.search) self.treeView.clicked['QModelIndex'].connect(MainWindow.treeClicked) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", "( ´∀`)")) self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) self.actionchange.setText(_translate("MainWindow", "change")) self.actionBye.setText(_translate("MainWindow", "Bye")) self.actionclear.setText(_translate("MainWindow", "clear")) self.actionreload.setText(_translate("MainWindow", "reload")) self.actionplay.setText(_translate("MainWindow", "play")) self.actionpause.setText(_translate("MainWindow", "pause")) self.actionstop.setText(_translate("MainWindow", "stop")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_()) ``` ---- - python3,pyQt5,mutagen,vlcと、vlcの実行ファイルが別に必要 - まずdatabase.pyでデータベースを作成します 検索フォルダはコンストラクタの一行目で自分で設定します リストにしてあるので複数フォルダOKですが、まずは少な目のフォルダをオススメします - そのあとでsqlviewer.pyを実行します 初回起動時にリストにタイトルを設定しないと次回起動しないかもしれません --- → [pythonでfoobarのalternativeを作る](https://mimemo.io/m/3kyw8o3neWG6Lrg)