Skip to content

Commit 8e12727

Browse files
committed
Start: Refactor f3d to use worker threads
1 parent c4fc4ef commit 8e12727

File tree

7 files changed

+312
-105
lines changed

7 files changed

+312
-105
lines changed

src/Mod/Start/App/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,14 @@ SET(Start_SRCS
3636
DisplayedFilesModel.h
3737
ExamplesModel.cpp
3838
ExamplesModel.h
39+
FileUtilities.cpp
40+
FileUtilities.h
3941
PreCompiled.cpp
4042
PreCompiled.h
4143
RecentFilesModel.cpp
42-
RecentFilesModel.h)
44+
RecentFilesModel.h
45+
ThumbnailSource.cpp
46+
ThumbnailSource.h)
4347

4448
add_library(Start SHARED ${Start_SRCS})
4549
target_link_libraries(Start ${Start_LIBS})

src/Mod/Start/App/DisplayedFilesModel.cpp

Lines changed: 17 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
#endif
3737

3838
#include "DisplayedFilesModel.h"
39+
40+
#include <QThreadPool>
41+
42+
#include "FileUtilities.h"
3943
#include <App/Application.h>
4044
#include <App/ProjectFile.h>
4145
#include <Base/FileInfo.h>
@@ -89,73 +93,6 @@ FileStats fileInfoFromFreeCADFile(const std::string& path)
8993
return result;
9094
}
9195

92-
std::string getThumbnailsImage()
93-
{
94-
return "thumbnails/Thumbnail.png";
95-
}
96-
97-
QString getThumbnailsName()
98-
{
99-
#if defined(Q_OS_LINUX)
100-
return QString::fromLatin1("thumbnails/normal");
101-
#else
102-
return QString::fromLatin1("FreeCADStartThumbnails");
103-
#endif
104-
}
105-
106-
QDir getThumnailsParentDir()
107-
{
108-
return {QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)};
109-
}
110-
111-
QString getThumbnailsDir()
112-
{
113-
QDir dir = getThumnailsParentDir();
114-
return dir.absoluteFilePath(getThumbnailsName());
115-
}
116-
117-
void createThumbnailsDir()
118-
{
119-
QString name = getThumbnailsName();
120-
QDir dir(getThumnailsParentDir());
121-
if (!dir.exists(name)) {
122-
dir.mkpath(name);
123-
}
124-
}
125-
126-
QString getMD5Hash(const std::string& path)
127-
{
128-
// Use MD5 hash as specified here:
129-
// https://specifications.freedesktop.org/thumbnail-spec/0.8.0/thumbsave.html
130-
QUrl url(QString::fromStdString(path));
131-
url.setScheme(QString::fromLatin1("file"));
132-
QCryptographicHash hash(QCryptographicHash::Md5);
133-
hash.addData(url.toEncoded());
134-
QByteArray ba = hash.result().toHex();
135-
return QString::fromLatin1(ba);
136-
}
137-
138-
QString getUniquePNG(const std::string& path)
139-
{
140-
QDir dir = getThumbnailsDir();
141-
QString md5 = getMD5Hash(path) + QLatin1String(".png");
142-
return dir.absoluteFilePath(md5);
143-
}
144-
145-
bool useCachedPNG(const std::string& image, const std::string& project)
146-
{
147-
Base::FileInfo f1(image);
148-
Base::FileInfo f2(project);
149-
if (!f1.exists()) {
150-
return false;
151-
}
152-
if (!f2.exists()) {
153-
return false;
154-
}
155-
156-
return f1.lastModified() > f2.lastModified();
157-
}
158-
15996
/// Load the thumbnail image data (if any) that is stored in an FCStd file.
16097
/// \returns The image bytes, or an empty QByteArray (if no thumbnail was stored)
16198
QByteArray loadFCStdThumbnail(const std::string& pathToFCStdFile)
@@ -187,39 +124,6 @@ QByteArray loadFCStdThumbnail(const std::string& pathToFCStdFile)
187124
return {};
188125
}
189126

