FreeCAD

Форк
0
/
StartPage.py 
900 строк · 31.9 Кб
1
# ***************************************************************************
2
# *   Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net>              *
3
# *                                                                         *
4
# *   This program is free software; you can redistribute it and/or modify  *
5
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
6
# *   as published by the Free Software Foundation; either version 2 of     *
7
# *   the License, or (at your option) any later version.                   *
8
# *   for detail see the LICENCE text file.                                 *
9
# *                                                                         *
10
# *   This program is distributed in the hope that it will be useful,       *
11
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13
# *   GNU Library General Public License for more details.                  *
14
# *                                                                         *
15
# *   You should have received a copy of the GNU Library General Public     *
16
# *   License along with this program; if not, write to the Free Software   *
17
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
18
# *   USA                                                                   *
19
# *                                                                         *
20
# ***************************************************************************
21

22

23
# This is the start page template. It builds an HTML global variable that
24
# contains the html code of the start page.
25
# Note: It is built only once per FreeCAD session for now...
26

27
import sys
28
import os
29
import tempfile
30
import time
31
import zipfile
32
import re
33
import FreeCAD
34
import FreeCADGui
35
import codecs
36
import hashlib
37
import urllib.parse
38
from . import TranslationTexts
39
from PySide import QtCore, QtGui
40

41
try:
42
    from addonmanager_macro import Macro as AM_Macro
43

44
    has_am_macro = True
45
except ImportError:
46
    has_am_macro = False
47

48

49
FreeCADGui.addLanguagePath(":/translations")
50
FreeCADGui.updateLocale()
51

52
iconprovider = QtGui.QFileIconProvider()
53
iconbank = {}  # store pre-existing icons so we don't overpollute temp dir
54
tempfolder = None  # store icons inside a subfolder in temp dir
55
defaulticon = None  # store a default icon for problematic file types
56

57

58
def getThumbnailDir():
59
    parent_dir = FreeCAD.getUserCachePath()
60
    return os.path.join(parent_dir, "FreeCADStartThumbnails")
61

62

63
def createThumbnailDir():
64
    path = getThumbnailDir()
65
    if not os.path.exists(path):
66
        os.mkdir(path)
67
    return path
68

69

70
def getSha1Hash(path, encode="utf-8"):
71
    sha_hash = hashlib.sha1()
72
    hashed = hashlib.sha1(path.encode(encode))
73
    hex_digest = hashed.hexdigest().encode(encode)
74
    sha_hash.update(hex_digest)
75
    return sha_hash.hexdigest()
76

77

78
def getUniquePNG(filename):
79
    parent_dir = getThumbnailDir()
80
    sha1 = getSha1Hash(filename) + ".png"
81
    return os.path.join(parent_dir, sha1)
82

83

84
def useCachedPNG(image, project):
85
    if not os.path.exists(image):
86
        return False
87
    if not os.path.exists(project):
88
        return False
89

90
    stamp = os.path.getmtime
91
    return stamp(image) > stamp(project)
92

93

94
def gethexcolor(color):
95

96
    "returns a color hex value #000000"
97

98
    r = str(hex(int(((color >> 24) & 0xFF))))[2:].zfill(2)
99
    g = str(hex(int(((color >> 16) & 0xFF))))[2:].zfill(2)
100
    b = str(hex(int(((color >> 8) & 0xFF))))[2:].zfill(2)
101
    return "#" + r + g + b
102

103

104
def isOpenableByFreeCAD(filename):
105

106
    "check if FreeCAD can handle this file type"
107

108
    if os.path.isdir(filename):
109
        return False
110
    if os.path.basename(filename)[0] == ".":
111
        return False
112
    extensions = [key.lower() for key in FreeCAD.getImportType().keys()]
113
    ext = os.path.splitext(filename)[1].lower()
114
    if ext:
115
        if ext[0] == ".":
116
            ext = ext[1:]
117
    if ext in extensions:
118
        return True
119
    return False
120

121

122
def getInfo(filename):
123

124
    "returns available file information"
125

126
    global iconbank, tempfolder
127

128
    tformat = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetString(
129
        "TimeFormat", "%c"
130
    )
131

132
    def getLocalTime(timestamp):
133
        "returns a local time from a timestamp"
134
        return time.strftime(tformat, time.localtime(timestamp))
135

136
    def getSize(size):
137
        "returns a human-readable size"
138
        if size > 1024 * 1024:
139
            hsize = str(int(size / (1024 * 1024))) + "Mb"
140
        elif size > 1024:
141
            hsize = str(int(size / 1024)) + "Kb"
