Как открыть диалоговое окно загрузки файла с помощью QWebEngineView?

#python #browser #pyqt5 #downloading-website-files

Вопрос:

Я создаю интерфейс рабочего стола pyqt5, в котором я использую QWebEngineView для отображения html-файла, в котором я показываю карту листовки. Это работает нормально. Следующий шаг-экспортировать все объекты, добавленные пользователем на карту. Когда я нажимаю «Экспортировать функции» на карте, ничего не происходит, но когда я открываю тот же html-файл в своем веб-браузере Chromium, «Функция экспорта» открывает диалоговое окно загрузки нормально.

Скрипт PyQt5:

 self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
self.html_path = os.path.split(os.path.abspath(__file__))[0]   r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
 

HTML:

 <!DOCTYPE html>
        <html lang="en">
          <head>
            <meta charset="utf-8">
            <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
            <title>FazMaraneyRGB_transparent_mosaic_group1</title>

            <!-- Leaflet -->
            <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
            <script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>

            <!-- Leaflet.draw -->
            <link rel="stylesheet" href="https://unpkg.com/leaflet-draw@0.4.1/dist/leaflet.draw.css" />
            <script src="https://unpkg.com/leaflet-draw@0.4.1/dist/leaflet.draw.js"></script>

            <!-- Leaflet Ajax -->
            <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>

            <!-- Leaflet Measument -->
            <link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
            <script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>


            <style>
                body { margin:0; padding:0; }
                body, table, tr, td, th, div, h1, h2, input { font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif; font-size: 11pt; }
                #map { position:absolute; top:0; bottom:0; width:100%; } /* full size */
                .ctl {
                    padding: 2px 10px 2px 10px;
                    background: white;
                    background: rgba(255,255,255,0.9);
                    box-shadow: 0 0 15px rgba(0,0,0,0.2);
                    border-radius: 5px;
                    text-align: right;
                }
                .title {
                    font-size: 18pt;
                    font-weight: bold;
                }
                .src {
                    font-size: 10pt;
                }
                #delete, #export {
                    position: absolute;
                    top:100px;
                    right:10px;
                    z-index:100;
                    background:white;
                    color:black;
                    padding:6px;
                    border-radius:4px;
                    font-family: 'Helvetica Neue';
                    cursor: pointer;
                    font-size:12px;
                    text-decoration:none;
                }
                #export {
                    top:130px;
                }
            </style>

        </head>
        <body>

        <div id='map'></div>
        <div id='delete'>Delete Features</div>
        <a href='#' id='export'>Export Features</a>

        <script>
        /* **** Leaflet **** */

        // Base layers
        //  .. OpenStreetMap
        var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: 'amp;copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'});

        //  .. White background
        var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX/// nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");

        // Overlay layers (TMS)
        var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {tms: true, maxZoom: 22, opacity: 0.9, attribution: ""});

        // Map
        var map = L.map('map', {
            measureControl: true,
            center: [-18.3604868606589, -52.694255477616245],
            zoom: 22,
            minZoom: 0,
            maxZoom: 22,
            layers: [osm]
        });

        lyr1.addTo(map);

        //Geojson Layers


        var basemaps = {"OpenStreetMap": osm, "Without background": white}
        var overlaymaps = {"Layer 1": lyr1}

        // Title
        var title = L.control();
        title.onAdd = function(map) {
            this._div = L.DomUtil.create('div', 'ctl title');
            this.update();
            return this._div;
        };
        title.update = function(props) {
            this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
        };
        title.addTo(map);

        // Note
        var src = 'Generated by Hawkit';
        var title = L.control({position: 'bottomleft'});
        title.onAdd = function(map) {
            this._div = L.DomUtil.create('div', 'ctl src');
            this.update();
            return this._div;
        };
        title.update = function(props) {
            this._div.innerHTML = src;
        };
        title.addTo(map);

        var featureGroup = L.featureGroup().addTo(map);

        var drawControl = new L.Control.Draw({
            edit: {
                featureGroup: featureGroup
            }
        }).addTo(map);

        map.on('draw:created', function(e) {

            // Each time a feaute is created, it's added to the over arching feature group
            featureGroup.addLayer(e.layer);
        });

        // on click, clear all layers
        document.getElementById('delete').onclick = function(e) {
            featureGroup.clearLayers();
        }

        document.getElementById('export').onclick = function(e) {
            // Extract GeoJson from featureGroup
            var data = featureGroup.toGeoJSON();

            // Stringify the GeoJson
            var convertedData = 'text/json;charset=utf-8,'   encodeURIComponent(JSON.stringify(data));

            // Create export
            document.getElementById('export').setAttribute('href', 'data:'   convertedData);
            document.getElementById('export').setAttribute('download','data.geojson');
        }


        // Add base layers
        L.control.layers(basemaps, overlaymaps, {collapsed: true}).addTo(map);

        // Fit to overlay bounds (SW and NE points with (lat, lon))
        map.fitBounds([[-18.36827062251916, -52.6871074784942], [-18.35270287637126, -52.7014028427423]]);

        </script>

        </body>
        </html>
 

