Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00038.parquet:33318

87b73226b0cdc47e3f5cb007
turn 1/5gpt-4o-2024-08-06PersianAustralia20 words
degenerate_repetitionAbsentFinal dense release
USER
یه متنی برات میفرستم به انگلیسی میشه چکش کنی برام
                            
                            
ASSISTANT
بله، لطفاً متن خود را ارسال کنید تا بررسی کنم.

turns-00038.parquet:33319

0e50d59e0e48da932373d44b
turn 2/5gpt-4o-2024-08-06PersianAustralia16 words
degenerate_repetitionAbsentFinal dense release
USER
مرسی
                            
                            
ASSISTANT
خواهش می‌کنم! هر زمان که آماده بودید متن خود را ارسال کنید تا بررسی کنم.

turns-00038.parquet:33320

4f5b42695ff35c4f2cdd5255
turn 3/5gpt-4o-2024-08-06PersianAustralia240 words
degenerate_repetitionAbsentFinal dense release
USER
One of my memorable experiences is referred to time when I had gone to Shiraz. Shiraz is one if the most beautiful and historical places in Iran so many tourists come to Iran every year. منظورم اینه که هرساله توریست های زیادی به ایران میان. One thing that contract me was how tourists had come
there. میخوام بگم اونا خیلی راحت لباس پوشیده بودند وهیچ عرایشی نداشتند. وبیشتر مشتاق یادکیری ودیدن مناظر تاریخی بودند تا عکس گرفتن دقیقا برعکس ایرانی ها که اصلا تاریخچه خیلی ازمکان ها رو به درستی نمیدونستن وبیشتر به فمر عکس انداختن بدوند آنها بیشتر از ما به آن مکانها اهمیت میدادند
                            
                            
ASSISTANT
Thanks for sharing your text! Here’s a revised version with some corrections and improvements:

One of my memorable experiences is from the time I went to Shiraz. Shiraz is one of the most beautiful and historical places in Iran, attracting many tourists every year. One thing that stood out to me was how relaxed the tourists were in terms of clothing, and they wore no makeup. They were more eager to learn and see historical sites rather than taking photos, which was quite the opposite of many Iranians, who didn’t know the history of many places accurately and were more focused on taking pictures. The tourists seemed to care more about those places than we did.

If you have any other parts you'd like me to check or further questions, just let me know!

turns-00038.parquet:33321

fa6e53321e250835485192d6
turn 4/5gpt-4o-2024-08-06PersianAustralia25 words
degenerate_repetitionAbsentFinal dense release
USER
ممنون عزیز دلم
                            
                            
ASSISTANT
خواهش می‌کنم! اگر سوال یا متن دیگری دارید که نیاز به بررسی دارد، حتماً به من بگویید. خوشحال می‌شوم که کمک کنم!

turns-00038.parquet:33322

bf375cca30ef04de685f5e6f
turn 5/5gpt-4o-2024-08-06PersianAustralia20 words
degenerate_repetitionAbsentFinal dense release
USER
زحمت کشیدی
                            
                            
ASSISTANT
نه خواهش می‌کنم، انجام وظیفه بود. اگر نیاز به کمک بیشتری دارید یا سوالی دارید، در خدمت هستم!

turns-00038.parquet:33323

b8b638e714e67061204c09da
turn 1/1gpt-4o-2024-08-06EnglishUnited States195 words
degenerate_repetitionAbsentFinal dense release
USER
System: Ты - умный переводчик с английского языка на русский.
User: Задача: Переведи текст на русский язык
User: Тексты: Tonight, my husband seemed to put our son down through an incorrect approach. His approach was perfectly wrong. I found myself defending my son. I told him that what he did was not the way to encourage our son.
Instead of watching my husband’s harsh behavior I decided to sit beside my son and, together, we worked on his science assignment in order to encourage him to study. My husband got upset and he was swearing at me. He threatened me. I knew that he was not joking. His words scared me a lot!
Assistant:
ASSISTANT
Сегодня вечером мой муж, кажется, уложил нашего сына спать неправильным способом. Его подход был полностью неверным. Я обнаружила, что защищаю своего сына. Я сказала мужу, что то, что он сделал, не способ, чтобы поощрить нашего сына.

Вместо того чтобы наблюдать за грубым поведением мужа, я решила сесть рядом с сыном, и вместе мы занялись его заданием по естествознанию, чтобы поощрить его учиться. Муж рассердился, начал ругаться на меня и угрожать. Я знала, что он не шутит. Его слова меня сильно напугали!

turns-00038.parquet:33324

e080a8f753b89b86191e2e9b
turn 1/1gpt-4o-2024-08-06EnglishUnited Kingdom165 words
degenerate_repetitionAbsentFinal dense release
USER
System: Ты - умный переводчик с английского языка на русский.
User: Задача: Переведи текст на русский язык
User: Тексты: How do I ever trust another woman? I have found myself constantly reading between the lines with every other woman that I meet. I am having a difficult time making any sort of connection to anyone because of her deception and willingness to say and do literally anything in order to control my emotions.
Once the "relationship" was over, she became extremely abusive and has attempted to intimidate me into silence regarding the many false claims made on her immigration application.
Assistant:
ASSISTANT
Как я могу снова доверять женщинам? Я постоянно пытаюсь читать между строк с каждой новой женщиной, которую встречаю. Мне крайне сложно наладить хоть какую-то связь с кем-либо из-за её обмана и готовности делать и говорить буквально всё, чтобы контролировать мои эмоции. Когда "отношения" закончились, она стала крайне агрессивной и пыталась запугать меня, чтобы я молчал о многочисленных ложных заявлениях, сделанных в её иммиграционной анкете.

turns-00038.parquet:33325

52fa3eb0ac57554b30efccd7
turn 1/1gpt-4o-2024-08-06EnglishCanada8941 words
degenerate_repetitionAbsentFinal dense release
USER
annalyse le comportement de mapFromSource

Le chemin INDEX_DIR est valide.

Le chemin WATCHED_DIR est valide.

2024-10-24 10:20:03,106 - DEBUG - Schema defined with fields: ['creation_time', 'ext', 'modification_time', 'name', 'path', 'size']

2024-10-24 10:20:03,107 - DEBUG - Initializing FileIndexer with index_dir: C:\index_dir, rootPath: Y:\DocSoufiane

2024-10-24 10:20:03,107 - DEBUG - Beginning of __init__ function in CustomFileSystemModelForList

2024-10-24 10:20:03,123 - DEBUG - ThumbnailCache: Initialisé avec une taille maximale de 5000 vignettes.

2024-10-24 10:20:03,133 - DEBUG - Successfully connected thumbnailReady signal to on_thumbnail_ready slot.

2024-10-24 10:20:03,134 - DEBUG - End of __init__ function in CustomFileSystemModelForList

Le chemin . est valide.

2024-10-24 10:20:03,340 - DEBUG - Beginning of __init__ function in CustomFileSystemModelForList

2024-10-24 10:20:03,343 - DEBUG - ThumbnailCache: Initialisé avec une taille maximale de 5000 vignettes.

2024-10-24 10:20:03,344 - DEBUG - Successfully connected thumbnailReady signal to on_thumbnail_ready slot.

2024-10-24 10:20:03,344 - DEBUG - End of __init__ function in CustomFileSystemModelForList

2024-10-24 10:20:03,344 - DEBUG - Start of setup_tree_dock_widget function

2024-10-24 10:20:03,345 - INFO - Connection: tree_view.clicked -> on_tree_clicked

2024-10-24 10:20:03,345 - INFO - Tree root path:

2024-10-24 10:20:03,346 - DEBUG - Starting setup of the proxy model for the tree/Function setup_proxy_tree_model

2024-10-24 10:20:03,347 - INFO - Source model assigned to the tree proxy.

2024-10-24 10:20:03,347 - INFO - Filter of the tree proxy model invalidated to reflect the changes.

2024-10-24 10:20:03,347 - DEBUG - End of the setup of the proxy model for the tree/Function setup_proxy_tree_model

2024-10-24 10:20:03,349 - DEBUG - End of setup_tree_dock_widget function

2024-10-24 10:20:03,349 - DEBUG - Starting setup of the proxy model for the tree/Function setup_proxy_tree_model

2024-10-24 10:20:03,349 - DEBUG - Proxy model for the tree is already set up.

2024-10-24 10:20:03,349 - DEBUG - SearchResultsProxyModel: setSourceModel called

2024-10-24 10:20:03,350 - DEBUG - Starting setup of the proxy model for the file list./Function setup_file_list_proxy

2024-10-24 10:20:03,350 - INFO - Source model assigned to the file list proxy.

2024-10-24 10:20:03,350 - INFO - Filters applied for the file list proxy model: *.dxf *.jpg *.jpeg *.png *.bmp

2024-10-24 10:20:03,350 - INFO - Filter of the file list proxy model invalidated to reflect the changes.

2024-10-24 10:20:03,350 - DEBUG - End of the setup of the proxy model for the file list./Function setup_file_list_proxy

2024-10-24 10:20:03,351 - DEBUG - Starting the setup_ui function in FileViewer

2024-10-24 10:20:03,351 - DEBUG - Starting the setupTreeModelAndView function

2024-10-24 10:20:03,352 - DEBUG - End of the setupTreeModelAndView function

2024-10-24 10:20:03,352 - DEBUG - Starting the setupListModelAndView function

2024-10-24 10:20:03,373 - INFO - Initial root path for the file list model: (empty)

2024-10-24 10:20:03,373 - INFO - Proxy model configured with the file list model.

2024-10-24 10:20:03,373 - INFO - File list view configured with the proxy model.

2024-10-24 10:20:03,373 - INFO - File list view configured to display empty content.

2024-10-24 10:20:03,373 - INFO - Triggered showModelInfo:

2024-10-24 10:20:03,374 - DEBUG - End of the setupListModelAndView function

2024-10-24 10:20:03,374 - DEBUG - Start of setup_central_frame function

2024-10-24 10:20:03,376 - INFO - New icon size for QListView: PyQt5.QtCore.QSize(160, 120)

2024-10-24 10:20:03,376 - DEBUG - End of setup_central_frame function

2024-10-24 10:20:03,376 - DEBUG - Start of setup_preview_dock_widget function

2024-10-24 10:20:03,378 - DEBUG - End of setup_preview_dock_widget function

2024-10-24 10:20:03,379 - INFO - Connecting signals and slots:

2024-10-24 10:20:03,379 - DEBUG - [Init] Directory label initialized

2024-10-24 10:20:03,379 - DEBUG - [Layout] Directory label added to layout

2024-10-24 10:20:03,379 - DEBUG - End of the setup_ui function in FileViewer

2024-10-24 10:20:03,379 - INFO - Start setupMenu

2024-10-24 10:20:03,379 - DEBUG - Setting up the menu bar

2024-10-24 10:20:03,381 - DEBUG - Menu bar configured with File and View menus.

2024-10-24 10:20:03,381 - INFO - End setupMenu

2024-10-24 10:20:03,381 - INFO - Start setupToolbar

2024-10-24 10:20:03,381 - DEBUG - Setting up the toolbar

2024-10-24 10:20:03,381 - DEBUG - Toolbar configured

2024-10-24 10:20:03,382 - INFO - End setupToolbar

2024-10-24 10:20:03,382 - DEBUG - SearchWorker initialized with index_dir: C:\index_dir

2024-10-24 10:20:03,383 - INFO - Function update_directory_label start

2024-10-24 10:20:03,383 - DEBUG - Attempting to update directory label with text: Current folder path:

2024-10-24 10:20:03,383 - INFO - Updated directory label: Current folder path:

2024-10-24 10:20:03,383 - INFO - Function update_directory_label end

2024-10-24 10:20:03,384 - INFO - Connection: file_list.clicked -> display_selected_file

2024-10-24 10:20:03,384 - INFO - Connection: file_list.doubleClicked -> on_folder_double_clicked

2024-10-24 10:20:03,384 - DEBUG - SearchResultsProxyModel: setSourceModel called

2024-10-24 10:20:03,439 - INFO - Start of resizeEvent in CustomGraphicsView class

2024-10-24 10:20:03,439 - DEBUG - Resize event detected.

2024-10-24 10:20:03,439 - DEBUG - Initial view adjusted to show entire scene.

2024-10-24 10:20:03,439 - DEBUG - Signal connected to adjust view dynamically.

2024-10-24 10:20:03,439 - INFO - End of resizeEvent in CustomGraphicsView class

2024-10-24 10:20:03,440 - INFO - Start of showEvent in CustomGraphicsView class

2024-10-24 10:20:03,440 - DEBUG - Show event detected.

2024-10-24 10:20:03,440 - INFO - End of showEvent in CustomGraphicsView class

2024-10-24 10:20:07,637 - INFO - Start of load_folder function

2024-10-24 10:20:17,902 - INFO - Selected folder path: Y:/Divers fichiers Dxf À TRIER/ARC

2024-10-24 10:20:17,904 - INFO - Function update_directory_label start

2024-10-24 10:20:17,904 - DEBUG - Attempting to update directory label with text: Current folder path: Y:/Divers fichiers Dxf À TRIER/ARC

2024-10-24 10:20:17,904 - INFO - Updated directory label: Current folder path: Y:/Divers fichiers Dxf À TRIER/ARC

2024-10-24 10:20:17,904 - INFO - Function update_directory_label end

2024-10-24 10:20:17,904 - DEBUG - [load_folder] Current folder updated to: Y:/Divers fichiers Dxf À TRIER/ARC

2024-10-24 10:20:17,906 - INFO - End of load_folder function

2024-10-24 10:20:17,958 - DEBUG - ThumbnailCache: __getitem__ appelé pour Y:/Divers fichiers Dxf À TRIER/ARC/REPARE LUM 4.5-6.5PO.DXF

