How can I append a folder directory with many sub directories to a QTreeView using QStandardItemModel in PyQt6 - python-3.x

I am building a script that takes a path and gets the folders. Then appends those folder names as items to another item that I am calling "Root". I am using QStandardItemModel and not QFileSystemModel because I intend to replace "Root" with a name that the user will provide and I am using QTreeView as a preview to the entire structure of the folder.
Current issue:
I would like to add the entire directory and include the sub directories but have it all appended under "Root". In my attempt below I am only getting the first set of folder.
In this example, I created a folder, with many sub folders and am trying to add those folders in correct order to my "Root" item.
import os
from PyQt6.QtWidgets import QApplication, QTreeView
from PyQt6.QtGui import QStandardItemModel, QStandardItem
app = QApplication([])
# Create the model
model = QStandardItemModel()
# Set the path to the directory containing the folders you want to add to the tree view
path = r"replace\with\path\to\folder"
# Create an Item for everything to parent under
root_item = QStandardItem("Root")
model.appendRow(root_item)
#iterate through the directory provided
for index,(root, folders, files) in enumerate(os.walk(path)):
folder_root_name = (str(root))
folder_root_name = folder_root_name.split('\\')[-1]
for folder in folders:
folder_item = QStandardItem(folder)
if index == 0:
root_item.appendRow(folder_item)
else:
folder_root_name_item = QStandardItem(folder_root_name)
folder_root_name_item.appendRow(folder_item)
tree_view = QTreeView()
tree_view.setModel(model)
tree_view.show()
app.exec()
Here is a photo of what I am trying to accomplish:

Whenever you have to deal with tree/directory structures, you have to consider that you're using a 3-dimensional model, which automatically calls for a recursive behavior.
While, normally, such a structure would require a relative 3D "model" as a reference, a basic string-based dictionary can suffice with a simple directory-based model.
The assumption is based on the fact that os.walk will always walk through sub directories, even if they are empty.
The trick is to use a dictionary that has keys as full directory paths, and items as their values.
root_item = QStandardItem("Root")
parents = {path: root_item}
model.appendRow(root_item)
def getParent(path):
parent = parents.get(path)
if parent:
return parent
grandParentPath, parentName = path.rsplit(os.sep, 1)
parent = QStandardItem(parentName)
parents[path] = parent
getParent(grandParentPath).appendRow(parent)
return parent
for root, folders, files in os.walk(path):
getParent(root)

Related

How to copy from zip file to a folder without unzipping it?