Ответ №1:

Это всплывающее окно генерируется браузером, в случае QWebEngine , если мы должны его создать. Для начала должен быть обнаружен сигнал, указывающий на загрузку, и этот сигнал downloadRequested исходит от QWebEngineProfile . Этот сигнал посылает нам QWebEngineDownloadItem объект, который обрабатывает загрузку, в нем мы создаем диалоговое окно с помощью QFileDialog . В этом случае мы создадим пользовательскую страницу qwebengin, как показано ниже:

index.html

 <!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
    <title>FazMaraneyRGB_transparent_mosaic_group1</title>

    <!-- Leaflet -->
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
    <script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>

    <!-- Leaflet.draw -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet-draw@0.4.1/dist/leaflet.draw.css" />
    <script src="https://unpkg.com/leaflet-draw@0.4.1/dist/leaflet.draw.js"></script>

    <!-- Leaflet Ajax -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>

    <!-- Leaflet Measument -->
    <link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
    <script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>

    <style>
        body {
            margin: 0;
            padding: 0;
        }
        
        body,
        table,
        tr,
        td,
        th,
        div,
        h1,
        h2,
        input {
            font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif;
            font-size: 11pt;
        }
        
        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
        /* full size */
        
        .ctl {
            padding: 2px 10px 2px 10px;
            background: white;
            background: rgba(255, 255, 255, 0.9);
            box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
            border-radius: 5px;
            text-align: right;
        }
        
        .title {
            font-size: 18pt;
            font-weight: bold;
        }
        
        .src {
            font-size: 10pt;
        }
        
        #delete,
        #export {
            position: absolute;
            top: 100px;
            right: 10px;
            z-index: 100;
            background: white;
            color: black;
            padding: 6px;
            border-radius: 4px;
            font-family: 'Helvetica Neue';
            cursor: pointer;
            font-size: 12px;
            text-decoration: none;
        }
        
        #export {
            top: 130px;
        }
    </style>

</head>