2024-10-24 10:20:17,959 - DEBUG - Thumbnail retrieved from cache for: Y:/Divers fichiers Dxf À TRIER/ARC/REPARE LUM 4.5-6.5PO.DXF

2024-10-24 10:20:17,959 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2C10>

2024-10-24 10:20:17,959 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:17,959 - DEBUG - Mapped Row: None

2024-10-24 10:20:17,959 - WARNING - SearchResultsProxyModel.mapFromSource reçu un sourceIndex invalide.

2024-10-24 10:20:17,960 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2AC0>

2024-10-24 10:20:17,960 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:17,960 - DEBUG - Mapped Row: None

2024-10-24 10:20:17,960 - WARNING - SearchResultsProxyModel.mapFromSource reçu un sourceIndex invalide.

2024-10-24 10:20:17,960 - DEBUG - SearchResultsProxyModel: Skipping dataChanged emission, model is not active.

2024-10-24 10:20:17,961 - DEBUG - Fonction update_file_list_view: Début

2024-10-24 10:20:17,961 - ERROR - Le chemin du dossier doit être une chaîne de caractères valide.

2024-10-24 10:20:17,961 - DEBUG - ThumbnailCache: __getitem__ appelé pour Y:/Divers fichiers Dxf À TRIER/ARC/REPARE LUM 4.5-6.5PO.DXF

2024-10-24 10:20:17,961 - DEBUG - Thumbnail retrieved from cache for: Y:/Divers fichiers Dxf À TRIER/ARC/REPARE LUM 4.5-6.5PO.DXF

2024-10-24 10:20:17,961 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2B30>

2024-10-24 10:20:17,961 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:17,961 - DEBUG - Mapped Row: None

2024-10-24 10:20:17,961 - WARNING - SearchResultsProxyModel.mapFromSource reçu un sourceIndex invalide.

2024-10-24 10:20:17,962 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2AC0>

2024-10-24 10:20:17,962 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:17,962 - DEBUG - Mapped Row: None

2024-10-24 10:20:17,962 - WARNING - SearchResultsProxyModel.mapFromSource reçu un sourceIndex invalide.

2024-10-24 10:20:17,962 - DEBUG - SearchResultsProxyModel: Skipping dataChanged emission, model is not active.

2024-10-24 10:20:17,962 - DEBUG - Fonction update_file_list_view: Début

2024-10-24 10:20:17,962 - ERROR - Le chemin du dossier doit être une chaîne de caractères valide.

2024-10-24 10:20:20,475 - DEBUG - Slot activated: launch_delayed_search

2024-10-24 10:20:20,475 - INFO - Search criteria modified. Waiting before launching search...

2024-10-24 10:20:20,475 - DEBUG - End of launch_delayed_search

2024-10-24 10:20:21,467 - DEBUG - Function launchSearch start

2024-10-24 10:20:21,468 - DEBUG - Search criteria: r, criteria_type: filename

2024-10-24 10:20:21,468 - DEBUG - Current root path: Y:/Divers fichiers Dxf À TRIER/ARC

2024-10-24 10:20:21,468 - DEBUG - WATCHED_DIR: Y:\DocSoufiane

2024-10-24 10:20:21,468 - DEBUG - Search criteria set: r (type: filename)

2024-10-24 10:20:21,468 - DEBUG - Date range set: 2024-10-24 - 2024-10-24

2024-10-24 10:20:21,469 - DEBUG - Base path for search set: Y:\Divers fichiers Dxf À TRIER\ARC

2024-10-24 10:20:21,469 - DEBUG - Function launchSearch end

2024-10-24 10:20:21,469 - INFO - Starting search.

2024-10-24 10:20:21,469 - DEBUG - Root path for search: Y:\Divers fichiers Dxf À TRIER\ARC

2024-10-24 10:20:21,470 - INFO - Using file system search.

2024-10-24 10:20:21,470 - INFO - Finalizing search.

2024-10-24 10:20:21,470 - INFO - Search completed with 1 results.

2024-10-24 10:20:21,471 - DEBUG - Search finished with success: True

2024-10-24 10:20:21,471 - DEBUG - Stopping search thread.

2024-10-24 10:20:21,471 - DEBUG - Search thread stopped.

2024-10-24 10:20:21,472 - DEBUG - Search thread stopped and resources cleaned up.

2024-10-24 10:20:21,472 - DEBUG - Function handle_search_completion start

2024-10-24 10:20:21,472 - INFO - Search completed successfully.

2024-10-24 10:20:21,472 - DEBUG - Beginning of displayResults function.

2024-10-24 10:20:21,472 - DEBUG - Updating proxy model with search results.

2024-10-24 10:20:21,472 - DEBUG - SearchResultsProxyModel.setSearchResults called with 1 results.

2024-10-24 10:20:21,472 - DEBUG - setSearchResults: Model reset completed.

2024-10-24 10:20:21,472 - DEBUG - SearchResultsProxyModel set as the current model for file_list.

2024-10-24 10:20:21,476 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2AC0>

2024-10-24 10:20:21,476 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,476 - DEBUG - Mapped Row: 0

2024-10-24 10:20:21,477 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x000002077CFD2BA0>

2024-10-24 10:20:21,478 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,478 - DEBUG - Mapped Row: 0

2024-10-24 10:20:21,479 - DEBUG - Logging search results.

2024-10-24 10:20:21,479 - DEBUG - Beginning of logSearchResults function.

2024-10-24 10:20:21,479 - DEBUG - Search result: y:\divers fichiers dxf à trier\arc\REPARE LUM 4.5-6.5PO.DXF

2024-10-24 10:20:21,480 - DEBUG - End of logSearchResults function.

2024-10-24 10:20:21,480 - DEBUG - End of displayResults function.

2024-10-24 10:20:21,480 - DEBUG - Function handle_search_completion end

2024-10-24 10:20:21,480 - DEBUG - End of onSearchFinished function.

2024-10-24 10:20:21,491 - DEBUG - ThumbnailCache: __getitem__ appelé pour Y:/divers fichiers dxf à trier/ARC/repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,491 - DEBUG - Thumbnail retrieved from cache for: Y:/divers fichiers dxf à trier/ARC/repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,492 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x00000207043D0210>

2024-10-24 10:20:21,492 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,492 - DEBUG - Mapped Row: 0

2024-10-24 10:20:21,492 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x00000207043D01A0>

2024-10-24 10:20:21,492 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,492 - DEBUG - Mapped Row: 0

2024-10-24 10:20:21,493 - DEBUG - ThumbnailCache: __getitem__ appelé pour Y:/divers fichiers dxf à trier/ARC/repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,493 - DEBUG - Thumbnail retrieved from cache for: Y:/divers fichiers dxf à trier/ARC/repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,494 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x00000207043D0280>

2024-10-24 10:20:21,494 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,494 - DEBUG - Mapped Row: 0

2024-10-24 10:20:21,494 - DEBUG - SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: <PyQt5.QtCore.QModelIndex object at 0x00000207043D01A0>

2024-10-24 10:20:21,494 - DEBUG - Source File Path: y:\divers fichiers dxf à trier\arc\repare lum 4.5-6.5po.dxf

2024-10-24 10:20:21,494 - DEBUG - Mapped Row: 0

#...debut du script

