Make the difference between a file path and a url in Qt - qt

If I have a string which could be either a file or a URL, is there any existing clever method I could use to differentiate them?
For instance:
/Users/user/Documents/mydoc.txt -> path
c:\Program Files\myapp\mydoc.doc -> path
https://mywebsite.com/mydoc.txt -> url
ftp://myserver.com/myfile.txt -> url
This is to load a designer UI file, so I need to make a local temporary copy of the remote file.
So the bottom line is to know when I need to download the file.

Well, you might want to construct a QUrl object out of these strings and verify whether these URLs refer to local files. I.e.:
static bool isLocalFile(const QString &str)
{
return QUrl::fromUserInput(str).isLocalFile();
}
With your strings
QString s1("/Users/user/Documents/mydoc.txt");
QString s2("c:\\Program Files\\myapp\\mydoc.doc");
QString s3("https://mywebsite.com/mydoc.txt");
QString s4("ftp://myserver.com/myfile.txt");
bool b = isLocalFile(s1); // a path
b = isLocalFile(s2); // a path
b = isLocalFile(s3); // not a path
b = isLocalFile(s4); // not a path

You could create a QFile with the given name and check if it exists(). If not try to resolve string as a URL.

Related

Qt. Problem with the QFileDialog: setDirectory() and directory()

I have a problem with the QFileDialog class, namely with the setDirectory() and directory() methods. I need to make it so that after opening a file, my program remembers the directory in which the selected file is stored, and the next time QFileDialog is called, it automatically opens the directory that was used last. Here is a snippet of my code:
static QString _st_doc_last_directory;
void MainWindow::open()
{
if (!fileDialog)
{ fileDialog = new QFileDialog(this);
}
if (!_st_doc_last_directory.isEmpty()) fileDialog->setDirectory(_st_doc_last_directory);
QString fileName = fileDialog->getOpenFileName(this, tr("Open Document"), ".", tr("Compressed CAD Models (*.data)"));
if (!fileName.isEmpty())
{ _st_doc_last_directory = fileDialog->directory().dirName();
}
}
The crux of my problem is that when the setDirectory() or directory() method is called, my program crashes with a
"Segmentation fault"
message. How can I fix it, please advise. Thanks in advance.
Whenever you start this method, you have this as the start window: ".". (admittedly I don't know what's going on internally, but I think this leads to this problem).
You can query beforehand whether your defined string is empty. if so you set a path, otherwise you store one in your string. If you don't want to do this from the beginning every time you start the program, you can also use QSettings. This saves you the path in the registry (ie if you use windows).
With QFileInfo you can easily get the path
void MainWindow::open()
{
if(_st_doc_last_directory.isEmpty())
_st_doc_last_directory = QDir::homePath();
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Document"), _st_doc_last_directory, tr("Compressed CAD Models (*.data)"));
QFileInfo info(fileName);
if(!fileName.isEmpty())
_st_doc_last_directory = info.absolutePath();
}

Qt: How to find the nearest existing ancestor of a path, which itself may or may not exist

Motivating example:
In a previous session, the application has stored some path selected
by the user. In the meantime that path may have been deleted, moved,
renamed or the drive unmounted. The application would now like to let
the user browse for a path via a QFileDialog and for the user's convenience, the previous path is passed as the
starting directory of the file dialog, as presumably the new
path is likely to be near the old path. Unfortunately, if
QFileDialog is given a starting path that does not exist, it
defaults to the current working directory, which is very unlikely to
be helpful to the user as it is typically the installation directory
of the application.
So we would like to preprocess the old path to point to a directory
that actually exists before passing it to QFileDialog. If the old
path doesn't exist, we'd like to replace it with the nearest directory
that does.
So how does one take a file path (which may or may not exist) and search "up" that path until one finds something that actually exists in the filesystem?
These are the two approaches I've come up with so far, but suggestions for improvement would be very much appreciated.
Searching upward until a path exists:
QString GetNearestExistingAncestorOfPath(const QString & path)
{
if(QFileInfo::exists(path)) return path;
QDir dir(path);
if(!dir.makeAbsolute()) return {};
do
{
dir.setPath(QDir::cleanPath(dir.filePath(QStringLiteral(".."))));
}
while(!dir.exists() && !dir.isRoot());
return dir.exists() ? dir.path() : QString{};
}
Searching downward until a path doesn't exist:
QString GetNearestExistingAncestorOfPath(const QString & path)
{
if(QFileInfo::exists(path)) return path;
auto segments = QDir::cleanPath(path).split('/');
QDir dir(segments.takeFirst() + '/');
if(!dir.exists()) return {};
for(const auto & segment : qAsConst(segments))
{
if(!dir.cd(segment)) break;
}
return dir.path();
}
Try the following code:
QString getNearestExistingPath(const QString &path)
{
QString existingPath(path);
while (!QFileInfo::exists(existingPath)) {
const QString previousPath(existingPath);
existingPath = QFileInfo(existingPath).dir().absolutePath();
if (existingPath == previousPath) {
return QString();
}
}
return existingPath;
}
This function utilizes the QFileInfo::dir() method which returns the parent directory for a specified path. The code is looped until the existing path is met or the paths in the two latest iterations are identical (this helps us to avoid the infinite loop).
From the QFileInfo::dir() docs:
Returns the path of the object's parent directory as a QDir object.
Note: The QDir returned always corresponds to the object's parent directory, even if the QFileInfo represents a directory.
I still recommend you to run some tests because I may be missing something.

QFileDialog : get file name