<body>

    <div id='map'></div>
    <div id='delete'>Delete Features</div>
    <a href='#' id='export'>Export Features</a>

    <script>
        /* **** Leaflet **** */

        // Base layers
        //  .. OpenStreetMap
        var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: 'amp;copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        });

        //  .. White background
        var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX/// nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");

        // Overlay layers (TMS)
        var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {
            tms: true,
            maxZoom: 22,
            opacity: 0.9,
            attribution: ""
        });

        // Map
        var map = L.map('map', {
            measureControl: true,
            center: [-18.3604868606589, -52.694255477616245],
            zoom: 22,
            minZoom: 0,
            maxZoom: 22,
            layers: [osm]
        });

        lyr1.addTo(map);

        //Geojson Layers

        var basemaps = {
            "OpenStreetMap": osm,
            "Without background": white
        }
        var overlaymaps = {
            "Layer 1": lyr1
        }

        // Title
        var title = L.control();
        title.onAdd = function(map) {
            this._div = L.DomUtil.create('div', 'ctl title');
            this.update();
            return this._div;
        };
        title.update = function(props) {
            this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
        };
        title.addTo(map);

        // Note
        var src = 'Generated by Hawkit';
        var title = L.control({
            position: 'bottomleft'
        });
        title.onAdd = function(map) {
            this._div = L.DomUtil.create('div', 'ctl src');
            this.update();
            return this._div;
        };
        title.update = function(props) {
            this._div.innerHTML = src;
        };
        title.addTo(map);

        var featureGroup = L.featureGroup().addTo(map);

        var drawControl = new L.Control.Draw({
            edit: {
                featureGroup: featureGroup
            }
        }).addTo(map);

        map.on('draw:created', function(e) {

            // Each time a feaute is created, it's added to the over arching feature group
            featureGroup.addLayer(e.layer);
        });

        // on click, clear all layers
        document.getElementById('delete').onclick = function(e) {
            featureGroup.clearLayers();
        }

        document.getElementById('export').onclick = function(e) {
            // Extract GeoJson from featureGroup
            var data = featureGroup.toGeoJSON();

            // Stringify the GeoJson
            var convertedData = 'text/json;charset=utf-8,'   encodeURIComponent(JSON.stringify(data));

            // Create export
            document.getElementById('export').setAttribute('href', 'data:'   convertedData);
            document.getElementById('export').setAttribute('download', 'data.geojson');
        }

        // Add base layers
        L.control.layers(basemaps, overlaymaps, {
            collapsed: true
        }).addTo(map);

        // Fit to overlay bounds (SW and NE points with (lat, lon))
        map.fitBounds([
            [-18.36827062251916, -52.6871074784942],
            [-18.35270287637126, -52.7014028427423]
        ]);
    </script>

</body>

</html> 

main.py

 from PyQt5 import QtWebEngineWidgets, QtWidgets, QtCore

class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
    def __init__(self, *args, **kwargs):
        QtWebEngineWidgets.QWebEnginePage.__init__(self, *args, **kwargs)
        self.profile().downloadRequested.connect(self.on_downloadRequested)

    @QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
    def on_downloadRequested(self, download):
        old_path = download.path()
        suffix = QtCore.QFileInfo(old_path).suffix()
        path, _ = QtWidgets.QFileDialog.getSaveFileName(self.view(), "Save File", old_path, "*." suffix)
        if path:
            download.setPath(path)
            download.accept()


if __name__ == '__main__':
    import sys

    sys.argv.append("--remote-debugging-port=8000")
    sys.argv.append("--disable-web-security")

    app = QtWidgets.QApplication(sys.argv)
    view = QtWebEngineWidgets.QWebEngineView()
    page = WebEnginePage(view)
    view.setPage(page)
    path = QtCore.QDir.current().filePath("index.html")
    view.load(QtCore.QUrl.fromLocalFile(path))
    view.show()
    sys.exit(app.exec_())
 

В вашем случае:

 self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
page = WebEnginePage(self.MainWindow.webMapViewer) # create page
self.MainWindow.webMapViewer.setPage(page) # set page
self.html_path = os.path.split(os.path.abspath(__file__))[0]   r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
 

Plus:

Если вы хотите задать определенный маршрут, используйте следующее:

 @QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def on_downloadRequested(self, download):
    directory = "/path/of/directory"
    filename = QtCore.QFileInfo(download.path()).fileName()
    download.setPath(QtCore.QDir(directory).filePath(filename))
    download.accept()
 

Комментарии:

1. Привет @eyllanesc. Ваше решение работает очень хорошо. Еще раз спасибо вам!

2. Просто в дополнение, могу ли я изменить строку: «путь, _ = QtWidgets. QFileDialog.GetSaveFileName(self.view(), «Сохранить файл», old_path, «*.» суффикс)» к определенному пути моего главного окна для автоматического сохранения файла?

3. @GabrielSchubert используйте setPath (), и вы должны указать полный путь, я приложил пример в своем ответе.

4. Привет @eyllanesc. Еще раз спасибо вам! Графический интерфейс работает очень хорошо. С наилучшими пожеланиями!