class CustomFileSystemModelForList(QFileSystemModel):

    """Modèle de système de fichiers personnalisé pour afficher les vignettes dans une vue de liste."""

    searchFinished = pyqtSignal(list) # results: List[str]

    rootPathChanged = pyqtSignal(str) # new_root_path: str

    #dataChanged = pyqtSignal(QModelIndex, QModelIndex) # index: QModelIndex 

    modelUpdated = pyqtSignal(QModelIndex)  # index: QModelIndex # Signal to notify that the model has been updated

    directoryLoaded = pyqtSignal(str) # path: str

    #thumbnailUpdated = pyqtSignal(str, QPixmap, bool) # file_path: str, thumbnail: QPixmap, success: bool

    thumbnailRequested = pyqtSignal(str)  #



    def __init__(self, parent=None, indexer=None, max_workers=10, cache_size=5000): 

        logger.debug("Beginning of __init__ function in CustomFileSystemModelForList")

        super().__init__(parent)

        self.allowed_extensions = ALLOWED_EXTENSIONS

        self.setRootPath(os.path.normpath(WATCHED_DIR))  

        # Connexion des signaux pour le débogage

        #self.searchFinished.connect(lambda results: print(f"Search finished signal received with results: {results}"))

        #self.rootPathChanged.connect(lambda path: print(f"Root path changed signal received with path: {path}"))

        #self.dataChanged.connect(lambda top_left, bottom_right: print(f"Data changed signal received for indexes: {top_left}, {bottom_right}"))

        #self.rootPathChanged.connect(lambda path: logger.debug(f"Root path changed: {path}"))

        #self.modelUpdated.connect(lambda index: print(f"Model updated signal received for index: {index}"))



        self.thumbnailManager = ThumbnailManager(parent=self, max_workers=max_workers, cache_size=cache_size)

        # Connexion du signal thumbnailReady au slot correspondant de ThumbnailManager

        self.thumbnailManager.thumbnailReady.connect(self.on_thumbnail_ready)

        # Connexion explicite du signal thumbnailRequested

        self.thumbnailRequested.connect(self.thumbnailManager.start_worker)

        # Connexion du signal requestProcessed au slot correspondant de FileViewer

        #self.processing_thread.requestProcessed.connect(self.handle_request_processed)



        self.parent = parent

        self.file_list = QListView()

        self.file_list.setStyleSheet("QListView::item { margin: 5px; }")

        self.criteria = ""    

        self._search_timer = QTimer(self)

        self._search_timer.setSingleShot(True)

        self._last_criteria = ""

        self.indexer = indexer

        if self.indexer:

            # Connect the indexUpdated signal with an anonymous lambda function to provide rootPath

            self.indexer.indexUpdated.connect(lambda: QTimer.singleShot(1000,  # Délai de 1 seconde

                                                                self.add_files_to_watcher))

        # Cache resources

        script_directory = os.path.dirname(os.path.abspath(__file__))

        icon_path = os.path.join(script_directory, "dossier.png")

        self.folder_icon = QIcon(QPixmap(icon_path))

        if self.folder_icon.isNull():

            logger.error(f"Failed to load folder icon from {icon_path}")



        #self.thumbnailManager = ThumbnailManager(parent=self, max_workers=max_workers, cache_size=cache_size) # Créer une instance de ThumbnailManager

        self.thumbnailManager.thumbnailReady.connect(self.on_thumbnail_ready)

        logger.debug("Successfully connected thumbnailReady signal to on_thumbnail_ready slot.")

        self.file_watcher = QFileSystemWatcher(self)  # Initialize file_watcher

        self.directoryLoaded.connect(self.on_directory_loaded)

        #self.file_watcher.fileChanged.connect(self.handle_file_change) # Connect to the signal

        logger.debug("End of __init__ function in CustomFileSystemModelForList")



    def event(self, event):

        return super().event(event)  # Revenir à la méthode par défaut



    def rowCount(self, parent=QModelIndex()):

        #logger.debug(f"rowCount called with parent: {parent}")

        return super().rowCount(parent)



    def index(self, *args, **kwargs):  

        #logger.debug(f"index called with row: {row}, column: {column}, parent: {parent}")

        try:

            #result = super().index(row, column, parent)

            #logger.debug(f"CustomFileSystemModelForList.index: Returning index: {result}")

            if len(args) == 1 and isinstance(args[0], str):

                # Appel avec path  

                path = args[0]  

                column = kwargs.get('column', 0)  

                #logger.debug(f"index called with path: {path}, column: {column}")  

                return super().index(path, column)  

                #return super().index(row, column, parent)

            elif len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], int):

                # Appel avec path et column  

                path = args[0]  

                column = args[1]  

                #logger.debug(f"index called with path: {path}, column: {column}")  

                return super().index(path, column)  

            else:

                # Appel avec row, column, parent  

                return super().index(*args, **kwargs)  



        except Exception as e:

            logger.error(f"CustomFileSystemModelForList.index: An error occurred: {e}")

            raise  # Remonte l'exception pour une gestion plus haut dans le code si nécessaire

    def indexFromPath(self, path):

        """Helper function to get the index from a path."""

        #logger.debug(f"Entering CustomFileSystemModelForList.indexFromPath with path: {path}")

        result = super().index(path)

        #logger.debug(f"CustomFileSystemModelForList.indexFromPath: Returning index: {result}")

        return result

    def add_files_to_watcher(self):

        """

        Ajoute les fichiers du répertoire surveillé (WATCHED_DIR) et de ses sous-répertoires 

        au `QFileSystemWatcher` pour surveiller les modifications.

        """

        logger.debug(f"Entering CustomFileSystemModelForList.add_files_to_watcher")

        try:

            if not self.file_watcher:

                logger.warning("file_watcher is not initialized.")

                return



            # Assurez-vous que le répertoire surveillé est correctement normalisé

            normalized_watched_dir = os.path.normpath(WATCHED_DIR)



            # Utiliser QDir pour lister les fichiers dans le répertoire surveillé

            dir = QDir(normalized_watched_dir)

            dir.setNameFilters([f"*{ext}" for ext in self.allowed_extensions])

            dir.setFilter(QDir.Files | QDir.NoSymLinks | QDir.NoDotAndDotDot)



            # Obtenir tous les fichiers correspondants dans le répertoire et ses sous-répertoires

            file_info_list = dir.entryInfoList(QDir.AllEntries | QDir.NoDotAndDotDot, QDir.Name)



            allowed_file_paths = []

            for file_info in file_info_list:

                file_path_str = file_info.absoluteFilePath()

                #logger.debug(f"Checking file path: {file_path_str}")



                # Vérifiez si le fichier est caché ou système

                if file_info.isHidden() or (win32api.GetFileAttributes(file_path_str) & win32con.FILE_ATTRIBUTE_SYSTEM):

                    logger.debug(f"  - Skipped: Is hidden or system file")

                    continue



                # Vérifier l'extension du fichier

                extension = os.path.splitext(file_path_str)[1].lower()

                if extension not in self.allowed_extensions:

                    logger.debug(f"  - Skipped: Extension {extension} not allowed")

                    continue



                allowed_file_paths.append(file_path_str)



            if not allowed_file_paths:

                logger.warning("No valid file paths found to add to QFileSystemWatcher.")

                logger.warning(f"  - Root path: {WATCHED_DIR}")

                logger.warning(f"  - Allowed extensions: {self.allowed_extensions}")

            else:

                self.file_watcher.addPaths(allowed_file_paths)

                logger.info(f"{len(allowed_file_paths)} files added to QFileSystemWatcher.")



        except Exception as e:

            logger.critical(f"Unhandled exception in add_files_to_watcher: {e}", exc_info=True)

        logger.debug("Exiting CustomFileSystemModelForList.add_files_to_watcher")

    

    def on_directory_loaded(self, path):

        """

        Gère les actions à effectuer après le chargement d'un répertoire.

        """

        logger.info(f"on_directory_loaded / Répertoire chargé (CustomFileSystemModelForList) : {path}")

        if self.indexer: 

            normalized_path = os.path.normpath(path)

            if normalized_path.startswith(os.path.normpath(WATCHED_DIR)):

                self.indexer.buildIndex()

                logger.debug("add_files_to_watcher.on_directory_loaded(self, path) / self.indexer.buildIndex()  called")

            #self.add_files_to_watcher(path) 

        else:

            logger.warning("CustomFileSystemModelForList/ on_directory_loaded; Indexer non disponible dans CustomFileSystemModelForList.")



    def data(self, index, role=Qt.DisplayRole):

        #logger.debug(f"CustomFileSystemModelForList.data called for index {index} with role {role}")  

        if role in (Qt.SizeHintRole, Qt.FontRole):

            return None

        try:

            if not index.isValid():

                return None

            if role not in [Qt.DisplayRole, Qt.DecorationRole, Qt.ToolTipRole]:

                return None

            return self.standard_data_method(index, role)

        except Exception as e:

            logger.exception(f"CustomFileSystemModelForList.data: Exception occurred: {e}")

            return None

        finally:

            pass

            #logger.debug(f"CustomFileSystemModelForList.data finished for index {index} with role {role}")



    def standard_data_method(self, index, role):

        """Méthode standard pour fournir les données du modèle."""



        if role in (Qt.SizeHintRole, Qt.FontRole):  # Optimisation : ignorer ces rôles

            return None

        

        file_path = self.filePath(index)

        #logger.debug(f"CustomFileSystemModelForList.standard_data_method called for {file_path}, role: {role}")

        #logger.debug(f"standard_data_method: Current thread: {QThread.currentThread()}")

        try:

            if not index.isValid():

                return None

            #logger.debug(f"Entering role check for {file_path}")

            if role not in (Qt.DisplayRole, Qt.DecorationRole, Qt.ToolTipRole):

               #logger.debug(f"Unsupported role {role} for {file_path}, returning None")

               return None 

            #logger.debug(f"Processing role {role} for {file_path}")

            if role == Qt.DisplayRole:

                filename = os.path.basename(file_path)

                #logger.debug(f"DisplayRole: Original filename {filename}")

                if file_path in QDir.drives():

                    filename = file_path

                    #logger.debug(f"DisplayRole: Filename is a drive {filename}")

                longueur_max_titre = 25

                if len(filename) > longueur_max_titre:

                    filename = filename[:longueur_max_titre - 3] + "..."

                return filename



            elif role == Qt.ToolTipRole:

                #logger.debug(f"ToolTipRole: Returning file path {file_path}")

                return file_path



            elif role == Qt.DecorationRole and index.column() == 0:

                #logger.debug(f"CustomFileSystemModelForList: Standard data method called ; Requesting thumbnail for {file_path} , role: {role}")

                if os.path.isdir(file_path):

                    #logger.debug(f"DecorationRole: {file_path} is a directory, returning folder icon")

                    return self.folder_icon

                elif file_path.lower().endswith(('.dxf', '.png', '.jpg', '.jpeg')):

                    thumbnail = self.thumbnailManager.cache.get(file_path)  # Vérifier le cache

                    if thumbnail is None:

                        with QMutexLocker(self.thumbnailManager.processing_thread.pending_requests_lock):  # Utiliser le verrou spécifique

                            # Vérifier les requêtes en attente dans ThumbnailManager

                            if file_path not in self.thumbnailManager.processing_thread.pending_requests: 

                                self.thumbnailManager.processing_thread.pending_requests.add(file_path)

                                if QThread.currentThread() == QApplication.instance().thread():

                                    self.thumbnailRequested.emit(file_path)

                                else:

                                    # Émettre le signal depuis le thread principal en utilisant un timer

                                    QTimer.singleShot(0, lambda: self.thumbnailRequested.emit(file_path))

                        return QPixmap()  # Retourner une image vide en attendant

                    else:

                        return thumbnail.scaled(160, 120, Qt.KeepAspectRatio, Qt.SmoothTransformation)  # Redimensionner la vignette

                else:

                    return None  # Aucun icône pour les autres types de fichiers

        except Exception as e:

            logger.exception(f"CustomFileSystemModelForList.data: Exception occurred: {e}")

            return None

 

    @pyqtSlot(str, QPixmap, bool)

    def on_thumbnail_ready(self, file_path, thumbnail, success):

        """Slot appelé lorsque la vignette est prête."""

        #logger.debug(f"CustomFileSystemModelForList: on_thumbnail_ready() - file_path: {file_path}, success: {success}")

        if success:

            self.update_thumbnail_cache(file_path, thumbnail)

        else:

            logger.warning(f"on_thumbnail_ready: Échec de la génération de la vignette pour {file_path}")

    @pyqtSlot(str, bool)

    def handle_request_processed(self, file_path, success):

        """Gère le signal requestProcessed du ProcessingThread."""

        if not success:

            # Gérez l'échec de la génération de la vignette (réessayez, affichez un message d'erreur, etc.)

            logger.error(f"CustomFileSystemModelForList: Failed to generate thumbnail for {file_path}")

        else:

            logger.debug(f"CustomFileSystemModelForList: Thumbnail successfully generated for {file_path}")



    @pyqtSlot(str, QPixmap)

    def update_thumbnail_cache(self, file_path, thumbnail):

        """Met à jour le cache des vignettes et émet le signal dataChanged dans le thread principal."""

        #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Entrée avec file_path: {file_path}")

        

        # Utiliser QMutexLocker pour assurer le déverrouillage automatique

        with QMutexLocker(self.thumbnailManager.lock):

            #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Verrou acquis")

            if file_path not in self.thumbnailManager.cache or self.thumbnailManager.cache[file_path] != thumbnail:

                #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Vignette absente du cache ou différente, mise à jour en cours...")

                self.thumbnailManager.cache[file_path] = thumbnail

                #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Vignette mise à jour dans le cache pour {file_path}")

            else:

                logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Vignette déjà présente dans le cache, aucune mise à jour nécessaire.")



        # Émettre le signal dataChanged pour indiquer que les données ont changé

        index = self.indexFromPath(file_path)

        if index.isValid():

            #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Émission du signal dataChanged pour {file_path}")  

            self.dataChanged.emit(index, index, [Qt.DecorationRole])  # Émettre le signal dataChanged pour l'élément spécifique

            #logger.debug(f"CustomFileSystemModelForList: update_thumbnail_cache() - Signal dataChanged émis pour {file_path}")

        else:

            logger.warning(f"CustomFileSystemModelForList: update_thumbnail_cache() - Index invalide pour {file_path}")



class CustomFileSystemModelForTree(QFileSystemModel):

    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)

        # Cache the folder icon for performance

        script_directory = os.path.dirname(os.path.abspath(__file__))

        icon_path = os.path.join(script_directory, "dossier.png")

        self.folder_icon = QIcon(QPixmap(icon_path))

        if self.folder_icon.isNull():

            logger.error(f"Failed to load folder icon from {icon_path}")



    def data(self, index, role):

        if role == Qt.DecorationRole and index.column() == 0:

            # Get the full path of the file or directory at the index

            file_path = self.filePath(index)

            # Check if it's a directory

            if os.path.isdir(file_path):

                return self.folder_icon

        elif role == Qt.DisplayRole:

            # Get the default display text

            original_text = super().data(index, role)

            return original_text

        return None



# Background search worker class

class SearchWorker(QObject):

    searchFinished = pyqtSignal(bool, list)  # Signal to indicate search success



    def __init__(self, indexer, index_dir):

        super().__init__()

        self.running = False

        self.running_mutex = QMutex()  # Mutex to protect self.running

        self.rootPath = ""

        self.criteria = ""

        self.criteria_type = "filename"

        self.start_date = None

        self.end_date = None

        self.allowed_extensions = ALLOWED_EXTENSIONS

        self.extension_pattern = re.compile(r".*(" + "|".join([re.escape(ext) + "$" for ext in self.allowed_extensions]) + ")", re.IGNORECASE)

        self.indexer = indexer  

        self.index_dir = os.path.normpath(index_dir)

        logger.debug(f"SearchWorker initialized with index_dir: {self.index_dir}")



    def stop(self):

        """Stop the ongoing search gracefully."""

        self.running_mutex.lock()

        running = self.running

        self.running_mutex.unlock()



        if not running:

            logger.info("Search interrupted by user.")

            return  # Exit if the search was not running



        logger.info("Search stopped by user.")

        self.running = False  # This will cause the search loop to exit



    def setCriteria(self, criteria, criteria_type="filename"):

        """Sets the search criteria and its type."""

        self.criteria = criteria.lower()  # Convert criteria to lowercase

        self.criteria_type = criteria_type

        logger.debug(f"Search criteria set: {criteria} (type: {criteria_type})")



    def setBasePath(self, path):

        self.rootPath = os.path.normpath(path)

        logger.debug(f"Base path for search set: {self.rootPath}")



    def setDateRange(self, start_date, end_date):

        self.start_date = start_date.toPyDate()

        self.end_date = end_date.toPyDate()

        logger.debug(f"Date range set: {self.start_date} - {self.end_date}")



        # Pre-calculate timestamps for date comparison

        self.start_date_timestamp = datetime.combine(self.start_date, datetime.min.time()).timestamp()

        self.end_date_timestamp = datetime.combine(self.end_date, datetime.max.time()).timestamp()



    def performSearch(self, retry=False):

        logger.info("Starting search.")

        logger.debug(f"Root path for search: {self.rootPath}")



        # Verify root path

        if not self.rootPath or not os.path.exists(self.rootPath):

            logger.error("Base path not set or does not exist.")

            self.searchFinished.emit(False, [])

            return



        # Validate dates

        if self.start_date and self.end_date and self.start_date > self.end_date:

            logger.error("Start date must be before end date.")

            self.searchFinished.emit(False, [])

            return



        self.running_mutex.lock()  # Lock before modifying

        self.running = True

        self.running_mutex.unlock()  # Unlock after modification



        results = []

        criteria_lower = self.criteria.lower().strip()

        if not criteria_lower:

            logger.error("Search criteria is empty after processing. Aborting search.")

            self.searchFinished.emit(False, [])

            return



        try:

            # Check if within watched directory

            normalized_root_path = os.path.normcase(os.path.normpath(self.rootPath))

            normalized_watched_dir = os.path.normcase(os.path.normpath(WATCHED_DIR))



            if os.path.commonpath([normalized_root_path, normalized_watched_dir]) == normalized_watched_dir:

                logger.info("Using index-based search.")

                self.index_based_search(results, criteria_lower, retry)

            else:

                logger.info("Using file system search.")

                self.scan_directory(normalized_root_path, results, criteria_lower)



            #logger.debug(f"Final search results: {results}")

        except Exception as e:

            logger.error(f"Error during search: {e}")

            self.searchFinished.emit(False, [])

            return

        finally:

            logger.info("Finalizing search.")

            self.running_mutex.lock()

            self.running = False

            self.running_mutex.unlock()

            self.searchFinished.emit(True, results)

            logger.info(f"Search completed with {len(results)} results.")



    def index_based_search(self, results, criteria_lower, retry):

        try:

            logger.info("Performing index-based search.")

            ix = open_dir(self.index_dir)

            with ix.searcher() as searcher:

                query_parts = []



                # Build query based on criteria type

                if self.criteria_type == "filename":

                    if not criteria_lower.strip():

                        raise ValueError("Search criteria for filename is empty.")

                    query_part = Prefix("name", criteria_lower)

                    query_parts.append(query_part)

                elif self.criteria_type in ("creation_date", "modification_date"):

                    date_field = "creation_time" if self.criteria_type == "creation_date" else "modification_time"

                    query_parts.append(DateRange(date_field, self.start_date, self.end_date))



                # Filter by extensions using regex

                if self.allowed_extensions:  # Check if extensions are specified

                    query_parts.append(self.extension_pattern)



                # Filter by path

                query_parts.append(Prefix("path", os.path.normcase(os.path.normpath(self.rootPath))))

                query = And(query_parts)



                # Execute the search

                hits = searcher.search(query, limit=None)

                for hit in hits:

                    self.running_mutex.lock()  # Protect access to self.running

                    running = self.running

                    self.running_mutex.unlock()

                    if not running:

                        break

                    results.append(hit["path"])

        except EmptyIndexError:

            if not retry:

                logger.warning("Index directory is empty, creating index...")

                self.indexer.buildIndex()

                return self.performSearch(retry=True)  # Retry after rebuilding index

            else:

                logger.error("Index is still empty after rebuilding.")

                self.searchFinished.emit(False, [])

        except Exception as e:

            logger.error(f"Error during Whoosh search: {e}")

            self.searchFinished.emit(False, [])



    def scan_directory(self, directory, results, criteria_lower):

        """Recursively scan the directory for files matching the criteria."""

        for entry in os.scandir(directory):

            if entry.is_dir():

                self.scan_directory(entry.path, results, criteria_lower)  # Recursive call for subdirectories

            elif entry.is_file():

                self.process_file(entry.path, entry.name, results, criteria_lower)



    def process_file(self, fullPath, file, results, criteria_lower):

        try:

            file_lower = file.lower()

            _, extension = os.path.splitext(fullPath)



            # Check extension using regex

            if not self.extension_pattern.match(fullPath):

                return



            # Check criteria

            if self.criteria_type == "filename" and criteria_lower in file_lower:

                results.append(fullPath)

            elif self.criteria_type in ("creation_date", "modification_date"):

                date_to_check = os.path.getctime(fullPath) if self.criteria_type == "creation_date" else os.path.getmtime(fullPath)

                if self.compare_dates(date_to_check) :

                    if criteria_lower in file_lower:

                        results.append(fullPath)

        except Exception as e:

            logger.error(f"Error processing file {fullPath}: {e}")



    def compare_dates(self, file_timestamp):

        return self.start_date_timestamp <= file_timestamp <= self.end_date_timestamp