142
        else:
143
            hsize = str(int(size)) + "b"
144
        return hsize
145

146
    def getFreeDesktopThumbnail(filename):
147
        "if we have gnome libs available, try to find a system-generated thumbnail"
148
        path = os.path.abspath(filename)
149
        thumb = None
150
        try:
151
            import gnome.ui
152
            import gnomevfs
153
        except Exception:
154
            # alternative method
155
            import hashlib
156

157
            fhash = hashlib.md5(
158
                bytes(urllib.parse.quote("file://" + path, safe=":/"), "ascii")
159
            ).hexdigest()
160
            thumb = os.path.join(os.path.expanduser("~"), ".thumbnails", "normal", fhash + ".png")
161
        else:
162
            uri = gnomevfs.get_uri_from_local_path(path)
163
            thumb = gnome.ui.thumbnail_path_for_uri(uri, "normal")
164
        if thumb and os.path.exists(thumb):
165
            return thumb
166
        return None
167

168
    if os.path.exists(filename):
169

170
        if os.path.isdir(filename):
171
            return None
172

173
        # get normal file info
174
        s = os.stat(filename)
175
        size = getSize(s.st_size)
176
        ctime = getLocalTime(s.st_ctime)
177
        mtime = getLocalTime(s.st_mtime)
178
        author = ""
179
        company = TranslationTexts.get("T_UNKNOWN")
180
        lic = TranslationTexts.get("T_UNKNOWN")
181
        image = None
182
        descr = ""
183
        path = os.path.abspath(filename)
184

185
        # get additional info from fcstd files
186
        if filename.lower().endswith(".fcstd"):
187
            try:
188
                zfile = zipfile.ZipFile(filename)
189
            except Exception:
190
                print("Cannot read file: ", filename)
191
                return None
192
            files = zfile.namelist()
193
            # check for meta-file if it's really a FreeCAD document
194
            if files[0] == "Document.xml":
195
                try:
196
                    doc = zfile.read(files[0]).decode("utf-8")
197
                except OSError as e:
198
                    print(
199
                        "Fail to load corrupted FCStd file: '{0}' with this error: {1}".format(
200
                            filename, str(e)
201
                        )
202
                    )
203
                    return None
204
                doc = doc.replace("\n", " ")
205
                r = re.findall('Property name="CreatedBy.*?String value="(.*?)"/>', doc)
206
                if r:
207
                    author = r[0]
208
                    # remove email if present in author field
209
                    if "&lt;" in author:
210
                        author = author.split("&lt;")[0].strip()
211
                r = re.findall('Property name="Company.*?String value="(.*?)"/>', doc)
212
                if r:
213
                    company = r[0]
214
                r = re.findall('Property name="License.*?String value="(.*?)"/>', doc)
215
                if r:
216
                    lic = r[0]
217
                r = re.findall('Property name="Comment.*?String value="(.*?)"/>', doc)
218
                if r:
219
                    descr = r[0]
220
                if "thumbnails/Thumbnail.png" in files:
221
                    image_png = getUniquePNG(filename)
222
                    if filename in iconbank:
223
                        image = iconbank[filename]
224
                    elif useCachedPNG(image_png, filename):
225
                        image = image_png
226
                        iconbank[filename] = image
227
                    else:
228
                        imagedata = zfile.read("thumbnails/Thumbnail.png")
229
                        image = image_png
230
                        thumb = open(image, "wb")
231
                        thumb.write(imagedata)
232
                        thumb.close()
233
                        iconbank[filename] = image
234

235
        elif filename.lower().endswith(".fcmacro"):
236
            # For FreeCAD macros, use the Macro Editor icon (but we have to have it in a file for
237
            # the web view to load it)
238
            image = os.path.join(tempfolder, "fcmacro_icon.svg")
239
            if not os.path.exists(image):
240
                f = QtCore.QFile(":/icons/MacroEditor.svg")
241
                f.copy(image)
242
            iconbank[filename] = image
243

244
            if has_am_macro:
245
                macro = AM_Macro(os.path.basename(filename))
246
                macro.fill_details_from_file(filename)
247
                author = macro.author
248

249
        elif QtGui.QImageReader.imageFormat(filename):
250
            # use image itself as icon if it's an image file
251
            image = filename
252
            iconbank[filename] = image
253

254
        else:
255
            # use freedesktop thumbnail if available
256
            fdthumb = getFreeDesktopThumbnail(filename)
257
            if fdthumb:
258
                image = fdthumb
259
                iconbank[filename] = fdthumb
260