190-
/// Attempt to generate a thumbnail image from a file using f3d or load from cache
191-
/// \returns The image bytes, or an empty QByteArray (if thumbnail generation fails)
192-
QByteArray getF3dThumbnail(const std::string& pathToFile)
193-
{
194-
QString thumbnailPath = getUniquePNG(pathToFile);
195-
if (!useCachedPNG(thumbnailPath.toStdString(), pathToFile)) {
196-
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
197-
"User parameter:BaseApp/Preferences/Mod/Start");
198-
auto f3d = QString::fromUtf8(hGrp->GetASCII("f3d", "f3d").c_str());
199-
const int resolution = 128;
200-
QStringList args;
201-
args << QLatin1String("--config=thumbnail") << QLatin1String("--load-plugins=occt")
202-
<< QLatin1String("--verbose=quiet") << QLatin1String("--output=") + thumbnailPath
203-
<< QLatin1String("--resolution=") + QString::number(resolution) + QLatin1String(",")
204-
+ QString::number(resolution)
205-
<< QString::fromStdString(pathToFile);
206-
207-
QProcess process;
208-
process.start(f3d, args);
209-
process.waitForFinished();
210-
if (process.exitCode() != 0) {
211-
return {};
212-
}
213-
}
214-
215-
QFile thumbnailFile(thumbnailPath);
216-
if (thumbnailFile.exists()) {
217-
thumbnailFile.open(QIODevice::OpenModeFlag::ReadOnly);
218-
return thumbnailFile.readAll();
219-
}
220-
return {};
221-
}
222-
223127
FileStats getFileInfo(const std::string& path)
224128
{
225129
FileStats result;
@@ -329,10 +233,12 @@ void DisplayedFilesModel::addFile(const QString& filePath)
329233
}
330234
}
331235
else {
332-
auto thumbnail = getF3dThumbnail(filePath.toStdString());
333-
if (!thumbnail.isEmpty()) {
334-
_imageCache.insert(filePath, thumbnail);
335-
}
236+
auto runner = new ThumbnailSource(filePath);
237+
connect(runner->signals(),
238+
&ThumbnailSourceSignals::thumbnailAvailable,
239+
this,
240+
&DisplayedFilesModel::processNewThumbnail);
241+
QThreadPool::globalInstance()->start(runner);
336242
}
337243
}
338244

@@ -357,3 +263,10 @@ QHash<int, QByteArray> DisplayedFilesModel::roleNames() const
357263
};
358264
return nameMap;
359265
}
266+
267+
void DisplayedFilesModel::processNewThumbnail(const QString& file, const QByteArray& thumbnail)
268+
{
269+
if (!thumbnail.isEmpty()) {
270+
_imageCache.insert(file, thumbnail);
271+
}
272+
}

src/Mod/Start/App/DisplayedFilesModel.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <Base/Parameter.h>
2929

3030
#include "../StartGlobal.h"
31+
#include "ThumbnailSource.h"
3132

