Results 1 to 9 of 9

Thread: QFileDialog and QSortFilterProxyModel

  1. #1
    Join Date
    Oct 2015
    Posts
    45
    Thanks
    8
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default QFileDialog and QSortFilterProxyModel

    I've been beating my head against this for a while now.

    Ultimately, I'd like to filter on a substring in the file name.
    If I understand correctly, the vanilla QFileDialog will only do file extensions (i.e. the characters after the period, like .exe or .xml)

    So I wrote the following in an attempt to "try it".:

    Qt Code:
    1. import sys
    2.  
    3. from PyQt5 import Qt, QtCore, QtGui, QtWidgets
    4. from PyQt5.QtGui import QColor, QBrush
    5. from PyQt5.QtGui import QPainter, QColor, QPen, QFont
    6. from PyQt5.QtCore import *
    7. from PyQt5.QtWidgets import *
    8.  
    9. class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
    10. def __init__(self, parent=None):
    11. super(FileFilterProxyModel, self).__init__(parent)
    12.  
    13. def filterAcceptsRow(self, source_row, srcidx):
    14. model = self.sourceModel()
    15. index0 = model.index(source_row, 0, QtCore.QModelIndex())
    16. index1 = model.index(source_row, 1, QtCore.QModelIndex())
    17. index2 = model.index(source_row, 2, QtCore.QModelIndex())
    18. str0 = model.data(index0, Qt.DisplayRole)
    19. str1 = model.data(index1, Qt.DisplayRole)
    20. str2 = model.data(index2, Qt.DisplayRole)
    21. print('{}, data: {}{}{}'.format(source_row, str0, str1, str2))
    22. return True
    23.  
    24. app = QtWidgets.QApplication(sys.argv)
    25. dlg = QFileDialog()
    26. proxymodel = FileFilterProxyModel(dlg)
    27. dlg.setProxyModel(proxymodel)
    28. #dlg.setOption(QFileDialog.DontUseNativeDialog)
    29. dlg.show()
    30. app.exec_()
    To copy to clipboard, switch view to plain text mode 

    But, I can't seem to figure out how to get the file name to decide whether it has the substring.

    "model" is a QFileSystemModel.
    The code above returns '/' for str0, blank for str1, and the word 'Drive'.
    Tried model.data(index0, QFileSystemModel.FileNameRole) returns nothing
    In other versions of this code I tried model.fileName(index) which always returns nothing.

    But, it does display the file dialog with all files listed (with return = True in filterAcceptsRow) and no files (return False)

    Once again I'm confused...happens to me a lot.

  2. #2
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QFileDialog and QSortFilterProxyModel

    amm... your filterAcceptsRow() returns hard coded 'true'.
    So no wonder it shows everything or nothing if change that to 'false'.
    The return value should depend on if the string you want to filter out is found in the current examined string.
    For that you need to set the string you what to use as a filter, which I don't see in your code.(setFilterRegExp())
    It seems your code is based on the example from the documentation - so why did you left the actual string filtering out?
    And while you are at it, you might want to read all the QSortFilterProxyModel documentation.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  3. #3
    Join Date
    Oct 2015
    Posts
    45
    Thanks
    8
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QFileDialog and QSortFilterProxyModel

    Quote Originally Posted by high_flyer View Post
    amm... your filterAcceptsRow() returns hard coded 'true'.
    So no wonder it shows everything or nothing if change that to 'false'.
    Yes, as I mentioned, I tried it with true and get everything, false, get nothing.
    Yes, I realize it SHOULD, return true or false based on what the file name contains.
    (I got that from reading the documentation you assume I didn't read.)

    The return value should depend on if the string you want to filter out is found in the current examined string.
    For that you need to set the string you what to use as a filter, which I don't see in your code.(setFilterRegExp())
    Is it required that I use setFilterRegExp ()?
    Why can't I just check the contents in fileAcceptsRow?
    And my question is how to get the string to be searched, in this case the file name.

    It seems your code is based on the example from the documentation - so why did you left the actual string filtering out?
    Gleaned from several examples actually.
    I left it out because no two examples I've found do it the same and investment trying to figure out how this works.
    Not to mention trying to translate from c to python.
    And while you are at it, you might want to read all the QSortFilterProxyModel documentation.
    The qt docs are great if you need to reference how a function name is spelled or what the arguments are...not so much help on the how to or why...IMHO

    In addition, and yes I've read the docs, the question which I obviously wasn't clear on. How do I access the file name string from the model...so I can actually filter it? As noted in my original question, I tried different things and could not get the file name.

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QFileDialog and QSortFilterProxyModel

    Is it required that I use setFilterRegExp ()?
    No.

    Why can't I just check the contents in fileAcceptsRow?
    It's "filterAcceptsRow()" actually.

    You can, but by doing that, you will be, in effect, doing exactly what setting the filter expression does for you. Why reinvent a wheel and likely introduce your bugs into it when someone else has already provided a debugged wheel for you?

    The filterAcceptsRow() method is more general, allowing you to accept or reject rows based on any set of criteria using any Qt:: ItemRole (or not). setFilterRegExp() is used in conjunction with the base class' implementation of filterAcceptsRow() to implement regular expression-based matching and filtering based on Qt:: DisplayRole.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. #5
    Join Date
    Apr 2014
    Posts
    59
    Thanks
    7
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QFileDialog and QSortFilterProxyModel

    How do I access the file name string from the model...so I can actually filter it? As noted in my original question, I tried different things and could not get the file name.
    Try QFileInfo.

  6. #6
    Join Date
    Oct 2015
    Posts
    45
    Thanks
    8
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QFileDialog and QSortFilterProxyModel

    After much trials and error the following code works, that is, it shows only folders and xml files that contain the string "_project":

    Qt Code:
    1. import sys
    2.  
    3. from PyQt5 import Qt, QtCore, QtGui, QtWidgets
    4. from PyQt5.QtGui import QColor, QBrush
    5. from PyQt5.QtGui import QPainter, QColor, QPen, QFont
    6. from PyQt5.QtCore import *
    7. from PyQt5.QtWidgets import *
    8.  
    9. class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
    10. def __init__(self, parent=None):
    11. super(FileFilterProxyModel, self).__init__(parent)
    12.  
    13. def filterAcceptsRow(self, source_row, srcidx):
    14. model = self.sourceModel()
    15. index0 = model.index(source_row, 0, srcidx)
    16. index1 = model.index(source_row, 1, srcidx)
    17. index2 = model.index(source_row, 2, srcidx)
    18. str0_filenamerole = model.data(index0, QFileSystemModel.FileNameRole)
    19. str0_displayrole = model.data(index0, Qt.DisplayRole)
    20. fname = model.fileName(index0)
    21. fname1 = model.fileName(index1)
    22. str1_filenamerole = model.data(index1, QFileSystemModel.FileNameRole)
    23. str1_displayrole = model.data(index1, Qt.DisplayRole)
    24. str2_filenamerole = model.data(index2, QFileSystemModel.FileNameRole)
    25. str2_displayrole = model.data(index2, Qt.DisplayRole)
    26. print('source_rowe: {}'.format(source_row))
    27. print('str0_filenamerole: {}'.format(str0_filenamerole))
    28. print('str0_displayrole: {}'.format(str0_displayrole))
    29. print('str1_filenamerole: {}'.format(str1_filenamerole))
    30. print('str1_displayrole: {}'.format(str1_displayrole))
    31. print('str2_filenamerole: {}'.format(str2_filenamerole))
    32. print('str2_displayrole: ~{}~'.format(str2_displayrole))
    33. if str2_displayrole in ('Folder', 'Drive'): return True
    34. if '_project' in str1_filenamerole and str2_displayrole == 'xml File':
    35. print('Returning True')
    36. return True
    37. else:
    38. return False
    39.  
    40. app = QtWidgets.QApplication(sys.argv)
    41. dlg = QFileDialog()
    42. proxymodel = FileFilterProxyModel(dlg)
    43. dlg.setDirectory('/home/mac/Shows/Fiddler/')
    44. dlg.setProxyModel(proxymodel)
    45. dlg.show()
    46. app.exec_()
    To copy to clipboard, switch view to plain text mode 

    My initial confusion was that model.fileName() did not return anything. Of course when I gave it the correct argument, model.fileName(index0), the file name is returned.
    With that is a simple matter to find the existence desired substring and return True/False accordingly.

    To respond to d_stranz:
    I have no problem using ready made wheels when I understand what and how they do what they do.
    Unfortunately, I also tend to grock things a bit at a time. This seems to be a real drawback in figuring out how things work in qt. But, thats my burden.

    In addition, I use regular expressions so infrequently, that I always end cross eyed when I use them.

    In any case, I have now attempted to to use the reqex way...to no avail. The following only get content in the dialog when the regex is "".
    I tried "_project" and a bunch of other combinations to no avail. Interestingly, "_?" returns all files and folders.
    I'm tired, blurry eyed, etc. and have to re-learn (for the umteenth time over 40 years) regex tomorrow.

    Qt Code:
    1. import sys
    2.  
    3. from PyQt5 import Qt, QtCore, QtGui, QtWidgets
    4. from PyQt5.QtGui import QColor, QBrush
    5. from PyQt5.QtGui import QPainter, QColor, QPen, QFont
    6. from PyQt5.QtCore import *
    7. from PyQt5.QtWidgets import *
    8.  
    9. class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
    10. def __init__(self, parent=None):
    11. super(FileFilterProxyModel, self).__init__(parent)
    12.  
    13.  
    14. app = QtWidgets.QApplication(sys.argv)
    15. dlg = QFileDialog()
    16. proxymodel = QtCore.QSortFilterProxyModel(dlg)
    17. proxymodel.setFilterRole(Qt.DisplayRole)
    18. proxymodel.setFilterRegExp("")
    19. dlg.setDirectory('/home/mac/Shows/Fiddler/')
    20. dlg.setProxyModel(proxymodel)
    21. dlg.show()
    22. app.exec_()
    To copy to clipboard, switch view to plain text mode 

  7. #7
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QFileDialog and QSortFilterProxyModel

    EDIT: ah you beat me to it... never the less, I'll leave the answer.

    Is it required that I use setFilterRegExp ()?
    As d_stranz said, you don't have to use that specific method.
    What ever way you choose to set your search string, you have to do it.
    Your code does not include any such way.
    Again, in your filterAcceptsRow() you are not doing any filtering.
    What ever way you choose to bring the filter string in, you have to use it.
    Your filterAcceptsRow() as you posted it simply gets a string for the 3 first columns in your row, and then returns without doing anything with these strings.

    Anyways, As I said in my first reply,in the documentation for QSortFilterProxyModel the following code snippet is doing EXACTLY what you are trying to do with the only difference being the roles of the data you are interested in:
    Qt Code:
    1. bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,
    2. const QModelIndex &sourceParent) const
    3. {
    4. QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
    5. QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
    6. QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
    7.  
    8. return (sourceModel()->data(index0).toString().contains(filterRegExp())
    9. || sourceModel()->data(index1).toString().contains(filterRegExp()))
    10. && dateInRange(sourceModel()->data(index2).toDate());
    11. }
    To copy to clipboard, switch view to plain text mode 

    as you can see the return here depends on if the examined tiring contains the filter string - which would have solved your problem with getting all or nothing.

    To your second question:
    How do I access the file name string from the model...so I can actually filter it?
    Again this is found in the docs, in this case for QFileSystemModel:
    Qt Code:
    1. enumQFileSystemModel::FileIconRole Qt::DecorationRole
    2.  
    3. QFileSystemModel::FilePathRole Qt::UserRole + 1
    4. QFileSystemModel::FileNameRole Qt::UserRole + 2
    5. QFileSystemModel::FilePermissions Qt::UserRole + 3 QFileSystemModel::Roles
    To copy to clipboard, switch view to plain text mode 

    Which means, in your filterAcceptsRow() you call data() with the role you are interested in.

    The qt docs are great if you need to reference how a function name is spelled or what the arguments are...not so much help on the how to or why...IMHO
    As you can see, the docs offer more then just the signatures of methods.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  8. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QFileDialog and QSortFilterProxyModel

    I have no problem using ready made wheels when I understand what and how they do what they do.
    Unfortunately, I also tend to grock things a bit at a time. This seems to be a real drawback in figuring out how things work in qt. But, that's my burden.
    The comments weren't meant as a criticism. All of us went (and are still going) through the same learning curve - reading the docs, not understanding them, looking at examples that don't quite apply, and then eventually trying enough different things that you finally find something that works. Hopefully you will also understand -why- it works at the end of it all. There are plenty of parts of Qt I haven't even looked at, much less used.

    Qt's Model-View system is very powerful and flexible and it is worth taking the time to learn how to use it effectively.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  9. #9
    Join Date
    Oct 2015
    Posts
    45
    Thanks
    8
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QFileDialog and QSortFilterProxyModel

    Quote Originally Posted by d_stranz View Post
    The comments weren't meant as a criticism. All of us went (and are still going) through the same learning curve - reading the docs, not understanding them, looking at examples that don't quite apply, and then eventually trying enough different things that you finally find something that works. Hopefully you will also understand -why- it works at the end of it all. There are plenty of parts of Qt I haven't even looked at, much less used.

    Qt's Model-View system is very powerful and flexible and it is worth taking the time to learn how to use it effectively.
    As with most powerful systems, the concept is rather simple, the implementation not always.

    Just to be complete I worked at it until I had a functional version of the code that used the filterRegExp.

    I did go off and do a refresher of regex, but, it really wasn't needed for the results I wanted to attain.

    The following code shows any xml file that has the string "_project" in the file name. Also retaining folders/drives so the user can search elsewhere.

    There may be better ways to code this example, comments are welcome.

    As I mentioned earlier, I found that index 0 and 2 of the model had the filename and the type respectively by trial and error.
    I was unable to to glean this info from the docs on FileSystemModel. It may be there, I just didn't find it or interpret it from what I read.
    (I'd be happy to be instructed on how to discern it.)

    Qt Code:
    1. import sys
    2.  
    3. from PyQt5 import Qt, QtCore, QtWidgets
    4. from PyQt5.QtCore import *
    5. from PyQt5.QtWidgets import *
    6.  
    7. class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
    8. def __init__(self, parent=None):
    9. super(FileFilterProxyModel, self).__init__(parent)
    10.  
    11. def filterAcceptsRow(self, source_row, srcidx):
    12. model = self.sourceModel()
    13. index0 = model.index(source_row, 0, srcidx)
    14. index2 = model.index(source_row, 2, srcidx)
    15. str0_filenamerole = model.data(index0, QFileSystemModel.FileNameRole)
    16. str2_displayrole = model.data(index2, Qt.DisplayRole)
    17. indexofstring = self.filterRegExp().indexIn(str0_filenamerole)
    18. if (indexofstring >= 0 and str2_displayrole == 'xml File')\
    19. or (str2_displayrole in ('Folder', 'Drive')):
    20. return True
    21. else:
    22. return False
    23.  
    24. app = QtWidgets.QApplication(sys.argv)
    25. dlg = QFileDialog()
    26. proxymodel = FileFilterProxyModel(dlg)
    27. proxymodel.setFilterRegExp(QRegExp("_project", Qt.CaseInsensitive, QRegExp.FixedString))
    28. dlg.setDirectory('/home/mac/Shows/Fiddler/')
    29. dlg.setProxyModel(proxymodel)
    30. dlg.show()
    31. app.exec_()
    To copy to clipboard, switch view to plain text mode 
    Last edited by drmacro; 14th November 2017 at 16:59.

Similar Threads

  1. Replies: 7
    Last Post: 5th March 2014, 18:36
  2. QSortFilterProxyModel nothing changes
    By unix7777 in forum Qt Programming
    Replies: 7
    Last Post: 19th August 2012, 09:13
  3. Using QSortFilterProxyModel
    By Jennie Bystrom in forum Qt Programming
    Replies: 3
    Last Post: 6th December 2007, 11:28
  4. QSortFilterProxyModel
    By evgenM in forum Qt Programming
    Replies: 1
    Last Post: 18th March 2007, 12:53

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.