소스 검색

add notes, complete playslist playing

Olivier Massot 4 년 전
부모
커밋
ebe4b43aac

+ 1 - 1
core/constants.py

@@ -13,7 +13,7 @@ VLC_PATH = APP_ROOT / 'core' / 'vlc-core'
 
 DATA_DIR = APP_ROOT / 'data'
 
-SETTING_DIST_PATH = DATA_DIR / 'settings.yaml.dist'
+SETTING_DIST_PATH = APP_ROOT / 'core' / 'settings.yaml.dist'
 
 PROFILE_DIR = APP_ROOT / 'data' / 'default'
 

+ 8 - 1
core/repositories.py

@@ -99,7 +99,7 @@ class TrackRepository(Repository):
             .group_by(Track.album)\
             .all()
 
-    def add_to_playlist(self, track_id, session_id):
+    def add_to_session(self, track_id, session_id):
         track_session = SessionTrack()
         track_session.track_id = track_id
         track_session.session_id = session_id
@@ -109,6 +109,13 @@ class TrackRepository(Repository):
         self.create(track_session)
         self.commit()
 
+    def get_by_session_id(self, session_id):
+        return self.session.query(Track) \
+            .join(SessionTrack, Track.id == SessionTrack.track_id) \
+            .filter(SessionTrack.session_id == session_id)\
+            .order_by(SessionTrack.order)\
+            .all()
+
 
 class SessionTrackRepository(Repository):
     MODEL_CLS = SessionTrack

+ 3 - 0
core/settings.yaml.dist

@@ -0,0 +1,3 @@
+playlist: 0
+volume: 50
+muted: False

+ 8 - 6
main.py

@@ -41,7 +41,7 @@ sys.excepthook = err_handler
 if not SETTING_PATH.exists():
     SETTING_DIST_PATH.copy(SETTING_PATH)
 
-settings = yaml.load(SETTING_PATH.text())
+settings = yaml.load(SETTING_PATH.text(), Loader=yaml.FullLoader)
 
 # Create db if not existing
 try:
@@ -59,11 +59,13 @@ main_window.show()
 try:
     r = app.exec_()
 finally:
-    indexer.stop()
-    db.close()
+    try:
+        settings = main_window.currentSettings()
+        with open(SETTING_PATH, 'w') as f:
+            yaml.dump(settings, f)
+    finally:
+        indexer.stop()
+        db.close()
 
-    settings = main_window.currentSettings()
-    with open(SETTING_PATH, 'w') as f:
-        yaml.dump(settings, f)
 
 logger.info("-- Closed --")

+ 2 - 2
notes

@@ -24,11 +24,11 @@ Priorité 1:
 * faire une modale pour l'édition des metadonnées
 * afficher l'activité de l'indexation dans la barre de statut
 * Enregistrer les notes pour un morceau et s'assurer qu'elles ne soient pas perdues
-* memoriser les données d'une session à l'autre: playlist en cours, volume, ...
 
 Priorité 2:
 
 * Optimiser la rapidité de l'indexation (insertions de masse, optim du hashage, ne pas update les metadata si déjà renseignées...)
 * Ajouter le module de téléchargement depuis youtube
 * Permettre de prioriser l'indexation selon certaines actions (sélection d'un artiste, téléchargement, nouveaux morceaux)
-* Charger l'explorer de manière asynchrone
+* Charger l'explorer de manière asynchrone
+* faire un prompt premier lancement pour ajouter des dossiers de musique et créer une première séance

+ 1 - 1
ui/qt/dlg_playlist_ui.py

@@ -2,7 +2,7 @@
 
 # Form implementation generated from reading ui file 'dlg_playlist.ui'
 #
-# Created by: PyQt5 UI code generator 5.15.4
+# Created by: PyQt5 UI code generator 5.15.2
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic5 is
 # run again.  Do not edit this file unless you know what you are doing.

+ 1 - 0
ui/qt/dlg_select_playlist.py

@@ -26,6 +26,7 @@ class DlgSelectPlaylist(QtWidgets.QDialog):
         self.ui.tree.itemClicked.connect(self.playlistSelected)
 
         self.ui.btnSelect.clicked.connect(self.ok)
+        self.ui.tree.itemDoubleClicked.connect(self.ok)
 
         self.ui.tree.setColumnCount(3)
         self.ui.tree.setColumnWidth(0, 54)

+ 26 - 1
ui/qt/dlg_select_playlist.ui

@@ -50,6 +50,9 @@
        </item>
        <item>
         <widget class="QToolButton" name="btnAddFolder">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
          <property name="minimumSize">
           <size>
            <width>28</width>
@@ -68,6 +71,10 @@
          <property name="text">
           <string>D</string>
          </property>
+         <property name="icon">
+          <iconset resource="rsc.qrc">
+           <normaloff>:/img/rsc/create_folder.png</normaloff>:/img/rsc/create_folder.png</iconset>
+         </property>
         </widget>
        </item>
        <item>
@@ -90,6 +97,10 @@
          <property name="text">
           <string>+</string>
          </property>
+         <property name="icon">
+          <iconset resource="rsc.qrc">
+           <normaloff>:/img/rsc/plus.png</normaloff>:/img/rsc/plus.png</iconset>
+         </property>
         </widget>
        </item>
        <item>
@@ -115,6 +126,10 @@
          <property name="text">
           <string>E</string>
          </property>
+         <property name="icon">
+          <iconset resource="rsc.qrc">
+           <normaloff>:/img/rsc/edit.png</normaloff>:/img/rsc/edit.png</iconset>
+         </property>
         </widget>
        </item>
        <item>
@@ -140,6 +155,10 @@
          <property name="text">
           <string>C</string>
          </property>
+         <property name="icon">
+          <iconset resource="rsc.qrc">
+           <normaloff>:/img/rsc/duplicate.png</normaloff>:/img/rsc/duplicate.png</iconset>
+         </property>
         </widget>
        </item>
        <item>
@@ -165,6 +184,10 @@
          <property name="text">
           <string>-</string>
          </property>
+         <property name="icon">
+          <iconset resource="rsc.qrc">
+           <normaloff>:/img/rsc/minus.png</normaloff>:/img/rsc/minus.png</iconset>
+         </property>
         </widget>
        </item>
       </layout>
@@ -227,6 +250,8 @@
    </item>
   </layout>
  </widget>
- <resources/>
+ <resources>
+  <include location="rsc.qrc"/>
+ </resources>
  <connections/>
 </ui>

+ 18 - 1
ui/qt/dlg_select_playlist_ui.py

@@ -2,7 +2,7 @@
 
 # Form implementation generated from reading ui file 'dlg_select_playlist.ui'
 #
-# Created by: PyQt5 UI code generator 5.15.4
+# Created by: PyQt5 UI code generator 5.15.2
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic5 is
 # run again.  Do not edit this file unless you know what you are doing.
@@ -31,31 +31,47 @@ class Ui_dlgSelectPlaylist(object):
         self.label.setObjectName("label")
         self.horizontalLayout.addWidget(self.label)
         self.btnAddFolder = QtWidgets.QToolButton(dlgSelectPlaylist)
