diff --git a/DataPlotly/core/plot_factory.py b/DataPlotly/core/plot_factory.py index ace3be58..781487c7 100644 --- a/DataPlotly/core/plot_factory.py +++ b/DataPlotly/core/plot_factory.py @@ -722,7 +722,7 @@ def build_html(self, config) -> str: match = re.search(r'Plotly.newPlot\(\s*[\'"](.+?)[\'"]', raw_plot) substr = match.group(1) raw_plot = raw_plot.replace('ReplaceTheDiv', substr) - return raw_plot + return raw_plot, fig def build_figure(self) -> str: """ @@ -753,9 +753,11 @@ def build_figure(self) -> str: 'modeBarButtonsToRemove': ['toImage', 'sendDataToCloud', 'editInChartStudio'] } + raw_plot, fig = self.build_html(config) + with open(self.plot_path, "w", encoding="utf8") as f: - f.write(self.build_html(config)) - return self.plot_path + f.write(raw_plot) + return self.plot_path, fig def build_figures(self, plot_type, ptrace, config=None) -> str: """ @@ -819,7 +821,7 @@ def build_figures(self, plot_type, ptrace, config=None) -> str: with open(self.plot_path, "w", encoding="utf8") as f: f.write(self.raw_plot) - return self.plot_path + return self.plot_path, figures def build_sub_plots(self, grid, row, column, ptrace): # pylint:disable=too-many-arguments """ @@ -877,7 +879,7 @@ def build_sub_plots(self, grid, row, column, ptrace): # pylint:disable=too-many with open(self.plot_path, "w", encoding="utf8") as f: f.write(self.raw_plot) - return self.plot_path + return self.plot_path, fig def build_plot_dict(self) -> dict: """ diff --git a/DataPlotly/data_plotly.py b/DataPlotly/data_plotly.py index 08f45f60..58f7de2b 100644 --- a/DataPlotly/data_plotly.py +++ b/DataPlotly/data_plotly.py @@ -270,6 +270,9 @@ def unload(self): # unregister the function QgsExpression.unregisterFunction('get_symbol_colors') + # TODO QGIS 4 + # QgsGui.layoutItemGuiRegistry().removeLayoutItemGuiMetadata(self.plot_item_gui_metadata) + # disconnect signals for easy dev when using plugin reloader QgsProject.instance().cleared.disconnect(self.dock_manager.removeDocks) QgsProject.instance().readProject.disconnect( diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index 174d6b40..07aa7925 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -208,12 +208,14 @@ def __init__(self, mode=MODE_CANVAS, parent=None, override_iface=None, message_b self.z_combo.fieldChanged.connect(self.setLegend) self.draw_btn.clicked.connect(self.create_plot) - self.update_btn.clicked.connect(self.UpdatePlot) + self.update_btn.clicked.connect(self.updatePlot) self.clear_btn.clicked.connect(self.clearPlotView) self.save_plot_btn.clicked.connect(self.save_plot_as_image) self.save_plot_html_btn.clicked.connect(self.save_plot_as_html) + self.save_plot_json_btn.clicked.connect(self.save_plot_as_json) self.save_plot_btn.setIcon(GuiUtils.get_icon('save_as_image.svg')) self.save_plot_html_btn.setIcon(GuiUtils.get_icon('save_as_html.svg')) + self.save_plot_json_btn.setIcon(GuiUtils.get_icon('save_as_json.svg')) # initialize the empty dictionary of plots self.plot_factories = {} @@ -1496,7 +1498,7 @@ def refresh_plot(self, factory): """ Refreshes the plot built by the specified factory """ - self.plot_path = factory.build_figure() + self.plot_path, _ = factory.build_figure() self.refreshPlotView() def create_plot(self): @@ -1519,7 +1521,7 @@ def create_plot(self): # plot single plot, check the object dictionary length if len(self.plot_factories) <= 1 or self.ptype == 'radar': - self.plot_path = plot_factory.build_figure() + self.plot_path, self.fig = plot_factory.build_figure() # to plot many plots in the same figure else: @@ -1527,7 +1529,7 @@ def create_plot(self): pl = [] for _, v in self.plot_factories.items(): pl.append(v.trace[0]) - self.plot_path = plot_factory.build_figures(self.ptype, pl) + self.plot_path, self.fig = plot_factory.build_figures(self.ptype, pl) # choice to draw subplots instead depending on the combobox elif self.subcombo.currentData() == 'subplots': @@ -1541,13 +1543,13 @@ def create_plot(self): # plot in single row and many columns if self.radio_rows.isChecked(): - self.plot_path = plot_factory.build_sub_plots( + self.plot_path, self.fig = plot_factory.build_sub_plots( 'row', 1, gr, pl) # plot in single column and many rows elif self.radio_columns.isChecked(): - self.plot_path = plot_factory.build_sub_plots( + self.plot_path, self.fig = plot_factory.build_sub_plots( 'col', gr, 1, pl) except: # pylint: disable=bare-except # noqa: F401 if self.message_bar: @@ -1559,7 +1561,7 @@ def create_plot(self): # connect to a simple function that reloads the view self.refreshPlotView() - def UpdatePlot(self): + def updatePlot(self): """ updates only the LAST plot created get the key of the last plot created and delete it from the plot container @@ -1642,7 +1644,7 @@ def save_plot_as_html(self): """ plot_file, _ = QFileDialog.getSaveFileName( - self, self.tr("Save Plot"), "", "*.html") + self, self.tr("Save Plot as HTML"), "", "*.html") if not plot_file: return @@ -1657,6 +1659,29 @@ def save_plot_as_html(self): plot_file).toString(), QDir.toNativeSeparators(plot_file))) + def save_plot_as_json(self): + """ + Saves the plot as a local json file. The whole plot canvas is saves, + even if there are stacked plots or different subplots + """ + + plot_file, _ = QFileDialog.getSaveFileName( + self, self.tr("Save Plot as JSON"), "", "*.json") + if not plot_file: + return + + plot_file = QgsFileUtils.ensureFileNameHasExtension(plot_file, [ + 'json']) + + self.fig.write_json(plot_file, validate=True, pretty=True) + + if self.message_bar: + self.message_bar.pushSuccess(self.tr('DataPlotly'), + self.tr('Saved plot to {}').format( + QUrl.fromLocalFile( + plot_file).toString(), + QDir.toNativeSeparators(plot_file))) + def showPlotFromDic(self, plot_input_dic): """ Allows to call the plugin from the python console @@ -1720,7 +1745,7 @@ def showPlotFromDic(self, plot_input_dic): # create Plot instance factory = PlotFactory(settings) - standalone_plot_path = factory.build_figure() + standalone_plot_path, _ = factory.build_figure() standalone_plot_url = QUrl.fromLocalFile(standalone_plot_path) self.plot_view.load(standalone_plot_url) diff --git a/DataPlotly/icons/save_as_json.svg b/DataPlotly/icons/save_as_json.svg new file mode 100644 index 00000000..dc29eb67 --- /dev/null +++ b/DataPlotly/icons/save_as_json.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + diff --git a/DataPlotly/layouts/plot_layout_item.py b/DataPlotly/layouts/plot_layout_item.py index 24922f47..024c4514 100644 --- a/DataPlotly/layouts/plot_layout_item.py +++ b/DataPlotly/layouts/plot_layout_item.py @@ -180,7 +180,8 @@ def create_plot(self): if len(self.plot_settings) == 1: plot_factory = PlotFactory(self.plot_settings[0], self, polygon_filter=polygon_filter) self.plot_settings[0].properties['visible_features_only'] = visible_features_only - return plot_factory.build_html(config) + raw_plot, _ = plot_factory.build_html(config) + return raw_plot # to plot many plots in the same figure elif len(self.plot_settings) > 1: @@ -194,7 +195,7 @@ def create_plot(self): factory = PlotFactory(plot_setting, self, polygon_filter=polygon_filter) pl.append(factory.trace[0]) - plot_path = plot_factory.build_figures(self.plot_settings[0].plot_type, pl, config=config) + plot_path, _ = plot_factory.build_figures(self.plot_settings[0].plot_type, pl, config=config) with open(plot_path) as myfile: return myfile.read() diff --git a/DataPlotly/ui/dataplotly_dockwidget_base.ui b/DataPlotly/ui/dataplotly_dockwidget_base.ui index d320d1ed..c8f26fc6 100644 --- a/DataPlotly/ui/dataplotly_dockwidget_base.ui +++ b/DataPlotly/ui/dataplotly_dockwidget_base.ui @@ -320,9 +320,9 @@ QListWidget::item::selected { 0 - -537 - 673 - 1252 + 0 + 666 + 1666 @@ -1012,8 +1012,8 @@ QListWidget::item::selected { 0 0 - 406 - 803 + 477 + 1043 @@ -1504,7 +1504,24 @@ QListWidget::item::selected { - + + + + + + + + + + + Export as html + + + + + + + @@ -1514,20 +1531,7 @@ QListWidget::item::selected { - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + Export as image @@ -1546,22 +1550,18 @@ QListWidget::item::selected { - - - - Export as html - - - + + + + Qt::Horizontal - - - - - - + + + 40 + 20 + - + @@ -1573,6 +1573,16 @@ QListWidget::item::selected { + + + + Export as json + + + + + +