How to make this code works?
There is a zip file with folders and .png files in it. Folder ".\icons_by_year" is empty. I need to get every file one by one without unzipping it and copy to the root of the selected folder (so no extra folders made).
class ArrangerOutZip(Arranger):
def __init__(self):
self.base_source_folder = '\\icons.zip'
self.base_output_folder = ".\\icons_by_year"
def proceed(self):
self.create_and_copy()
def create_and_copy(self):
reg_pattern = re.compile('.+\.\w{1,4}$')
f = open(self.base_source_folder, 'rb')
zfile = zipfile.ZipFile(f)
for cont in zfile.namelist():
if reg_pattern.match(cont):
with zfile.open(cont) as file:
shutil.copyfileobj(file, self.base_output_folder)
zfile.close()
f.close()
arranger = ArrangerOutZip()
arranger.proceed()
shutil.copyfileobj uses file objects for source and destination files. To open the destination you need to construct a file path for it. pathlib is a part of the standard python library and is a nice way to handle file paths. And ZipFile.extract does some of the work of creating intermediate output directories for you (plus sets file metadata) and can be used instead of copyfileobj.
One risk of unzipping files is that they can contain absolute or relative paths outside of the target directory you intend (e.g., "../../badvirus.exe"). extract is a bit too lax about that - putting those files in the root of the target directory - so I wrote a little something to reject the whole zip if you are being messed with.
With a few tweeks to make this a testable program,
from pathlib import Path
import re
import zipfile
#import shutil
#class ArrangerOutZip(Arranger):
class ArrangerOutZip:
def __init__(self, base_source_folder, base_output_folder):
self.base_source_folder = Path(base_source_folder).resolve(strict=True)
self.base_output_folder = Path(base_output_folder).resolve()
def proceed(self):
self.create_and_copy()
def create_and_copy(self):
"""Unzip files matching pattern to base_output_folder, raising
ValueError if any resulting paths are outside of that folder.
Output folder created if it does not exist."""
reg_pattern = re.compile('.+\.\w{1,4}$')
with open(self.base_source_folder, 'rb') as f:
with zipfile.ZipFile(f) as zfile:
wanted_files = [cont for cont in zfile.namelist()
if reg_pattern.match(cont)]
rebased_files = self._rebase_paths(wanted_files,
self.base_output_folder)
for cont, rebased in zip(wanted_files, rebased_files):
print(cont, rebased, rebased.parent)
# option 1: use shutil
#rebased.parent.mkdir(parents=True, exist_ok=True)
#with zfile.open(cont) as file, open(rebased, 'wb') as outfile:
# shutil.copyfileobj(file, outfile)
# option 2: zipfile does the work for you
zfile.extract(cont, self.base_output_folder)
#staticmethod
def _rebase_paths(pathlist, target_dir):
"""Rebase relative file paths to target directory, raising
ValueError if any resulting paths are not within target_dir"""
target = Path(target_dir).resolve()
newpaths = []
for path in pathlist:
newpath = target.joinpath(path).resolve()
newpath.relative_to(target) # raises ValueError if not subpath
newpaths.append(newpath)
return newpaths
#arranger = ArrangerOutZip('\\icons.zip', '.\\icons_by_year')
import sys
try:
arranger = ArrangerOutZip(sys.argv[1], sys.argv[2])
arranger.proceed()
except IndexError:
print("usage: test.py zipfile targetdir")
I'd take a look at the zipfile libraries' getinfo() and also ZipFile.Path() for construction since the constructor class can also use paths that way if you intend to do any creation.
Specifically PathObjects. This is able to do is to construct an object with a path in it, and it appears to be based on pathlib. Assuming you don't need to create zipfiles, you can ignore this ZipFile.Path()
However, that's not exactly what I wanted to point out. Rather consider the following:
zipfile.getinfo()
There is a person who I think is getting at this exact situation here:
https://www.programcreek.com/python/example/104991/zipfile.getinfo
This person seems to be getting a path using getinfo(). It's also clear that NOT every zipfile has the info.

How to save files to a directory and append those files to a list in Python?

Scenario:
I want to check whether if a directory contains a certain '.png' image file. If so, this image file along with all the other files (with png extension only) gets stored in a different directory. (The solution I am looking for should work in all OS platforms i.e Windows, Unix, etc.) and in a remote server i.e (FTP etc.)
I have tried the following code below:
import os, sys
import shutil
import pathlib
import glob
def search():
image_file = 'picture.png'
try:
arr = [] #List will be used to append all the files in a particular directory.
directory = pathlib.Path("collection") #checks if the collection directory exists.
files = []
#need to convert the PosixPath (directory) to a string.
[files.extend(glob.glob(str(directory) + "/**/*.png", recursive = True))]
res = [img for img in files if(img in image_file)] #checks if the image is within the list of files i.e 'picture.png' == 'collection\\picture.png'
if str(bool(res)): #If True...proceed
print("Image is available in image upload storage directory")
for file in files:
transfer_file = str(file)
shutil.copy(file, 'PNG_files/') #send all the files to a different directory i.e 'PNG_files' by using the shutil module.
arr.append(transfer_file)
return arr
else:
print("image not found in directory")
except OSError as e:
return e.errno
result = search() #result should return the 'arr' list. This list should contain png images only.
However, during execution, the For loop is not getting executed. Which means:
The image files are not stored in the 'PNG_files' directory.
The images are not getting appended in the 'arr' list.
The code above the For loop worked as expected. Can anyone explain to me what went wrong?
There are several issues:
In this line
res = [img for img in files if(img in image_file)] #checks if the image is within the list of files i.e 'picture.png' == 'collection\\picture.png'
you should check the other way around (as written in the comment): image_file in img, e.g. picture.png in collection/picture.png.
str(directory) + "/**/*.png" is not OS independent. If you need this to work on Windows, too, you should use os.path.join(str(directory), '**', '*.png') instead!
This check is incorrect: if str(bool(res)):. It's actually always true, because bool(res) is either True or False, str(bool(res)) is either "True" or "False", but both are actually True, as neither is an empty string. Correctly: if res:.
And finally, you're missing the creation of the PNG_files directory. You need to either manually create it before running the script, or call os.mkdir().