3233
namespace Start
3334
{
@@ -74,6 +75,9 @@ class StartExport DisplayedFilesModel: public QAbstractListModel
7475
/// a new list of files, only re-caches the existing ones.
7576
void reCacheFileInfo();
7677

78+
/// Process a new thumbnail produces by some sort of worker thread
79+
void processNewThumbnail(const QString& file, const QByteArray& thumbnail);
80+
7781
private:
7882
std::vector<FileStats> _fileInfoCache;
7983
QMap<QString, QByteArray> _imageCache;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
/****************************************************************************
3+
* *
4+
* Copyright (c) 2025 The FreeCAD Project Association AISBL *
5+
* *
6+
* This file is part of FreeCAD. *
7+
* *
8+
* FreeCAD is free software: you can redistribute it and/or modify it *
9+
* under the terms of the GNU Lesser General Public License as *
10+
* published by the Free Software Foundation, either version 2.1 of the *
11+
* License, or (at your option) any later version. *
12+
* *
13+
* FreeCAD is distributed in the hope that it will be useful, but *
14+
* WITHOUT ANY WARRANTY; without even the implied warranty of *
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16+
* Lesser General Public License for more details. *
17+
* *
18+
* You should have received a copy of the GNU Lesser General Public *
19+
* License along with FreeCAD. If not, see *
20+
* <https://www.gnu.org/licenses/>. *
21+
* *
22+
***************************************************************************/
23+
24+
#include "PreCompiled.h"
25+
#ifndef _PreComp_
26+
#include <QCryptographicHash>
27+
#include <QStandardPaths>
28+
#include <QUrl>
29+
#endif
30+
31+
#include "FileUtilities.h"
32+
33+
using namespace Start;
34+
35+
std::string Start::getThumbnailsImage()
36+
{
37+
return "thumbnails/Thumbnail.png";
38+
}
39+
40+
QString Start::getThumbnailsName()
41+
{
42+
#if defined(Q_OS_LINUX)
43+
return QLatin1String("thumbnails/normal");
44+
#else
45+
return QLatin1String("FreeCADStartThumbnails");
46+
#endif
47+
}
48+
49+
QDir Start::getThumbnailsParentDir()
50+
{
51+
return {QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)};
52+
}
53+
54+
QString Start::getThumbnailsDir()
55+
{
56+
const QDir dir = getThumbnailsParentDir();
57+
return dir.absoluteFilePath(getThumbnailsName());
58+
}
59+
60+
void Start::createThumbnailsDir()
61+
{
62+
const QString name = getThumbnailsName();
63+
const QDir dir(getThumbnailsParentDir());
64+
if (!dir.exists(name)) {
65+
dir.mkpath(name);
66+
}
67+
}
68+
69+
QString Start::getMD5Hash(const std::string& path)
70+
{
71+
// Use MD5 hash as specified here:
72+
// https://specifications.freedesktop.org/thumbnail-spec/0.8.0/thumbsave.html
73+
QUrl url(QString::fromStdString(path));
74+
url.setScheme(QString::fromLatin1("file"));
75+
QCryptographicHash hash(QCryptographicHash::Md5);
76+
hash.addData(url.toEncoded());
77+
QByteArray ba = hash.result().toHex();
78+
return QString::fromLatin1(ba);
79+
}
80+
81+
QString Start::getUniquePNG(const std::string& path)
82+
{
83+
QDir dir = getThumbnailsDir();
84+
QString md5 = getMD5Hash(path) + QLatin1String(".png");
85+
return dir.absoluteFilePath(md5);
86+
}
87+
88+
bool Start::useCachedPNG(const std::string& image, const std::string& project)
89+
{
90+
Base::FileInfo f1(image);
91+
Base::FileInfo f2(project);
92+
if (!f1.exists()) {
93+
return false;
94+
}
95+
if (!f2.exists()) {
96+
return false;
97+
}
98+
99+
return f1.lastModified() > f2.lastModified();
100+
}

src/Mod/Start/App/FileUtilities.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
/****************************************************************************
3+
* *
4+
* Copyright (c) 2025 The FreeCAD Project Association AISBL *
5+
* *
6+
* This file is part of FreeCAD. *
7+
* *
8+
* FreeCAD is free software: you can redistribute it and/or modify it *
9+
* under the terms of the GNU Lesser General Public License as *
10+
* published by the Free Software Foundation, either version 2.1 of the *
11+
* License, or (at your option) any later version. *
12+
* *
13+
* FreeCAD is distributed in the hope that it will be useful, but *
14+
* WITHOUT ANY WARRANTY; without even the implied warranty of *
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16+
* Lesser General Public License for more details. *
17+
* *
18+
* You should have received a copy of the GNU Lesser General Public *
19+
* License along with FreeCAD. If not, see *
20+
* <https://www.gnu.org/licenses/>. *
21+
* *
22+
***************************************************************************/
23+
24+
#ifndef FREECAD_FILEUTILITIES_H
25+
#define FREECAD_FILEUTILITIES_H
26+
27+
#include <string>
28+
#include <Base/FileInfo.h>
29+
#include <QString>
30+
#include <QDir>
31+
32+
namespace Start
33+
{
34+
35+
std::string getThumbnailsImage();
36+
37+
QString getThumbnailsName();
38+
39+
QDir getThumbnailsParentDir();
40+
41+
QString getThumbnailsDir();
42+
43+
void createThumbnailsDir();
44+
45+
QString getMD5Hash(const std::string& path);
46+
47+
QString getUniquePNG(const std::string& path);
48+
49+
bool useCachedPNG(const std::string& image, const std::string& project);
50+
51+
} // namespace Start
52+
53+
#endif // FREECAD_FILEUTILITIES_H

0 commit comments

Comments
 (0)