pythonpyqtpyqt5qfilesystemmodel

I want to get working QFileSystemModel in Tree view?


I am making a program in which I want a FileTree (I already have this but not with QfileSystemMoodel and that makes it harder to do things afterwards). Here I have to be able to record a bit of sound with the file name entered (this works) in the active folder (which I cannot yet select so this is now hard-coded) and then the file tree must be updated. I prefer to use QFileSystemModel for this because it makes it easier to edit things afterwards.

So my question: a treeview with QFileSystemModel, active / selected path as record location and update after recording or other modification.

This I have tried but I can't get it to work and I don't need a filter:

import os, sys
import sounddevice as sd
from scipy.io.wavfile import write
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QTreeWidgetItem, QFileSystemModel
from pathlib import Path

qtcreator_file  = "mainwindow.ui" # Enter file here.
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtcreator_file)

class MyWindow(QtWidgets.QMainWindow, QtWidgets.QFileSystemModel, Ui_MainWindow):

    def __init__(self):
        QtWidgets.QMainWindow.__init__(self, parent=None)
        Ui_MainWindow.__init__(self)
        QtGui.QFileSystemModel.__init__(self, None)
        self.checks = {}
        self.FileStruckture
        self.setupUi(self)
        self.opnemen.clicked.connect(self.capture)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.CheckStateRole:
            return QtGui.QFileSystemModel.data(self, index, role)
        else:
            if index.column() == 0:
                return self.checkState(index)

    def flags(self, index):
        return QtGui.QFileSystemModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable

    def checkState(self, index):
        if index in self.checks:
            return self.checks[index]
        else:
            return QtCore.Qt.Checked

    def setData(self, index, value, role):
        if (role == QtCore.Qt.CheckStateRole and index.column() == 0):
            self.checks[index] = value
            self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index)
            return True 
        return QtGui.QFileSystemModel.setData(self, index, value, role)

    self.dirTreeView = QtWidgets.QTreeWidget(self.FileStruckture)
    self.dirModel = CheckableDirModel()
    self.dirTreeView.setModel(self.dirModel)

    def capture(self):

        fs = 44100  # Sample rate
        seconds = 6  # Duration of recording

        myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
        sd.wait()  # Wait until recording is finished
        locatie = "D:/DemoGIPhoofdmap/bedrijf 08"
        locatiepath = Path(locatie)
        file_name = "/" + (self.filename.text()) + ".wav"
        write_path = locatie + file_name
        write(write_path, fs, myrecording)  # Save as WAV file

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

This works the most but it isn't with QFileSystemMoodel and it does not update and I can't get the active path out of it:

import os, sys
import sounddevice as sd
from scipy.io.wavfile import write
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QTreeWidgetItem
from PyQt5.QtGui import QIcon
from pathlib import Path

qtcreator_file  = "mainwindow.ui" # Enter file here.
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtcreator_file)

class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.FileStruckture
        self.file_tree("D:/DemoGIPhoofdmap", self.FileStruckture)
        self.opnemen.clicked.connect(self.capture)


    def file_tree(self, startpath, tree):
        startpath = Path(startpath)
        for element in os.listdir(startpath):
            path_info = startpath / element
            parent_itm = QTreeWidgetItem(tree, [os.path.basename(element)])
            if os.path.isdir(path_info):
                self.file_tree(path_info, parent_itm)
                parent_itm.setIcon(0, QIcon('assets/folder.ico'))
            else:
                parent_itm.setIcon(0, QIcon('assets/file.ico'))

    def capture(self):

        fs = 44100  # Sample rate
        seconds = 6  # Duration of recording

        myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
        sd.wait()  # Wait until recording is finished
        locatie = "D:/DemoGIPhoofdmap/bedrijf 08"
        locatiepath = Path(locatie)
        file_name = "/" + (self.filename.text()) + ".wav"
        write_path = locatie + file_name
        write(write_path, fs, myrecording)  # Save as WAV file

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

This is the XML code from QT Creator

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
    <x>0</x>
    <y>0</y>
    <width>827</width>
    <height>622</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QPushButton" name="opnemen">
    <property name="geometry">
    <rect>
    <x>320</x>
    <y>60</y>
    <width>75</width>
    <height>23</height>
    </rect>
    </property>
    <property name="text">
    <string>Capture</string>
    </property>
</widget>
<widget class="QPushButton" name="importeer">
    <property name="geometry">
    <rect>
    <x>400</x>
    <y>60</y>
    <width>75</width>
    <height>23</height>
    </rect>
    </property>
    <property name="text">
    <string>Inport</string>
    </property>
</widget>
<widget class="QTreeWidget" name="FileStruckture">
    <property name="geometry">
    <rect>
    <x>10</x>
    <y>10</y>
    <width>301</width>
    <height>561</height>
    </rect>
    </property>
</widget>
<widget class="QLabel" name="label">
    <property name="geometry">
    <rect>
    <x>320</x>
    <y>10</y>
    <width>131</width>
    <height>16</height>
    </rect>
    </property>
    <property name="text">
    <string>File name for new item:</string>
    </property>