261
        # retrieve default mime icon if needed
262
        if not image:
263
            i = QtCore.QFileInfo(filename)
264
            t = iconprovider.type(i)
265
            filename_png = getUniquePNG(filename)
266
            if not t:
267
                t = "Unknown"
268
            if t in iconbank:
269
                image = iconbank[t]
270
            elif os.path.exists(filename_png):
271
                image = filename_png
272
                iconbank[t] = image
273
            else:
274
                icon = iconprovider.icon(i)
275
                if icon.availableSizes():
276
                    preferred = icon.actualSize(QtCore.QSize(128, 128))
277
                    px = icon.pixmap(preferred)
278
                    image = filename_png
279
                    px.save(image)
280
                else:
281
                    image = getDefaultIcon()
282
                iconbank[t] = image
283
        return [image, size, author, ctime, mtime, descr, company, lic, path]
284

285
    return None
286

287

288
def getDefaultIcon():
289

290
    "retrieves or creates a default file icon"
291

292
    global defaulticon
293

294
    if not defaulticon:
295
        i = QtCore.QFileInfo(__file__)  # MUST provide an existing file in qt5
296
        icon = iconprovider.icon(i)
297
        preferred = icon.actualSize(QtCore.QSize(128, 128))
298
        px = icon.pixmap(preferred)
299
        image = getUniquePNG("default_icon")
300
        px.save(image)
301
        defaulticon = image
302

303
    return defaulticon
304

305

306
def build_new_file_card(template):
307

308
    """builds an html <li> element representing a new file
309
    quick start button"""
310

311
    templates = {
312
        "empty_file": [
313
            TranslationTexts.get("T_TEMPLATE_EMPTYFILE_NAME"),
314
            TranslationTexts.get("T_TEMPLATE_EMPTYFILE_DESC"),
315
        ],
316
        "open_file": [
317
            TranslationTexts.get("T_TEMPLATE_OPENFILE_NAME"),
318
            TranslationTexts.get("T_TEMPLATE_OPENFILE_DESC"),
319
        ],
320
        "parametric_part": [
321
            TranslationTexts.get("T_TEMPLATE_PARAMETRICPART_NAME"),
322
            TranslationTexts.get("T_TEMPLATE_PARAMETRICPART_DESC"),
323
        ],
324
        "assembly": [
325
            TranslationTexts.get("T_TEMPLATE_ASSEMBLY_NAME"),
326
            TranslationTexts.get("T_TEMPLATE_ASSEMBLY_DESC"),
327
        ],
328
        # "csg_part": [TranslationTexts.get("T_TEMPLATE_CSGPART_NAME"), TranslationTexts.get("T_TEMPLATE_CSGPART_DESC")],
329
        "2d_draft": [
330
            TranslationTexts.get("T_TEMPLATE_2DDRAFT_NAME"),
331
            TranslationTexts.get("T_TEMPLATE_2DDRAFT_DESC"),
332
        ],
333
        "architecture": [
334
            TranslationTexts.get("T_TEMPLATE_ARCHITECTURE_NAME"),
335
            TranslationTexts.get("T_TEMPLATE_ARCHITECTURE_DESC"),
336
        ],
337
    }
338

339
    if template not in templates:
340
        return ""
341

342
    image = "file:///" + os.path.join(
343
        os.path.join(FreeCAD.getResourceDir(), "Mod", "Start", "StartPage"),
344
        "images/new_" + template + ".png",
345
    ).replace("\\", "/")
346

347
    result = ""
348
    result += '<li class="quickstart-button-card">'
349
    result += '<a href="LoadNew.py?template=' + urllib.parse.quote(template) + '">'
350
    result += '<img src="' + image + '" alt="' + template + '">'
351
    result += '<div class="caption">'
352
    result += "<h3>" + templates[template][0] + "</h3>"
353
    result += "<p>" + templates[template][1] + "</p>"
354
    result += "</div>"
355
    result += "</a>"
356
    result += "</li>"
357
    return result
358

359

360
def buildCard(filename, method, arg=None):
361

362
    """builds an html <li> element representing a file.
363
    method is a script + a keyword, for ex. url.py?key="""
364

365
    result = ""
366
    if os.path.exists(filename) and isOpenableByFreeCAD(filename):
367
        basename = os.path.basename(filename)
368
        if not arg:
369
            arg = basename
370
        finfo = getInfo(filename)
371
        if finfo:
372
            image, size, author, ctime, mtime, descr, company, lic, path = finfo
373
            infostring = TranslationTexts.get("T_CREATIONDATE") + ": " + ctime + "\n"