Creating automatic folder and files in soapui

I wrote a groovy script in soapui to create files in certain location in my pc. How can I make it dynamic and enable the user to write the location the files are saved to by write the location in configuration file imported at test suite level.
if(context.expand('${#Project#ProduceReports}') == 'true') {
def resultDir = new File("D:\\Reports");
if(!resultDir.exists()) {
resultDir.mkdirs();
}
def resultsFile = new File(resultDir, "CSVReport.csv");
}
If you want to get the path from a testSuite property, you can do it as you do with the project property, using context.expand:
def yourPath = context.expand('${#TestSuite#pathDirectory}')
Or alternatively you can do the same with:
def yourPath = context.testCase.testSuite.getPropertyValue('pathDirectory')
Maybe this is out of scope for your question, but could be helpful. If you need you can also use UISupport to ask the user to enter the path he wants with the follow code:
def ui = com.eviware.soapui.support.UISupport;
// the prompt question, title, and default value
def path = ui.prompt("Enter the path","Title","/base/path");
log.info path
This shows:
Define project level custom property REPORT_PATH with value D:/Reports/CSVReport.csv i.e., full path including file and path separate by / slash even on windows platform.
Then use the below script to write the data.
//Define the content that goes as report file. Of course, you may change the content as need by you
def content = """Name,Result
Test1,passed
Test2,failed"""
//Read the project property where path is configured
def reportFileName = context.expand('${#Project#REPORT_PATH}')
//Create file object for reports
def reportFile = new File(reportFileName)
//Create parent directories if does not exists
if (!reportFile.parentFile.exists()) {
reportFile.parentFile.mkdirs()
}
//Write the content into file
reportFile.write(content)

Get folder name from list of string

I have a list of a hundred folders like below:
C:\Mother\Son\foler_A\a_file.txt
C:\Mother\Son\foler_A\foler_B\a_file.txt
C:\Mother\Son\foler_B\a_file.txt
C:\Mother\Son\foler_C\foler_D\a_file.txt
...
Can someone help me to get the list of the lastest folder level like:
['folder_A', [folder_B], [folder_B],[folder_D]]
If these files actually exist on your system, you could do:
def parents = folders.collect { new File(it).parentFile?.name }
If not, you could do:
def parents = folders*.split( '\\\\' )*.getAt(-2)

How to display Folders and recent items

I have 2 questions in trying to retrieve a set of data from a directory and displays it out into the ListWidget.
As I am a linux user, I set my ListWidget to read my directory from Desktop in which insides contains say 5 folders and 5 misc items (.txt, .py etc)
Currently I am trying to make my ListWidget to display just the folders but apparently it does that but it also displays all the items, making it a total of 10 items instead of 5.
I tried looking up on the net but I am unable to find any info. Can someone help me?
Pertaining to Qns 1, I am wondering if it is possible to display the top 3 recent folders in the ListWidget, if a checkbox is being checked?
import glob
import os
def test(object):
testList = QListWidget()
localDir = os.listdir("/u/ykt/Desktop/test")
testList.addItems(localDir)
Maybe you should try "QFileDialog" like the following:
class MyWidget(QDialog):
def __init__(self):
QDialog.__init__(self)
fileNames = QFileDialog.getExistingDirectory(self, "list dir", "C:\\",QFileDialog.ShowDirsOnly)
print fileNames
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = MyWidget()
widget.show()
app.exec_()
2nd question, you could reference to this: enter link description here
I guess that you are expecting that os.listdir() will return only the directory names from the given path. Actually it returns the file names too. If you want to add only directories to the listWidget, do the following:
import os
osp = os.path
def test(object):
testList = QListWidget()
dirPath = "/u/ykt/Desktop/test"
localDir = os.listdir(dirPath)
for dir in lacalDir:
path = osp.join(dirPath, dir)
if osp.isdir(path):
testList.addItem(dir)
This will add only directories to the listWidget ignoring the files.
If you want to get the access time for the files and/or folders, use time following method:
import os.path as osp
accessTime = osp.getatime("path/to/dir") # returns the timestamp
Get access time for all the directories and one which has the greatest value is the latest accessed directory. This way you can get the latest accessed 3 directories.

Resources