Results 1 to 20 of 30

Thread: Saving a file using a relative path

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Apr 2014
    Posts
    59
    Thanks
    7
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    Oops, I guess I did a bad job of explaining.

    In one instance what I am doing is saving the path, as a string, in a QTableView. In that case I have a dialog that opens a QFileDialog to get the path to an image.
    Qt Code:
    1. void AddImageDialog::on_selectBtn_clicked()
    2. {
    3. QString home = "debug";
    4. QString path = QFileDialog::getOpenFileName(this, "Select Imagae", home + "/images",
    5. "Images(*.jpg);;All Files(*.*)");
    6. if(ui->relativeCB->isChecked())
    7. {
    8. QFileInfo relativePath(path);
    9. ui->pathTxt->setText(home + "/images/" + relativePath.fileName());
    10. }
    11. else
    12. ui->pathTxt->setText(path);
    13. }
    To copy to clipboard, switch view to plain text mode 
    The path is then retrieved from pathTxt by the delegate, and put in the model.
    Qt Code:
    1. else if(header == "Image")
    2. {
    3. AddImageDialog *dialog = new AddImageDialog(parent);
    4. dialog->exec();
    5.  
    6. model->setData(index, dialog->getPath());
    7. }
    To copy to clipboard, switch view to plain text mode 
    I then display the image in the cell.

    If I use QApplication::applicationDirPath(), then the path ends up being hard coded to my development directory, witch will break the path, if I move the folder. I have several other, similar, situations scattered throughout various classes, so you can imagine how unmanageable it is.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,315
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    One way I avoid this is to not use the QFileDialog::getOpenFileName() static method. Instead, I use the non-static mechanism of creating a QFileDialog instance on the stack and connecting slots to the QFileDialog::fileSelected() and QFileDialog::directoryEntered() signals. I then call QFileDialog::exec() and let the slots handle it.

    In particular, I use the directoryEntered() signal to keep track of where the user last browsed for a file, and can use that location to initialize the dialog the next time I use it. If I want to remember between executions, I use QSettings to put it in an ini file. The first time I run the program, I can choose to either use the applicationDirPath -or- (better) use something appropriate from QStandardPaths.

    An additional remedy is to give the user a way to browse for the directory containing the images if the stored path doesn't exist. You can do that with QFileDialog with FileMode set to Directory and Option set to ShowDirsOnly. Once you get the name of the directory (from the directoryEntered() or maybe fileSelected() signal). You can then update your table to use this for subsequent image loads.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

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

    Default Re: Saving a file using a relative path

    If I understand you correctly, you are suggesting I make the user set a new path every time they switch machines. I also have another column that links to a PDF. While it is not an issue if the PDF is online, it defeats the purpose if it is stored locally.

    Let me give a use case example of what I am trying to accomplish. If I am working on a project in Qt, for example, and want to move to another machine, I just need to move the project folder. In school, we used Android Studio, in a class, and I could not easily work on a project at home, and in class, because the path was hard coded into the project, I forget were. I could put it on a thumb drive and switch, as long as I made sure the root directory was the same, which meant plugging in various USB devices, until it was the same. Alternately, I could go into the setting, somewhere, and change the root manually, each time. Neither of those options were convenient, and making the root the same was the easiest. While making sure the root of the thumb drive is the same, compared to changing, possibly, hundreds of links when switching machines, is one way around it, I want the user to not have to worry about any of that, and have it just work, like a Qt project would.

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,315
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    If I understand you correctly, you are suggesting I make the user set a new path every time they switch machines.
    No, I am suggesting that you ask the user to browse for the correct path if the path that is saved can't be found on a different machine (or maybe also, if the path exists, the images aren't there).

    We use this method in some of our apps. Most of the information is stored in the app's "project" file, but this file can refer to external files that contain experimental data that is too big or not appropriate to store in the project file. These are stored with absolute paths. When we open the project file, we also try to automatically find the external files. If we can't (eg. we're working on our laptop instead of our desktop PC) we put up a dialog that says, "Can't find XYZ. Do you want to browse for it?" If they say yes and browse to the new location, we remember that and correct the absolute paths to point to the new location so we won't have to ask again. If the user says "no", then you have to go to Plan B, which in our case is to allow the user to do as much processing as possible in the absence of the external data.

    Storing relative paths doesn't avoid this problem, because the relative path on one PC might not be the same (or even exist) on a different PC and you're stuck in the same boat. The only way relative paths work is if everything you need is stored under a single "root" directory, so all you need remember is the directory where your project file lives on the system where you are now using it, and everythings else lives in or beneath that.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

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

    Default Re: Saving a file using a relative path

    OK, I understand what you are saying now. I guess I am still not explaining myself well as that is not really what I am going for. I only need it to work if the file is saved in a folder, inside the application folder, or the one that opens by default in my code. It sounds like my only other option is to store the files as BLOBs in the data base, as opposed to saving the paths as a strings. I might try setting a global is main(), but I am not sure if that will even work.

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,315
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    I only need it to work if the file is saved in a folder, inside the application folder
    Well, if you're on Windows 7 or 10, this isn't going to work because most users don't have permission to create files in the application's install directory except at install time. Sometimes they aren't even allowed to write to files that already exist there. So using the application directory only works when you're the developer and the application directory is just a normal location on your disk somewhere.

    It is better to use QStandardPaths to find a directory and use that as the default location to create and save project files instead of hard-coding a fixed path in your source code. Using QStandardPaths is basically finding a relative location; it just changes as a function of the PC and user.

    It sounds like my only other option is to store the files as BLOBs in the data base
    If your app won't work if the files aren't present somewhere in the file system, then this is about your only viable solution.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

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

    Default Re: Saving a file using a relative path

    Hi, I missed the notification, so sorry for my late reply.

    Well, if you're on Windows 7 or 10, this isn't going to work because most users don't have permission to create files in the application's install directory except at install time
    I think you are misunderstanding my purpose, I do not intend to use an installer as this is meant to be portable, run on a thumb drive. The user should be able to put the application anyplace they want, or easily move it around. They should have no problem copying a PDF or image into the application folder.

    I am trying to avoid using BLOBs as I do not believe this is how they were intended to be used. Databases are far from my strong point, so I could be mistaken about that.

  8. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,315
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    You have completely baffled me about what you are trying to accomplish. If you are storing everything on a thumb drive, and everything is stored relative to the directory the program is running in, then why are you having such a hard time getting the current (application) directory and creating paths relative to that?

    If you are storing images or whatever as separate files, then either they are stored relative to the program directory or they aren't. And if they aren't stored in the relative location where you expect them to be then either a) the user has moved or deleted them (or they weren't there to begin with) or b) the relative path you think you are constructing isn't correctly pointing where you think it is.

    So if your app can't find the files it needs, then you have the choice of a) running without them or b) trying to find them in an alternative location. The easiest way to do this is to simply ask the user where he or she put them. If the answer to that is "I deleted them" then you are back to Plan A.

    By the way, if you are using SQLite as a database, BLOBs in the database take basically the same space as a file on disk (and possibly less because there is less file system overhead), so you can guarantee you'll always have the files you need by storing them in a database. Then you just have the problem of finding the database at runtime.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

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

    Default Re: Saving a file using a relative path

    I apologize for not doing a very good job of describing my situation, but I think you hit on the problem with
    then why are you having such a hard time getting the current (application) directory and creating paths relative to that?
    I am storing the path, in the database, as a string(VARCHAR), either absolute, or relative, I am leaving it to the user to select which. So, once it is entered into the database, it basically becomes an absolute path (which is why I cannot use QApplication::applicationDirPath()). For example, this is how I handle it in the model.
    Qt Code:
    1. QVariant TableModel::data(const QModelIndex &idx, int role) const
    2. {
    3. QString header = QSqlTableModel::headerData(idx.column(), Qt::Horizontal).toString();
    4. if(header == "Datasheet")
    5. {
    6. QString link = QSqlTableModel::data(idx, Qt::DisplayRole).toString();
    7.  
    8. switch(role)
    9. {
    10. case Qt::DisplayRole:
    11. return QString();
    12. break;
    13. case Qt::DecorationRole:
    14. if(QStringRef(&link, 0, 4) == "http")
    15. {
    16. QPixmap pixmap(":/Images/Images/internet.png");
    17. return pixmap;
    18. }
    19. else if(link == "")
    20. return QVariant();
    21. else
    22. {
    23. QPixmap pixmap(":/Images/Images/pdf.png");
    24. return pixmap;
    25. }
    26. break;
    27. case Qt::UserRole:
    28. return link;
    29. break;
    30. }
    31. }
    32.  
    33. if(header == "Image")
    34. {
    35. QString link = QSqlTableModel::data(idx, Qt::DisplayRole).toString();
    36.  
    37. switch(role)
    38. {
    39. case Qt::ToolTipRole:
    40. return QString("<img scr='%1'>").arg(link);
    41. break;
    42. }
    43. }
    44.  
    45. return QSqlTableModel::data(idx, role);
    46. }
    To copy to clipboard, switch view to plain text mode 

    And just for completeness, unless I forgot something, the PDFs are handled this way
    Qt Code:
    1. void MainWindow::on_dataView_activated(const QModelIndex &index)
    2. {
    3. QString header = model->headerData(index.column(), Qt::Horizontal).toString();
    4.  
    5. if(header == "Datasheet")
    6. {
    7. QString link = index.data(Qt::UserRole).toString();
    8.  
    9. if(QStringRef(&link, 0, 4) == "http")
    10. QDesktopServices::openUrl(link);
    11. else if(link == "")
    12. return;
    13. else
    14. QDesktopServices::openUrl("file///" + link);
    15. }
    16. }
    To copy to clipboard, switch view to plain text mode 
    Then you just have the problem of finding the database at runtime.
    There is no problem using QApplication::applicationDirPath() for that, since it is not based on a string.

    This has gotten me to thinking that I might be able to use similar mechanics, like I do in the last bit using QStringRef(), to determine if the link has been truncated, to a relative link, in which case I should then be able to use QApplication::applicationDirPath(). I am going to have to think on that a bit.

    <edit> I expect the user to not worry about the relative path part, if they are not interested having the application portable.</edit>
    Last edited by admkrk; 28th June 2017 at 05:15.

  10. #10
    Join Date
    Mar 2008
    Location
    Kraków, Poland
    Posts
    1,540
    Thanked 284 Times in 279 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Saving a file using a relative path

    First of all the application must have a fixed status. So put in main() this line
    Qt Code:
    1. QDir::setCurrent(QCoreApplication::applicationDirPath());
    To copy to clipboard, switch view to plain text mode 
    After that, you can use paths relative to the application directory.

  11. The following user says thank you to Lesiok for this useful post:

    admkrk (3rd July 2017)

  12. #11
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,315
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Saving a file using a relative path

    either absolute, or relative, I am leaving it to the user to select which.
    So you seem to have set up a built-in contradiction in use cases: If you store an absolute location in the database, you cannot have a portable app because that absolute location most likely won't exist or have the same content on a different PC. If you want a portable app and portable data, you cannot store an absolute location in the DB.

    I see three options to solving this contradiction:

    1 - The app can't be portable and you are free to store absolute locations in the DB
    2 - The app is portable and stored locations must be relative to the app
    3 - The app is portable but must be able to run with missing files

    Option 2 makes the most sense to me, but it will only work if you either: a) store the files the user needs in the database as BLOBs or b) copy the files the user needs to a known subdirectory of the app. In either case, you have the problem of the original files getting out of sync with the copies. You could set up a file system watcher to watch for changes, but then you're back to the original problem of finding an absolute location. However, if the files are static you may not have to worry about changes. If they aren't static, then you could implement a "scan for changes" feature that lets the user sync up the copies with the originals.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

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

    Default Re: Saving a file using a relative path

    Actually, option 3 is closest to what I have since the user is made aware that if they do not save in the appropriate folder, it will be missing. While BLOBs would work for images, PDFs would be problematic since they can be online, or local. In that case, a link is the only way to do it. For the files to be portable, they are stored in a subdirectory of the application, that is why the file dialog opens up where it does. I could have it open using a standard path and copy it, I do do that elsewhere, but if the user does not want the portability, then they would have two copies, taking up space on their machine.

    If I understand Lesiok's reply(I need to read the documents better and do some experimenting), I need to reverse my process, and just store the filename. then use something like
    Qt Code:
    1. return QString("<img scr='%1'>").arg(home + "/images/" + link);
    To copy to clipboard, switch view to plain text mode 
    except home would be however I get the application path. Or better yet, store it as "/images/" + relativePath.fileName(), which would make determining if it is relative easier. If that will work, that will solve my problem.

Similar Threads

  1. Replies: 0
    Last Post: 14th September 2013, 10:14
  2. Replies: 16
    Last Post: 24th January 2013, 02:52
  3. Downloading a file and saving in a path
    By StarRocks in forum Qt Programming
    Replies: 19
    Last Post: 3rd January 2013, 06:43
  4. qmake absolute vs relative LIBS path
    By TheShow in forum Newbie
    Replies: 7
    Last Post: 12th October 2010, 14:40
  5. Replies: 8
    Last Post: 17th October 2009, 08:10

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.