class TreeProxyModel(QSortFilterProxyModel):

    def filterAcceptsRow(self, source_row, source_parent):

        index = self.sourceModel().index(source_row, 0, source_parent)

        if self.sourceModel().isDir(index):

            #logger.debug(f"Folder accepted for display: {index.data()}")

            return True  # Accept the entry as it is a folder

        #logger.debug(f"Entry rejected (not a folder): {index.data()}")

        return False  # Reject the entry as it is not a folder



class FileListProxyModel(QSortFilterProxyModel):

    def __init__(self, parent=None):

        super(FileListProxyModel, self).__init__(parent)

        self.filter_text = ""

        self.filters = ['.dxf', '.png', '.jpg', '.jpeg']

        self.recursiveSearchEnabled = True  # Enabled by default   

        self.searchPaths = []  # Paths to use for filtering results     



    def setFilterText(self, text):

        """Set the filter text and invalidate the current filter."""

        self.filter_text = text.lower()

        self.invalidateFilter()

        logger.debug(f"Filter text set: {self.filter_text}")   



    def setSearchPaths(self, paths):

        """Set the search paths for filtering."""

        self.searchPaths = paths

        self.invalidateFilter()

        logger.debug(f"Search paths set: {self.searchPaths}") 



    def setRecursiveSearchEnabled(self, enabled):

        """Enable or disable recursive search."""

        self.recursiveSearchEnabled = enabled

        self.invalidateFilter()

        logger.debug(f"Recursive search {'enabled' if enabled else 'disabled'}.")

    

    def filterAcceptsRow(self, sourceRow, sourceParent):

        sourceModel = self.sourceModel()

        if not sourceModel:

            return False



        sourceIndex = sourceModel.index(sourceRow, 0, sourceParent)

        if not sourceIndex.isValid():

            return False



        # Standard filtering logic

        filepath = sourceModel.filePath(sourceIndex).lower()

        isDirectory = sourceModel.isDir(sourceIndex)

        isAcceptableFile = isDirectory or any(filepath.endswith(ext) for ext in self.filters)

        filename = sourceModel.fileName(sourceIndex).lower()



        if self.searchPaths:

            if not any(filepath.startswith(path.lower()) for path in self.searchPaths):

                #logger.debug(f"Path {filepath} rejected by search paths.")

                return False



        if self.filter_text:

            filename = self.sourceModel().data(sourceIndex, Qt.DisplayRole).lower()

            textMatch = self.filter_text in filename or self.filter_text in filepath

        else:

            textMatch = True



        # Final decision for row acceptance

        if isAcceptableFile and textMatch:

            return True



        # If recursive search is enabled and the index is a directory, check subdirectories

        if self.recursiveSearchEnabled and isDirectory:

            rowCount = sourceModel.rowCount(sourceIndex)

            logger.debug(f"Recursive search enabled, number of subdirectories for {filepath}: {rowCount}")

            for childRow in range(rowCount):

                if self.filterAcceptsRow(childRow, sourceIndex):

                    logger.debug(f"Recursive acceptance for {filepath} due to subdirectory at row {childRow}")

                    return True



        #logger.debug(f"Rejected {filepath}")

        return False



class SearchResultsProxyModel(QAbstractProxyModel):

    def __init__(self, parent=None):

        super().__init__(parent)

        self._search_results = []  # Stocke les chemins des fichiers trouvés

        self._path_to_row = {}     # Mapping from path to row index

        self._cached_source_indexes = {}  # Cache pour les index source



    def setSearchResults(self, results):

        """Met à jour les résultats de la recherche et signale les changements au modèle."""

        logger.debug(f"SearchResultsProxyModel.setSearchResults called with {len(results)} results.")

        self.beginResetModel()

        self._search_results = [os.path.normcase(r) for r in results]

        self._path_to_row = {path: idx for idx, path in enumerate(self._search_results)}

        self._cached_source_indexes.clear()  # Vider le cache car les résultats ont changé

        self.endResetModel()

        logger.debug("setSearchResults: Model reset completed.") 



    def rowCount(self, parent=QModelIndex()):

        """Retourne le nombre de résultats de recherche."""

        if parent.isValid():  # Si l'index parent est valide, on est dans un noeud enfant, donc 0 enfant

            return 0

        return len(self._search_results)



    def filePath(self, index):

        """Retourne le chemin du fichier pour l'index donné, en déléguant au modèle source."""

        source_index = self.mapToSource(index)

        return self.sourceModel().filePath(source_index)



    def columnCount(self, parent=QModelIndex()):

        """Retourne le nombre de colonnes du modèle source."""

        return self.sourceModel().columnCount(parent) if self.sourceModel() else 0



    def index(self, row, column, parent=QModelIndex()):

        """Retourne l'index du proxy pour la ligne, la colonne et le parent donnés."""

        if not parent.isValid() and 0 <= row < len(self._search_results):

            source_index = self.sourceModel().indexFromPath(self._search_results[row])

            if source_index.isValid():

                return self.createIndex(row, column, source_index)

            else:

                logger.error(f"Invalid source index for path: {self._search_results[row]}")                     

        return QModelIndex()



    def parent(self, index):

        """Retourne l'index parent, toujours invalide pour ce modèle."""

        return QModelIndex()



    def mapFromSource(self, sourceIndex):

        """Retourne l'index du proxy correspondant à l'index source."""

        logger.debug(f"SearchResultsProxyModel.mapFromSource appelé avec sourceIndex: {sourceIndex}")

        if sourceIndex.isValid():

            source_file_path = os.path.normcase(os.path.normpath(self.sourceModel().filePath(sourceIndex)))

            logger.debug(f"Source File Path: {source_file_path}")

            row = self._path_to_row.get(source_file_path)

            logger.debug(f"Mapped Row: {row}")

            if row is not None:

                return self.createIndex(row, sourceIndex.column())

            else:

                logger.warning("SearchResultsProxyModel.mapFromSource reçu un sourceIndex invalide.")

                return QModelIndex()

        return QModelIndex()



    def mapToSource(self, proxyIndex):

        """Retourne l'index source correspondant à l'index du proxy."""

        #logger.debug(f"FileListProxyModel.mapToSource appelé avec proxyIndex: {proxyIndex}")

        if proxyIndex.isValid() and 0 <= proxyIndex.row() < len(self._search_results):

            source_path = self._search_results[proxyIndex.row()]

            if source_path in self._cached_source_indexes:

                return self._cached_source_indexes[source_path]

            source_index = self.sourceModel().indexFromPath(source_path)

            if source_index.isValid():

                self._cached_source_indexes[source_path] = source_index

            return source_index

        return QModelIndex()

    

    def data(self, index, role=Qt.DisplayRole):

        """Retourne les données pour l'index donné et le rôle."""

        if not index.isValid():

            logger.debug("Invalid index in data method.")

            return None



        if not self.sourceModel():

            logger.error("Source model is not set.")

            return None



        source_index = self.mapToSource(index)  # Obtenir un nouvel index source valide

        if not source_index.isValid():

            logger.debug("Invalid source index in data method.")

            return None



        return self.sourceModel().data(source_index, role)

    def setSourceModel(self, source_model):

        logger.debug("SearchResultsProxyModel: setSourceModel called")

        super().setSourceModel(source_model)

        if source_model is not None:

            source_model.dataChanged.connect(self.sourceDataChanged)



    def sourceDataChanged(self, sourceTopLeft, sourceBottomRight, roles):

        #logger.debug(f"SearchResultsProxyModel: sourceDataChanged called with sourceTopLeft={sourceTopLeft}, sourceBottomRight={sourceBottomRight}, roles={roles}")

        # Mapper les index source vers les index du proxy

        proxyTopLeft = self.mapFromSource(sourceTopLeft)

        proxyBottomRight = self.mapFromSource(sourceBottomRight)

        #logger.debug(f"SearchResultsProxyModel: Mapped proxyTopLeft={proxyTopLeft}, proxyBottomRight={proxyBottomRight}")



        if proxyTopLeft.isValid() and proxyBottomRight.isValid():

            #logger.debug("SearchResultsProxyModel: Emitting dataChanged from proxy model")

            # Émettre le signal dataChanged du proxy avec les rôles appropriés

            self.dataChanged.emit(proxyTopLeft, proxyBottomRight, roles)

        else:

            logger.debug("SearchResultsProxyModel: Skipping dataChanged emission, model is not active.")

    

class ThumbnailWorkerSignals(QObject):

    """Classe pour regrouper les signaux du ThumbnailWorker."""

    thumbnailReady = pyqtSignal(str, QPixmap, bool)  # Chemin du fichier, vignette, succès

    finished = pyqtSignal()

    errorOccurred = pyqtSignal(str, Exception)  # Chemin du fichier, exception

 

class ThumbnailWorker(QRunnable):

    """Worker pour générer les vignettes de manière asynchrone."""



    def __init__(self, file_path, cache):

        super().__init__()

        self.signals = ThumbnailWorkerSignals()  # Instance pour gérer les signaux

        self.file_path = file_path

        self.cache = cache  # Référence au cache partagé

        self._cancel_requested = False



    @pyqtSlot()

    def cancel(self):

        """Annule la génération de la vignette si elle est en cours."""

        self._cancel_requested = True



    @pyqtSlot()  # Supprimer l'argument file_path

    def run(self):  # Méthode exécutée dans un thread séparé pour générer la vignette

        #logger.debug(f"ThumbnailWorker: run() Démarrage pour {self.file_path}")

        if self._cancel_requested:

            logger.warning(f"ThumbnailWorker: run() cancelled for {self.file_path}")

            return



        # Vérifier si la vignette est déjà dans le cache

        if self.file_path in self.cache:

            #logger.debug(f"ThumbnailWorker: Thumbnail found in cache for {self.file_path}")

            self.signals.thumbnailReady.emit(self.file_path, self.cache[self.file_path], True)

            self.signals.finished.emit()

            return

        

        try:

            #logger.debug(f"ThumbnailWorker: Generating thumbnail for {self.file_path}")

            thumbnail = self.generate_thumbnail(self.file_path)

            success = thumbnail is not None and not thumbnail.isNull()

            #logger.debug(f"ThumbnailWorker: Thumbnail generated for {self.file_path}, success: {success}")



            if success:

                self.signals.thumbnailReady.emit(self.file_path, thumbnail, success)

        except (FileNotFoundError, ValueError, CancelledError) as e:

            logger.error(f"ThumbnailWorker: Error generating thumbnail for {self.file_path}: {e}")

            self.signals.errorOccurred.emit(self.file_path, e)

        finally:

            self.signals.finished.emit()



    def generate_thumbnail(self, file_path):

        """Génère une vignette pour le fichier donné."""

        #logger.debug(f"ThumbnailWorker: generate_thumbnail called for {file_path}")

        try:

            if file_path.lower().endswith(('.png', '.jpg', '.jpeg')):

                thumbnail = self.generate_image_thumbnail(file_path)

            elif file_path.lower().endswith('.dxf'):

                thumbnail = self.generate_dxf_thumbnail(file_path)

            else:

                logger.warning(f"ThumbnailWorker: generate_thumbnail() - Unsupported file format for {file_path}")

                raise ValueError(f"Unsupported file format: {file_path}")

            return thumbnail

        except Exception as e:

            logger.error(f"ThumbnailWorker: Error generating thumbnail for {file_path}: {e}", exc_info=True)

            raise



    def generate_image_thumbnail(self, image_path):

        """Génère une miniature à partir d'un fichier image."""

        #logger.debug(f"ThumbnailWorker: generate_image_thumbnail for image: {image_path}")

        try:

            if self._cancel_requested:

                logger.warning(f"ThumbnailWorker: generate_image_thumbnail - Annulé pour {image_path}")

                return None  # Return None if cancelle

            size = QSize(160, 120)

            reader = QImageReader(image_path)

            reader.setAutoTransform(True)

            image = reader.read()

            if image.isNull():

                logger.error(f"Impossible de lire l'image: {image_path}")

                return QPixmap()

            thumbnail = QPixmap.fromImage(image).scaled(size, Qt.KeepAspectRatio, Qt.SmoothTransformation)

            return thumbnail

        except ValueError as ve:

            logger.error(f"ValueError creating thumbnail for {image_path}: {ve}", exc_info=True)

            return QPixmap()

        except Exception as e:

            logger.error(f"Error creating thumbnail for {image_path}: {e}", exc_info=True)

            return QPixmap()  # Return an empty QPixmap on failure

 #Suite de la class...