374
            infostring += TranslationTexts.get("T_LASTMODIFIED") + ": " + mtime + "\n"
375
            infostring += TranslationTexts.get("T_SIZE") + ": " + size + "\n"
376
            infostring += TranslationTexts.get("T_AUTHOR") + ": " + author + "\n"
377
            infostring += TranslationTexts.get("T_LICENSE") + ": " + lic + "\n"
378
            infostring += TranslationTexts.get("T_FILEPATH") + ": " + path + "\n"
379
            if finfo[5]:
380
                infostring += "\n\n" + descr
381
            if size:
382
                result += '<li class="file-card">'
383
                result += (
384
                    '<a href="' + method + urllib.parse.quote(arg) + '" title="' + infostring + '">'
385
                )
386
                result += (
387
                    '<img src="file:///' + image.replace("\\", "/") + '" alt="' + basename + '">'
388
                )
389
                result += '<div class="caption">'
390
                result += "<h4>" + basename + "</h4>"
391
                result += "<p>" + author + "</p>"
392
                result += "<p>" + size + "</p>"
393
                result += "</div>"
394
                result += "</a>"
395
                result += "</li>"
396
    return result
397

398

399
def handle():
400

401
    "builds the HTML code of the start page"
402

403
    global iconbank, tempfolder
404

405
    # reuse stuff from previous runs to reduce temp dir clutter
406

407
    import Start
408

409
    if hasattr(Start, "iconbank"):
410
        iconbank = Start.iconbank
411
    if hasattr(Start, "tempfolder"):
412
        tempfolder = Start.tempfolder
413
    else:
414
        tempfolder = createThumbnailDir()
415

416
    # build the html page skeleton
417

418
    resources_dir = os.path.join(FreeCAD.getResourceDir(), "Mod", "Start", "StartPage")
419
    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start")
420
    template = p.GetString("Template", "")
421
    if template:
422
        html_filename = template
423
    else:
424
        html_filename = os.path.join(resources_dir, "StartPage.html")
425
    js_filename = os.path.join(resources_dir, "StartPage.js")
426
    css_filename = p.GetString("CSSFile", os.path.join(resources_dir, "StartPage.css"))
427
    with open(html_filename, "r") as f:
428
        HTML = f.read()
429
    with open(js_filename, "r") as f:
430
        JS = f.read()
431
    with open(css_filename, "r") as f:
432
        CSS = f.read()
433
    HTML = HTML.replace("JS", JS)
434
    HTML = HTML.replace("DEFAULT_CSS", CSS)
435
    HTML = HTML.replace(
436
        "CUSTOM_CSS",
437
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start")
438
        .GetString("CustomCSS", "")
439
        .replace("\n", ""),
440
    )
441

442
    # set the language
443

444
    HTML = HTML.replace("BCP47_LANGUAGE", QtCore.QLocale().bcp47Name())
445

446
    # get the stylesheet if we are using one
447

448
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
449
        "UseStyleSheet", False
450
    ):
451
        qssfile = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/MainWindow").GetString(
452
            "StyleSheet", ""
453
        )
454
        if qssfile:
455
            # Search for stylesheet in user, system and resources locations
456
            user = os.path.join(FreeCAD.getUserAppDataDir(), "Gui", "Stylesheets")
457
            system = os.path.join(FreeCAD.getResourceDir(), "Gui", "Stylesheets")
458
            resources = ":/stylesheets"
459

460
            res = False
461
            if QtCore.QFile.exists(os.path.join(user, qssfile)):
462
                path = os.path.join(user, qssfile)
463
            elif QtCore.QFile.exists(os.path.join(system, qssfile)):
464
                path = os.path.join(system, qssfile)
465
            elif QtCore.QFile.exists(os.path.join(resources, qssfile)):
466
                res = True
467
                path = os.path.join(resources, qssfile)
468
            else:
469
                path = None
470

471
            if path:
472
                if res:
473
                    f = QtCore.QFile(path)
474
                    if f.open(QtCore.QIODevice.ReadOnly | QtCore.QFile.Text):
475
                        ALTCSS = QtCore.QTextStream(f).readAll()
476
                        HTML = HTML.replace(
477
                            "<!--QSS-->", '<style type="text/css">' + ALTCSS + "</style>"
478
                        )
479
                else:
480
                    with codecs.open(path, encoding="utf-8") as f:
481
                        ALTCSS = f.read()
482
                        HTML = HTML.replace(
483
                            "<!--QSS-->", '<style type="text/css">' + ALTCSS + "</style>"
484
                        )