I am using Qt 5 and QFileDialog. I want to restrict user to give forward slash (/) in file name.
I have below code for QFileDialog to save the file name.
QFileDialog saveAsdialog(this);
QString filename = saveAsdialog.getSaveFileName(this, tr("Save
file"), ".", tr("Files (*.csv)"));
In dialog, if user gives file name "abc.csv" in "download" folder then "getSaveFileName" returns "/home/user/Downloads/abc.csv" which is correct.
But my question is when user give forward slash in file name (/) then it is not behaving correctly.
e.g. If user want to give file name "abc/xyz.csv" then i am not getting the correct file name.
How to get the correct file name "abc/xyz.csv" when user click "OK"?
Please watch on this wikipedia link.
As you can see, / character is prohibited in file name. So file name abc/xyz.csv is incorrect.
Also you can always check wich is current directory in file dialog, and based on this information you can track selected file name. Try this:
QFileDialog saveAsdialog(this);
QString curDir(QDir('.').absolutePath());
connect(&saveAsdialog, &QFileDialog::directoryEntered, [&curDir](const QString& dir) {
curDir = dir;
});
// We cann't use static member getSaveFileName
//QString filename = saveAsdialog.getSaveFileName(this, tr("Save file"), ".", tr("Files (*.csv)"));
//here manual saveAsdialog setup
...........
if (saveAsdialog.exec() == QDialog::Accepted) {
QString filename = saveAsdialog.selectedFiles().first(); //add check that list is not empty!!!
QString f = filename.replace(curDir, "");
}

How to copy local file to QClipboard (in Gnome)?

QClipboard offers several ways to copy stuff into the clipboard. There are high level functions for standard desktop (text, pixmaps, etc), but I could not figure out how to implement the standard copy file operation. Google did not help.
Just put appropriate mime type and URL of the local file into clipboard. Docs reference.
QMimeData* mimeData = new QMimeData();
mimeData->setData("text/uri-list", "file:///C:/fileToCopy.txt");
clipboard->setMimeData(mimeData);
You can use static method QUrl::fromLocalFile to get QUrl instance to be used in mimeData->setData :
mimeData->setData("text/uri-list", QUrl::fromLocalFile("C:/fileToCopy.txt"));
Okay I found the solution to my problem. The problem is that gnome (working on linux) does its own thing. The file(s) are not stored in the text/uri-list format like N1ghtLight mentioned, but uses the special x-special/gnome-copied-files format. The following code did it:
// Get clipboard
QClipboard *cb = QApplication::clipboard();
// Ownership of the new data is transferred to the clipboard.
QMimeData* newMimeData = new QMimeData();
// Copy old mimedata
const QMimeData* oldMimeData = cb->mimeData();
for ( const QString &f : oldMimeData->formats())
newMimeData->setData(f, oldMimeData->data(f));
// Copy path of file
newMimeData->setText(_file->absolutePath());
// Copy file
newMimeData->setUrls({QUrl::fromLocalFile(_file->absolutePath())});
// Copy file (gnome)
QByteArray gnomeFormat = QByteArray("copy\n").append(QUrl::fromLocalFile(_file->absolutePath()).toEncoded());
newMimeData->setData("x-special/gnome-copied-files", gnomeFormat);
// Set the mimedata
cb->setMimeData(newMimeData);

Is there way to read a text file from an assembly by using Reflection in C#?

I have a text file inside the assembly say MyAssembly. I am trying to access that text file from the code like this :
Stream stream = Assembly.GetAssembly(typeof(MyClass)).GetFile("data");
where data is data.txt file containing some data and I have added that .txt as Embedded Resources. I have dome reading of the images from the Assebly as embedded resources with code like this :
protected Stream GetLogoImageStream()
{
Assembly current = Assembly.GetExecutingAssembly();
string imageFileNameFormat = "{0}.{1}";
string imageName = "myLogo.GIF";
string assemblyName = current.ManifestModule.Name;
int extensionIndex = assemblyName.LastIndexOf(".dll", StringComparison.CurrentCultureIgnoreCase);
string file = string.Format(imageFileNameFormat, assemblyName.Remove(extensionIndex, 4), imageName);
Stream thisImageStream = current.GetManifestResourceStream(file);
return thisImageStream;
}
However, this approach did not work while reading the .txt file from an the executing assembly. I would really appreciate if anybody can point me to the approach to read .txt file from an assembly. Please dont ask me why I am not reading the file from the drive or the network share. Just say that the requirement is to read the .txt file from the Assembly.
Thank you so much
GetManifestResourceStream is indeed the correct way to read the data. However, when it returns null, that usually means you have specified the wrong name. Specifying the correct name is not as simple as it seems. The rules are:
The VB.NET compiler generates a resource name of <root namespace>.<physical filename>.
The C# compiler generates a resource name of <default namespace>.<folder location>.<physical filename>, where <folder location> is the relative folder path of the file within the project, using dots as path separators.
You can call the Assembly.GetManifestResourceNames method in the debugger to check the actual names generated by the compiler.
Your approach should work. GetManifestResourceStream returns null, if the resource is not found. Try checking the run-time value of your file variable with the actual name of the resource stored in the assembly (you could check it using Reflector).
I really appreciate for everybody's help on this question. I was able to read the file with the code like this :
Assembly a = Assembly.GetExecutingAssembly();
string[] nameList = a.GetManifestResourceNames();
string manifestanme = string.Empty;
if (nameList != null && nameList.Length > 0)
{
foreach (string name in nameList)
{
if (name.IndexOf("c.txt") != -1)
{
manifestanme = name;
break;
}
}
}
Stream stream = a.GetManifestResourceStream(manifestanme);
Thanks and +1 for Christian Hayter for this method : a.GetManifestResourceNames();

Resources