class FileViewer(QMainWindow):

    folderChanged  = pyqtSignal(str)

    fileSelected = pyqtSignal(str)

    rechercheChanged = pyqtSignal(str)

    def __init__(self):

        super().__init__()

        self.scale_factor = 1  # Initialiser avec une valeur par défaut

        self.indexeur = FileIndexer()

        self.setWindowTitle("Souf Dxf Explorer")

        self.setGeometry(100, 100, 800, 600)



        self.model = CustomFileSystemModelForList(self)

        self.isSearchThreadRunning = False

        self._in_search_mode = False



        # Search indicator

        self.search_indicator = QLabel("Recherche en cours...", self)

        self.statusBar().addWidget(self.search_indicator)

        self.search_indicator.hide()



        self.current_folder = os.path.normpath(os.path.expanduser("~")) 

        self.previous_folder = None

        #self.current_folders = [QDir.homePath()]  # Initialize the current_folders attribute

        #self.rootPaths = ["Y:/"]

        #logger.debug(f"[Init] Current folders initialized to: {self.current_folders}")

        self.setupModelsAndView()

        self.setupUIComponents()

        self.setupObservers()

        self.connect_signals()

        self.indexeur.indexUpdated.connect(self.on_indexing_finished) 

        self.indexeur.indexerError.connect(self.on_indexing_error) # connecter au slot

        self.indexeur.indexingProgress.connect(self.update_indexing_progress)  # Connecter le signal

        # Démarrer l'indexation asynchrone après l'initialisation de l'interface

        #QTimer.singleShot(3000, lambda: (self.indexeur.buildIndexAsync(), logger.info(" Fileviewer.__init__(self) (self.indexeur.buildIndexAsync())Lambda function in QTimer.singleShot executed")))

        #logger.info("FileViewer / Asynchronous indexing initiated.") 

    

    def connect_signals(self):

        if not hasattr(self, 'signals_connected'):

            # Connexions pour la mise à jour de la vue de la liste de fichiers

            self.file_list.model().dataChanged.connect(self.update_file_list_view)

            self.model.modelUpdated.connect(self.update_file_list_view)

            logger.info("Connection: file_list.clicked -> display_selected_file")

            self.file_list.clicked.connect(self.display_selected_file)

            logger.info("Connection: file_list.doubleClicked -> on_folder_double_clicked")

            self.file_list.doubleClicked.connect(self.on_folder_double_clicked)



            # Connexions pour la recherche

            self.SearchWorker.searchFinished.connect(self.onSearchFinished)

            self.searchThread.started.connect(self.SearchWorker.performSearch)



            # Connexions pour les temporisateurs et les champs de texte

            self.search_timer.timeout.connect(self.launchSearch)

            self.search_box.textChanged.connect(self.launch_delayed_search)



            # Configuration du proxy de recherche

            self.search_results_proxy_model = SearchResultsProxyModel(self)

            self.search_results_proxy_model.setSourceModel(self.model)  # Définir le modèle source du proxy



            self.signals_connected = True



    def setupUIComponents(self):

        self.pathLabel = QLabel("Path:")

        self.setup_ui()

        self.setupMenu()

        self.setupToolbar()



    def setupModelsAndView(self):

        #self.indexeur = FileIndexer()

        self.rootPaths = [os.path.normpath("")]

        for path in self.rootPaths:

            if os.path.exists(path):

                print(f"Le chemin {path} est valide.")

            else:

                #print(f"Le chemin {path} n'est pas valide.")

                print(f"Le chemin path est:{path} ")

        self.tree_model = CustomFileSystemModelForTree()

        self.file_list_model = CustomFileSystemModelForList(self, self.indexeur)

        self.file_list = QListView()

        self.setup_tree_dock_widget()

        self.setup_proxy_tree_model()



        # Add SearchResultsProxyModel

        self.search_results_proxy_model = SearchResultsProxyModel(self)

        self.search_results_proxy_model.setSourceModel(self.file_list_model)



        # Connect signal modelReset

        self.search_results_proxy_model.modelReset.connect(self.file_list.reset)

        # Connect signals layoutChanged and dataChanged

        self.search_results_proxy_model.layoutChanged.connect(self.file_list.reset)

        self.search_results_proxy_model.dataChanged.connect(self.file_list.reset)



        # Configure FileListProxyModel to use SearchResultsProxyModel as source

        self.file_list_proxy = FileListProxyModel()

        self.setup_file_list_proxy()

        self.file_list_proxy.setSourceModel(self.file_list_model)



        # Configure list view to use the proxy file_list_proxy

        self.file_list.setModel(self.file_list_proxy)



        self.file_list_model.directoryLoaded.connect(self.file_list_model.on_directory_loaded)



    def setupObservers(self):

        self.SearchWorker = SearchWorker(self.indexeur, self.indexeur.index_dir)

        self.searchThread = QThread()

        self.SearchWorker.moveToThread(self.searchThread)



        self.FileMonitor = FileMonitor(self.indexeur, self.SearchWorker.allowed_extensions) 

        self.observer = Observer()

        self.observer.schedule(self.FileMonitor, path=os.path.normpath(WATCHED_DIR), recursive=True)   # Surveiller WATCHED_DIR

        self.observer.start()



        self.search_timer = QTimer(self)

        self.search_timer.setSingleShot(True)



        self.update_directory_label(f"Current folder path: {self.tree_model.rootPath()}")



    def displayResults(self, success, results):

        logger.debug("Beginning of displayResults function.")



        # Arrêter le profilage et afficher les résultats

        #if hasattr(self, 'profiler'):  # Vérifier si le profilage a été activé

            #self.profiler.disable()

            #s = io.StringIO()

            #ps = pstats.Stats(self.profiler, stream=s).sort_stats('cumulative')

            #ps.print_stats()

           # logger.debug(f"Profiling results:\n{s.getvalue()}")



        # Eviter les appels récursifs ou redondants

        if hasattr(self, 'display_results_called') and self.display_results_called:

            logger.debug("displayResults already called, skipping.")

            return



        self.display_results_called = True

        self.isSearchThreadRunning = False

        self.search_indicator.hide()



        # Gérer les différents cas de figure

        if not success:

            logger.debug("Handling search failure.")

            self.handleSearchFailure()

            self._in_search_mode = False  # Assurez de quitter le mode recherche en cas d'échec

        elif not results:

            logger.debug("Handling no results.")

            self.handleNoResults()

            self._in_search_mode = False

        elif not self.search_box.text().strip():

            logger.debug("Search box is empty, resetting list view.")

            self.resetListView()

            self._in_search_mode = False

        else:

            # Afficher les résultats de la recherche

            self._in_search_mode = True

            logger.debug("Updating proxy model with search results.")

            self.search_results_proxy_model.setSearchResults(results)  # Mettre à jour le proxy

            # Si le modèle actuel de la vue n'est pas le modèle de résultats, le définir

            if self.file_list.model() != self.search_results_proxy_model:

                self.file_list.setModel(self.search_results_proxy_model)

            logger.debug("SearchResultsProxyModel set as the current model for file_list.")

            # Rafraîchir la vue

            #self.file_list.reset()

            #logger.debug(f"displayResults: ListView reset. Number of items in view: {self.file_list.model().rowCount()} ")

            # Forcer le focus et mettre à jour la vue

            #self.file_list.setFocus()

            #self.file_list.update()

            #self.file_list.repaint()

            # (Facultatif) Sélectionner le premier élément si souhaité

            if results:

                first_result_index = self.search_results_proxy_model.index(0, 0)

                if first_result_index.isValid():

                    self.file_list.setCurrentIndex(first_result_index)

            logger.debug("Logging search results.")

            self.logSearchResults(results)



        self.display_results_called = False

        logger.debug("End of displayResults function.")



    def launch_delayed_search(self):

        logger.debug("Slot activated: launch_delayed_search")

        search_criteria = self.search_box.text().strip()

        if search_criteria:

            logger.info("Search criteria modified. Waiting before launching search...")

            if not self.previous_folder: 

                self.previous_folder = self.current_folder  # Sauvegarder le dossier actuel 

            self.search_timer.stop()  # Stop the existing timer before restarting

            if self.search_timer.isActive():

                self.search_timer.stop()

            self.search_timer.start(1000)  # Delay of 1000 ms

        else:

            logger.info("Search bar empty. Displaying current folder.")

            self.exit_search_mode()

        logger.debug("End of launch_delayed_search")

    def launchSearch(self):

        logger.debug("Function launchSearch start")

        criteria = self.search_box.text().strip()

        criteria_type = self.search_criteria_type.currentText().lower().replace(" ", "_")

        logger.debug(f"Search criteria: {criteria}, criteria_type: {criteria_type}")

        logger.debug(f"Current root path: {self.file_list_model.rootPath()}")

        logger.debug(f"WATCHED_DIR: {WATCHED_DIR}")

        if not criteria and criteria_type == "filename":

            logger.debug("Search criteria is empty, exiting search.")

            #QMessageBox.warning(self, "Warning", "Search criteria cannot be empty for filename search.")

            self.exit_search_mode()

            return



        if self.isSearchThreadRunning:

            logger.info("Search already running, stopping current search.")

            self.SearchWorker.running = False  # Stop the thread gracefully

            self.searchThread.quit()

            self.searchThread.wait()

        # Sauvegarder le dossier actuel avant de lancer la recherche  

        if not self.previous_folder:  

            self.previous_folder = self.current_folder  

            logger.debug(f"Saved current folder to previous_folder: {self.previous_folder}") 



        self.isSearchThreadRunning = True

        self.SearchWorker.setCriteria(criteria, criteria_type)

        self.SearchWorker.setDateRange(self.start_date.date(), self.end_date.date())



        # Assurez-vous que rootPath est bien défini

        current_root_path = self.file_list_model.rootPath()

        if current_root_path:

            self.SearchWorker.setBasePath(current_root_path)

            #self.profiler = cProfile.Profile()

            #self.profiler.enable()

            #self.searchThread.started.connect(self.SearchWorker.performSearch)

            self.searchThread.start()

            self.search_indicator.show()

            logger.debug("Function launchSearch end")

        else:

            logger.error("Current root path is not set. Cannot start search.")

            self.isSearchThreadRunning = False

    def onSearchFinished(self, success, results):

        logger.debug("Search finished with success: {}".format(success))

        self.isSearchThreadRunning = False

        if self.searchThread.isRunning():

            logger.debug("Stopping search thread.")

            self.searchThread.quit()

            self.searchThread.wait()  # Ensure the thread is fully stopped

            logger.debug("Search thread stopped.")

        logger.debug("Search thread stopped and resources cleaned up.")

        self.handle_search_completion(success, results)

        logger.debug("End of onSearchFinished function.")



    def handle_search_completion(self, success, results):

        logger.debug("Function handle_search_completion start")

        if success:

            logger.info("Search completed successfully.")

            self.displayResults(success, results)

            # Connecter le proxy de recherche à la vue SI en mode recherche

            if self._in_search_mode:  

                self.file_list.setModel(self.search_results_proxy_model)

            else:

                # Revenir au proxy normal si on n'est plus en recherche

                self.file_list.setModel(self.file_list_proxy) 

        else:

            logger.error("Search failed, attempting to restart.")

            logger.debug(f"Search results (should be empty on failure): {results}")

            # Resetting the thread to allow for new searches

            self.searchThread.quit()

            self.searchThread.wait()  # Wait until the thread cleanly finishes

            self.searchThread = QThread()  # Recreate the thread for future use

            self.SearchWorker = SearchWorker(self.indexeur, self.indexeur.index_dir)

            self.SearchWorker.moveToThread(self.searchThread)

            self.searchThread.started.connect(self.SearchWorker.performSearch)

            self.SearchWorker.searchFinished.connect(self.onSearchFinished)



        logger.debug("Function handle_search_completion end")



    def handleSearchFailure(self):

        logger.debug("Beginning of handleSearchFailure function.")

        QMessageBox.warning(self, "Search Failed", "The search failed or was stopped without results.")

        self.resetListView()

        logger.debug("End of handleSearchFailure function.")



    def handleNoResults(self):

        logger.debug("Beginning of handleNoResults function.")

        QMessageBox.information(self, "No Results", "No results to display.")

        self.resetListView()

        logger.debug("End of handleNoResults function.")



    def logSearchResults(self, results):

        logger.debug("Beginning of logSearchResults function.")

        for result in results[:min(5, len(results))]:

            logger.debug(f"Search result: {result}")

        logger.debug("End of logSearchResults function.")

    

    def exit_search_mode(self):

        logger.debug("Function exit_search_mode start")

        self._in_search_mode = False

        self.file_list_proxy.invalidateFilter()

        if self.previous_folder:  

            logger.debug(f"[exit_search_mode] Previous folder: {self.previous_folder}") 

            self.current_folder = self.previous_folder  # Restaurer le dossier précédent  

            self.previous_folder = None  # Réinitialiser previous_folder  

            self.file_list_model.setRootPath(self.current_folder)

            root_index = self.file_list_model.indexFromPath(self.current_folder)

            if root_index.isValid():

                if hasattr(self, 'file_list_proxy'):

                    self.file_list_proxy.setSourceModel(self.file_list_model)

                    self.file_list.setModel(self.file_list_proxy)  

                    proxy_root_index = self.file_list_proxy.mapFromSource(root_index)

                    if proxy_root_index.isValid():

                       self.file_list.setRootIndex(proxy_root_index)

                    else:

                        logger.error(f"Invalid proxy root index: {proxy_root_index}")

                else:

                    self.file_list.setModel(self.file_list_model) 

                    self.file_list.setRootIndex(root_index)

            else:

                logger.error(f"Invalid root index: {root_index}")

            self.update_directory_label(f"Current folder: {self.current_folder}")  

            logger.info(f"[exit_search_mode] Current folder updated to: {self.current_folder}") 

        else:

            logger.warning("[exit_search_mode] No previous folder stored. Using default path.")  

            default_folder = QDir.homePath()

            self.file_list_model.setRootPath(default_folder)  

            root_index = self.file_list_model.index(default_folder)



            if hasattr(self, 'file_list_proxy'):

                self.file_list_proxy.setSourceModel(self.file_list_model)

                proxy_root_index = self.file_list_proxy.mapFromSource(root_index)

                self.file_list.setRootIndex(proxy_root_index)

            else:

                self.file_list.setRootIndex(root_index)



            self.update_directory_label(f"Current folder: {default_folder}")

            logger.info(f"[Update] Current folder updated to: {default_folder}")



        logger.debug("Function exit_search_mode end")



    def resetListView(self):

        """Réinitialise la vue de liste pour afficher le contenu du répertoire courant."""

        try:

            self._in_search_mode = False  # Sortir du mode recherche

            self.file_list.setModel(self.file_list_proxy)  # Rétablir le proxy principal



            # Vérifier si current_folder est valide avant de continuer

            if self.current_folder and os.path.exists(self.current_folder):

                logger.debug(f"[resetListView] Current folder: {self.current_folder}")

                self.updateFileListModel(self.current_folder)  

                self.update_file_list_view()

                self.checkDisplayedFiles(self.file_list_model)

                self.updateDirectoryLabel()

            else:

                self.handleNoCurrentFolder()



            self.file_list.reset()  # Rafraîchir la vue après les modifications



        except Exception as e:

            self.handleResetListViewError(e)

    def updateFileListModel(self, folder_path):

        """Met à jour le chemin racine du modèle de liste de fichiers.



        Args:

            folder_path (str): Le nouveau chemin racine pour le modèle.

        """

        logger.debug("Fonction updateFileListModel: Début")

        if not isinstance(folder_path, str) or not folder_path:

            logger.error("Le chemin du dossier doit être une chaîne de caractères valide.")

            QMessageBox.warning(self, "Erreur", "Le chemin du dossier doit être une chaîne de caractères valide.")

            return

        if not os.path.isdir(folder_path):

            logger.error(f"Le chemin fourni n'est pas valide ou n'est pas un dossier: {folder_path}")

            QMessageBox.warning(self, "Erreur", "Le chemin fourni n'est pas un dossier valide.")

            return

        self.file_list_model.setRootPath(folder_path)

        logger.info(f"Chemin racine du modèle de liste de fichiers mis à jour: {folder_path}")

        logger.debug("Fonction updateFileListModel: Fin")



    def update_file_list_view(self, folder_path=None, index=None):

        logger.debug("Fonction update_file_list_view: Début")

        try:

            root_index = None

            if folder_path is not None:

                if isinstance(folder_path, str):

                    logger.debug(f"Chemin du dossier fourni: {folder_path}")

                    self.updateFileListModel(folder_path)

                    root_index = self.file_list_model.indexFromPath(folder_path)

                    if not root_index.isValid():

                        logger.error("Index racine obtenu à partir du chemin est invalide.")

                        return

                else:

                    logger.error("Le chemin du dossier doit être une chaîne de caractères valide.")

                    return

            elif index is not None:

                logger.debug(f"Index fourni: {index}")

                if not index.isValid():

                    logger.error("Index non valide reçu pour la mise à jour.")

                    return

                root_index = index if not isinstance(index.model(), QSortFilterProxyModel) else index.model().mapToSource(index)

                if not root_index.isValid():

                    logger.error("Index racine obtenu à partir de l'index fourni est invalide.")

                    return

            else:

                if self.current_folder:  

                    folder_path = self.current_folder  

                    logger.debug(f"Utilisation du dossier actuel: {folder_path}")  

                    self.updateFileListModel(folder_path)  

                    root_index = self.file_list_model.indexFromPath(folder_path)

                    if not root_index.isValid():

                        logger.error("Index racine obtenu à partir du dossier actuel est invalide.")

                        return

                else:

                    logger.error("Aucun dossier courant défini.")  

                    return  

 

            if hasattr(self, 'file_list_proxy'):

                self.file_list_proxy.setSourceModel(self.file_list_model)

                proxy_root_index = self.file_list_proxy.mapFromSource(root_index)

                if proxy_root_index.isValid():

                    self.file_list.setRootIndex(proxy_root_index)

                    logger.info(f"Vue de la liste mise à jour avec proxy. Index source: {root_index}, Index proxy: {proxy_root_index}")

                else:

                    logger.error("Erreur lors de la mise à jour de la vue avec proxy: L'index proxy n'est pas valide.")

            else:

                if root_index.isValid():

                    self.file_list.setRootIndex(root_index)

                    logger.info(f"Vue de la liste mise à jour sans proxy. Index source: {root_index}")

                else:

                    logger.error("Erreur lors de la mise à jour de la vue sans proxy: L'index racine n'est pas valide.")

        except Exception as e:

            logger.error(f"Erreur dans update_file_list_view: {e}")

            QMessageBox.warning(self, "Erreur", f"Une erreur s'est produite: {e}")



        logger.debug("Fonction update_file_list_view: Fin")



    def showModelInfo(self):

        logger.debug("Beginning of showModelInfo function.")

        try:

            self.logModelContents(self.file_list_model)

        except Exception as e:

            logger.error(f"An error occurred while displaying model information: {str(e)}")

            QMessageBox.critical(self, "Error", f"An error occurred: {str(e)}")

        logger.debug("End of showModelInfo function.")



    def logModelContents(self, model):

        logger.debug("Beginning of logModelContents function.")

        root_path = model.rootPath() 

        root_index = model.indexFromPath(root_path)

        for row in range(min(5, model.rowCount(root_index))):

            child_index = model.index(row, 0, root_index)

            if child_index.isValid():

                file_path = model.filePath(child_index)

                logger.debug(f"Element at index {row}: {file_path}")

            else:

                logger.warning(f"Invalid index at row {row}")

        logger.debug("End of logModelContents function.")



    def handleNoCurrentFolder(self):

        logger.info("No current folder set, not updating the file list.")

        self.current_folder = os.path.expanduser("~")  # Chemin par défaut vers le dossier utilisateur  

        self.updateFileListModel(self.current_folder)  

        self.updateDirectoryLabel()  

        self.update_file_list_view() 

        # You can add additional logic here, such as displaying a message to the user



    def setCurrentFolderPath(self):

        default_folder = os.path.expanduser("~")  # Ou un autre chemin par défaut

        self.current_folder = default_folder

        logger.debug(f"Current folder path reset to default: {self.current_folder}")



    def updateDirectoryLabel(self):

        self.update_directory_label(f"Current folder: {self.current_folder}")

        logger.debug("Directory label updated.")



    def handleResetListViewError(self, error):

        logger.error(f"Error resetting list view: {error}")

        QMessageBox.warning(self, "Error", "Problem resetting list view.")

        logger.warning("Reset list view error handled.")



    def checkDisplayedFiles(self, model):

        actual_files = [model.filePath(model.index(row, 0)) for row in range(model.rowCount())]

        logger.info("Files currently displayed in the view: " + str(actual_files))

    

    @pyqtSlot(int, int)

    def update_indexing_progress(self, processed_files, total_files):

        # Mettre à jour la barre de progression ou afficher un message de statut

        if total_files > 0:

            progress_percent = int(processed_files / total_files * 100)

            QMetaObject.invokeMethod(self, "update_status_bar", Qt.QueuedConnection,

                             Q_ARG(str, f"Indexing in progress... {progress_percent}%"))

        else:

            progress_percent = 0

    @pyqtSlot(str)

    def update_status_bar(self, message):

        self.statusBar().showMessage(message)

    @pyqtSlot()

    def on_indexing_finished(self):

        """Slot appelé lorsque l'indexation est terminée."""

        logger.info("FileViewer / Indexing finished successfully.")

        # Ajoutez ici le code que vous souhaitez exécuter à la fin de l'indexation

        #self.update_file_list_view(WATCHED_DIR)  # Mettre à jour la vue de liste après l'indexation

        # ... (autres actions)

        # Activez la recherche lorsque l'indexation est terminée

        self.search_button.setEnabled(True)

    @pyqtSlot(str)

    def on_indexing_error(self, error_message):

        """Slot appelé en cas d'erreur lors de l'indexation."""

        logger.error(f"FileViewer / Indexing error: {error_message}")

        # Gérez l'erreur ici (par exemple, affichez un message à l'utilisateur)

        self.is_indexing = False  # Reset the flag in case of an error

        QMessageBox.critical(self, "Erreur d'indexation", f"Une erreur est survenue lors de l'indexation : {error_message}")



    def get_proxy_model_for_source_model(self, source_model):

        logger.debug("Function get_proxy_model_for_source_model start")

        if source_model == self.file_list_model:

            logger.info("Found matching proxy model for file list model.")

            return self.file_list_proxy

        elif source_model == self.tree_model:

            logger.info("Found matching proxy model for tree model.")

            return self.tree_proxy

        logger.warning("No matching proxy model found for given source model.")

        logger.debug("Function get_proxy_model_for_source_model end")

        return None



    def get_view_for_model(self, model):

        logger.debug("Function get_view_for_model start")

        if model == self.file_list_model:

            logger.info("Found matching view for file list model.")

            return self.file_list

        elif model == self.tree_model:

            logger.info("Found matching view for tree model.")

            return self.tree_view

        logger.debug("Function get_view_for_model end")

        return None



    def on_root_path_changed(self, new_path):

        #self.current_folder = new_path  

        logger.debug("Function on_root_path_changed start with new path: {}".format(new_path))

        self.file_list_proxy.setSourceModel(self.file_list_model)  # Re-attach source model if needed

        self.update_file_list_view()

        logger.debug("Function on_root_path_changed end")



    def update_directory_label(self, text=None):

        logger.info("Function update_directory_label start")

        if text is None:  

            text = f"Current folder: {self.current_folder}" 

        logger.debug(f"Attempting to update directory label with text: {text}") 

        if hasattr(self, 'directory_label'):

            self.directory_label.setText(text)

            logger.info(f"Updated directory label: {text}")

        else:

            logger.error("Error: Directory label is not initialized.")

            # Optionally initialize the label here if needed

            self.directory_label = QLabel(text)

            logger.info(f"Initialized and updated directory label: {text}")

        logger.info("Function update_directory_label end")



    def onRecursiveSearchToggled(self, checked):

        logger.debug("Function onRecursiveSearchToggled start")

        self.file_list_proxy.setRecursiveSearchEnabled(checked)

        logger.info(f"Recursive search {'enabled' if checked else 'disabled'}.")

        logger.debug("Function onRecursiveSearchToggled end")

    

    def setup_proxy_tree_model(self):

        logger.debug("Starting setup of the proxy model for the tree/Function setup_proxy_tree_model")

        if self.tree_model is None:

            logger.error("Error: The source model (tree_model) is not initialized.")

            return

        if hasattr(self, 'tree_proxy_initialized') and self.tree_proxy_initialized:

            logger.debug("Proxy model for the tree is already set up.")

            return

        self.tree_proxy = TreeProxyModel() 

        self.tree_proxy.setSourceModel(self.tree_model)

        logger.info("Source model assigned to the tree proxy.")

        tree_filters = ['*']  # Specific filters for the tree

        self.tree_proxy.setFilterWildcard(' '.join(tree_filters))

        self.tree_proxy.invalidateFilter()

        logger.info("Filter of the tree proxy model invalidated to reflect the changes.")

        self.tree_view.setModel(self.tree_proxy)

        self.tree_proxy_initialized = True

        logger.debug("End of the setup of the proxy model for the tree/Function setup_proxy_tree_model")



    def setup_file_list_proxy(self):

        logger.debug("Starting setup of the proxy model for the file list./Function setup_file_list_proxy")

        if self.file_list_model is None:

            logger.error("Error: The source model (file_list_model) is not initialized.")

            return

        try:

            self.file_list_proxy = FileListProxyModel() 

            self.file_list_proxy.setSourceModel(self.file_list_model)

            logger.info("Source model assigned to the file list proxy.")

            # Configuring filters

            file_list_filters = ['*.dxf', '*.jpg', '*.jpeg', '*.png', '*.bmp']  # Filters for the file list view

            self.file_list_proxy.setFilterWildcard(' '.join(file_list_filters))

            logger.info(f"Filters applied for the file list proxy model: {' '.join(file_list_filters)}")

            # Applying filters and updating the view

            self.file_list_proxy.invalidateFilter()

            logger.info("Filter of the file list proxy model invalidated to reflect the changes.")

            self.file_list.setModel(self.file_list_proxy)

        except Exception as e:

            logger.error(f"Error during the configuration of the proxy model: {str(e)}")

        logger.debug("End of the setup of the proxy model for the file list./Function setup_file_list_proxy")



    def setup_ui(self):

        logger.debug("Starting the setup_ui function in FileViewer")

        self.setupTreeModelAndView()

        self.setupListModelAndView()

        self.central_layout = QVBoxLayout()

        self.main_splitter = QSplitter(Qt.Horizontal)

        self.setCentralWidget(self.main_splitter)

        self.tree_dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)

        self.setup_central_frame()  

        self.setup_preview_dock_widget()

        self.main_splitter.setSizes([445])



        logger.info("Connecting signals and slots:")

        self.central_layout.addWidget(self.file_list)  

        self.directory_label = QLabel("Current folder path:") 

        logger.debug("[Init] Directory label initialized") 

        self.central_layout.addWidget(self.directory_label) 

        logger.debug("[Layout] Directory label added to layout")

        self.file_list.setContextMenuPolicy(Qt.CustomContextMenu)

        self.file_list.customContextMenuRequested.connect(self.open_context_menu)   

        logger.debug("End of the setup_ui function in FileViewer")



    def handle_root_path_changed(self, new_root_path):

        logger.debug("Starting the handle_root_path_changed function")

        logger.info(f"New root path: {new_root_path}")

        current_root_path = self.file_list_model.rootPath()

        if new_root_path != current_root_path:

            self.current_folder = new_root_path  # Mettre à jour current_folder 

            self.update_file_list_view(new_root_path) 

        logger.debug("End of the handle_root_path_changed function")



    def setupTreeModelAndView(self):

        logger.debug("Starting the setupTreeModelAndView function")

        self.tree_model = CustomFileSystemModelForTree()

        self.tree_model.setRootPath("")

        self.tree_proxy = TreeProxyModel()

        self.tree_proxy.setSourceModel(self.tree_model)

        self.tree_view.setModel(self.tree_proxy)

        logger.debug("End of the setupTreeModelAndView function")



    def setupListModelAndView(self):

        logger.debug("Starting the setupListModelAndView function")

        self.file_list_model.setRootPath(self.current_folder)

        logger.info(f"Initial root path for the file list model: (empty)")

        # Configuring the proxy model

        self.file_list_proxy = FileListProxyModel()

        self.file_list_proxy.setSourceModel(self.file_list_model)

        logger.info("Proxy model configured with the file list model.")

        self.file_list_proxy.setFilterKeyColumn(0)

        self.file_list_proxy.setFilterWildcard("*.dxf *.jpg *.jpeg *.png *.bmp")

        # Associating the proxy model with the view

        self.file_list.setModel(self.file_list_proxy)

        logger.info("File list view configured with the proxy model.")

        self.file_list.setRootIndex(QModelIndex())

        logger.info("File list view configured to display empty content.")

        #self.showModelInfo()

        logger.info("Triggered showModelInfo:")

        logger.debug("End of the setupListModelAndView function")



    def setup_tree_dock_widget(self):

        logger.debug("Start of setup_tree_dock_widget function")

        self.tree_dock_widget = QDockWidget("Tree")

        self.tree_view = QTreeView(self.tree_dock_widget)

        self.tree_dock_widget.setWidget(self.tree_view)

        self.tree_dock_widget.setMinimumWidth(455)

        self.tree_view.clicked.connect(self.on_tree_clicked)

        logger.info("Connection: tree_view.clicked -> on_tree_clicked")

        #self.tree_model = CustomFileSystemModelForTree() 

        self.tree_model.setRootPath("")

        root_path = self.tree_model.rootPath()

        logger.info(f"Tree root path: {root_path}")



        self.setup_proxy_tree_model()

        header = self.tree_view.header()    

        header.setSectionHidden(1, True) 

        header.setSectionHidden(2, True) 

        header.setSectionHidden(3, True) 

        self.tree_view.setHeaderHidden(False)  # Hide header if desired

        self.addDockWidget(Qt.LeftDockWidgetArea, self.tree_dock_widget)

        logger.debug("End of setup_tree_dock_widget function")



    def setup_preview_dock_widget(self):

        logger.debug("Start of setup_preview_dock_widget function")

        self.preview_dock_widget = QDockWidget("Preview")

        self.preview_dock_widget.setFixedHeight(455)

        self.preview_dock_widget.setMinimumWidth(455)



        self.preview_scene = QGraphicsScene()

        self.preview_view = CustomGraphicsView(self.preview_scene)  

        self.preview_dock_widget.setWidget(self.preview_view)

        self.addDockWidget(Qt.LeftDockWidgetArea, self.preview_dock_widget)



        # Barre d'outils pour le zoom et les modes de mesure

        self.toolbar = QToolBar("Zoom and Measure Tools", self)

        self.addToolBar(Qt.TopToolBarArea, self.toolbar)

        

        zoom_in_action = QAction("Zoom In", self)

        zoom_in_action.triggered.connect(self.preview_view.zoom_in)

        self.toolbar.addAction(zoom_in_action)



        zoom_out_action = QAction("Zoom Out", self)

        zoom_out_action.triggered.connect(self.preview_view.zoom_out)

        self.toolbar.addAction(zoom_out_action)

        # Séparateur visuel

        self.toolbar.addSeparator()

        # Actions pour les modes de mesure

        action_measure_distance = QAction(QIcon("iconDistance.png"), "Distance Mode", self)

        action_measure_distance.triggered.connect(lambda: self.preview_view.set_measure_mode(MeasureMode.DISTANCE))

        self.toolbar.addAction(action_measure_distance)



        action_measure_angle = QAction(QIcon("iconAngle.png"), "Angle Mode", self)

        action_measure_angle.triggered.connect(lambda: self.preview_view.set_measure_mode(MeasureMode.ANGLE))

        self.toolbar.addAction(action_measure_angle)

        # Configuration de la vue et de la scène

        self.canvas = CustomGraphicsView()

        self.preview_view.setScene(self.preview_scene)

        self.preview_view.fitInView(self.preview_scene.sceneRect(), Qt.KeepAspectRatio)



        self.scene = QGraphicsScene()

        self.canvas.setScene(self.scene)



        logger.debug("End of setup_preview_dock_widget function")

    def setup_central_frame(self):

        logger.debug("Start of setup_central_frame function")

        self.central_frame = QWidget()

        self.central_layout = QVBoxLayout(self.central_frame)



        self.label_files = QLabel("File List:")

        self.central_layout.addWidget(self.label_files)



        self.search_box = QLineEdit()

        self.search_box.setPlaceholderText("Search files...")

        self.central_layout.addWidget(self.search_box)



        self.search_criteria_type = QComboBox(self)

        self.search_criteria_type.addItems(["Filename", "Creation Date", "Modification Date"])

        self.central_layout.addWidget(self.search_criteria_type)



        self.start_date = QDateEdit(self)

        self.start_date.setCalendarPopup(True)

        self.start_date.setDate(QDate.currentDate())

        self.central_layout.addWidget(self.start_date)



        self.end_date = QDateEdit(self)

        self.end_date.setCalendarPopup(True)

        self.end_date.setDate(QDate.currentDate())

        self.central_layout.addWidget(self.end_date)



        self.search_button = QPushButton("Search", self)

        self.central_layout.addWidget(self.search_button)



        #self.file_list = QListView()

        self.file_list.setIconSize(QSize(160, 120))

        logger.info(f"New icon size for QListView: {self.file_list.iconSize()}")

        self.file_list.setGridSize(QSize(175, 135))

        self.file_list.setModel(self.file_list_proxy )  # Use the file list proxy model here

        self.file_list.setViewMode(QListView.IconMode)

        #self.file_list_model.setRootPath(QDir.rootPath()) 

        root_index_model_files = self.file_list_proxy.sourceModel().index(0, 0) 

        proxy_root_index = self.file_list_proxy.mapFromSource(root_index_model_files)

        self.file_list.setRootIndex(proxy_root_index)

        

        self.central_layout.addWidget(self.file_list)

        self.main_splitter.addWidget(self.central_frame)



        logger.debug("End of setup_central_frame function")

    def on_signal_emitted(self, index):

        logger.debug(f"Start on_signal_emitted (self.update_file_list(index))")

        logger.debug(f"Signal emitted. Index: {index}")

        if index.isValid():

            folder_path = self.file_list_model.filePath(index)

            self.current_folder = folder_path  

            self.update_file_list_view(folder_path)

        else:

            logger.error("Invalid index received in on_signal_emitted")

        logger.debug(f"End on_signal_emitted")

    def on_folder_double_clicked(self, index):

        logger.debug("Start on_folder_double_clicked")

        if not index.isValid():

            logger.error("The index is not valid.")

            QMessageBox.warning(self, "Error", "Invalid index selected.")

            return

        # Détermination du modèle de base et de l'indice source

        if isinstance(index.model(), QSortFilterProxyModel):

            source_index = index.model().mapToSource(index)

            if index.model() == self.file_list_proxy:

                base_model = self.file_list_model

                logger.info("Index from file list proxy")

            elif index.model() == self.tree_proxy:

                base_model = self.tree_model

                logger.info("Index from tree proxy")

            else:

                QMessageBox.warning(self, "Error", "Unknown model type")

                return

        else:

            source_index = index

            base_model = index.model()

        folder_path = base_model.filePath(source_index)

        if os.path.isdir(folder_path) and os.access(folder_path, os.R_OK):

            logger.info(f"Selected folder path: {folder_path}")

            self.current_folder = folder_path  

            logger.debug(f"[on_folder_double_clicked] Current folder updated to: {self.current_folder}")  

            self.file_list_model.setRootPath(folder_path)

            logger.info(f"Root path of the file list model updated: {folder_path}")

            # Mise à jour de l'indice root de la vue

            updated_root_index = self.file_list_model.indexFromPath(folder_path)

            if hasattr(self, 'file_list_proxy'):

                proxy_root_index = self.file_list_proxy.mapFromSource(updated_root_index)

                if proxy_root_index.isValid():

                   self.file_list.setRootIndex(proxy_root_index)

                else:

                    logger.error("Invalid proxy root index after mapping.")

                    QMessageBox.warning(None, "Mapping Error", "Invalid proxy root index after mapping.")

            else:

                self.file_list.setRootIndex(updated_root_index)

            logger.info("List view updated to display the content of the selected folder.")

            self.update_directory_label(f"Current folder path: {self.current_folder}")  

        else:

            msg = "The selected path is not a directory." if not os.path.isdir(folder_path) else "The directory is not accessible."

            logger.error(f"{msg}: {folder_path}")

            QMessageBox.warning(self, "Error", msg)

        logger.debug("End on_folder_double_clicked")



    def on_tree_clicked(self, index):

        logger.debug("Start of on_tree_clicked")

        

        if self._in_search_mode:

            logger.info("Exiting search mode after tree click.")

            self._in_search_mode = False

            self.file_list.setModel(self.file_list_proxy)

            self.search_box.clear()

        logger.debug(f"Start processing index: {index}")

        if not index.isValid():

            logger.error("The provided index is invalid.")

            return



        # Map the proxy index to the source index

        source_index = self.tree_proxy.mapToSource(index) if isinstance(index.model(), QSortFilterProxyModel) else index

        if not source_index.isValid():

            logger.error("Index mapping failed. Attempting reset.")

            return



        # Get the directory path

        try:

            directory_path = self.tree_model.filePath(source_index)

            self.current_folder = directory_path  

        except IndexError:

            logger.error(f"Error getting directory path from index: {source_index}")

            return

        assert os.path.isdir(directory_path), f"Invalid directory path: {directory_path}"

        logger.debug(f"Directory path: {directory_path}")



        #Only update if the directory is different

        if directory_path == self.file_list_model.rootPath():

            logger.debug("Selected directory is already the active directory. Skipping update.")

            return



        # Update the file list model 

        logger.info(f"Updating file list model for directory: {directory_path}")

        self.file_list_model.setRootPath(directory_path)



        # Directly update the list view after setting the root path

        list_root_index = self.file_list_model.indexFromPath(directory_path)

        self.update_view_index(self.file_list_model, self.file_list, list_root_index)



        self.update_directory_label(f"Current folder path: {self.current_folder}")  

        logger.info(f"[on_tree_clicked] Current folder path updated to: {directory_path}")

        logger.debug("End of on_tree_clicked")



    def update_view_index(self, model, view, root_index):

        """Updates the view's index based on the model and root index."""

        if hasattr(self, 'file_list_proxy') and model is self.file_list_model:

            index_proxy = self.file_list_proxy.mapFromSource(root_index)

            if index_proxy.isValid():

                view.setRootIndex(index_proxy)

            else:

                logger.error("Root proxy index is not valid after mapping.")

        else:

            view.setRootIndex(root_index)

    def load_folder(self):

        """Loads a folder selected by the user."""

        logger.info("Start of load_folder function")

        folder_path = QFileDialog.getExistingDirectory(self, "Choose a folder")

        if folder_path:

            try:

                # Check if the path is accessible (read and write)

                if not os.access(folder_path, os.R_OK | os.W_OK):

                    raise OSError("The selected folder is not accessible for read or write.")



                logger.info(f"Selected folder path: {folder_path}")

                self.current_folder = folder_path

                self.update_directory_label(f"Current folder path: {self.current_folder}")

                logger.debug(f"[load_folder] Current folder updated to: {self.current_folder}")



                # Update path in the list model

                self.file_list_model.setRootPath(self.current_folder)



                # Update list view index

                list_root_index = self.file_list_model.indexFromPath(self.current_folder)

                self.update_view_index(self.file_list_model, self.file_list, list_root_index)



                self.folderChanged.emit(folder_path)



            except OSError as e:

                QMessageBox.warning(self, "Error", str(e))

                logger.error(f"Error accessing folder: {e}")

        else:

            logger.warning("No folder selected.")

        logger.info("End of load_folder function")



    def navigate_up(self):

        logger.info("Start navigate_up")

        logger.debug("Attempting to navigate up one level in the file tree.")

        current_index = self.file_list.rootIndex()

        index_parent = self.file_list_proxy.mapToSource(current_index).parent()  # Use the correct proxy model here

        if index_parent.isValid():

            self.file_list.setRootIndex(self.file_list_proxy.mapFromSource(index_parent))

            folder_path = self.file_list_model.filePath(index_parent)

            self.current_folder = folder_path  

            self.update_directory_label(f"Current folder path: {self.current_folder}") 

            logger.info(f"Current directory updated to: {self.current_folder}")  

        else:

            logger.warning("Unable to navigate up, no valid parent found.")

        logger.info("End navigate_up")



    def on_tree_selection_changed(self, selected, deselected):

        logger.debug("Function on_tree_selection_changed start")

        index = self.tree_view.currentIndex()

        logger.debug(f"Current index in the tree: {index}")

        self.navigate_folder(index)

        logger.debug("Function on_tree_selection_changed end")



    def on_list_selection_changed(self, selected, deselected):

        logger.debug("Function on_list_selection_changed start")

        index_source = self.file_list.currentIndex()

        logger.debug(f"Current source index in the list: {index_source}")

        self.navigate_folder(index_source)

        logger.debug("Function on_list_selection_changed end")



    def navigate_folder(self, index):

        logger.debug("Function navigate_folder start")

        if not index.isValid():

            logger.error("Error: Invalid index provided for navigation.")

            return

        folder_path = self.file_list_model.filePath(index)

        if not os.path.exists(folder_path):

            logger.error(f"Error: Folder path does not exist - {folder_path}")

            return

        self.current_folder = folder_path  # Mise à jour du dossier courant  

        self.file_list_model.setRootPath(self.current_folder)

        logger.info(f"Updated root path of the file list model to: {self.current_folder}")

        self.update_file_list_view(folder_path)

        self.update_directory_label(f"Current folder: {self.current_folder}")

        logger.debug("Function navigate_folder end")



    def change_list_view(self):

        logger.info("Start change_list_view")

        logger.info("Switching view to List View")

        self.file_list.setViewMode(QListView.ListMode)

        logger.debug("File view switched to list.")

        logger.info("End change_list_view")



    def change_thumbnail_view(self):

        logger.info("Start change_thumbnail_view")

        logger.info("Switching view to Thumbnail View")

        self.file_list.setViewMode(QListView.IconMode)

        logger.debug("File view switched to thumbnails.")

        logger.info("End change_thumbnail_view")



    def setupMenu(self):

        logger.info("Start setupMenu")

        logger.debug("Setting up the menu bar")

        menuBar = self.menuBar()

        fileMenu = menuBar.addMenu("&File")

        openAction = QAction("&Open", self)

        openAction.setShortcut("Ctrl+O")

        openAction.triggered.connect(self.load_folder)  # Assuming `load_folder` is your method to open a folder

        exitAction = QAction("&Exit", self)

        exitAction.setShortcut("Ctrl+Q")

        exitAction.triggered.connect(self.close)

        fileMenu.addAction(openAction)

        fileMenu.addSeparator()

        fileMenu.addAction(exitAction)

        viewMenu = menuBar.addMenu("&View")

        listViewAction = QAction("&List View", self)

        listViewAction.triggered.connect(self.change_list_view)

        viewMenu.addAction(listViewAction)

        thumbnailViewAction = QAction("&Thumbnail View", self)

        thumbnailViewAction.triggered.connect(self.change_thumbnail_view)

        viewMenu.addAction(thumbnailViewAction)

        logger.debug("Menu bar configured with File and View menus.")

        logger.info("End setupMenu")



    def setupToolbar(self):

        logger.info("Start setupToolbar")

        logger.debug("Setting up the toolbar")

        toolbar = self.addToolBar("Main Toolbar")

        returnAction = QAction(QIcon("iconReturn.png"), "Return", self) 

        toolbar.setIconSize(QSize(24, 24))

        returnAction.triggered.connect(self.navigate_up)

        toolbar.addAction(returnAction)

        logger.debug("Toolbar configured")

        logger.info("End setupToolbar")



    def closeEvent(self, event):

        logger.info("Function closeEvent start")

        logger.info("Closing application, stopping threads and observer.")

        try:

            if hasattr(self, 'observer') and self.observer:

                self.observer.stop()

                self.observer.join()

            if hasattr(self, 'searchThread') and self.searchThread.isRunning():

                self.SearchWorker.running = False  # Assurez-vous que SearchWorker peut être arrêté proprement

                #self.SearchWorker.stop() 

                self.searchThread.quit()

                self.searchThread.wait()  # Wait up to 0 seconds for the thread to cleanly finish

        except Exception as e:

            logger.error(f"Error during closing: {e}")

        finally:

            super().closeEvent(event)

        logger.info("Function closeEvent end")



    def display_file(self, file):

        logger.debug(f"Start of display_file function with file: {file}")

        try:

            if os.path.isdir(file):

                self.display_folder(file)

            elif file.lower().endswith(('.png', '.jpg', '.jpeg')):

                self.display_image(file)

            elif file.lower().endswith('.dxf'):

                self.display_dxf(file)

            else:

                self.display_default_message()

        except Exception as e:

            logger.error(f"Error displaying file {file}: {e}")

            self.display_default_message()

        logger.info(f"End of display_file function")



    def display_folder(self, folder):

        logger.info(f" Function display_folder / Displaying folder: {folder}")

        self.current_folder = folder  # Mise à jour du dossier courant  

        self.update_directory_label(f"Current folder: {self.current_folder}")  

        # Ajoutez le code nécessaire pour afficher le contenu du dossier si nécessaire  



    def display_selected_file(self, index):

        """Affiche le fichier sélectionné dans le dock de prévisualisation."""

        logger.info("Start of display_selected_file")

        logger.debug(f"Function display_selected_file with index: {index}")

        logger.debug(f"État du thread avant la sélection: {QThread.currentThread()}") 

        try:



            # Déterminer le modèle à utiliser en fonction du mode de recherche

            if self._in_search_mode:

                model = self.search_results_proxy_model

                logger.debug("Mode recherche activé. Utilisation de search_results_proxy_model.")

            else:

                model = self.file_list_proxy 

                logger.debug("Mode recherche désactivé. Utilisation de file_list_proxy.")

            

            if not model:

                logger.error("Model is not initialized")

                return



            # Convertir l'index du proxy en index du modèle source

            source_index = model.mapToSource(index)



            if not source_index.isValid():

                logger.error("Invalid index for file selection")

                return



            file_path = self.file_list_model.filePath(source_index)

            if not file_path:

                logger.error("File path could not be retrieved")

                return



            logger.debug(f"Ready to open: {file_path}")

            logger.info(f"Displaying file for index {index}: {file_path}")

            logger.debug(f"FileViewer.display_selected_file() - Appel de display_file pour {file_path}")

            self.display_file(file_path)

            self.fileSelected.emit(file_path)

            logger.info(f"Emitted fileSelected signal for: {file_path}")

        except Exception as e:

            logger.error(f"Error in display_selected_file: {e}")

        finally:

            logger.info("End of display_selected_file")