485

486
    # handle file thumbnail icons visibility and size
487

488
    if not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
489
        "ShowFileThumbnailIcons", True
490
    ):
491
        HTML = HTML.replace(
492
            "display: block; /* thumb icons display */", "display: none; /* thumb icons display */"
493
        )
494
        HTML = HTML.replace("THUMBCARDSIZE", "75px")
495

496
    thumb_icons_size = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetInt(
497
        "FileThumbnailIconsSize", 128
498
    )
499
    HTML = HTML.replace("THUMBSIZE", str(thumb_icons_size) + "px")
500
    HTML = HTML.replace("THUMBCARDSIZE", str(thumb_icons_size + 75) + "px")
501

502
    # turn tips off if needed
503

504
    if not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
505
        "ShowTips", True
506
    ):
507
        HTML = HTML.replace(
508
            "display: block; /* footnote tips display */",
509
            "display: none; /* footnote tips display */",
510
        )
511

512
    # get FreeCAD version
513

514
    v = FreeCAD.Version()
515
    VERSIONSTRING = (
516
        TranslationTexts.get("T_VERSION")
517
        + " "
518
        + v[0]
519
        + "."
520
        + v[1]
521
        + "."
522
        + v[2]
523
        + " "
524
        + TranslationTexts.get("T_BUILD")
525
        + " "
526
        + v[3]
527
    )
528
    HTML = HTML.replace("VERSIONSTRING", VERSIONSTRING)
529

530
    # translate texts
531

532
    texts = [t for t in TranslationTexts.get("index") if t.startswith("T_")]
533
    for text in texts:
534
        HTML = HTML.replace(text, TranslationTexts.get(text))
535

536
    # build a "create new" icon with the FreeCAD background color gradient
537

538
    if not "createimg" in iconbank:
539
        p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
540
        c1 = gethexcolor(p.GetUnsigned("BackgroundColor2"))
541
        c2 = gethexcolor(p.GetUnsigned("BackgroundColor3"))
542
        gradient = QtGui.QLinearGradient(0, 0, 0, 128)
543
        gradient.setColorAt(0.0, QtGui.QColor(c1))
544
        gradient.setColorAt(1.0, QtGui.QColor(c2))
545
        i = QtGui.QImage(128, 128, QtGui.QImage.Format_RGB16)
546
        pa = QtGui.QPainter(i)
547
        pa.fillRect(i.rect(), gradient)
548
        pa.end()
549
        createimg = getUniquePNG("createimg")
550
        i.save(createimg)
551
        iconbank["createimg"] = createimg
552

553
    # build SECTION_NEW_FILE
554

555
    SECTION_NEW_FILE = "<h2>" + TranslationTexts.get("T_NEWFILE") + "</h2>"
556
    SECTION_NEW_FILE += "<ul>"
557
    SECTION_NEW_FILE += build_new_file_card("parametric_part")
558
    SECTION_NEW_FILE += build_new_file_card("assembly")
559
    # SECTION_NEW_FILE += build_new_file_card("csg_part")
560
    SECTION_NEW_FILE += build_new_file_card("2d_draft")
561
    SECTION_NEW_FILE += build_new_file_card("architecture")
562
    SECTION_NEW_FILE += build_new_file_card("empty_file")
563
    SECTION_NEW_FILE += build_new_file_card("open_file")
564
    SECTION_NEW_FILE += "</ul>"
565
    HTML = HTML.replace("SECTION_NEW_FILE", SECTION_NEW_FILE)
566

567
    # build SECTION_RECENTFILES
568

569
    rf = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/RecentFiles")
570
    rfcount = rf.GetInt("RecentFiles", 0)
571
    SECTION_RECENTFILES = "<h2>" + TranslationTexts.get("T_RECENTFILES") + "</h2>"
572
    SECTION_RECENTFILES += "<ul>"
573
    for i in range(rfcount):
574
        filename = rf.GetString("MRU%d" % (i))
575
        SECTION_RECENTFILES += buildCard(filename, method="LoadMRU.py?MRU=", arg=str(i))
576
    SECTION_RECENTFILES += "</ul>"
577
    HTML = HTML.replace("SECTION_RECENTFILES", SECTION_RECENTFILES)
578

579
    # build SECTION_EXAMPLES
580

581
    SECTION_EXAMPLES = ""
582
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
583
        "ShowExamples", True
584
    ):
585
        SECTION_EXAMPLES = "<h2>" + TranslationTexts.get("T_EXAMPLES") + "</h2>"
