How to get files from selected folder with GtkFileChooserButton? - gtk3

I am making a GTK+3 App with GJS where users select a folder from a GtkFileChooserButton (action property set to select-folder). I want to find all image files in the given folder the user have selected, so I can display one of the images.
I tried this._fileChooserButton.get_files() and this._folderChooseButton.get_uris() but they only return one file, which is the path to the selected folder. Like this:
_init(application) {
this._folderChooseButton.connect('file-set', () => {
this._onFolderChosen();
});
}
_onFolderChosen() {
let folder = this._folderChooseButton.get_file();
// somehow get files from the folder here
this._image.set_from_file(files[1]);
}
From the API it is not really clear to me, how do I find out which image files are inside the user's selected directory (and subdirectories)?

OK, after help from patrick, georges and matthias at guadec, here is what I got.
The get_file() function I tried returns a GFile, which in this case is a folder (in UNIX, folders are also files). In order to get the files within the directory path, we need to call enumerate_children_async() on our GFile, returned by the get_file() function.
The enumate_children_async() function takes five parameters:
A comma-separated attribute list. In our case, since we want the identifiers of the children in the directory, we want to use the attribute called standard::name.
FileQueryInfoFlag: This allows to either follow or not follow symbolic links. In this case, we will use FileQueryInfoFlag.NONE which will not follow symbolic links.
io_priority: How high priority the IO operation should have (we will use GLib.PRIORITY_DEFAULT)
cancellable: A cancellable, which is a way to cancel this operation, in this case we will leave it as null.
callback: This is the function/code you want to run in response to the files having been retreived.
More info on this function is at GJS-Docs at GNOME.org
The enumerate_children_async() function returns a GFileEnumerator, which we can use to retreive a number of the files, by calling next_files_async(), which takes these arguments:
num_files: How many files you want to retreive. In your case, we use 1.
io_priority and cancellable (same as above).
callback: Where we can run a function or code to actually retreive the file.
Below, is the final code for doing this.
const { Gio, GLib, GObject, Gtk } = imports.gi; // import Gio and GLib API at top of your document.
_onFolderChosen() {
let folder = this._folderChooseButton.get_file();
let files = folder.enumerate_children_async(
'standard::name',
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT,
null,
(source, result, data) => {
this._fileEnumerator = null;
try {
this._fileEnumerator = folder.enumerate_children_finish(result);
} catch (e) {
log('(Error) Could not retreive list of files! Error:' + e);
return;
}
this._readNextFile();
});
}
_readNextFile() {
if (!this._fileEnumerator)
return;
let fileInfo = null;
this._fileEnumerator.next_files_async(
1,
GLib.PRIORITY_DEFAULT,
null,
(source, result, data) => {
try {
fileInfo = this._fileEnumerator.next_files_finish(result);
} catch (e) {
log('Could not retreive the next file! Error:' + e);
return;
}
let file = fileInfo[0].get_name();
let filePath = GLib.build_filenamev([this._folderChooseButton.get_filename(), file]);
this._carousselImage.set_from_file(filePath);
});
}

Related

store data persistent for learn app with unity