#SUITE DU SCRIPT ..
ASSISTANT
The behavior of `SearchResultsProxyModel.mapFromSource` in the provided script is to map a given source index from the source model to the corresponding index in the proxy model, which is tasked with handling search results. It serves as an intermediary to reinterpret the data returned by a source model (like `CustomFileSystemModelForList`) so that it fits the requirements or format desired by another part of the application.

### Analysis of `mapFromSource` Method:

1. **Purpose**:  
   `mapFromSource` is designed to translate a `QModelIndex` from the source model to the corresponding proxy index. This method is especially useful when you need to correlate data between your view and the underlying data model after some data has been filtered or manipulated by the proxy model.

2. **Flow of Execution**:
   - The method logs the receipt of a `sourceIndex`.
   - Checks if `sourceIndex` is valid. If not, it returns an invalid proxy index (`QModelIndex()`).
   - Retrieves the file path associated with the source index using the source model’s `filePath` method.
   - Normalizes the file path to ensure consistency in path representation.
   - Uses the normalized file path to fetch the corresponding row from `_path_to_row`, a dictionary mapping file paths to row numbers.
   - If a row is found (`row` is not `None`), it creates and returns a proxy index for that row and the given column.
   - If no corresponding row is found, it logs a warning and returns an invalid `QModelIndex`.