586
        SECTION_EXAMPLES += "<ul>"
587
        examples_path = FreeCAD.getResourceDir() + "examples"
588
        if os.path.exists(examples_path):
589
            examples = os.listdir(examples_path)
590
            for basename in examples:
591
                filename = FreeCAD.getResourceDir() + "examples" + os.sep + basename
592
                SECTION_EXAMPLES += buildCard(filename, method="LoadExample.py?filename=")
593
        SECTION_EXAMPLES += "</ul>"
594
    HTML = HTML.replace("SECTION_EXAMPLES", SECTION_EXAMPLES)
595

596
    # build SECTION_CUSTOM
597

598
    SECTION_CUSTOM = ""
599
    cfolders = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetString(
600
        "ShowCustomFolder", ""
601
    )
602
    if cfolders:
603
        dn = 0
604
        for cfolder in cfolders.split(";;"):  # allow several paths separated by ;;
605
            if not os.path.isdir(cfolder):
606
                cfolder = os.path.dirname(cfolder)
607
            if not os.path.exists(cfolder):
608
                FreeCAD.Console.PrintWarning("Custom folder not found: %s" % cfolder)
609
            else:
610
                SECTION_CUSTOM += "<h2>" + os.path.basename(os.path.normpath(cfolder)) + "</h2>"
611
                SECTION_CUSTOM += "<ul>"
612
                for basename in os.listdir(cfolder):
613
                    filename = os.path.join(cfolder, basename)
614
                    SECTION_CUSTOM += buildCard(
615
                        filename, method="LoadCustom.py?filename=" + str(dn) + "_"
616
                    )
617
                SECTION_CUSTOM += "</ul>"
618
                # hide the custom section tooltip if custom section is set (users know about it if they enabled it)
619
                HTML = HTML.replace(
620
                    'id="customtip" class', 'id="customtip" style="display:none;" class'
621
                )
622
                dn += 1
623
    HTML = HTML.replace("SECTION_CUSTOM", SECTION_CUSTOM)
624

625
    # build IMAGE_SRC paths
626

627
    HTML = HTML.replace(
628
        "IMAGE_SRC_FREECAD",
629
        "file:///" + os.path.join(resources_dir, "images/freecad.png").replace("\\", "/"),
630
    )
631
    HTML = HTML.replace(
632
        "IMAGE_SRC_ICON_DOCUMENTS",
633
        "file:///" + os.path.join(resources_dir, "images/icon_documents.png").replace("\\", "/"),
634
    )
635
    HTML = HTML.replace(
636
        "IMAGE_SRC_ICON_HELP",
637
        "file:///" + os.path.join(resources_dir, "images/icon_help.png").replace("\\", "/"),
638
    )
639
    HTML = HTML.replace(
640
        "IMAGE_SRC_ICON_ACTIVITY",
641
        "file:///" + os.path.join(resources_dir, "images/icon_activity.png").replace("\\", "/"),
642
    )
643
    HTML = HTML.replace(
644
        "IMAGE_SRC_ICON_BLOG",
645
        "file:///" + os.path.join(resources_dir, "images/icon_blog.png").replace("\\", "/"),
646
    )
647
    HTML = HTML.replace(
648
        "IMAGE_SRC_USERHUB",
649
        "file:///" + os.path.join(resources_dir, "images/userhub.png").replace("\\", "/"),
650
    )
651
    HTML = HTML.replace(
652
        "IMAGE_SRC_POWERHUB",
653
        "file:///" + os.path.join(resources_dir, "images/poweruserhub.png").replace("\\", "/"),
654
    )
655
    HTML = HTML.replace(
656
        "IMAGE_SRC_DEVHUB",
657
        "file:///" + os.path.join(resources_dir, "images/developerhub.png").replace("\\", "/"),
658
    )
659
    HTML = HTML.replace(
660
        "IMAGE_SRC_MANUAL",
661
        "file:///" + os.path.join(resources_dir, "images/manual.png").replace("\\", "/"),
662
    )
663
    HTML = HTML.replace(
664
        "IMAGE_SRC_SETTINGS",
665
        "file:///" + os.path.join(resources_dir, "images/icon_settings.png").replace("\\", "/"),
666
    )
667
    HTML = HTML.replace(
668
        "IMAGE_SRC_INSTALLED",
669
        "file:///" + os.path.join(resources_dir, "images/installed.png").replace("\\", "/"),
670
    )
671

672
    # build UL_WORKBENCHES
673

674
    wblist = []
675
    UL_WORKBENCHES = '<ul class="workbenches">'