I'm currently working on a language learn app with unity. I want to implement that when you guessed a work (e.g. a number) incorrect, you need to guess the word again in the next iteration. I thought of a way that you store for each word in every play mode a value between +10 to -10 and when an item has a big negative number the word occurrence more often than if it has a big positiv number.
My Problem is that I don't know how to store the data properly. PlayerPrefs are too inconvenient for this problem, and I don't know how to modify a JSON file properly.
Currently, I store the data for the items in a class.
Maybe you could have a structure like:
Numbers
write
zero: -5
one: +3
match
zero: +5
one: +4
Alphabet
write:
A: -10
match:
A: +5
Does anyone have an idea how to solve this problem?
One of the best JSON serialization libraries is Newtonsoft.Json.
You can use your class and serialize an object to JSON object, and then save it as a string to file.
public static string Serialize(object obj)
{
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};
return JsonConvert.SerializeObject(obj, settings);
}
After that you can save it to file in the Application.persistentDataPath directory.
var text = Serialize(data);
var tmpFilePath = Path.Combine(Application.persistentDataPath, "filename");
Directory.CreateDirectory(Path.GetDirectoryName(tmpFilePath));
if (File.Exists(tmpFilePath))
{
File.Delete(tmpFilePath);
}
File.WriteAllText(tmpFilePath, text);
After that you can read the file at any time using File.ReadAllText and deserialize it to an object.
public static T Deserialize<T>(string text)
{
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};
try
{
var result = JsonConvert.DeserializeObject<T>(text, settings);
return result ?? default;
}
catch (Exception e)
{
Debug.Log(e);
}
return default;
}
T result = default;
try
{
if (File.Exists(path))
{
var text = File.ReadAllText(path);
result = Deserialize<T>(text);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
return result;
Unfortunately, there is no easy way to store data persistently between play sessions in Unity. PlayerPrefs and creating your own JSON file are the simplest ways of doing this.
The good news is that JSON files are quite easy to make and modify, thanks to the builtin JSONUtility Unity provides.
If you make a separate class or struct to hold your scores, give that clas a [Serializable] tag and keep a reference to that in your current setup (which is probably a MonoBehaviour).
You can use the File class (specifically File.CreateText() and File.OpenText()) to write to/read from a file. If you do this every time a value changes, you should end up with persistent saved data across multiple play sessions.

Protractor - Create a txt file as report with the "Expect..." result

I'm trying to create a report for my scenario, I want to execute some validations and add the retults in a string, then, write this string in a TXT file (for each validation I would like to add the result and execute again till the last item), something like this:
it ("Perform the loop to search for different strings", function()
{
browser.waitForAngularEnabled(false);
browser.get("http://WebSite.es");
//strings[] contains 57 strings inside the json file
for (var i = 0; i == jsonfile.strings.length ; ++i)
{
var valuetoInput = json.Strings[i];
var writeInFile;
browser.wait;
httpGet("http://website.es/search/offers/list/"+valuetoInput+"?page=1&pages=3&limit=20").then(function(result) {
writeInFile = writeInFile + "Validation for String: "+ json.Strings[i] + " Results is: " + expect(result.statusCode).toBe(200) + "\n";
});
if (i == jsonfile.strings.length)
{
console.log("Executions finished");
var fs = require('fs');
var outputFilename = "Output.txt";
fs.writeFile(outputFilename, "Validation of Get requests with each string:\n " + writeInFile, function(err) {
if(err)
{
console.log(err);
}
else {
console.log("File saved to " + outputFilename);
}
});
}
};
});
But when I check my file I only get the first row writen in the way I want and nothing else, could you please let me know what am I doing wrong?
*The validation works properly in the screen for each of string in my file used as data base
**I'm a newbie with protractor
Thank you a lot!!
writeFile documentation
Asynchronously writes data to a file, replacing the file if it already
exists
You are overwriting the file every time, which is why it only has 1 line.
The easiest way would probably (my opinion) be appendFile. It writes to a file without overwriting existing data and will also create the file if it doesnt exist in the first place.
You could also re-read that log file, store that data in a variable, and re-write to that file with the old AND new data included in it. You could also create a writeStream etc.
There are quite a few ways to go about it and plenty of other answers
on SO specifically on those functions that can provide more info.
Node.js Write a line into a .txt file
Node.js read and write file lines
Final note, if you are using Jasmine you can also create a custom jasmine reporter. They have methods that contain exactly what you want (status Pass/Fail, actual vs expected values etc) and it's fairly easy to set up with Protractor

How do I find the full directory for an IFile in Cake?

I'm writing a script to produce some artefacts from my build so I want to clean up unwanted files first. I'm using CleanDirectory(dirPath, predicate).
I'm finding it disturbingly hard to work out the directory for a file. If I use GetDirectoryName() that seems to just get me the immediate parent, not the full directory path.
Func<IFileSystemInfo, bool> predicate =
fileSystemInfo => {
// Dont filter out any directories
if (fileSystemInfo is IDirectory)
return false;
var path = fileSystemInfo.Path.FullPath;
var directory = ((DirectoryPath)path).GetDirectoryName();
...
}
Obviously I can use the .NET Framework System.IO classes to do this easily but then I get strings with the slashes in the wrong direction, and things do not smoothly inter-operate with Cake which uses POSIX paths.
OK I've worked out a solution. The key to IFileSystemInfo is to try and cast the Path to various derived types/interfaces, which then provide the functionality you are probably looking for. Example:
Func<IFileSystemInfo, bool> predicate =
fileSystemInfo => {
// Dont filter out any directories
if (fileSystemInfo is IDirectory)
return false;
// We can try and cast Path as an FilePath as know it's not a directory
var file = (FilePath) fileSystemInfo.Path;
if (file.FullPath.EndsWith("Help.xml", StringComparison.OrdinalIgnoreCase))
return false;
// GetDirectory() returns a Path of type DirectoryPath
var directory = file.GetDirectory().FullPath;
...
}

folder.getSheets() returning null?

I'm trying to go through and entire workspace and list each element in a treeview, but when I attempt to grab the sheets in the found folders the getSheets() method in the Folder class always returns null. Am I doing something wrong?
TreeItem<String> workspace = new TreeItem<>("Earls Workspace", workspaceIcon);
workspace.setExpanded(true);
try {
Workspace earlsWorkspace = SmartsheetConnector.getInstance().getSmartsheet().workspaces().getWorkspace(*****);
List<Sheet> sheets = earlsWorkspace.getSheets();
List<Folder> folders = earlsWorkspace.getFolders();
for (Folder folder : folders) {
TreeItem<String> item = new TreeItem<>(folder.getName(), new ImageView(folderIcon));
System.out.println("Folder: " + folder.getName());
List<Sheet> folderSheets = folder.getSheets(); <- problem is here
System.out.println(folderSheets.toArray());
for (Sheet sheet : folderSheets) {
TreeItem<String> subItem = new TreeItem<>(sheet.getName(), new ImageView(sheetIcon));
item.getChildren().add(subItem);
}
workspace.getChildren().add(item);
}
for (Sheet sheet : sheets) {
TreeItem<String> item = new TreeItem<>(sheet.getName(), new ImageView(sheetIcon));
workspace.getChildren().add(item);
}
sheetExplorer.setRoot(workspace);
} catch (SmartsheetException ex) {
ex.printStackTrace();
}
I've purposefully blocked out the workspace ID.
Since the Java SDK mimics the Smartsheet API, the GET workspace by ID function only returns high level folder information, and does not include the sheets within the folder, which is why it is returning null. There are two ways to solve your problem:
After getting a list of folders from your workspace, you can call the GET folder by ID function, and that will return a list of sheets within a folder.
Workspace earlsWorkspace = SmartsheetConnector.getInstance().getSmartsheet().workspaces().getWorkspace(*****);
List<Folder> folders = earlsWorkspace.getFolders();
for (Folder folder : folders) {
List<Sheet> folderSheets = SmartsheetConnector.getInstance().getSmartsheet().folders().getFolder(folder.getId()).getSheets();
for (Sheet sheet : folderSheets) {
System.out.println(sheet.getName());
}
}
Call the getHome() function. This will return a nested list of all Home objects, including sheets, workspaces and folders, and optionally reports and/or templates depending on the EnumSet that you pass in. You will have to go through the list of workspaces and find the one that you are interested in. From there, you can loop through the folders, and call folder.getSheets() to get a list of sheets within a folder.
Home home = SmartsheetConnector.getInstance().getSmartsheet().home().getHome(EnumSet.of(ObjectInclusion.TEMPLATES);
In the next release of the SDK, there will be an option to specify if you want to load all of the contents, including nested folders and nested sheets within a workspace, so you can easily call folder.getSheets(), just like you were doing, and get back a list of sheets.

Flex/Air file dragging: how to restrict filetypes?

I'm using a <S:nativeDragDrop> and getting files dragged over a component like so:
var arr:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
I'm not sure how to restrict what type of files can be dragged. Is there a native control for this? The help documents mention the possibility of defining completely different ClipboardFormats, but I have no idea how to do that; I could run regex on the filenames as well, but that seems overcomplicated.
Wondering if there's a way like with FileReference.browse to specify specific file extensions
As far as I know, there is not a built-in way to filter dropped files. However, in your NATIVE_DRAG_ENTER handler, you could loop through the list of files and choose not accept the drag based on their file types. Or, you could merely ignore the unsupported types when you are processing the NATIVE_DRAG_DROP.
var validTypes:Object = {png : true, jpg : true, gif : true};
function nativeDragEnter(event:NativeDragEvent):void {
var files:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array
for each(var file:File in files) {
if(!validTypes[file.extension.toLowerCase()]) // Don't accept drag if any of the dropped files aren't supported.
return;
}
DragManager.acceptDrag(InteractiveObject(event.target));
}
function nativeDragDrop(event:NativeDragEvent):void {
var files:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array
for each(var file:File in files) {
if(validTypes[file.extension]) //accept only certain files
processFile(file);
}
}
As a side note, I assumed you are working on an AIR app here, but if you aren't, you'll have to use the FileReference class instead of File.