</widget>
<widget class="QLineEdit" name="filename">
    <property name="geometry">
    <rect>
    <x>320</x>
    <y>30</y>
    <width>151</width>
    <height>20</height>
    </rect>
    </property>
</widget>
<widget class="QPushButton" name="nieuwemap">
    <property name="geometry">
    <rect>
    <x>320</x>
    <y>90</y>
    <width>75</width>
    <height>23</height>
    </rect>
    </property>
    <property name="text">
    <string>New folder</string>
    </property>
</widget>
<widget class="QPushButton" name="verwijderen">
    <property name="geometry">
    <rect>
    <x>320</x>
    <y>120</y>
    <width>75</width>
    <height>23</height>
    </rect>
    </property>
    <property name="text">
    <string>Delete</string>
    </property>
</widget>
<widget class="QPushButton" name="hernoem">
    <property name="geometry">
    <rect>
    <x>400</x>
    <y>90</y>
    <width>75</width>
    <height>23</height>
    </rect>
    </property>
    <property name="text">
    <string>Rename</string>
    </property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>827</width>
    <height>21</height>
    </rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

Solution

  • If you want to use a QFileSystemModel you must use a QTreeView, but your .ui does not implement it so modify your .ui. On the other hand sd.wait() is blocking that freezes the GUI, to avoid this task must be executed in another thread.

    Considering the above, the solution is:

    *.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>615</width>
        <height>598</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="QTreeView" name="filestruckture"/>
        </item>
        <item>
         <layout class="QGridLayout" name="gridLayout">
          <item row="1" column="0" colspan="2">
           <widget class="QLineEdit" name="filename"/>
          </item>
          <item row="3" column="0">
           <widget class="QPushButton" name="nieuwemap">
            <property name="text">
             <string>New folder</string>
            </property>
           </widget>
          </item>
          <item row="4" column="0">
           <widget class="QPushButton" name="verwijderen">
            <property name="text">
             <string>Delete</string>
            </property>
           </widget>
          </item>
          <item row="2" column="1">
           <widget class="QPushButton" name="importeer">
            <property name="text">
             <string>Inport</string>
            </property>
           </widget>
          </item>
          <item row="0" column="0" colspan="2">
           <widget class="QLabel" name="label">
            <property name="text">
             <string>File name for new item:</string>
            </property>
           </widget>
          </item>
          <item row="3" column="1">
           <widget class="QPushButton" name="hernoem">
            <property name="text">
             <string>Rename</string>
            </property>
           </widget>
          </item>
          <item row="2" column="0">
           <widget class="QPushButton" name="opnemen">
            <property name="text">
             <string>Capture</string>
            </property>
           </widget>
          </item>
          <item row="5" column="0">
           <spacer name="verticalSpacer">
            <property name="orientation">
             <enum>Qt::Vertical</enum>
            </property>
            <property name="sizeHint" stdset="0">
             <size>
              <width>20</width>
              <height>40</height>
             </size>
            </property>
           </spacer>
          </item>
         </layout>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>615</width>
         <height>30</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    *.py

    import os
    import sys
    from pathlib import Path
    
    from functools import partial
    
    import sounddevice as sd
    from scipy.io.wavfile import write
    
    from PyQt5 import QtCore, QtGui, QtWidgets, uic
    
    qtcreator_file = "mainwindow.ui"  # Enter file here.
    Ui_MainWindow, QtBaseClass = uic.loadUiType(qtcreator_file)
    
    
    class CaptureWorker(QtCore.QObject):
        started = QtCore.pyqtSignal()
        finished = QtCore.pyqtSignal()
    
        @QtCore.pyqtSlot(str)
        def save_to(self, path):
            self.started.emit()
            fs = 44100  # Sample rate
            seconds = 6  # Duration of recording
            myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
            sd.wait()
            write(path, fs, myrecording)
            self.finished.emit()
    
    
    class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self, parent=None):
            super(MyWindow, self).__init__(parent)
            self.setupUi(self)
    
            self.root_path = "D:/DemoGIPhoofdmap/bedrijf 08"
    
            model = QtWidgets.QFileSystemModel(self)
            self.filestruckture.setModel(model)
            model.setRootPath(self.root_path)
            self.filestruckture.setRootIndex(model.index(model.rootPath()))
    
            thread = QtCore.QThread(self)
            thread.start()
    
            self.capture_worker = CaptureWorker()
            self.capture_worker.moveToThread(thread)
    
            self.capture_worker.started.connect(partial(self.opnemen.setEnabled, False))
            self.capture_worker.finished.connect(partial(self.opnemen.setEnabled, True))
    
            self.opnemen.clicked.connect(self.capture)
    
        def capture(self):
            name = self.filename.text()
            if name:
                locatie = self.root_path
                locatiepath = Path(locatie)
                file_name = os.path.join(locatiepath, name + ".wav")
                wrapper = partial(self.capture_worker.save_to, file_name)
                QtCore.QTimer.singleShot(0, wrapper)
            else:
                print("set the filename")
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = MyWindow()
        window.show()
        sys.exit(app.exec_())