676
    FreeCAD.getResourceDir()
677
    for wb in sorted(FreeCADGui.listWorkbenches().keys()):
678
        if wb.endswith("Workbench"):
679
            wn = wb[:-9]
680
        else:
681
            wn = wb
682
        # fixes for non-standard names
683
        if wn == "flamingoTools":
684
            wn = "flamingo"
685
        elif wn == "Geodat":
686
            wn = "geodata"
687
        elif wn == "a2p":
688
            wn = "A2plus"
689
        elif wn == "ArchTexture":
690
            wn = "ArchTextures"
691
        elif wn == "CadQuery":
692
            wn = "cadquery_module"
693
        elif wn == "DefeaturingWB":
694
            wn = "Defeaturing"
695
        elif wn == "ksuWB":
696
            wn = "kicadStepUp"
697
        elif wn == "ManipulatorWB":
698
            wn = "Manipulator"
699
        elif wn == "PartOMagic":
700
            wn = "Part-o-magic"
701
        elif wn == "SM":
702
            wn = "sheetmetal"
703
        elif wn == "gear":
704
            wn = "FCGear"
705
        elif wn == "frame_":
706
            wn = "frame"
707
        elif wn == "ReverseEngineering":
708
            wn = "Reverse_Engineering"
709
        elif wn == "None":
710
            continue
711
        wblist.append(wn.lower())
712
        if wb in iconbank:
713
            img = iconbank[wb]
714
        else:
715
            img = os.path.join(
716
                FreeCAD.getResourceDir(), "Mod", wn, "Resources", "icons", wn + "Workbench.svg"
717
            )
718
            if not os.path.exists(img):
719
                w = FreeCADGui.listWorkbenches()[wb]
720
                if hasattr(w, "Icon") and w.Icon:
721
                    xpm = w.Icon
722
                    if "XPM" in xpm:
723
                        xpm = xpm.replace(
724
                            "\n        ", "\n"
725
                        )  # some XPMs have some indent that QT doesn't like
726
                        r = [
727
                            s[:-1].strip('"')
728
                            for s in re.findall("(?s){(.*?)};", xpm)[0].split("\n")[1:]
729
                        ]
730
                        p = QtGui.QPixmap(r)
731
                        p = p.scaled(24, 24)
732
                        img = getUniquePNG(wb)
733
                        p.save(img)
734
                    else:
735
                        img = xpm
736
                else:
737
                    img = os.path.join(resources_dir, "images/freecad.png")
738
            iconbank[wb] = img
739
        UL_WORKBENCHES += "<li>"
740
        UL_WORKBENCHES += (
741
            '<img src="file:///' + img.replace("\\", "/") + '" alt="' + wn + '">&nbsp;'
742
        )
743
        UL_WORKBENCHES += (
744
            '<a href="https://www.freecad.org/wiki/'
745
            + wn
746
            + '_Workbench">'
747
            + wn.replace("Reverse_Engineering", "ReverseEng")
748
            + "</a>"
749
        )
750
        UL_WORKBENCHES += "</li>"
751
    UL_WORKBENCHES += "</ul>"
752
    HTML = HTML.replace("UL_WORKBENCHES", UL_WORKBENCHES)
753

754
    # Detect additional addons that are not a workbench
755

756
    try:
757
        import dxfLibrary
758
    except Exception:
759
        pass
760
    else:
761
        wblist.append("dxf-library")
762
    try:
763
        import RebarTools
764
    except Exception:
765
        pass
766
    else:
767
        wblist.append("reinforcement")
768
    try:
769
        import CADExchangerIO
770
    except Exception:
771
        pass
772
    else:
773
        wblist.append("cadexchanger")
774
    HTML = HTML.replace("var wblist = [];", "var wblist = " + str(wblist) + ";")
775

776
    # set and replace colors and font settings
777

778
    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start")
779
    if p.GetString("BackgroundImage", ""):
780
        BACKGROUND = (
781
            gethexcolor(p.GetUnsigned("BackgroundColor1", 1331197183))
782
            + " url("
783
            + p.GetString("BackgroundImage", "")
784
            + ")"
785
        )
786
    else:
787
        BACKGROUND = gethexcolor(p.GetUnsigned("BackgroundColor1", 1331197183))
788
        # linear gradient not supported by QT "linear-gradient("+gethexcolor(p.GetUnsigned("BackgroundColor1",1331197183))+","+gethexcolor(p.GetUnsigned("BackgroundColor2",2141107711))+")"
789
    LINKCOLOR = gethexcolor(p.GetUnsigned("LinkColor", 65535))
