I have an application (C++ Builder 6.0) that needs to know the total of images there are in a specific folder, and then I have to load them: in an ImageList or in a ComboBoxEx... or any other control...
How can I do that?
I know how to load an image in a control, or to save in a TList, or in an ImageList... but How to know how many files files there are in the directory, and how to load every image in it??
I am Sorry about my English.
I did something like this yesterday with C++ using the boost::filesystem library. However, if you are not using boost already, I would strongly recommend you just use the windows libraries instead. This was my code though in case you're interested:
#include <algorithm>
#include <boost/filesystem.hpp>
#include <set>
namespace fs = boost::filesystem;
typedef std::vector<fs::path> PathVector;
std::auto_ptr<PathVector> ImagesInFolder(const fs::path& folderPath) {
std::set<std::string> targetExtensions;
targetExtensions.insert(".JPG");
targetExtensions.insert(".BMP");
targetExtensions.insert(".GIF");
targetExtensions.insert(".PNG");
std::auto_ptr<PathVector> paths(new PathVector());
fs::directory_iterator end;
for(fs::directory_iterator iter(folderPath); iter != end; ++iter) {
if(!fs::is_regular_file(iter->status())) { continue; }
std::string extension = iter->path().extension();
std::transform(extension.begin(), extension.end(), extension.begin(), ::toupper);
if(targetExtensions.find(extension) == targetExtensions.end()) { continue; }
paths->push_back(iter->path());
}
return paths;
}
This doesn't answer the part of your question about how to actually put the paths into a listbox though.
Use the Win32 functions FindFirstFile and FindNextFile ...?
There's no practical way to identify every image in an arbitrary folder. Almost anything you can't identify as something else, could be some sort of image. Then again, using steganography, even something you can identify as something else still might be (or contain) at least part of an image as well.
Realistically, you want to pick out a set of formats you want to support, and write code that knows about them. For quite a few purposes, a half dozen formats or so is quite adequate, though the exact half dozen you pick will vary by the type of application -- only a few programs have any use for both bitmapped and vector graphics, for one example.
Once you've decided what you want, DlgDirList is probably the easiest way to list some files. If that isn't flexible enough for your purposes, the next obvious choice is FindFirstFile, FindNextFile, and FindClose.
To get a list of all files in a folder, have a look at the FindFirst and FindNext functions in SysUtils.
Here is an example function which shows how to get a list of files.
void __fastcall TForm1::GetDirList(TStrings *List, const AnsiString SearchStr)
{
TSearchRec SRec;
AnsiString TempFName;
List->Clear();
// start search
if (FindFirst(SearchStr, faAnyFile, SRec) == 0)
{
do
{
if ((SRec.Attr & faDirectory) != faDirectory) // exclude directories
{
List->Add(SRec.Name);
} // end if
}
while (FindNext(SRec) == 0);
FindClose(SRec);
} // end if
}
Examples:
// get list of all files in directory
GetDirList(MyStringList, "C:\images*.*");
// get list of all .bmp files in directory
GetDirList(MyStringList, "C:\images\*.bmp");
If you can upgrade to newer version of C++Builder, have a look at TMS AdvSmoothImageListBox, from TMS Software.
The TMS Smooth Controls are available free for C++Builder 2010 users on the from Embarcadero website.
Related
I'm working on a solution within Visual Studio. It currently has two projects.
I will represent Directories or folders with capitals letters, and filenames will be all lower case. My solution structure is as follows:
SolutionDir
ProjectLib
source files
Shaders
shader files
ProjectApp
source files
x64
Debug
app.exe // debug build
Release
app.exe // release build
Within ProjectLib I have a function to open and read my Shader files. Here is what my function looks like:
std::vector<char> VRXShader::readFile(std::string_view shadername) {
std::string filename = std::string("Shaders/");
filename.append(shadername);
std::ifstream file(filename.data(), std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("failed to open file!");
}
size_t fileSize = static_cast<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
This function is being called within my VRXDevices::createPipeline function and here is the relevant code:
void VRXDevices::createPipeline(
VkDevice device, VkExtent2D swapChainExtent, VkRenderPass renderPass,
const std::vector<std::string_view>& shaderNames,
VkPipelineLayout& pipelineLayout, VkPipeline& pipeline
) {
std::vector<std::vector<char>> shaderCodes;
shaderCodes.resize(shaderNames.size());
for (auto& name : shaderNames) {
auto shaderCode = VRXShader::readFile(name.data());
}
// .... more code
}
The names are being created and passed to this function from my VRXEngine::initVulkan function which can be seen here:
void VRXEngine::initVulkan(
std::string_view app_name, std::string_view engine_name,
glm::ivec3 app_version, glm::ivec3 engine_version
) {
//... code
std::vector<std::string_view> shaderFilenames{ "vert.spv", "frag.spv" };
VRXDevices::createPipeline(device_, swapChainExtent_, renderPass_, shaderFilenames, pipelineLayout_, graphicsPipeline_);
}
I'm using just the name of the shader files such as vert.spv, frag.spv, geom.spv etc. I'm not including the paths here because these will be used as the key to a std::map<string_view, object>. So I'm passing a vector of these names from my ::initVulkan function into ::createPipeline().
Within ::createPipeline() is where ::readFile() is being called passing in the string_view.
Now as for my question... within ::readFile() I'm creating a local string and trying to initialize it with the appropriate path... then append to it the string_view for the shader's filename as can be seen from these two lines...
std::string filename = std::string("Shaders/");
filename.append(shadername);
I'm trying to figure out the appropriate string to initialize filename with... Shaders/ will be a part of the name, but it's not finding the file and I'm not sure what the appropriate prefix should be...
My working directories within both projects are as follows:
ProjectApp -> $(SolutionDir)x64/Release AND $(SolutionDir)x64/Debug
ProjectLib -> $(SolutionDir)x64/Release AND $(SolutionDir)x64/Debug
So I need to go back 2 directories then into VRX Engine/Shader...
What is the correct string value for navigating back directories?
Would I initialize filename with "../../VRX Engine/Shaders/" or is it "././" also, should I have quotes around VRX Engine since there is a space in the folder name? What do I need to initialize filename with before I append the shader name to it?
How to properly navigate directory paths in C++
It depends on which C++ standard your implementation claims to be compliant with.
Or else which additional libraries can you use.
C++ is useful on computers without directories (e.g. inside some operating system kernel coded in C++ and compiled with GCC, see OSDEV for examples).
Look on en.cppreference.com for details.
Licensing constraints could matter when using extra open source libraries.
If your implementation is C++17 compliant (in a "hosted" not "freestanding" way), use the std::filesystem part of the standard library.
If your operating system supports the Qt or POCO frameworks and you are allowed to use them (e.g. on C++11), you could use appropriate APIs. So QDir and related classes with Qt, Poco::Path and related classes with POCO.
Perhaps you want to code just for the WinAPI. Then read its documentation (I never coded on Windows myself, just on POSIX or Unix -e.g. Linux- and MSDOS....).
I was originally initializing my local temp string properly with "../../VRX Engine/Shaders/" before appending the string_view to it to be able to open the file. This was actually correct, but because it didn't initially work, I was assuming that it was wrong.
The correct string value for going back one directory should be "../" at least on Windows, I'm not sure about Linux, Mac, Android, etc...
My problem wasn't with the string at all, it pertained to settings within my projects. Within my project that builds into an executable, I had its working directory set to $(SolutionDir)x64/Debug and $(SolutionDir)x64/Release respectively which is correct for my solutions structure.
The issue was within my Engine project that is being built as a static library. Within its settings for its working directory, I had forgotten to modify both of the Debug and Release build options... These were still set to the default values of Visual Studio which I believe is (ProjectDir). Once I changed these to $(SolutionDir)x64/Debug and $(SolutionDir)x64/Release to match that of my ApplicationProject, I was able to open and read the contents of the files.
I found many examples on walking through directory tree, but I need something a little different. I need a class with some method which each call returns one file from directory and gradually walking through directory tree. How can I do this please? I am using functions FindFirstFile, FindNextFile and FindClose, I am newbie in c++. I have something like this...
For example I have this simple directory tree
Parent(folder)\
file1.txt
file2.txt
Child(folder)\
file3.txt
file4.txt
and I need a class with a method for example getNextFile(), that first call returns file1.txt; second call returns file2.txt, third call returns Child(folder), fourth call returns file3.txt and so on...
Edit on duplicate flag: I basically need walk through tree without do/while, while or for...I need some kind of iterator, which can be stored for later use and which can continue from last file, when I interrupt browsing, but ideally only with using winapi calls
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
return false;
}
do
{
//do some job with fdFile
}
while(FindNextFile(hFind, &fdFile));
Here is the native C++ way of doing it on Windows platform (using MFC framework):
void ListFiles(const CString& sPath)
{
CFileFind finder;
CString sWildcard(sPath);
sWildcard += _T("\\*.*");
BOOL bWorking = finder.FindFile(sWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDots())
continue;
if (finder.IsDirectory())
{
CString sFilePath = finder.GetFilePath();
// TODO: do stuff here
ListFiles(sFilePath);
}
}
finder.Close();
}
You can change wild card string to target specific files, like *.txt etc. You can also pass it as a parameter to this function to make it more general purpose.
Use the right tools. Boost is available as good as everywhere, and has the methods you want.
From http://rosettacode.org/wiki/Walk_a_directory/Recursively#C.2B.2B:
#include "boost/filesystem.hpp"
#include "boost/regex.hpp"
#include <iostream>
using namespace boost::filesystem;
int main()
{
path current_dir("."); //
boost::regex pattern("a.*"); // list all files starting with a
for (recursive_directory_iterator iter(current_dir), end;
iter != end;
++iter)
{
std::string name = iter->path().filename().string();
if (regex_match(name, pattern))
std::cout << iter->path() << "\n";
}
}
remove the whole regex business if you don't care whether your file matches a certain pattern.
EDIT:
Could you please explain why it would be bad to use directly API calls ?
it's ugly and hard to read, even harder to get right,
it's not portable at all, and what's most important,
there's a million corner cases you'd have to take care of, possibly, when using the raw win api. Boost has been written by people who did this a few hundred times and has underwent serious code review, so take the save route, and don't reinvent a wheel.
In essence, winapi is about two decades old; there's been a lot of usability improvement in the rest of the world. Unless you have a really good reason, I would try to abstract as much of it away as possible by using common libraries, such as Boost.
I think this does not solves my problem, I edited the original post to make it clearer.
basically need walk through tree without do/while, while or for...I need some kind of iterator, which can be stored for later use
That's exactly what my answer does: give you an Iterator in a for loop. I don't understand what's not fulfilling your Edit's specification about that.
In addition, it would be best to use only WinAPI, because it has to work on different computers with windows and installing boost could be a problem.
You don't have to install boost on any of these computers. Boost::filesystem can comfortable be linked in statically; also, the old-school windows way of doing this is just delivering boost_filesystem*.dll and boost_system*.dll along with your binary. However, if your goal is a single executable that contains all needed functions, you'll go for static linkage, anyway, so this is absolutely no problem.
I'm trying to convert a bunch of images to textures using SDL. So far, I know its possible to do everything manually:
//Load front alpha texture
if (!gModulatedTexture.loadFromFile("14_animated_sprites_and_vsync/text2.png"))
{
printf("Failed to load front texture!\n");
success = false;
}
else
.....
However, I have quite a few images I want to load so what I'm looking for is a way to automate the process. I want to put all my images into a single folder, and then do something like this:
i=0
while (there are still images to load) {
textureBank[i] = current image
i++
}
I wast thinking there might be some easy way to just read in the file path of all the files in a directory, but I haven't been able to find a way to do that.
Any suggestions?
You don't need to use any 3rd-party library like boost, just call the following function (for Windows OS). After this, you will get all file paths within given folder in vector<string>.
#include <Windows.h>
// folder must end with "/", e.g. "D:/images/"
vector<string> get_all_files_full_path_within_folder(string folder)
{
vector<string> names;
char search_path[200];
sprintf(search_path, "%s*.*", folder.c_str());
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// read all (real) files in current folder, delete '!' read other 2 default folder . and ..
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
names.push_back(folder+fd.cFileName);
}
}while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}
Since you are using SDL, I’ll assume you want to be cross-platform. The boost::filesystem library can do this.
Take a look at their directory iteration example.
Although it’s part of a 3rd-party library, boost::filesystem is proposed for inclusion in a future C++ standard, TR2, so it’s worth the effort to learn. It should eventually be the standard C++ way to work with files and directories.
I have a bunch of images called Apple-1, ..., Apple-n but I don't know if Apple-x is a .jpeg or .png and I want to determine that without knowing that it's a .jpeg or something else.
What I know is how the images are labeled and I want to get their extensions into a string (not all in one string, I would check for every image).
I appreciate any ideas :)
(There was a similar question asked here, but that's for C#: Get extension of file without providing extension in the path)
You can easily do that with boost::filesystem:
Example
boost::filesystem::path folder(boost::filesystem::current_path());
for (boost::filesystem::directory_iterator it(folder), end;
it != end; ++it)
{
auto ext = it->path().extension();
if (ext == ".jpeg")
{
std::cout << "is jpeg" << std::endl;
}
}
The boost filesystem library offers an excellent set of functions for this and related path issues" see here
I just made a program in Qt that creates a folder in a specific diretory.
the code is:
QDir directory;
directory.mkdir("Sample");
my problem is how could i convert the folder to a shared folder using Qt codes?
Or is there a way to create a shared folder using Qt??
You can share a directory using NetShareAdd. As far as I know, Qt doesn't provide anything with the same basic capability as NetShareAdd.
Edit: here's a quite bit of demo code:
#include <windows.h>
#include <lm.h>
int main() {
SHARE_INFO_2 info = {0};
info.shi2_netname = L"test_share";
info.shi2_type = STYPE_DISKTREE;
info.shi2_permissions = ACCESS_ALL;
info.shi2_max_uses = -1;
info.shi2_path = L"C:\\a\\b\\c";
NetShareAdd(NULL, 2, (BYTE *)&info, NULL);
return 0;
}
Note that NetShareAdd (like most of the Net* functions) is only available in a "wide" version that uses wide character strings.
This seems like it would be operating system dependent; Qt's abstraction of the OS-native directory functions isn't likely to be concerned with such a thing.
You'll probably want to look into your OS' specific methods for changing the "shared" status of a directory. On Windows, this might involve using WMI.