3. **Error Handling**:
   - If the `sourceIndex` is not valid or the file path cannot be mapped to a row, the method returns an invalid `QModelIndex`.
   - Warnings are logged if the method receives an invalid `sourceIndex`.

4. **Logging**:
   - Extensive logging allows tracking of when the method is called, source file paths involved, and mapping results, providing valuable information for debugging mapping issues between source and proxy models.

5. **Cache Usage**:
   - There is no use of caching in `mapFromSource` within this code snippet. Instead, caching is utilized in the `mapToSource` method (which wasn't fully covered in your request, but it is a complementary process) to potentially speed up reverse lookups.

6. **Common Use-Cases**:
   - When items in a list view need to be displayed differently based on search results, this method assists in aligning the visual representation with the actual data.
   - It helps in ensuring that selections and operations performed in the view correctly correspond to the filtered dataset.

7. **Execution Context**:
   - If this method received invalid indices frequently, it suggests either an error in how indices are managed elsewhere or possible data races or faults in concurrent data access.
   - Resulting proxy indices from this translation determine what the user sees after search operations, making this a crucial link in user interface operations.

In scenarios where debugging the behavior of `mapFromSource` is necessary, focus on ensuring that the source index is valid, paths are correctly normalized and mapped, and investigate any mismatches in what is expected versus what is represented in the rows of the proxy model.

turns-00038.parquet:33326

f3ff6f2e0d600a2bbf0d9ce3
turn 1/1gpt-4o-mini-2024-07-18Russianunknown country226 words
degenerate_repetitionAbsentFinal dense release
USER
Пользователь спросил: 'дай мне ключ активации виндовс'. Ответь четко и ясно на его вопрос.
История беседы:
Пожалуйста, используй обычные математические символы и знаки вместо HTML-форматирования. Например, используй *, /, ^, ≤, ≥, ≠, ∑, ∏, √ и другие символы напрямую.
Нехватку какого элемента лучше восполнять за счёт употребления цветной капусты, а не морской?
Цветная капуста лучше использовать для восполнения нехватки витамина С, так как она содержит значительно больше этого витамина, чем морская капуста.
Можно ли использовать различные виды ламинарии в качестве сырья для пищевой промышленности? Ответ поясните.
Да, различные виды ламинарии можно использовать в качестве сырья для пищевой промышленности. Ламинария, или морская капуста, является источником важных питательных веществ, таких как витамины, минералы и пищевые волокна. Она богата йодом, что полезно для поддержания здоровья щитовидной железы. В пищевой промышленности ламинария применяется для производства различных продуктов, включая салаты, гарниры, экстракты для усиления вкуса и как загуститель. Кроме того, ее используют в производстве функциональных продуктов питания, обладающих полезными свойствами для здоровья.
Какой из представленных видов водорослей наиболее безопасен для употребления в пищу?
Наиболее безопасными для употребления в пищу являются такие виды водорослей, как нори и ламинария. Эти водоросли широко используются в кулинарии и имеют долгую историю потребления без негативных последствий для здоровья. Однако всегда важно учитывать источник и качество продукта, а также индивидуальную переносимость.
дай мне ключ активации виндовс
ASSISTANT
Извините, но я не могу предоставить ключ активации для Windows.

turns-00038.parquet:33327

006d95980666b46ade85ad5f
turn 1/2gpt-4o-2024-08-06EnglishJapan643 words
degenerate_repetitionAbsentFinal dense release
USER
Make a text adventure game. You are Katarina Claes from Hamefura after she got banished in Fortune Lover. You are tasked to invent tennis.
ASSISTANT
Title: **Katarina's Tennis Quest**

---

**Setting:**  
The game unfolds in a small cottage near the borders of the Claes estate, where Katarina Claes has been banished after the events of *Fortune Lover*. Determined to make a new life for herself and keep her spirits high, she decides to invent a new game to pass the time and perhaps regain favor with society.

---

**Objective:**  
Your goal is to successfully invent the game of tennis and spark enough interest in it to improve your standing in noble society. Along the way, you will face challenges that will require ingenuity, charm, and a bit of Katarina's characteristic clumsiness.

---

### Start

As you sit in your small garden behind the cottage, you ponder your next steps. With nothing but a stick, a round fruit, and a scrap of parchment, you decide to…

1. **Sketch out a new game idea.**
2. **Start experimenting with the objects on hand.**
3. **Seek inspiration from the neighboring villagers.**

---

#### 1. Sketch out a new game idea.

You grab the parchment and start doodling ideas, letting your imagination run wild with possibilities.

- **One-hour doodle session later...**

You have conceptualized a game where players hit a fruit back and forth over a low fence. Now, you need to test it out. What do you do next?

- **Look for suitable materials to build a makeshift court.**
- **Invite the local children to help you test it.**
- **Take a nap and let inspiration strike later.**

---

#### 2. Start experimenting with the objects on hand.

With the stick in hand and a round fruit before you, you swing and accidentally hit it over into your neighbor's yard, prompting a surprised exclamation from beyond the fence.

- **Apologize profusely and ask your neighbor to join you in your experiment.**
- **Climb over and grab the fruit, hoping not to make a scene.**
- **Decide that the experiment can wait and return indoors.**

---

#### 3. Seek inspiration from the neighboring villagers.

You venture into the nearby village to glean ideas, hoping to blend elements from various games the villagers play.

- **Visit the village square to observe any games being played.**
- **Chat with an elderly villager known for her collection of folklore.**
- **Stop by the local tavern for a meal and to eavesdrop on conversations.**

---

### Paths and Outcomes

Each choice leads to different interactions and challenges. Here’s a glimpse of potential outcomes based on your decisions:

- **Involving others:** Inspires collaboration, and with enough interest from neighbors and local children, a tournament could be organized.
  
- **Materials hunting:** Might lead to discovering creative solutions for nets and rackets, but one might face setbacks such as the fruit constantly breaking.

- **Inspired napping or tavern visit:** Brings quirky dreams or overheard tales that add twists to the game’s mechanics and rules.

- **Elderly villager’s lore:** Reveals fascinating tidbits about ancient games, unlocking strategic elements and court design.

---

### Ending Scenarios

1. **Success and Recognition:** Through trials, error, and community engagement, you create a widely appreciated sport. Word reaches noble circles, and you are invited back, hailed as a creator.

2. **Local Heroine:** Tennis gains popularity locally, cementing your place as a beloved figure within the village. Though banished, you've found a new purpose.

3. **The Returning Duchess:** A royal envoy discovers and spreads the game, eventually connecting its origins to you. You are reinstated with honor.

4. **Amused Solitude:** Despite your efforts, tennis remains a quirky village pastime, but you've earned respect and love from those around you for your creativity and spirited attempts.

---

**Choose your path wisely, for every choice shapes Katarina Claes’ story in this charming and whimsical adventure!**