790
    BASECOLOR = gethexcolor(p.GetUnsigned("PageColor", 4294967295))
791
    BOXCOLOR = gethexcolor(p.GetUnsigned("BoxColor", 3722305023))
792
    TEXTCOLOR = gethexcolor(p.GetUnsigned("PageTextColor", 255))
793
    BGTCOLOR = gethexcolor(p.GetUnsigned("BackgroundTextColor", 1600086015))
794
    SHADOW = "#888888"
795
    if QtGui.QColor(BASECOLOR).valueF() < 0.5:  # dark page - we need to make darker shadows
796
        SHADOW = "#000000"
797
    FONTFAMILY = p.GetString("FontFamily", "Arial,Helvetica,sans")
798
    if not FONTFAMILY:
799
        FONTFAMILY = "Arial,Helvetica,sans"
800
    FONTSIZE = p.GetInt("FontSize", 13)
801
    HTML = HTML.replace("BASECOLOR", BASECOLOR)
802
    HTML = HTML.replace("BOXCOLOR", BOXCOLOR)
803
    HTML = HTML.replace("LINKCOLOR", LINKCOLOR)
804
    HTML = HTML.replace("TEXTCOLOR", TEXTCOLOR)
805
    HTML = HTML.replace("BGTCOLOR", BGTCOLOR)
806
    HTML = HTML.replace("BACKGROUND", BACKGROUND)
807
    HTML = HTML.replace("SHADOW", SHADOW)
808
    HTML = HTML.replace("FONTFAMILY", FONTFAMILY)
809
    HTML = HTML.replace("FONTSIZE", str(FONTSIZE) + "px")
810

811
    # enable web access if permitted
812

813
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
814
        "AllowDownload", False
815
    ):
816
        HTML = HTML.replace("var allowDownloads = 0;", "var allowDownloads = 1;")
817

818
    # enable or disable forum
819

820
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool("ShowForum", False):
821
        HTML = HTML.replace("var showForum = 0;", "var showForum = 1;")
822
        HTML = HTML.replace(
823
            "display: none; /* forum display */", "display: block; /* forum display */"
824
        )
825

826
    # enable or disable notepad
827

828
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool("ShowNotes", False):
829
        HTML = HTML.replace(
830
            "display: none; /* notes display */", "display: block; /* notes display */"
831
        )
832
        HTML = HTML.replace("width: 100%; /* thumbs display */", "width: 70%; /* thumbs display */")
833

834
    # store variables for further use
835

836
    Start.iconbank = iconbank
837
    Start.tempfolder = tempfolder
838

839
    return HTML
840

841

842
def exportTestFile():
843

844
    "Allow to check if everything is Ok"
845

846
    with codecs.open(
847
        os.path.expanduser("~") + os.sep + "freecad-startpage.html",
848
        encoding="utf-8",
849
        mode="w",
850
    ) as f:
851
        f.write(handle())
852
        f.close()
853

854

855
def postStart(switch_wb=True):
856

857
    "executes needed operations after loading a file"
858

859
    param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start")
860

861
    # switch workbench
862
    if switch_wb:
863
        wb = param.GetString("AutoloadModule", "")
864
        if "$LastModule" == wb:
865
            wb = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/General").GetString(
866
                "LastModule", ""
867
            )
868
        if wb:
869
            # don't switch workbenches if we are not in Start anymore
870
            if FreeCADGui.activeWorkbench() and (
871
                FreeCADGui.activeWorkbench().name() == "StartWorkbench"
872
            ):
873
                FreeCADGui.activateWorkbench(wb)
874

875
    # close start tab
876
    cl = param.GetBool("closeStart", False)
877
    if cl:
878
        title = QtGui.QApplication.translate("Workbench", "Start page")
879
        mw = FreeCADGui.getMainWindow()
880
        if mw:
881
            mdi = mw.findChild(QtGui.QMdiArea)
882
            if mdi:
883
                for mdichild in mdi.children():
884
                    for subw in mdichild.findChildren(QtGui.QMdiSubWindow):
885
                        if subw.windowTitle() == title:
886
                            subw.close()
887

888

889
def checkPostOpenStartPage():
890

891
    "on Start WB startup, check if we are loading a file and therefore need to close the StartPage"
892

893
    import Start
894

895
    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Start").GetBool(
896
        "DoNotShowOnOpen", False
897
    ) and (not hasattr(Start, "CanOpenStartPage")):
898
        if len(sys.argv) > 1:
899
            postStart()
900
    Start.CanOpenStartPage = True
901

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.