+        self.btnAddFolder.setEnabled(False)
         self.btnAddFolder.setMinimumSize(QtCore.QSize(28, 28))
         self.btnAddFolder.setMaximumSize(QtCore.QSize(28, 28))
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap(":/img/rsc/create_folder.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnAddFolder.setIcon(icon)
         self.btnAddFolder.setObjectName("btnAddFolder")
         self.horizontalLayout.addWidget(self.btnAddFolder)
         self.btnAdd = QtWidgets.QToolButton(dlgSelectPlaylist)
         self.btnAdd.setMinimumSize(QtCore.QSize(28, 28))
         self.btnAdd.setMaximumSize(QtCore.QSize(28, 28))
+        icon1 = QtGui.QIcon()
+        icon1.addPixmap(QtGui.QPixmap(":/img/rsc/plus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnAdd.setIcon(icon1)
         self.btnAdd.setObjectName("btnAdd")
         self.horizontalLayout.addWidget(self.btnAdd)
         self.btnEdit = QtWidgets.QToolButton(dlgSelectPlaylist)
         self.btnEdit.setEnabled(False)
         self.btnEdit.setMinimumSize(QtCore.QSize(28, 28))
         self.btnEdit.setMaximumSize(QtCore.QSize(28, 28))
+        icon2 = QtGui.QIcon()
+        icon2.addPixmap(QtGui.QPixmap(":/img/rsc/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnEdit.setIcon(icon2)
         self.btnEdit.setObjectName("btnEdit")
         self.horizontalLayout.addWidget(self.btnEdit)
         self.btnDuplicate = QtWidgets.QToolButton(dlgSelectPlaylist)
         self.btnDuplicate.setEnabled(False)
         self.btnDuplicate.setMinimumSize(QtCore.QSize(28, 28))
         self.btnDuplicate.setMaximumSize(QtCore.QSize(28, 28))
+        icon3 = QtGui.QIcon()
+        icon3.addPixmap(QtGui.QPixmap(":/img/rsc/duplicate.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnDuplicate.setIcon(icon3)
         self.btnDuplicate.setObjectName("btnDuplicate")
         self.horizontalLayout.addWidget(self.btnDuplicate)
         self.btnDelete = QtWidgets.QToolButton(dlgSelectPlaylist)
         self.btnDelete.setEnabled(False)
         self.btnDelete.setMinimumSize(QtCore.QSize(28, 28))
         self.btnDelete.setMaximumSize(QtCore.QSize(28, 28))
+        icon4 = QtGui.QIcon()
+        icon4.addPixmap(QtGui.QPixmap(":/img/rsc/minus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnDelete.setIcon(icon4)
         self.btnDelete.setObjectName("btnDelete")
         self.horizontalLayout.addWidget(self.btnDelete)
         self.verticalLayout.addLayout(self.horizontalLayout)
@@ -96,3 +112,4 @@ class Ui_dlgSelectPlaylist(object):
         self.tree.headerItem().setText(1, _translate("dlgSelectPlaylist", "2"))
         self.btnSelect.setToolTip(_translate("dlgSelectPlaylist", "Choisir cette séance"))
         self.btnSelect.setText(_translate("dlgSelectPlaylist", "OK"))
+from . import rsc_rc

+ 1 - 0
ui/qt/gen.cmd

@@ -2,4 +2,5 @@ pyrcc5 rsc.qrc -o rsc_rc.py
 pyuic5 main.ui -o main_ui.py --from-imports
 pyuic5 dlg_select_playlist.ui -o dlg_select_playlist_ui.py --from-imports
 pyuic5 dlg_playlist.ui -o dlg_playlist_ui.py --from-imports
+pyuic5 widgets/frame_notes.ui -o widgets/frame_notes_ui.py --import-from=..
 pyuic5 widgets/vlcframe.ui -o widgets/vlcframe_ui.py --import-from=..

+ 239 - 32
ui/qt/main.ui

@@ -6,13 +6,13 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1042</width>
+    <width>1029</width>
     <height>783</height>
    </rect>
   </property>
   <property name="minimumSize">
    <size>
-    <width>859</width>
+    <width>1029</width>
     <height>618</height>
    </size>
   </property>
@@ -102,28 +102,14 @@
            <layout class="QHBoxLayout" name="horizontalLayout_2">
             <item>
              <layout class="QVBoxLayout" name="verticalLayout_3">
-              <property name="topMargin">
-               <number>0</number>
+              <property name="bottomMargin">
+               <number>20</number>
               </property>
               <item>
                <layout class="QHBoxLayout" name="horizontalLayout_15">
                 <property name="topMargin">
                  <number>0</number>
                 </property>
-                <item>
-                 <widget class="QLabel" name="sessionLblTitle">
-                  <property name="text">
-                   <string>Ma séance</string>
-                  </property>
-                 </widget>
-                </item>
-                <item>
-                 <widget class="QLabel" name="sessionLblDate">
-                  <property name="text">
-                   <string>dd/mm/yyyy</string>
-                  </property>
-                 </widget>
-                </item>
                 <item>
                  <widget class="QToolButton" name="sessionBtnChange">
                   <property name="minimumSize">
@@ -132,21 +118,40 @@
                     <height>28</height>
                    </size>
                   </property>
+                  <property name="toolTip">
+                   <string>Sélectionner une séance</string>
+                  </property>
                   <property name="text">
                    <string/>
                   </property>
                   <property name="icon">
                    <iconset resource="rsc.qrc">
-                    <normaloff>:/img/rsc/swap.png</normaloff>:/img/rsc/swap.png</iconset>
+                    <normaloff>:/img/rsc/folder.png</normaloff>:/img/rsc/folder.png</iconset>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="sessionLblTitle">
+                  <property name="font">
+                   <font>
+                    <weight>75</weight>
+                    <bold>true</bold>
+                   </font>
+                  </property>
+                  <property name="text">
+                   <string>(Séléctionnez une séance)</string>
                   </property>
                  </widget>
                 </item>
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" name="horizontalLayout_14">
+               <layout class="QHBoxLayout" name="horizontalLayout_14" stretch="1,1">
                 <item>
-                 <widget class="QTableWidget" name="sessionPlaylist">
+                 <widget class="PlaylistTable" name="sessionPlaylist">
+                  <property name="enabled">
+                   <bool>false</bool>
+                  </property>
                   <property name="editTriggers">
                    <set>QAbstractItemView::NoEditTriggers</set>
                   </property>
@@ -156,6 +161,15 @@
                   <property name="selectionBehavior">
                    <enum>QAbstractItemView::SelectRows</enum>
                   </property>
+                  <property name="iconSize">
+                   <size>
+                    <width>8</width>
+                    <height>8</height>
+                   </size>
+                  </property>
+                  <attribute name="horizontalHeaderVisible">
+                   <bool>false</bool>
+                  </attribute>
                   <attribute name="horizontalHeaderStretchLastSection">
                    <bool>true</bool>
                   </attribute>
@@ -169,7 +183,7 @@
                   </column>
                   <column>
                    <property name="text">
-                    <string>Ordre</string>
+                    <string>track_id</string>
                    </property>
                   </column>
                   <column>
@@ -180,7 +194,57 @@
                  </widget>
                 </item>
                 <item>
-                 <widget class="QTextBrowser" name="sessionNotes"/>
+                 <widget class="FrameNotes" name="frameNotes">
+                  <property name="frameShape">
+                   <enum>QFrame::StyledPanel</enum>
+                  </property>
+                  <property name="frameShadow">
+                   <enum>QFrame::Raised</enum>
+                  </property>
+                  <layout class="QHBoxLayout" name="horizontalLayout_20"/>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_17">
+                <item>
+                 <widget class="QPushButton" name="btnSessionStart">
+                  <property name="minimumSize">
+                   <size>
+                    <width>180</width>
+                    <height>28</height>
+                   </size>
+                  </property>
+                  <property name="maximumSize">
+                   <size>
+                    <width>16777215</width>
+                    <height>28</height>
+                   </size>
+                  </property>
+                  <property name="text">
+                   <string>Lancer la séance</string>
+                  </property>
+                  <property name="autoDefault">
+                   <bool>true</bool>
+                  </property>
+                  <property name="default">
+                   <bool>true</bool>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <spacer name="horizontalSpacer_2">
+                  <property name="orientation">
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" stdset="0">
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
                 </item>
                </layout>
               </item>
@@ -220,6 +284,12 @@
                  <layout class="QHBoxLayout" name="horizontalLayout_12">
                   <item>
                    <widget class="QLabel" name="label_3">
+                    <property name="minimumSize">
+                     <size>
+                      <width>284</width>
+                      <height>0</height>
+                     </size>
+                    </property>
                     <property name="font">
                      <font>
                       <family>Verdana</family>
@@ -301,7 +371,7 @@
                </layout>
               </item>
               <item>
-               <layout class="QVBoxLayout" name="verticalLayout_5" stretch="0,0,0,0,0,0">
+               <layout class="QVBoxLayout" name="verticalLayout_5" stretch="0,0,0,0,0,0,0,0">
                 <property name="leftMargin">
                  <number>10</number>
                 </property>
@@ -374,6 +444,12 @@
                        <layout class="QHBoxLayout" name="horizontalLayout_13">
                         <item>
                          <widget class="QLabel" name="label_11">
+                          <property name="minimumSize">
+                           <size>
+                            <width>182</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="font">
                            <font>
                             <family>Verdana</family>
@@ -413,14 +489,17 @@
                       </item>
                       <item>
                        <layout class="QHBoxLayout" name="horizontalLayout_8">
-                        <property name="spacing">
-                         <number>0</number>
-                        </property>
                         <property name="sizeConstraint">
                          <enum>QLayout::SetMinimumSize</enum>
                         </property>
                         <item>
                          <widget class="QLabel" name="label_4">
+                          <property name="minimumSize">
+                           <size>
+                            <width>80</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="maximumSize">
                            <size>
                             <width>80</width>
@@ -434,9 +513,18 @@
                         </item>
                         <item>
                          <widget class="QLabel" name="explorerLblTrackTitle">
+                          <property name="minimumSize">
+                           <size>
+                            <width>130</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="text">
                            <string/>
                           </property>
+                          <property name="wordWrap">
+                           <bool>true</bool>
+                          </property>
                          </widget>
                         </item>
                        </layout>
@@ -445,6 +533,12 @@
                        <layout class="QHBoxLayout" name="horizontalLayout_9">
                         <item>
                          <widget class="QLabel" name="label_6">
+                          <property name="minimumSize">
+                           <size>
+                            <width>80</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="maximumSize">
                            <size>
                             <width>80</width>
@@ -458,9 +552,18 @@
                         </item>
                         <item>
                          <widget class="QLabel" name="explorerLblTrackArtist">
+                          <property name="minimumSize">
+                           <size>
+                            <width>130</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="text">
                            <string/>
                           </property>
+                          <property name="wordWrap">
+                           <bool>true</bool>
+                          </property>
                          </widget>
                         </item>
                        </layout>
@@ -469,6 +572,12 @@
                        <layout class="QHBoxLayout" name="horizontalLayout_10">
                         <item>
                          <widget class="QLabel" name="label_7">
+                          <property name="minimumSize">
+                           <size>
+                            <width>80</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="maximumSize">
                            <size>
                             <width>80</width>
@@ -482,9 +591,18 @@
                         </item>
                         <item>
                          <widget class="QLabel" name="explorerLblTrackAlbum">
+                          <property name="minimumSize">
+                           <size>
+                            <width>130</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="text">
                            <string/>
                           </property>
+                          <property name="wordWrap">
+                           <bool>true</bool>
+                          </property>
                          </widget>
                         </item>
                        </layout>
@@ -493,6 +611,12 @@
                        <layout class="QHBoxLayout" name="horizontalLayout_11">
                         <item>
                          <widget class="QLabel" name="label_8">
+                          <property name="minimumSize">
+                           <size>
+                            <width>80</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="maximumSize">
                            <size>
                             <width>80</width>
@@ -506,9 +630,18 @@
                         </item>
                         <item>
                          <widget class="QLabel" name="explorerLblTrackNumber">
+                          <property name="minimumSize">
+                           <size>
+                            <width>130</width>
+                            <height>0</height>
+                           </size>
+                          </property>
                           <property name="text">
                            <string/>
                           </property>
+                          <property name="wordWrap">
+                           <bool>true</bool>
+                          </property>
                          </widget>
                         </item>
                        </layout>
@@ -550,16 +683,25 @@
                        </widget>
                       </item>
                       <item>
-                       <widget class="QTextBrowser" name="explorerTrackNotepad">
+                       <widget class="QTextEdit" name="explorerTrackNotepad">
                         <property name="minimumSize">
                          <size>
-                          <width>0</width>
+                          <width>218</width>
                           <height>120</height>
                          </size>
                         </property>
+                        <property name="autoFormatting">
+                         <set>QTextEdit::AutoAll</set>
+                        </property>
                         <property name="undoRedoEnabled">
                          <bool>true</bool>
                         </property>
+                        <property name="readOnly">
+                         <bool>false</bool>
+                        </property>
+                        <property name="placeholderText">
+                         <string>Mes notes sur ce morceau...</string>
+                        </property>
                        </widget>
                       </item>
                      </layout>
@@ -568,15 +710,47 @@
                   </widget>
                  </widget>
                 </item>
+                <item>
+                 <spacer name="verticalSpacer_5">
+                  <property name="orientation">
+                   <enum>Qt::Vertical</enum>
+                  </property>
+                  <property name="sizeType">
+                   <enum>QSizePolicy::Fixed</enum>
+                  </property>
+                  <property name="sizeHint" stdset="0">
+                   <size>
+                    <width>20</width>
+                    <height>10</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+                <item>
+                 <widget class="Line" name="line_2">
+                  <property name="minimumSize">
+                   <size>
+                    <width>220</width>
+                    <height>0</height>
+                   </size>
+                  </property>
+                  <property name="orientation">
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                 </widget>
+                </item>
                 <item>
                  <spacer name="verticalSpacer_4">
                   <property name="orientation">
                    <enum>Qt::Vertical</enum>
                   </property>
+                  <property name="sizeType">
+                   <enum>QSizePolicy::Fixed</enum>
+                  </property>
                   <property name="sizeHint" stdset="0">
                    <size>
                     <width>20</width>
-                    <height>40</height>
+                    <height>30</height>
                    </size>
                   </property>
                  </spacer>
@@ -586,6 +760,12 @@
                   <property name="enabled">
                    <bool>false</bool>
                   </property>
+                  <property name="minimumSize">
+                   <size>
+                    <width>220</width>
+                    <height>0</height>
+                   </size>
+                  </property>
                   <property name="text">
                    <string>  Lire le morceau sélectionné</string>
                   </property>
@@ -606,6 +786,12 @@
                   <property name="enabled">
                    <bool>false</bool>
                   </property>
+                  <property name="minimumSize">
+                   <size>
+                    <width>220</width>
+                    <height>0</height>
+                   </size>
+                  </property>
                   <property name="text">
                    <string>  Ajouter à la playlist</string>
                   </property>
@@ -620,6 +806,12 @@
                   <property name="enabled">
                    <bool>false</bool>
                   </property>
+                  <property name="minimumSize">
+                   <size>
+                    <width>220</width>
+                    <height>0</height>
+                   </size>
+                  </property>
                   <property name="text">
                    <string>  Retirer de la playlist</string>
                   </property>
@@ -634,10 +826,13 @@
                   <property name="orientation">
                    <enum>Qt::Vertical</enum>
                   </property>
+                  <property name="sizeType">
+                   <enum>QSizePolicy::Fixed</enum>
+                  </property>
                   <property name="sizeHint" stdset="0">
                    <size>
                     <width>20</width>
-                    <height>40</height>
+                    <height>30</height>
                    </size>
                   </property>
                  </spacer>
@@ -669,7 +864,7 @@
                    <number>6</number>
                   </property>
                   <item>
-                   <widget class="QLabel" name="lblPlaylistName">
+                   <widget class="QLabel" name="explorerLblPlaylistTitle">
                     <property name="text">
                      <string>(pas de séance en cours)</string>
                     </property>
@@ -735,6 +930,12 @@
                   <property name="selectionBehavior">
                    <enum>QAbstractItemView::SelectRows</enum>
                   </property>
+                  <property name="iconSize">
+                   <size>
+                    <width>8</width>
+                    <height>8</height>
+                   </size>
+                  </property>
                   <attribute name="horizontalHeaderVisible">
                    <bool>false</bool>
                   </attribute>
@@ -990,6 +1191,12 @@
    <extends>QTableWidget</extends>
    <header location="global">.widgets.playlist_table.h</header>
   </customwidget>
+  <customwidget>
+   <class>FrameNotes</class>
+   <extends>QFrame</extends>
+   <header location="global">.widgets.frame_notes.h</header>
+   <container>1</container>
+  </customwidget>
  </customwidgets>
  <resources>
   <include location="rsc.qrc"/>

+ 96 - 46
ui/qt/main_ui.py

@@ -2,7 +2,7 @@
 
 # Form implementation generated from reading ui file 'main.ui'
 #
-# Created by: PyQt5 UI code generator 5.15.4
+# Created by: PyQt5 UI code generator 5.15.2
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic5 is
 # run again.  Do not edit this file unless you know what you are doing.
@@ -14,8 +14,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets
 class Ui_mainWindow(object):
     def setupUi(self, mainWindow):
         mainWindow.setObjectName("mainWindow")
-        mainWindow.resize(1042, 783)
-        mainWindow.setMinimumSize(QtCore.QSize(859, 618))
+        mainWindow.resize(1029, 783)
+        mainWindow.setMinimumSize(QtCore.QSize(1029, 618))
         font = QtGui.QFont()
         font.setFamily("Verdana")
         font.setPointSize(8)
@@ -56,32 +56,35 @@ class Ui_mainWindow(object):
         self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.page_1)
         self.horizontalLayout_2.setObjectName("horizontalLayout_2")
         self.verticalLayout_3 = QtWidgets.QVBoxLayout()
-        self.verticalLayout_3.setContentsMargins(-1, 0, -1, -1)
+        self.verticalLayout_3.setContentsMargins(-1, -1, -1, 20)
         self.verticalLayout_3.setObjectName("verticalLayout_3")
         self.horizontalLayout_15 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_15.setContentsMargins(-1, 0, -1, -1)
         self.horizontalLayout_15.setObjectName("horizontalLayout_15")
-        self.sessionLblTitle = QtWidgets.QLabel(self.page_1)
-        self.sessionLblTitle.setObjectName("sessionLblTitle")
-        self.horizontalLayout_15.addWidget(self.sessionLblTitle)
-        self.sessionLblDate = QtWidgets.QLabel(self.page_1)
-        self.sessionLblDate.setObjectName("sessionLblDate")
-        self.horizontalLayout_15.addWidget(self.sessionLblDate)
         self.sessionBtnChange = QtWidgets.QToolButton(self.page_1)
         self.sessionBtnChange.setMinimumSize(QtCore.QSize(28, 28))
         self.sessionBtnChange.setText("")
         icon1 = QtGui.QIcon()
-        icon1.addPixmap(QtGui.QPixmap(":/img/rsc/swap.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        icon1.addPixmap(QtGui.QPixmap(":/img/rsc/folder.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
         self.sessionBtnChange.setIcon(icon1)
         self.sessionBtnChange.setObjectName("sessionBtnChange")
         self.horizontalLayout_15.addWidget(self.sessionBtnChange)
+        self.sessionLblTitle = QtWidgets.QLabel(self.page_1)
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+        self.sessionLblTitle.setFont(font)
+        self.sessionLblTitle.setObjectName("sessionLblTitle")
+        self.horizontalLayout_15.addWidget(self.sessionLblTitle)
         self.verticalLayout_3.addLayout(self.horizontalLayout_15)
         self.horizontalLayout_14 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_14.setObjectName("horizontalLayout_14")
-        self.sessionPlaylist = QtWidgets.QTableWidget(self.page_1)
+        self.sessionPlaylist = PlaylistTable(self.page_1)
+        self.sessionPlaylist.setEnabled(False)
         self.sessionPlaylist.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
         self.sessionPlaylist.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
         self.sessionPlaylist.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+        self.sessionPlaylist.setIconSize(QtCore.QSize(8, 8))
         self.sessionPlaylist.setObjectName("sessionPlaylist")
         self.sessionPlaylist.setColumnCount(3)
         self.sessionPlaylist.setRowCount(0)
@@ -91,13 +94,32 @@ class Ui_mainWindow(object):
         self.sessionPlaylist.setHorizontalHeaderItem(1, item)
         item = QtWidgets.QTableWidgetItem()
         self.sessionPlaylist.setHorizontalHeaderItem(2, item)
+        self.sessionPlaylist.horizontalHeader().setVisible(False)
         self.sessionPlaylist.horizontalHeader().setStretchLastSection(True)
         self.sessionPlaylist.verticalHeader().setVisible(False)
         self.horizontalLayout_14.addWidget(self.sessionPlaylist)
-        self.sessionNotes = QtWidgets.QTextBrowser(self.page_1)
-        self.sessionNotes.setObjectName("sessionNotes")
-        self.horizontalLayout_14.addWidget(self.sessionNotes)
+        self.frameNotes = FrameNotes(self.page_1)
+        self.frameNotes.setFrameShape(QtWidgets.QFrame.StyledPanel)
+        self.frameNotes.setFrameShadow(QtWidgets.QFrame.Raised)
+        self.frameNotes.setObjectName("frameNotes")
+        self.horizontalLayout_20 = QtWidgets.QHBoxLayout(self.frameNotes)
+        self.horizontalLayout_20.setObjectName("horizontalLayout_20")
+        self.horizontalLayout_14.addWidget(self.frameNotes)
+        self.horizontalLayout_14.setStretch(0, 1)
+        self.horizontalLayout_14.setStretch(1, 1)
         self.verticalLayout_3.addLayout(self.horizontalLayout_14)
+        self.horizontalLayout_17 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_17.setObjectName("horizontalLayout_17")
+        self.btnSessionStart = QtWidgets.QPushButton(self.page_1)
+        self.btnSessionStart.setMinimumSize(QtCore.QSize(180, 28))
+        self.btnSessionStart.setMaximumSize(QtCore.QSize(16777215, 28))
+        self.btnSessionStart.setAutoDefault(True)
+        self.btnSessionStart.setDefault(True)
+        self.btnSessionStart.setObjectName("btnSessionStart")
+        self.horizontalLayout_17.addWidget(self.btnSessionStart)
+        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_17.addItem(spacerItem)
+        self.verticalLayout_3.addLayout(self.horizontalLayout_17)
         self.horizontalLayout_2.addLayout(self.verticalLayout_3)
         self.stack.addWidget(self.page_1)
         self.page_2 = QtWidgets.QWidget()
@@ -121,6 +143,7 @@ class Ui_mainWindow(object):
         self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_12.setObjectName("horizontalLayout_12")
         self.label_3 = QtWidgets.QLabel(self.page_3)
+        self.label_3.setMinimumSize(QtCore.QSize(284, 0))
         font = QtGui.QFont()
         font.setFamily("Verdana")
         font.setPointSize(8)
@@ -174,11 +197,12 @@ class Ui_mainWindow(object):
         self.horizontalLayout_16.setObjectName("horizontalLayout_16")
         self.verticalLayout_9 = QtWidgets.QVBoxLayout()
         self.verticalLayout_9.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
-        self.verticalLayout_9.setSpacing(0)
+        self.verticalLayout_9.setSpacing(4)
         self.verticalLayout_9.setObjectName("verticalLayout_9")
         self.horizontalLayout_13 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_13.setObjectName("horizontalLayout_13")
         self.label_11 = QtWidgets.QLabel(self.page_6)
+        self.label_11.setMinimumSize(QtCore.QSize(182, 0))
         font = QtGui.QFont()
         font.setFamily("Verdana")
         font.setPointSize(8)
@@ -198,52 +222,63 @@ class Ui_mainWindow(object):
         self.verticalLayout_9.addLayout(self.horizontalLayout_13)
         self.horizontalLayout_8 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_8.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
-        self.horizontalLayout_8.setSpacing(0)
         self.horizontalLayout_8.setObjectName("horizontalLayout_8")
         self.label_4 = QtWidgets.QLabel(self.page_6)
+        self.label_4.setMinimumSize(QtCore.QSize(80, 0))
         self.label_4.setMaximumSize(QtCore.QSize(80, 16777215))
         self.label_4.setObjectName("label_4")
         self.horizontalLayout_8.addWidget(self.label_4)
         self.explorerLblTrackTitle = QtWidgets.QLabel(self.page_6)
+        self.explorerLblTrackTitle.setMinimumSize(QtCore.QSize(130, 0))
         self.explorerLblTrackTitle.setText("")
+        self.explorerLblTrackTitle.setWordWrap(True)
         self.explorerLblTrackTitle.setObjectName("explorerLblTrackTitle")
         self.horizontalLayout_8.addWidget(self.explorerLblTrackTitle)
         self.verticalLayout_9.addLayout(self.horizontalLayout_8)
         self.horizontalLayout_9 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_9.setObjectName("horizontalLayout_9")
         self.label_6 = QtWidgets.QLabel(self.page_6)
+        self.label_6.setMinimumSize(QtCore.QSize(80, 0))
         self.label_6.setMaximumSize(QtCore.QSize(80, 16777215))
         self.label_6.setObjectName("label_6")
         self.horizontalLayout_9.addWidget(self.label_6)
         self.explorerLblTrackArtist = QtWidgets.QLabel(self.page_6)
+        self.explorerLblTrackArtist.setMinimumSize(QtCore.QSize(130, 0))
         self.explorerLblTrackArtist.setText("")
+        self.explorerLblTrackArtist.setWordWrap(True)
         self.explorerLblTrackArtist.setObjectName("explorerLblTrackArtist")
         self.horizontalLayout_9.addWidget(self.explorerLblTrackArtist)
         self.verticalLayout_9.addLayout(self.horizontalLayout_9)
         self.horizontalLayout_10 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_10.setObjectName("horizontalLayout_10")
         self.label_7 = QtWidgets.QLabel(self.page_6)
+        self.label_7.setMinimumSize(QtCore.QSize(80, 0))
         self.label_7.setMaximumSize(QtCore.QSize(80, 16777215))
         self.label_7.setObjectName("label_7")
         self.horizontalLayout_10.addWidget(self.label_7)
         self.explorerLblTrackAlbum = QtWidgets.QLabel(self.page_6)
+        self.explorerLblTrackAlbum.setMinimumSize(QtCore.QSize(130, 0))
         self.explorerLblTrackAlbum.setText("")
+        self.explorerLblTrackAlbum.setWordWrap(True)
         self.explorerLblTrackAlbum.setObjectName("explorerLblTrackAlbum")
         self.horizontalLayout_10.addWidget(self.explorerLblTrackAlbum)
         self.verticalLayout_9.addLayout(self.horizontalLayout_10)
         self.horizontalLayout_11 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_11.setObjectName("horizontalLayout_11")
         self.label_8 = QtWidgets.QLabel(self.page_6)
+        self.label_8.setMinimumSize(QtCore.QSize(80, 0))
         self.label_8.setMaximumSize(QtCore.QSize(80, 16777215))
         self.label_8.setObjectName("label_8")
         self.horizontalLayout_11.addWidget(self.label_8)
         self.explorerLblTrackNumber = QtWidgets.QLabel(self.page_6)
+        self.explorerLblTrackNumber.setMinimumSize(QtCore.QSize(130, 0))
         self.explorerLblTrackNumber.setText("")
+        self.explorerLblTrackNumber.setWordWrap(True)
         self.explorerLblTrackNumber.setObjectName("explorerLblTrackNumber")
         self.horizontalLayout_11.addWidget(self.explorerLblTrackNumber)
         self.verticalLayout_9.addLayout(self.horizontalLayout_11)
-        spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
-        self.verticalLayout_9.addItem(spacerItem)
+        spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        self.verticalLayout_9.addItem(spacerItem1)
         self.label_10 = QtWidgets.QLabel(self.page_6)
         self.label_10.setMaximumSize(QtCore.QSize(361, 14))
         font = QtGui.QFont()
@@ -253,18 +288,29 @@ class Ui_mainWindow(object):
         self.label_10.setFont(font)
         self.label_10.setObjectName("label_10")
         self.verticalLayout_9.addWidget(self.label_10)
-        self.explorerTrackNotepad = QtWidgets.QTextBrowser(self.page_6)
-        self.explorerTrackNotepad.setMinimumSize(QtCore.QSize(0, 120))
+        self.explorerTrackNotepad = QtWidgets.QTextEdit(self.page_6)
+        self.explorerTrackNotepad.setMinimumSize(QtCore.QSize(218, 120))
+        self.explorerTrackNotepad.setAutoFormatting(QtWidgets.QTextEdit.AutoAll)
         self.explorerTrackNotepad.setUndoRedoEnabled(True)
+        self.explorerTrackNotepad.setReadOnly(False)
         self.explorerTrackNotepad.setObjectName("explorerTrackNotepad")
         self.verticalLayout_9.addWidget(self.explorerTrackNotepad)
         self.horizontalLayout_16.addLayout(self.verticalLayout_9)
         self.explorerTrackMetaStack.addWidget(self.page_6)
         self.verticalLayout_5.addWidget(self.explorerTrackMetaStack)
-        spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
-        self.verticalLayout_5.addItem(spacerItem1)
+        spacerItem2 = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        self.verticalLayout_5.addItem(spacerItem2)
+        self.line_2 = QtWidgets.QFrame(self.page_3)
+        self.line_2.setMinimumSize(QtCore.QSize(220, 0))
+        self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+        self.line_2.setObjectName("line_2")
+        self.verticalLayout_5.addWidget(self.line_2)
+        spacerItem3 = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        self.verticalLayout_5.addItem(spacerItem3)
         self.explorerTrackPlay = QtWidgets.QPushButton(self.page_3)
         self.explorerTrackPlay.setEnabled(False)
+        self.explorerTrackPlay.setMinimumSize(QtCore.QSize(220, 0))
         icon4 = QtGui.QIcon()
         icon4.addPixmap(QtGui.QPixmap(":/img/rsc/play.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
         self.explorerTrackPlay.setIcon(icon4)
@@ -273,6 +319,7 @@ class Ui_mainWindow(object):
         self.verticalLayout_5.addWidget(self.explorerTrackPlay)
         self.explorerAddToPlaylist = QtWidgets.QPushButton(self.page_3)
         self.explorerAddToPlaylist.setEnabled(False)
+        self.explorerAddToPlaylist.setMinimumSize(QtCore.QSize(220, 0))
         icon5 = QtGui.QIcon()
         icon5.addPixmap(QtGui.QPixmap(":/img/rsc/double-right-arrows-symbol.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
         self.explorerAddToPlaylist.setIcon(icon5)
@@ -280,13 +327,14 @@ class Ui_mainWindow(object):
         self.verticalLayout_5.addWidget(self.explorerAddToPlaylist)
         self.explorerRemoveFromPlaylist = QtWidgets.QPushButton(self.page_3)
         self.explorerRemoveFromPlaylist.setEnabled(False)
+        self.explorerRemoveFromPlaylist.setMinimumSize(QtCore.QSize(220, 0))
         icon6 = QtGui.QIcon()
         icon6.addPixmap(QtGui.QPixmap(":/img/rsc/double-left-arrows-symbol.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
         self.explorerRemoveFromPlaylist.setIcon(icon6)
         self.explorerRemoveFromPlaylist.setObjectName("explorerRemoveFromPlaylist")
         self.verticalLayout_5.addWidget(self.explorerRemoveFromPlaylist)
-        spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
-        self.verticalLayout_5.addItem(spacerItem2)
+        spacerItem4 = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        self.verticalLayout_5.addItem(spacerItem4)
         self.horizontalLayout_6.addLayout(self.verticalLayout_5)
         self.verticalLayout_12 = QtWidgets.QVBoxLayout()
         self.verticalLayout_12.setObjectName("verticalLayout_12")
@@ -301,15 +349,13 @@ class Ui_mainWindow(object):
         self.horizontalLayout_18 = QtWidgets.QHBoxLayout()
         self.horizontalLayout_18.setContentsMargins(6, -1, 6, -1)
         self.horizontalLayout_18.setObjectName("horizontalLayout_18")
-        self.lblPlaylistName = QtWidgets.QLabel(self.page_3)
-        self.lblPlaylistName.setObjectName("lblPlaylistName")
-        self.horizontalLayout_18.addWidget(self.lblPlaylistName)
+        self.explorerLblPlaylistTitle = QtWidgets.QLabel(self.page_3)
+        self.explorerLblPlaylistTitle.setObjectName("explorerLblPlaylistTitle")
+        self.horizontalLayout_18.addWidget(self.explorerLblPlaylistTitle)
         self.btnSelectPlaylist = QtWidgets.QToolButton(self.page_3)
         self.btnSelectPlaylist.setMinimumSize(QtCore.QSize(28, 28))
         self.btnSelectPlaylist.setMaximumSize(QtCore.QSize(28, 28))
-        icon7 = QtGui.QIcon()
-        icon7.addPixmap(QtGui.QPixmap(":/img/rsc/folder.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
-        self.btnSelectPlaylist.setIcon(icon7)
+        self.btnSelectPlaylist.setIcon(icon1)
         self.btnSelectPlaylist.setObjectName("btnSelectPlaylist")
         self.horizontalLayout_18.addWidget(self.btnSelectPlaylist)
         self.verticalLayout_12.addLayout(self.horizontalLayout_18)
@@ -324,6 +370,7 @@ class Ui_mainWindow(object):
         self.explorerPlaylist.setAlternatingRowColors(True)
         self.explorerPlaylist.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
         self.explorerPlaylist.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+        self.explorerPlaylist.setIconSize(QtCore.QSize(8, 8))
         self.explorerPlaylist.setObjectName("explorerPlaylist")
         self.explorerPlaylist.setColumnCount(3)
         self.explorerPlaylist.setRowCount(0)
@@ -392,18 +439,18 @@ class Ui_mainWindow(object):
         self.horizontalLayout_7.setObjectName("horizontalLayout_7")
         self.musicFoldersRemoveButton = QtWidgets.QPushButton(self.page_5)
         self.musicFoldersRemoveButton.setMinimumSize(QtCore.QSize(0, 32))
-        icon8 = QtGui.QIcon()
-        icon8.addPixmap(QtGui.QPixmap(":/img/rsc/delete.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
-        self.musicFoldersRemoveButton.setIcon(icon8)
+        icon7 = QtGui.QIcon()
+        icon7.addPixmap(QtGui.QPixmap(":/img/rsc/delete.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.musicFoldersRemoveButton.setIcon(icon7)
         self.musicFoldersRemoveButton.setObjectName("musicFoldersRemoveButton")
         self.horizontalLayout_7.addWidget(self.musicFoldersRemoveButton)
-        spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
-        self.horizontalLayout_7.addItem(spacerItem3)
+        spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_7.addItem(spacerItem5)
         self.musicFoldersAddButton = QtWidgets.QPushButton(self.page_5)
         self.musicFoldersAddButton.setMinimumSize(QtCore.QSize(128, 32))
-        icon9 = QtGui.QIcon()
-        icon9.addPixmap(QtGui.QPixmap(":/img/rsc/plus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
-        self.musicFoldersAddButton.setIcon(icon9)
+        icon8 = QtGui.QIcon()
+        icon8.addPixmap(QtGui.QPixmap(":/img/rsc/plus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.musicFoldersAddButton.setIcon(icon8)
         self.musicFoldersAddButton.setObjectName("musicFoldersAddButton")
         self.horizontalLayout_7.addWidget(self.musicFoldersAddButton)
         self.verticalLayout_6.addLayout(self.horizontalLayout_7)
@@ -412,8 +459,8 @@ class Ui_mainWindow(object):
         self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
         self.line.setObjectName("line")
         self.verticalLayout_6.addWidget(self.line)
-        spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
-        self.verticalLayout_6.addItem(spacerItem4)
+        spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+        self.verticalLayout_6.addItem(spacerItem6)
         self.horizontalLayout_5.addLayout(self.verticalLayout_6)
         self.stack.addWidget(self.page_5)
         self.verticalLayout_7.addWidget(self.stack)
@@ -436,20 +483,21 @@ class Ui_mainWindow(object):
         self.retranslateUi(mainWindow)
         self.menu.setCurrentRow(-1)
         self.stack.setCurrentIndex(2)
-        self.explorerTrackMetaStack.setCurrentIndex(0)
+        self.explorerTrackMetaStack.setCurrentIndex(1)
         QtCore.QMetaObject.connectSlotsByName(mainWindow)
 
     def retranslateUi(self, mainWindow):
         _translate = QtCore.QCoreApplication.translate
         mainWindow.setWindowTitle(_translate("mainWindow", "Mew~"))
-        self.sessionLblTitle.setText(_translate("mainWindow", "Ma séance"))
-        self.sessionLblDate.setText(_translate("mainWindow", "dd/mm/yyyy"))
+        self.sessionBtnChange.setToolTip(_translate("mainWindow", "Sélectionner une séance"))
+        self.sessionLblTitle.setText(_translate("mainWindow", "(Séléctionnez une séance)"))
         item = self.sessionPlaylist.horizontalHeaderItem(0)
         item.setText(_translate("mainWindow", "id"))
         item = self.sessionPlaylist.horizontalHeaderItem(1)
-        item.setText(_translate("mainWindow", "Ordre"))
+        item.setText(_translate("mainWindow", "track_id"))
         item = self.sessionPlaylist.horizontalHeaderItem(2)
         item.setText(_translate("mainWindow", "Titre"))
+        self.btnSessionStart.setText(_translate("mainWindow", "Lancer la séance"))
         self.label_3.setText(_translate("mainWindow", "Mes musiques"))
         self.btnExplorerRefresh.setToolTip(_translate("mainWindow", "Rafraichir la liste"))
         self.explorerTable.headerItem().setText(1, _translate("mainWindow", "2"))
@@ -460,11 +508,12 @@ class Ui_mainWindow(object):
         self.label_7.setText(_translate("mainWindow", "Album"))
         self.label_8.setText(_translate("mainWindow", "N°"))
         self.label_10.setText(_translate("mainWindow", "Notes"))
+        self.explorerTrackNotepad.setPlaceholderText(_translate("mainWindow", "Mes notes sur ce morceau..."))
         self.explorerTrackPlay.setText(_translate("mainWindow", "  Lire le morceau sélectionné"))
         self.explorerAddToPlaylist.setText(_translate("mainWindow", "  Ajouter à la playlist"))
         self.explorerRemoveFromPlaylist.setText(_translate("mainWindow", "  Retirer de la playlist"))
         self.label_9.setText(_translate("mainWindow", "Ma séance: "))
-        self.lblPlaylistName.setText(_translate("mainWindow", "(pas de séance en cours)"))
+        self.explorerLblPlaylistTitle.setText(_translate("mainWindow", "(pas de séance en cours)"))
         self.btnSelectPlaylist.setText(_translate("mainWindow", "..."))
         item = self.explorerPlaylist.horizontalHeaderItem(0)
         item.setText(_translate("mainWindow", "id"))
@@ -483,6 +532,7 @@ class Ui_mainWindow(object):
         self.musicFoldersRemoveButton.setText(_translate("mainWindow", "Supprimer"))
         self.musicFoldersAddButton.setText(_translate("mainWindow", "Ajouter"))
 from .widgets.explorertable import ExplorerTable
+from .widgets.frame_notes import FrameNotes
 from .widgets.playlist_table import PlaylistTable
 from .widgets.vlcframe import VlcFrame
 from . import rsc_rc

+ 11 - 0
ui/qt/rsc.qrc

@@ -1,5 +1,16 @@
 <RCC>
   <qresource prefix="/img">
+    <file>rsc/undo-arrow.png</file>
+    <file>rsc/redo-arrow.png</file>
+    <file>rsc/underline.png</file>
+    <file>rsc/italic-text.png</file>
+    <file>rsc/bold.png</file>
+    <file>rsc/repeat.png</file>
+    <file>rsc/shuffle.png</file>
+    <file>rsc/rewind.png</file>
+    <file>rsc/fast-forward.png</file>
+    <file>rsc/duplicate.png</file>
+    <file>rsc/create_folder.png</file>
     <file>rsc/double-left-arrows-symbol.png</file>
     <file>rsc/double-right-arrows-symbol.png</file>
     <file>rsc/sound-frecuency.png</file>

BIN
ui/qt/rsc/bold.png


BIN
ui/qt/rsc/create_folder.png


BIN
ui/qt/rsc/duplicate.png


BIN
ui/qt/rsc/fast-forward.png


BIN
ui/qt/rsc/italic-text.png


BIN
ui/qt/rsc/redo-arrow.png


BIN
ui/qt/rsc/repeat.png


BIN
ui/qt/rsc/rewind.png


BIN
ui/qt/rsc/shuffle.png


BIN
ui/qt/rsc/underline.png


BIN
ui/qt/rsc/undo-arrow.png


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1135 - 1896
ui/qt/rsc_rc.py


+ 71 - 0
ui/qt/widgets/frame_notes.py

@@ -0,0 +1,71 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import pyqtSignal
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import QTextEdit
+
+from ui.qt.widgets.frame_notes_ui import Ui_frameNotes
+
+
+class FrameNotes(QtWidgets.QFrame):
+    def __init__(self, parent=None):
+        super().__init__(parent)
+        self.createWidgets()
+
+    def createWidgets(self):
+        self.ui = Ui_frameNotes()
+        self.ui.setupUi(self)
+
+        # Setup the QTextEdit editor configuration
+        self.ui.sessionNotes.setAutoFormatting(QTextEdit.AutoAll)
+        self.ui.sessionNotes.selectionChanged.connect(self.update_format)
+
+        # Initialize default font size.
+        font = QFont('Verdana', 12)
+        self.ui.sessionNotes.setFont(font)
+
+        # We need to repeat the size to init the current format.
+        self.ui.sessionNotes.setFontPointSize(12)
+
+        self.ui.btnBold.toggled.connect(self.toggleBold)
+        self.ui.btnItalic.toggled.connect(self.toggleItalic)
+        self.ui.btnUnderline.toggled.connect(self.toggleUnderlined)
+
+        self.ui.btnUndo.clicked.connect(self.ui.sessionNotes.undo)
+        self.ui.btnRedo.clicked.connect(self.ui.sessionNotes.redo)
+
+        # A list of all format-related widgets/actions, so we can disable/enable signals when updating.
+        self._format_actions = [
+            self.ui.btnBold,
+            self.ui.btnItalic,
+            self.ui.btnUnderline,
+        ]
+
+        self.update_format()
+
+    def toggleBold(self, active):
+        self.ui.sessionNotes.setFontWeight(QFont.Bold if active else QFont.Normal)
+
+    def toggleItalic(self, active):
+        self.ui.sessionNotes.setFontItalic(active)
+
+    def toggleUnderlined(self, active):
+        self.ui.sessionNotes.setFontUnderline(active)
+
+    def _block_signals(self, objects, b):
+        for o in objects:
+            o.blockSignals(b)
+
+    def update_format(self):
+        """
+        Update the font format toolbar/actions when a new text selection is made. This is neccessary to keep
+        toolbars/etc. in sync with the current edit state.
+        :return:
+        """
+        # # Disable signals for all format widgets, so changing values here does not trigger further formatting.
+        self._block_signals(self._format_actions, True)
+
+        self.ui.btnItalic.setChecked(self.ui.sessionNotes.fontItalic())
+        self.ui.btnItalic.setChecked(self.ui.sessionNotes.fontUnderline())
+        self.ui.btnBold.setChecked(self.ui.sessionNotes.fontWeight() == QFont.Bold)
+
+        self._block_signals(self._format_actions, False)

+ 210 - 0
ui/qt/widgets/frame_notes.ui

@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>frameNotes</class>
+ <widget class="QFrame" name="frameNotes">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>748</width>
+    <height>683</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <widget class="QToolButton" name="btnBold">
+         <property name="minimumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/bold.png</normaloff>:/img/rsc/bold.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="btnItalic">
+         <property name="minimumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/italic-text.png</normaloff>:/img/rsc/italic-text.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="btnUnderline">
+         <property name="minimumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/underline.png</normaloff>:/img/rsc/underline.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Fixed</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>10</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QToolButton" name="btnUndo">
+         <property name="minimumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/undo-arrow.png</normaloff>:/img/rsc/undo-arrow.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="btnRedo">
+         <property name="minimumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>28</width>
+           <height>28</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/redo-arrow.png</normaloff>:/img/rsc/redo-arrow.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="WysiwygTextEdit" name="sessionNotes">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="cursor" stdset="0">
+        <cursorShape>IBeamCursor</cursorShape>
+       </property>
+       <property name="autoFormatting">
+        <set>QTextEdit::AutoAll</set>
+       </property>
+       <property name="readOnly">
+        <bool>false</bool>
+       </property>
+       <property name="placeholderText">
+        <string>Mes notes...</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>WysiwygTextEdit</class>
+   <extends>QTextEdit</extends>
+   <header location="global">.wysiwygTextEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../rsc.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 94 - 0
ui/qt/widgets/frame_notes_ui.py

@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'widgets/frame_notes.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again.  Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_frameNotes(object):
+    def setupUi(self, frameNotes):
+        frameNotes.setObjectName("frameNotes")
+        frameNotes.resize(748, 683)
+        self.horizontalLayout = QtWidgets.QHBoxLayout(frameNotes)
+        self.horizontalLayout.setObjectName("horizontalLayout")
+        self.verticalLayout = QtWidgets.QVBoxLayout()
+        self.verticalLayout.setObjectName("verticalLayout")
+        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+        self.btnBold = QtWidgets.QToolButton(frameNotes)
+        self.btnBold.setMinimumSize(QtCore.QSize(28, 28))
+        self.btnBold.setMaximumSize(QtCore.QSize(28, 28))
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap(":/img/rsc/bold.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnBold.setIcon(icon)
+        self.btnBold.setCheckable(True)
+        self.btnBold.setObjectName("btnBold")
+        self.horizontalLayout_2.addWidget(self.btnBold)
+        self.btnItalic = QtWidgets.QToolButton(frameNotes)
+        self.btnItalic.setMinimumSize(QtCore.QSize(28, 28))
+        self.btnItalic.setMaximumSize(QtCore.QSize(28, 28))
+        icon1 = QtGui.QIcon()
+        icon1.addPixmap(QtGui.QPixmap(":/img/rsc/italic-text.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnItalic.setIcon(icon1)
+        self.btnItalic.setCheckable(True)
+        self.btnItalic.setObjectName("btnItalic")
+        self.horizontalLayout_2.addWidget(self.btnItalic)
+        self.btnUnderline = QtWidgets.QToolButton(frameNotes)
+        self.btnUnderline.setMinimumSize(QtCore.QSize(28, 28))
+        self.btnUnderline.setMaximumSize(QtCore.QSize(28, 28))
+        icon2 = QtGui.QIcon()
+        icon2.addPixmap(QtGui.QPixmap(":/img/rsc/underline.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnUnderline.setIcon(icon2)
+        self.btnUnderline.setCheckable(True)
+        self.btnUnderline.setObjectName("btnUnderline")
+        self.horizontalLayout_2.addWidget(self.btnUnderline)
+        spacerItem = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_2.addItem(spacerItem)
+        self.btnUndo = QtWidgets.QToolButton(frameNotes)
+        self.btnUndo.setMinimumSize(QtCore.QSize(28, 28))
+        self.btnUndo.setMaximumSize(QtCore.QSize(28, 28))
+        icon3 = QtGui.QIcon()
+        icon3.addPixmap(QtGui.QPixmap(":/img/rsc/undo-arrow.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnUndo.setIcon(icon3)
+        self.btnUndo.setObjectName("btnUndo")
+        self.horizontalLayout_2.addWidget(self.btnUndo)
+        self.btnRedo = QtWidgets.QToolButton(frameNotes)
+        self.btnRedo.setMinimumSize(QtCore.QSize(28, 28))
+        self.btnRedo.setMaximumSize(QtCore.QSize(28, 28))
+        icon4 = QtGui.QIcon()
+        icon4.addPixmap(QtGui.QPixmap(":/img/rsc/redo-arrow.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnRedo.setIcon(icon4)
+        self.btnRedo.setObjectName("btnRedo")
+        self.horizontalLayout_2.addWidget(self.btnRedo)
+        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_2.addItem(spacerItem1)
+        self.verticalLayout.addLayout(self.horizontalLayout_2)
+        self.sessionNotes = WysiwygTextEdit(frameNotes)
+        self.sessionNotes.setEnabled(True)
+        self.sessionNotes.viewport().setProperty("cursor", QtGui.QCursor(QtCore.Qt.IBeamCursor))
+        self.sessionNotes.setAutoFormatting(QtWidgets.QTextEdit.AutoAll)
+        self.sessionNotes.setReadOnly(False)
+        self.sessionNotes.setObjectName("sessionNotes")
+        self.verticalLayout.addWidget(self.sessionNotes)
+        self.horizontalLayout.addLayout(self.verticalLayout)
+
+        self.retranslateUi(frameNotes)
+        QtCore.QMetaObject.connectSlotsByName(frameNotes)
+
+    def retranslateUi(self, frameNotes):
+        _translate = QtCore.QCoreApplication.translate
+        frameNotes.setWindowTitle(_translate("frameNotes", "Frame"))
+        self.btnBold.setText(_translate("frameNotes", "..."))
+        self.btnItalic.setText(_translate("frameNotes", "..."))
+        self.btnUnderline.setText(_translate("frameNotes", "..."))
+        self.btnUndo.setText(_translate("frameNotes", "..."))
+        self.btnRedo.setText(_translate("frameNotes", "..."))
+        self.sessionNotes.setPlaceholderText(_translate("frameNotes", "Mes notes..."))
+from .wysiwygTextEdit import WysiwygTextEdit
+from .. import rsc_rc

+ 11 - 0
ui/qt/widgets/playlist_table.py

@@ -1,4 +1,5 @@
 from PyQt5.QtCore import pyqtSignal
+from PyQt5.QtGui import QIcon, QPixmap
 from PyQt5.QtWidgets import QTableWidgetItem
 
 from core.repositories import TrackRepository, SessionTrackRepository
@@ -77,3 +78,13 @@ class PlaylistTable(DragDropTableWidget):
         if not session_track_id:
             return None
         return SessionTrackRepository().get_by_id(int(session_track_id))
+
+    def set_is_playing(self, track=None):
+        for i in range(self.rowCount()):
+            track_id = self.item(i, 1).data(0)
+
+            if track is not None and track_id == track.id:
+                icon = QIcon(":/img/rsc/play.png")
+            else:
+                icon = QIcon()
+            self.item(i, 2).setIcon(icon)

+ 74 - 17
ui/qt/widgets/vlcframe.py

@@ -1,5 +1,4 @@
-import platform
-import sys
+from collections import deque
 
 import vlc
 from PyQt5 import QtWidgets, QtGui, QtCore
@@ -11,18 +10,23 @@ from ui.qt.widgets.vlcframe_ui import Ui_VlcFrame
 
 class VlcFrame(QtWidgets.QFrame):
     DEFAULT_VOLUME = 50
-    playStarted = pyqtSignal(object)
-    playEnded = pyqtSignal(object)
+    trackStarted = pyqtSignal(object)
+    trackEnded = pyqtSignal(object)
+    playlistStarted = pyqtSignal(object)
+    playlistEnded = pyqtSignal(object)
 
     def __init__(self, parent=None):
         super().__init__(parent)
 
-        self.currentTrack = None
         self._media = None
         self._is_paused = True
-        self._is_muted = False
+        self.is_muted = False
         self.volume = self.DEFAULT_VOLUME
 
+        self.playlist = None
+        self.tracks = deque()
+        self.current_index = None
+
         # Create a basic vlc instance
         self._instance = vlc.Instance()
 
@@ -48,13 +52,34 @@ class VlcFrame(QtWidgets.QFrame):
 
         self.ui.btnMute.clicked.connect(self.toggle_muted)
 
+        self.ui.btnPrevious.clicked.connect(self.go_to_previous_track)
+        self.ui.btnNext.clicked.connect(self.go_to_next_track)
+
         self.timer = QtCore.QTimer(self)
         self.timer.setInterval(100)
         self.timer.timeout.connect(self.update_ui)
 
         self.setEnabled(False)
 
-    def play_track(self, track):
+    def load_playlist(self, playlist, tracks, start_at=0):
+        self.tracks = deque(tracks)
+        self.current_index = start_at
+        self.playlist = playlist
+
+    def load_track(self, track):
+        self.tracks = deque([track])
+        self.current_index = 0
+        self.playlist = None
+
+    @property
+    def currentTrack(self):
+        if self.current_index is None:
+            return None
+        return self.tracks[self.current_index]
+
+    def play(self):
+
+        track = self.currentTrack
 
         self._media = self._instance.media_new(track.path)
 
@@ -64,14 +89,25 @@ class VlcFrame(QtWidgets.QFrame):
         # Parse the metadata of the file
         self._media.parse()
 
-        # Set the title of the track
-        self.ui.lblTrack.setText(f"{track.artist or '(artiste inconnu)'} - {track.title}")
-
         self.play_pause()
-        self.currentTrack = track
+
         self.setEnabled(True)
 
-        self.playStarted.emit(self.currentTrack)
+        if self.playlist:
+            self.playlistStarted.emit(self.playlist)
+        self.trackStarted.emit(self.currentTrack)
+
+        self.refresh_ui()
+
+    def refresh_ui(self):
+        self.ui.btnPrevious.setEnabled(
+            len(self.tracks) > 0 and self.current_index > 0
+        )
+        self.ui.btnNext.setEnabled(
+            len(self.tracks) > 0 and self.current_index < (len(self.tracks) - 1)
+        )
+        # Set the title of the track
+        self.ui.lblTrack.setText(f"{self.currentTrack.artist or '(artiste inconnu)'} - {self.currentTrack.title}")
 
     def play_pause(self):
         """Toggle play/pause status
@@ -96,7 +132,28 @@ class VlcFrame(QtWidgets.QFrame):
         self._mediaplayer.stop()
         self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/play.png"))
         self.ui.progressionSlider.setSliderPosition(0)
-        self.playEnded.emit(self.currentTrack)
+
+        if self.playlist:
+            self.playlistEnded.emit(self.playlist)
+        self.trackEnded.emit(self.currentTrack)
+
+        if len(self.tracks) > 0 and self.current_index < (len(self.tracks) - 1):
+            self.current_index += 1
+            self.play()
+        else:
+            self.current_index = None
+
+    def go_to_previous_track(self):
+        if not len(self.tracks) > 0 or self.current_index == 0:
+            return
+        self.current_index -= 1
+        self.play()
+
+    def go_to_next_track(self):
+        if not len(self.tracks) > 0 or self.current_index == (len(self.tracks) - 1):
+            return
+        self.current_index += 1
+        self.play()
 
     def update_ui(self):
         """Updates the user interface"""
@@ -121,7 +178,7 @@ class VlcFrame(QtWidgets.QFrame):
         """Set the volume
         """
         # if muted: unmute
-        if self._is_muted:
+        if self.is_muted:
             self.toggle_muted()
 
         self._mediaplayer.audio_set_volume(volume)
@@ -146,14 +203,14 @@ class VlcFrame(QtWidgets.QFrame):
         self.timer.start()
 
     def toggle_muted(self):
-        if not self._is_muted:
+        if not self.is_muted:
             self._mediaplayer.audio_set_volume(0)
             self.ui.btnMute.setIcon(QIcon(":/img/rsc/mute.png"))
-            self._is_muted = True
+            self.is_muted = True
         else:
             self._mediaplayer.audio_set_volume(self.volume)
             self.ui.btnMute.setIcon(QIcon(":/img/rsc/volume.png"))
-            self._is_muted = False
+            self.is_muted = False
 
     def current(self):
         return self.currentTrack

+ 65 - 21
ui/qt/widgets/vlcframe.ui

@@ -150,29 +150,73 @@
       </layout>
      </item>
      <item>
-      <widget class="QLabel" name="lblTrack">
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>14</height>
-        </size>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="textFormat">
-        <enum>Qt::PlainText</enum>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignCenter</set>
-       </property>
-       <property name="wordWrap">
-        <bool>true</bool>
+      <layout class="QHBoxLayout" name="horizontalLayout_3">
+       <property name="leftMargin">
+        <number>12</number>
        </property>
-       <property name="textInteractionFlags">
-        <set>Qt::NoTextInteraction</set>
+       <property name="rightMargin">
+        <number>12</number>
        </property>
-      </widget>
+       <item>
+        <widget class="QToolButton" name="btnPrevious">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Revenir au morceau précédent</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/rewind.png</normaloff>:/img/rsc/rewind.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="btnNext">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Aller au morceau suivant</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../rsc.qrc">
+           <normaloff>:/img/rsc/fast-forward.png</normaloff>:/img/rsc/fast-forward.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="lblTrack">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>14</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::PlainText</enum>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::NoTextInteraction</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
      </item>
     </layout>
    </item>

+ 24 - 2
ui/qt/widgets/vlcframe_ui.py

@@ -2,7 +2,7 @@
 
 # Form implementation generated from reading ui file 'widgets/vlcframe.ui'
 #
-# Created by: PyQt5 UI code generator 5.15.4
+# Created by: PyQt5 UI code generator 5.15.2
 #
 # WARNING: Any manual changes made to this file will be lost when pyuic5 is
 # run again.  Do not edit this file unless you know what you are doing.
@@ -58,6 +58,23 @@ class Ui_VlcFrame(object):
         self.volumeSlider.setObjectName("volumeSlider")
         self.horizontalLayout.addWidget(self.volumeSlider)
         self.verticalLayout_2.addLayout(self.horizontalLayout)
+        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_3.setContentsMargins(12, -1, 12, -1)
+        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+        self.btnPrevious = QtWidgets.QToolButton(VlcFrame)
+        self.btnPrevious.setEnabled(False)
+        icon2 = QtGui.QIcon()
+        icon2.addPixmap(QtGui.QPixmap(":/img/rsc/rewind.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnPrevious.setIcon(icon2)
+        self.btnPrevious.setObjectName("btnPrevious")
+        self.horizontalLayout_3.addWidget(self.btnPrevious)
+        self.btnNext = QtWidgets.QToolButton(VlcFrame)
+        self.btnNext.setEnabled(False)
+        icon3 = QtGui.QIcon()
+        icon3.addPixmap(QtGui.QPixmap(":/img/rsc/fast-forward.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        self.btnNext.setIcon(icon3)
+        self.btnNext.setObjectName("btnNext")
+        self.horizontalLayout_3.addWidget(self.btnNext)
         self.lblTrack = QtWidgets.QLabel(VlcFrame)
         self.lblTrack.setMinimumSize(QtCore.QSize(0, 14))
         self.lblTrack.setText("")
@@ -66,7 +83,8 @@ class Ui_VlcFrame(object):
         self.lblTrack.setWordWrap(True)
         self.lblTrack.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
         self.lblTrack.setObjectName("lblTrack")
-        self.verticalLayout_2.addWidget(self.lblTrack)
+        self.horizontalLayout_3.addWidget(self.lblTrack)
+        self.verticalLayout_2.addLayout(self.horizontalLayout_3)
         self.horizontalLayout_2.addLayout(self.verticalLayout_2)
 
         self.retranslateUi(VlcFrame)
@@ -77,5 +95,9 @@ class Ui_VlcFrame(object):
         VlcFrame.setWindowTitle(_translate("VlcFrame", "Frame"))
         self.btnPlayPause.setText(_translate("VlcFrame", "Lire"))
         self.btnMute.setText(_translate("VlcFrame", "Vol."))
+        self.btnPrevious.setToolTip(_translate("VlcFrame", "Revenir au morceau précédent"))
+        self.btnPrevious.setText(_translate("VlcFrame", "..."))
+        self.btnNext.setToolTip(_translate("VlcFrame", "Aller au morceau suivant"))
+        self.btnNext.setText(_translate("VlcFrame", "..."))
 from .clickable_slider import ClickableSlider
 from .. import rsc_rc

+ 51 - 0
ui/qt/widgets/wysiwygTextEdit.py

@@ -0,0 +1,51 @@
+from PyQt5.QtGui import QImage, QTextDocument
+from PyQt5.QtWidgets import QTextEdit
+from path import Path
+import uuid
+
+IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.bmp']
+FONT_SIZES = [7, 8, 9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288]
+HTML_EXTENSIONS = ['.htm', '.html']
+
+
+class WysiwygTextEdit(QTextEdit):
+    def __init__(self, parent):
+        super().__init__(parent)
+
+    def canInsertFromMimeData(self, source):
+        if source.hasImage():
+            return True
+        else:
+            return super(WysiwygTextEdit, self).canInsertFromMimeData(source)
+
+    def insertFromMimeData(self, source):
+
+        cursor = self.textCursor()
+        document = self.document()
+
+        if source.hasUrls():
+
+            for u in source.urls():
+                file_ext = Path(str(u.toLocalFile())).splitext()
+                if u.isLocalFile() and file_ext in IMAGE_EXTENSIONS:
+                    image = QImage(u.toLocalFile())
+                    document.addResource(QTextDocument.ImageResource, u, image)
+                    cursor.insertImage(u.toLocalFile())
+
+                else:
+                    # If we hit a non-image or non-local URL break the loop and fall out
+                    # to the super call & let Qt handle it
+                    break
+
+            else:
+                # If all were valid images, finish here.
+                return
+
+        elif source.hasImage():
+            image = source.imageData()
+            uuid_ = uuid.uuid4().hex
+            document.addResource(QTextDocument.ImageResource, uuid_, image)
+            cursor.insertImage(uuid)
+            return
+
+        super(WysiwygTextEdit, self).insertFromMimeData(source)

+ 96 - 19
ui/window.py

@@ -27,6 +27,11 @@ class MainWindow(QMainWindow):
         super().__init__()
 
         self.settings = settings or {}
+
+        self.selected_playlist = None
+        self.playing_queue = []
+        self.selected_track = None  # /!\ the selected track is not necessarily the one being played!
+
         self.createWidgets()
 
     def createWidgets(self):
@@ -37,7 +42,7 @@ class MainWindow(QMainWindow):
         self.ui.stack.setCurrentIndex(0)
 
         menu_items = {
-            0: (':/img/rsc/dancing.png', 'Ma séance'),
+            0: (':/img/rsc/dance.png', 'Ma séance'),
             # 1: (':/img/rsc/writing-tool.png', 'Préparer'),
             2: (':/img/rsc/map.png', 'Explorer'),
             # 3: (':/img/rsc/calendar.png', 'Agenda'),
@@ -53,12 +58,21 @@ class MainWindow(QMainWindow):
         # Menu item clicked
         self.ui.menu.itemClicked.connect(self.menu_item_selected)
 
+        # Page 1 - Homepage
+        self.ui.sessionPlaylist.trackDoubleClicked.connect(self.play_playlist)
+        self.ui.sessionBtnChange.clicked.connect(self.selectPlaylist)
+
+        self.ui.frameNotes.ui.sessionNotes.textChanged.connect(self.sessionNotesChanged)
+
+        self.ui.btnSessionStart.clicked.connect(self.play_playlist)
+
         # Page 3 - explorer
         self.ui.explorerTable.populate()
-        self.ui.explorerTable.trackSelected.connect(self.explorer_selection_changed)
+        self.ui.explorerTable.trackSelected.connect(self.newTrackSelected)
         self.ui.explorerTable.trackDoubleClicked.connect(self.play_track)
         self.ui.btnExplorerRefresh.clicked.connect(self.refresh_explorer_tree)
         self.ui.explorerTrackMetaStack.setCurrentIndex(0)
+        self.ui.explorerTrackNotepad.textChanged.connect(self.explorerTrackNotesChanged)
         self.ui.explorerTrackPlay.clicked.connect(self.explorerPlaySelected)
 
         self.ui.btnSelectPlaylist.clicked.connect(self.selectPlaylist)
@@ -66,10 +80,8 @@ class MainWindow(QMainWindow):
         self.ui.explorerAddToPlaylist.clicked.connect(self.add_to_playlist)
         self.ui.explorerRemoveFromPlaylist.clicked.connect(self.remove_from_playlist)
 
-        self.ui.explorerPlaylist.trackSelected.connect(self.explorer_selection_changed)
-        self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_track)
-
-        # self.ui.explorerCreatePlaylist.clicked.connect(self.createOrEditPlaylist)
+        self.ui.explorerPlaylist.trackSelected.connect(self.newTrackSelected)
+        self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_playlist)
 
         # Page 5 - settings
         self.ui.musicFoldersTable.setColumnHidden(0, 1)
@@ -77,12 +89,18 @@ class MainWindow(QMainWindow):
         self.ui.musicFoldersRemoveButton.clicked.connect(self.remove_music_folder)
         self.populate_music_folders_table()
 
+        # vlc frame
+        self.ui.vlcFrame.trackStarted.connect(self.vlcTrackStarted)
+        self.ui.vlcFrame.playlistEnded.connect(self.vlcPlaylistEnded)
+
         # Apply settings
         if self.settings:
             self.ui.vlcFrame.set_volume(int(self.settings['volume']))
+            if self.settings['muted']:
+                self.ui.vlcFrame.toggle_muted()
 
-            playlist_id = int(self.settings['playlist'])
-            if playlist_id > 0:
+            playlist_id = self.settings['playlist']
+            if playlist_id is not None and playlist_id > 0:
                 playlist_repo = SessionRepository()
                 playlist = playlist_repo.get_by_id(playlist_id)
                 self.loadPlaylist(playlist)
@@ -99,17 +117,20 @@ class MainWindow(QMainWindow):
     def refresh_explorer_tree(self):
         self.ui.explorerTable.populate()
 
-    def explorer_selection_changed(self, track):
+    def newTrackSelected(self, track):
         sender = self.sender()
-
+        self.selected_track = None
         self.ui.explorerTrackMetaStack.setCurrentIndex(0)
         self.ui.explorerTrackPlay.setEnabled(False)
         self.ui.explorerAddToPlaylist.setEnabled(False)
         self.ui.explorerRemoveFromPlaylist.setEnabled(False)
+        self.ui.explorerTrackNotepad.setText("")
 
         if not track:
             return
 
+        self.selected_track = track
+
         # track infos
         self.ui.explorerLblTrackTitle.setText(track.title)
         self.ui.explorerLblTrackArtist.setText(track.artist)
@@ -117,6 +138,10 @@ class MainWindow(QMainWindow):
         self.ui.explorerLblTrackNumber.setText(
             str(track.track_num if track.track_num is not None else "")
         )
+        self.ui.explorerTrackNotepad.textChanged.disconnect(self.explorerTrackNotesChanged)
+        self.ui.explorerTrackNotepad.setHtml(track.note)
+        self.ui.explorerTrackNotepad.textChanged.connect(self.explorerTrackNotesChanged)
+
         self.ui.explorerTrackMetaStack.setCurrentIndex(1)
         self.ui.explorerTrackPlay.setEnabled(True)
 
@@ -125,7 +150,6 @@ class MainWindow(QMainWindow):
 
         elif type(sender) is PlaylistTable:
             self.ui.explorerRemoveFromPlaylist.setEnabled(True)
-
         else:
             raise RuntimeError("Unknown sender")
 
@@ -154,7 +178,8 @@ class MainWindow(QMainWindow):
 
     def play_track(self, track):
         logger.info("Start playing: %s" % track)
-        self.ui.vlcFrame.play_track(track)
+        self.ui.vlcFrame.load_track(track)
+        self.ui.vlcFrame.play()
 
     def add_music_folder(self):
         path = QFileDialog.getExistingDirectory(self, "Sélectionnez le dossier à ajouter")
@@ -192,10 +217,24 @@ class MainWindow(QMainWindow):
             self.loadPlaylist(playlist)
 
     def loadPlaylist(self, playlist):
+
+        # home page
+        self.ui.sessionPlaylist.setEnabled(True)
+        self.ui.frameNotes.setEnabled(True)
+        self.ui.sessionLblTitle.setText(playlist.name)
+        self.ui.sessionPlaylist.populate(playlist)
+
+        self.ui.frameNotes.ui.sessionNotes.textChanged.disconnect(self.sessionNotesChanged)
+        self.ui.frameNotes.ui.sessionNotes.setHtml(playlist.notes)
+        self.ui.frameNotes.ui.sessionNotes.textChanged.connect(self.sessionNotesChanged)
+
+        # explorer page
         self.ui.explorerPlaylist.setEnabled(True)
-        self.ui.lblPlaylistName.setText(playlist.name)
+        self.ui.explorerLblPlaylistTitle.setText(playlist.name)
         self.ui.explorerPlaylist.populate(playlist)
 
+        self.selected_playlist = playlist
+
     def add_to_playlist(self):
         track = self.ui.explorerTable.selected_track()
         if track is None:
@@ -203,9 +242,8 @@ class MainWindow(QMainWindow):
         playlist = self.ui.explorerPlaylist.playlist
         if playlist is None:
             return
-        order = self.ui.explorerPlaylist.count + 1
         track_repo = TrackRepository()
-        track_repo.add_to_playlist(
+        track_repo.add_to_session(
             track.id,
             playlist.id
         )
@@ -219,16 +257,55 @@ class MainWindow(QMainWindow):
         session_track_repo.delete(session_track, True)
         self.ui.explorerPlaylist.populate()
 
-    def run(self):
-        pass
+    def play_playlist(self, track=None):
+
+        playlist = self.selected_playlist
+        track_repo = TrackRepository()
+        tracks = track_repo.get_by_session_id(playlist.id)
+        start_at = tracks.index(track) if track else 0
+
+        logger.info("Start playing playlist: %s from index %s" % (playlist, start_at))
+        self.ui.vlcFrame.load_playlist(playlist, tracks, start_at)
+        self.ui.vlcFrame.play()
+
+    def vlcTrackStarted(self, track):
+        if self.selected_playlist:
+            self.ui.explorerPlaylist.set_is_playing(track)
+            self.ui.sessionPlaylist.set_is_playing(track)
+        else:
+            self.ui.explorerPlaylist.set_is_playing(None)
+            self.ui.sessionPlaylist.set_is_playing(None)
+
+    def vlcPlaylistEnded(self):
+        self.ui.explorerPlaylist.set_is_playing(None)
+        self.ui.sessionPlaylist.set_is_playing(None)
+
+    def sessionNotesChanged(self):
+        if not self.selected_playlist:
+            return
+        notes = self.ui.frameNotes.ui.sessionNotes.toHtml()
+        session_repo = SessionRepository()
+        # session = session_repo.get_by_id(self.selected_playlist.id)
+        self.selected_playlist.notes = notes
+        session_repo.commit()
+
+    def explorerTrackNotesChanged(self):
+        if not self.selected_track:
+            return
+        note = self.ui.explorerTrackNotepad.toHtml()
+        track_repo = TrackRepository()
+        self.selected_track.note = note
+        track_repo.commit()
 
     def currentSettings(self):
         volume = self.ui.vlcFrame.volume
-        playlist_id = self.ui.explorerPlaylist.playlist.id
+        playlist_id = self.selected_playlist.id if self.selected_playlist else None
+        muted = self.ui.vlcFrame.is_muted
 
         return {
             "playlist": playlist_id,
-            "volume": volume
+            "volume": volume,
+            "muted": muted,
         }
 
     def __del__(self):

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.