From 97809b6de16ca0062fcaa7374a9860e673662ce2 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 25 Oct 2019 16:16:43 +0300 Subject: [PATCH] - QRCode Tool: added ability to save the generated QRCode as SVG file or PNG file - QRCode Tool: added the feature to save the PNG file with transparent background - QRCode Tool: added GUI category in Preferences window --- FlatCAMApp.py | 96 ++++++++++ README.md | 3 + flatcamGUI/PreferencesUI.py | 176 +++++++++++++++++ flatcamTools/ToolQRCode.py | 282 ++++++++++++++++++++++++++-- tclCommands/TclCommandAlignDrill.py | 2 + 5 files changed, 542 insertions(+), 17 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index b54867e3..bbc236d1 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -849,6 +849,17 @@ class App(QtCore.QObject): "tools_cr_dh": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_cb, "tools_cr_dh_val": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_entry, + # QRCode Tool + "tools_qrcode_version": self.ui.tools2_defaults_form.tools2_qrcode_group.version_entry, + "tools_qrcode_error": self.ui.tools2_defaults_form.tools2_qrcode_group.error_radio, + "tools_qrcode_box_size": self.ui.tools2_defaults_form.tools2_qrcode_group.bsize_entry, + "tools_qrcode_border_size": self.ui.tools2_defaults_form.tools2_qrcode_group.border_size_entry, + "tools_qrcode_qrdata": self.ui.tools2_defaults_form.tools2_qrcode_group.text_data, + "tools_qrcode_polarity": self.ui.tools2_defaults_form.tools2_qrcode_group.pol_radio, + "tools_qrcode_rounded": self.ui.tools2_defaults_form.tools2_qrcode_group.bb_radio, + "tools_qrcode_fill_color": self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry, + "tools_qrcode_back_color": self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry, + # Utilities # File associations "fa_excellon": self.ui.util_defaults_form.fa_excellon_group.exc_list_text, @@ -1308,6 +1319,17 @@ class App(QtCore.QObject): "tools_cr_dh": True, "tools_cr_dh_val": 0.011811, + # QRCode Tool + "tools_qrcode_version": 1, + "tools_qrcode_error": 'L', + "tools_qrcode_box_size": 3, + "tools_qrcode_border_size": 4, + "tools_qrcode_qrdata": '', + "tools_qrcode_polarity": 'pos', + "tools_qrcode_rounded": 's', + "tools_qrcode_fill_color": '#000000', + "tools_qrcode_back_color": '#FFFFFF', + # Utilities # file associations "fa_excellon": 'drd, drl, exc, ncd, tap, xln', @@ -1731,6 +1753,17 @@ class App(QtCore.QObject): self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['tools_film_color'])[:7]) + # Init the Tool QRCode colors + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value( + self.defaults['tools_qrcode_fill_color']) + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_qrcode_fill_color'])[:7]) + + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value( + self.defaults['tools_qrcode_back_color']) + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_qrcode_back_color'])[:7]) + # ### End of Data #### # ############################################## @@ -2113,11 +2146,22 @@ class App(QtCore.QObject): self.on_annotation_fontcolor_button) # ########## Tools related signals ############# + # Film Tool self.ui.tools_defaults_form.tools_film_group.film_color_entry.editingFinished.connect( self.on_film_color_entry) self.ui.tools_defaults_form.tools_film_group.film_color_button.clicked.connect( self.on_film_color_button) + # QRCode Tool + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.editingFinished.connect( + self.on_qrcode_fill_color_entry) + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.clicked.connect( + self.on_qrcode_fill_color_button) + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.editingFinished.connect( + self.on_qrcode_back_color_entry) + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.clicked.connect( + self.on_qrcode_back_color_button) + # portability changed signal self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked) @@ -6701,6 +6745,58 @@ class App(QtCore.QObject): self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(new_val_sel) self.defaults['tools_film_color'] = new_val_sel + def on_qrcode_fill_color_entry(self): + self.defaults['tools_qrcode_fill_color'] = \ + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.get_value() + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_qrcode_fill_color'])) + + def on_qrcode_fill_color_button(self): + current_color = QtGui.QColor(self.defaults['tools_qrcode_fill_color']) + + c_dialog = QtWidgets.QColorDialog() + fill_color = c_dialog.getColor(initial=current_color) + + if fill_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if fill_color != current_color: + self.on_preferences_edited() + + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet( + "background-color:%s" % str(fill_color.name())) + + new_val_sel = str(fill_color.name()) + self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(new_val_sel) + self.defaults['tools_qrcode_fill_color'] = new_val_sel + + def on_qrcode_back_color_entry(self): + self.defaults['tools_qrcode_back_color'] = \ + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.get_value() + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_qrcode_back_color'])) + + def on_qrcode_back_color_button(self): + current_color = QtGui.QColor(self.defaults['tools_qrcode_back_color']) + + c_dialog = QtWidgets.QColorDialog() + back_color = c_dialog.getColor(initial=current_color) + + if back_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if back_color != current_color: + self.on_preferences_edited() + + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet( + "background-color:%s" % str(back_color.name())) + + new_val_sel = str(back_color.name()) + self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(new_val_sel) + self.defaults['tools_qrcode_back_color'] = new_val_sel + def on_splash_changed(self, state): settings = QSettings("Open Source", "FlatCAM") settings.setValue('splash_screen', 1) if state else settings.setValue('splash_screen', 0) diff --git a/README.md b/README.md index 7f24ec0d..f98bf3fd 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ CAD program, and create G-Code for Isolation routing. - fixed imports in all TclCommands - fixed the requirements.txt and setup_ubuntu.sh files - QRCode Tool: change the plot method parameter +- QRCode Tool: added ability to save the generated QRCode as SVG file or PNG file +- QRCode Tool: added the feature to save the PNG file with transparent background +- QRCode Tool: added GUI category in Preferences window 24.10.2019 diff --git a/flatcamGUI/PreferencesUI.py b/flatcamGUI/PreferencesUI.py index 722793b4..679296fb 100644 --- a/flatcamGUI/PreferencesUI.py +++ b/flatcamGUI/PreferencesUI.py @@ -216,11 +216,15 @@ class Tools2PreferencesUI(QtWidgets.QWidget): self.tools2_optimal_group = Tools2OptimalPrefGroupUI() self.tools2_optimal_group.setMinimumWidth(220) + self.tools2_qrcode_group = Tools2QRCodePrefGroupUI() + self.tools2_qrcode_group.setMinimumWidth(220) + self.vlay = QtWidgets.QVBoxLayout() self.vlay.addWidget(self.tools2_checkrules_group) self.vlay.addWidget(self.tools2_optimal_group) self.vlay1 = QtWidgets.QVBoxLayout() + self.vlay1.addWidget(self.tools2_qrcode_group) self.vlay2 = QtWidgets.QVBoxLayout() @@ -5388,6 +5392,178 @@ class Tools2OptimalPrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class Tools2QRCodePrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + + super(Tools2QRCodePrefGroupUI, self).__init__(self) + + self.setTitle(str(_("QRCode Tool Options"))) + self.decimals = 4 + + # ## Parameters + self.qrlabel = QtWidgets.QLabel("%s:" % _("Parameters")) + self.qrlabel.setToolTip( + _("A tool to create a QRCode that can be inserted\n" + "into a selected Gerber file, or it can be exported as a file.") + ) + self.layout.addWidget(self.qrlabel) + + # ## Grid Layout + grid_lay = QtWidgets.QGridLayout() + self.layout.addLayout(grid_lay) + grid_lay.setColumnStretch(0, 0) + grid_lay.setColumnStretch(1, 1) + + # VERSION # + self.version_label = QtWidgets.QLabel('%s:' % _("Version")) + self.version_label.setToolTip( + _("QRCode version can have values from 1 (21x21 boxes)\n" + "to 40 (177x177 boxes).") + ) + self.version_entry = FCSpinner() + self.version_entry.set_range(1, 40) + self.version_entry.setWrapping(True) + + grid_lay.addWidget(self.version_label, 1, 0) + grid_lay.addWidget(self.version_entry, 1, 1) + + # ERROR CORRECTION # + self.error_label = QtWidgets.QLabel('%s:' % _("Error correction")) + self.error_label.setToolTip( + _("Parameter that controls the error correction used for the QR Code.\n" + "L = maximum 7% errors can be corrected\n" + "M = maximum 15% errors can be corrected\n" + "Q = maximum 25% errors can be corrected\n" + "H = maximum 30% errors can be corrected.") + ) + self.error_radio = RadioSet([{'label': 'L', 'value': 'L'}, + {'label': 'M', 'value': 'M'}, + {'label': 'Q', 'value': 'Q'}, + {'label': 'H', 'value': 'H'}]) + self.error_radio.setToolTip( + _("Parameter that controls the error correction used for the QR Code.\n" + "L = maximum 7% errors can be corrected\n" + "M = maximum 15% errors can be corrected\n" + "Q = maximum 25% errors can be corrected\n" + "H = maximum 30% errors can be corrected.") + ) + grid_lay.addWidget(self.error_label, 2, 0) + grid_lay.addWidget(self.error_radio, 2, 1) + + # BOX SIZE # + self.bsize_label = QtWidgets.QLabel('%s:' % _("Box Size")) + self.bsize_label.setToolTip( + _("Box size control the overall size of the QRcode\n" + "by adjusting the size of each box in the code.") + ) + self.bsize_entry = FCSpinner() + self.bsize_entry.set_range(1, 9999) + self.bsize_entry.setWrapping(True) + + grid_lay.addWidget(self.bsize_label, 3, 0) + grid_lay.addWidget(self.bsize_entry, 3, 1) + + # BORDER SIZE # + self.border_size_label = QtWidgets.QLabel('%s:' % _("Border Size")) + self.border_size_label.setToolTip( + _("Size of the QRCode border. How many boxes thick is the border.\n" + "Default value is 4. The width of the clearance around the QRCode.") + ) + self.border_size_entry = FCSpinner() + self.border_size_entry.set_range(1, 9999) + self.border_size_entry.setWrapping(True) + + grid_lay.addWidget(self.border_size_label, 4, 0) + grid_lay.addWidget(self.border_size_entry, 4, 1) + + # Text box + self.text_label = QtWidgets.QLabel('%s:' % _("QRCode Data")) + self.text_label.setToolTip( + _("QRCode Data. Alphanumeric text to be encoded in the QRCode.") + ) + self.text_data = FCTextArea() + self.text_data.setPlaceholderText( + _("Add here the text to be included in the QRCode...") + ) + grid_lay.addWidget(self.text_label, 5, 0) + grid_lay.addWidget(self.text_data, 6, 0, 1, 2) + + # POLARITY CHOICE # + self.pol_label = QtWidgets.QLabel('%s:' % _("Polarity")) + self.pol_label.setToolTip( + _("Choose the polarity of the QRCode.\n" + "It can be drawn in a negative way (squares are clear)\n" + "or in a positive way (squares are opaque).") + ) + self.pol_radio = RadioSet([{'label': _('Negative'), 'value': 'neg'}, + {'label': _('Positive'), 'value': 'pos'}]) + self.pol_radio.setToolTip( + _("Choose the type of QRCode to be created.\n" + "If added on a Silkscreen Gerber file the QRCode may\n" + "be added as positive. If it is added to a Copper Gerber\n" + "file then perhaps the QRCode can be added as negative.") + ) + grid_lay.addWidget(self.pol_label, 7, 0) + grid_lay.addWidget(self.pol_radio, 7, 1) + + # BOUNDING BOX TYPE # + self.bb_label = QtWidgets.QLabel('%s:' % _("Bounding Box")) + self.bb_label.setToolTip( + _("The bounding box, meaning the empty space that surrounds\n" + "the QRCode geometry, can have a rounded or a square shape.") + ) + self.bb_radio = RadioSet([{'label': _('Rounded'), 'value': 'r'}, + {'label': _('Square'), 'value': 's'}]) + self.bb_radio.setToolTip( + _("The bounding box, meaning the empty space that surrounds\n" + "the QRCode geometry, can have a rounded or a square shape.") + ) + grid_lay.addWidget(self.bb_label, 8, 0) + grid_lay.addWidget(self.bb_radio, 8, 1) + + # FILL COLOR # + self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill Color')) + self.fill_color_label.setToolTip( + _("Set the QRCode fill color (squares color).") + ) + self.fill_color_entry = FCEntry() + self.fill_color_button = QtWidgets.QPushButton() + self.fill_color_button.setFixedSize(15, 15) + + fill_lay_child = QtWidgets.QHBoxLayout() + fill_lay_child.setContentsMargins(0, 0, 0, 0) + fill_lay_child.addWidget(self.fill_color_entry) + fill_lay_child.addWidget(self.fill_color_button, alignment=Qt.AlignRight) + fill_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + fill_color_widget = QtWidgets.QWidget() + fill_color_widget.setLayout(fill_lay_child) + + grid_lay.addWidget(self.fill_color_label, 9, 0) + grid_lay.addWidget(fill_color_widget, 9, 1) + + # BACK COLOR # + self.back_color_label = QtWidgets.QLabel('%s:' % _('Back Color')) + self.back_color_label.setToolTip( + _("Set the QRCode background color.") + ) + self.back_color_entry = FCEntry() + self.back_color_button = QtWidgets.QPushButton() + self.back_color_button.setFixedSize(15, 15) + + back_lay_child = QtWidgets.QHBoxLayout() + back_lay_child.setContentsMargins(0, 0, 0, 0) + back_lay_child.addWidget(self.back_color_entry) + back_lay_child.addWidget(self.back_color_button, alignment=Qt.AlignRight) + back_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + back_color_widget = QtWidgets.QWidget() + back_color_widget.setLayout(back_lay_child) + + grid_lay.addWidget(self.back_color_label, 10, 0) + grid_lay.addWidget(back_color_widget, 10, 1) + + class FAExcPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): # OptionsGroupUI.__init__(self, "Excellon File associations Preferences", parent=None) diff --git a/flatcamTools/ToolQRCode.py b/flatcamTools/ToolQRCode.py index 8f7d57d5..61579eae 100644 --- a/flatcamTools/ToolQRCode.py +++ b/flatcamTools/ToolQRCode.py @@ -5,10 +5,11 @@ # MIT Licence # # ########################################################## -from PyQt5 import QtWidgets, QtCore +from PyQt5 import QtWidgets, QtCore, QtGui +from PyQt5.QtCore import Qt from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCDoubleSpinner +from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, OptionalInputSection from flatcamParsers.ParseSVG import * from shapely.geometry.base import * @@ -23,6 +24,7 @@ from copy import deepcopy import qrcode import qrcode.image.svg +import qrcode.image.pil from lxml import etree as ET import gettext @@ -164,7 +166,7 @@ class QRCode(FlatCAMTool): ) self.text_data = FCTextArea() self.text_data.setPlaceholderText( - _("Add here the text to be included in the QRData...") + _("Add here the text to be included in the QRCode...") ) grid_lay.addWidget(self.text_label, 5, 0) grid_lay.addWidget(self.text_data, 6, 0, 1, 2) @@ -202,13 +204,92 @@ class QRCode(FlatCAMTool): grid_lay.addWidget(self.bb_label, 8, 0) grid_lay.addWidget(self.bb_radio, 8, 1) - # ## Create QRCode - self.qrcode_button = QtWidgets.QPushButton(_("Create QRCode")) + # Export QRCode + self.export_cb = FCCheckBox(_("Export QRCode")) + self.export_cb.setToolTip( + _("Show a set of controls allowing to export the QRCode\n" + "to a SVG file or an PNG file.") + ) + grid_lay.addWidget(self.export_cb, 9, 0, 1, 2) + + # this way I can hide/show the frame + self.export_frame = QtWidgets.QFrame() + self.export_frame.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.export_frame) + self.export_lay = QtWidgets.QGridLayout() + self.export_lay.setContentsMargins(0, 0, 0, 0) + self.export_frame.setLayout(self.export_lay) + self.export_lay.setColumnStretch(0, 0) + self.export_lay.setColumnStretch(1, 1) + + # default is hidden + self.export_frame.hide() + + # FILL COLOR # + self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill Color')) + self.fill_color_label.setToolTip( + _("Set the QRCode fill color (squares color).") + ) + self.fill_color_entry = FCEntry() + self.fill_color_button = QtWidgets.QPushButton() + self.fill_color_button.setFixedSize(15, 15) + + fill_lay_child = QtWidgets.QHBoxLayout() + fill_lay_child.setContentsMargins(0, 0, 0, 0) + fill_lay_child.addWidget(self.fill_color_entry) + fill_lay_child.addWidget(self.fill_color_button, alignment=Qt.AlignRight) + fill_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + fill_color_widget = QtWidgets.QWidget() + fill_color_widget.setLayout(fill_lay_child) + + self.export_lay.addWidget(self.fill_color_label, 0, 0) + self.export_lay.addWidget(fill_color_widget, 0, 1) + + self.transparent_cb = FCCheckBox(_("Transparent back color")) + self.export_lay.addWidget(self.transparent_cb, 1, 0, 1, 2) + + # BACK COLOR # + self.back_color_label = QtWidgets.QLabel('%s:' % _('Back Color')) + self.back_color_label.setToolTip( + _("Set the QRCode background color.") + ) + self.back_color_entry = FCEntry() + self.back_color_button = QtWidgets.QPushButton() + self.back_color_button.setFixedSize(15, 15) + + back_lay_child = QtWidgets.QHBoxLayout() + back_lay_child.setContentsMargins(0, 0, 0, 0) + back_lay_child.addWidget(self.back_color_entry) + back_lay_child.addWidget(self.back_color_button, alignment=Qt.AlignRight) + back_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + back_color_widget = QtWidgets.QWidget() + back_color_widget.setLayout(back_lay_child) + + self.export_lay.addWidget(self.back_color_label, 2, 0) + self.export_lay.addWidget(back_color_widget, 2, 1) + + # ## Export QRCode as SVG image + self.export_svg_button = QtWidgets.QPushButton(_("Export QRCode SVG")) + self.export_svg_button.setToolTip( + _("Export a SVG file with the QRCode content.") + ) + self.export_lay.addWidget(self.export_svg_button, 3, 0, 1, 2) + + # ## Export QRCode as PNG image + self.export_png_button = QtWidgets.QPushButton(_("Export QRCode PNG")) + self.export_png_button.setToolTip( + _("Export a PNG image file with the QRCode content.") + ) + self.export_lay.addWidget(self.export_png_button, 4, 0, 1, 2) + + # ## Insert QRCode + self.qrcode_button = QtWidgets.QPushButton(_("Insert QRCode")) self.qrcode_button.setToolTip( _("Create the QRCode object.") ) - grid_lay.addWidget(self.qrcode_button, 9, 0, 1, 2) - grid_lay.addWidget(QtWidgets.QLabel(''), 10, 0) + self.layout.addWidget(self.qrcode_button) self.layout.addStretch() @@ -226,6 +307,21 @@ class QRCode(FlatCAMTool): self.qrcode_geometry = MultiPolygon() self.qrcode_utility_geometry = MultiPolygon() + self.old_back_color = '' + + # Signals # + self.qrcode_button.clicked.connect(self.execute) + self.export_cb.stateChanged.connect(self.on_export_frame) + self.export_png_button.clicked.connect(self.export_png_file) + self.export_svg_button.clicked.connect(self.export_svg_file) + + self.fill_color_entry.editingFinished.connect(self.on_qrcode_fill_color_entry) + self.fill_color_button.clicked.connect(self.on_qrcode_fill_color_button) + self.back_color_entry.editingFinished.connect(self.on_qrcode_back_color_entry) + self.back_color_button.clicked.connect(self.on_qrcode_back_color_button) + + self.transparent_cb.stateChanged.connect(self.on_transparent_back_color) + def run(self, toggle=True): self.app.report_usage("QRCode()") @@ -259,15 +355,24 @@ class QRCode(FlatCAMTool): def set_tool_ui(self): self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value() - self.version_entry.set_value(1) - self.error_radio.set_value('M') - self.bsize_entry.set_value(3) - self.border_size_entry.set_value(4) - self.pol_radio.set_value('pos') - self.bb_radio.set_value('r') + self.version_entry.set_value(int(self.app.defaults["tools_qrcode_version"])) + self.error_radio.set_value(self.app.defaults["tools_qrcode_error"]) + self.bsize_entry.set_value(int(self.app.defaults["tools_qrcode_box_size"])) + self.border_size_entry.set_value(int(self.app.defaults["tools_qrcode_border_size"])) + self.pol_radio.set_value(self.app.defaults["tools_qrcode_polarity"]) + self.bb_radio.set_value(self.app.defaults["tools_qrcode_rounded"]) - # Signals # - self.qrcode_button.clicked.connect(self.execute) + self.fill_color_entry.set_value(self.app.defaults['tools_qrcode_fill_color']) + self.fill_color_button.setStyleSheet("background-color:%s" % + str(self.app.defaults['tools_qrcode_fill_color'])[:7]) + + self.back_color_entry.set_value(self.app.defaults['tools_qrcode_back_color']) + self.back_color_button.setStyleSheet("background-color:%s" % + str(self.app.defaults['tools_qrcode_back_color'])[:7]) + + def on_export_frame(self, state): + self.export_frame.setVisible(state) + self.qrcode_button.setVisible(not state) def execute(self): text_data = self.text_data.get_value() @@ -327,8 +432,8 @@ class QRCode(FlatCAMTool): try: a, b, c, d = self.qrcode_utility_geometry.bounds self.box_poly = box(minx=a, miny=b, maxx=c, maxy=d) - except Exception as e: - log.debug("QRCode.make() bounds error --> %s" % str(e)) + except Exception as ee: + log.debug("QRCode.make() bounds error --> %s" % str(ee)) app_obj.call_source = 'qrcode_tool' app_obj.inform.emit(_("Click on the Destination point ...")) @@ -602,3 +707,146 @@ class QRCode(FlatCAMTool): # delete the utility geometry self.delete_utility_geo() self.app.call_source = 'app' + + def export_png_file(self): + text_data = self.text_data.get_value() + if text_data == '': + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Cancelled. There is no QRCode Data in the text box.")) + return 'fail' + + def job_thread_qr_png(app_obj, fname): + error_code = { + 'L': qrcode.constants.ERROR_CORRECT_L, + 'M': qrcode.constants.ERROR_CORRECT_M, + 'Q': qrcode.constants.ERROR_CORRECT_Q, + 'H': qrcode.constants.ERROR_CORRECT_H + }[self.error_radio.get_value()] + + qr = qrcode.QRCode( + version=self.version_entry.get_value(), + error_correction=error_code, + box_size=self.bsize_entry.get_value(), + border=self.border_size_entry.get_value(), + image_factory=qrcode.image.pil.PilImage + ) + qr.add_data(text_data) + qr.make(fit=True) + + img = qr.make_image(fill_color=self.fill_color_entry.get_value(), + back_color=self.back_color_entry.get_value()) + img.save(fname) + + app_obj.call_source = 'qrcode_tool' + + name = 'qr_code' + + _filter = "PNG File (*.png);;All Files (*.*)" + try: + filename, _f = QtWidgets.QFileDialog.getSaveFileName( + caption=_("Export PNG"), + directory=self.app.get_last_save_folder() + '/' + str(name) + '_png', + filter=_filter) + except TypeError: + filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export PNG"), filter=_filter) + + filename = str(filename) + + if filename == "": + self.app.inform.emit('[WARNING_NOTCL]%s' % _(" Export PNG cancelled.")) + return + else: + self.app.worker_task.emit({'fcn': job_thread_qr_png, 'params': [self.app, filename]}) + + def export_svg_file(self): + text_data = self.text_data.get_value() + if text_data == '': + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Cancelled. There is no QRCode Data in the text box.")) + return 'fail' + + def job_thread_qr_svg(app_obj, fname): + error_code = { + 'L': qrcode.constants.ERROR_CORRECT_L, + 'M': qrcode.constants.ERROR_CORRECT_M, + 'Q': qrcode.constants.ERROR_CORRECT_Q, + 'H': qrcode.constants.ERROR_CORRECT_H + }[self.error_radio.get_value()] + + qr = qrcode.QRCode( + version=self.version_entry.get_value(), + error_correction=error_code, + box_size=self.bsize_entry.get_value(), + border=self.border_size_entry.get_value(), + image_factory=qrcode.image.svg.SvgPathImage + ) + qr.add_data(text_data) + img = qr.make_image(fill_color=self.fill_color_entry.get_value(), + back_color=self.back_color_entry.get_value()) + img.save(fname) + + app_obj.call_source = 'qrcode_tool' + + name = 'qr_code' + + _filter = "SVG File (*.svg);;All Files (*.*)" + try: + filename, _f = QtWidgets.QFileDialog.getSaveFileName( + caption=_("Export SVG"), + directory=self.app.get_last_save_folder() + '/' + str(name) + '_svg', + filter=_filter) + except TypeError: + filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG"), filter=_filter) + + filename = str(filename) + + if filename == "": + self.app.inform.emit('[WARNING_NOTCL]%s' % _(" Export SVG cancelled.")) + return + else: + self.app.worker_task.emit({'fcn': job_thread_qr_svg, 'params': [self.app, filename]}) + + def on_qrcode_fill_color_entry(self): + color = self.fill_color_entry.get_value() + self.fill_color_button.setStyleSheet("background-color:%s" % str(color)) + + def on_qrcode_fill_color_button(self): + current_color = QtGui.QColor(self.fill_color_entry.get_value()) + + c_dialog = QtWidgets.QColorDialog() + fill_color = c_dialog.getColor(initial=current_color) + + if fill_color.isValid() is False: + return + + self.fill_color_button.setStyleSheet("background-color:%s" % str(fill_color.name())) + + new_val_sel = str(fill_color.name()) + self.fill_color_entry.set_value(new_val_sel) + + def on_qrcode_back_color_entry(self): + color = self.back_color_entry.get_value() + self.back_color_button.setStyleSheet("background-color:%s" % str(color)) + + def on_qrcode_back_color_button(self): + current_color = QtGui.QColor(self.back_color_entry.get_value()) + + c_dialog = QtWidgets.QColorDialog() + back_color = c_dialog.getColor(initial=current_color) + + if back_color.isValid() is False: + return + + self.back_color_button.setStyleSheet("background-color:%s" % str(back_color.name())) + + new_val_sel = str(back_color.name()) + self.back_color_entry.set_value(new_val_sel) + + def on_transparent_back_color(self, state): + if state: + self.back_color_entry.setDisabled(True) + self.back_color_button.setDisabled(True) + self.old_back_color = self.back_color_entry.get_value() + self.back_color_entry.set_value('transparent') + else: + self.back_color_entry.setDisabled(False) + self.back_color_button.setDisabled(False) + self.back_color_entry.set_value(self.old_back_color) diff --git a/tclCommands/TclCommandAlignDrill.py b/tclCommands/TclCommandAlignDrill.py index 55d5abd1..a585f0a8 100644 --- a/tclCommands/TclCommandAlignDrill.py +++ b/tclCommands/TclCommandAlignDrill.py @@ -46,6 +46,8 @@ class TclCommandAlignDrill(TclCommandSignaled): ('name', 'Name of the object (Gerber or Excellon) to mirror.'), ('dia', 'Tool diameter'), ('box', 'Name of object which act as box (cutout for example.)'), + ('holes', 'Tuple of tuples where each tuple it is a set of x, y coordinates. ' + 'E.g: (x0, y0), (x1, y1), ... '), ('grid', 'Aligning to grid, for those, who have aligning pins' 'inside table in grid (-5,0),(5,0),(15,0)...'), ('gridoffset', 'offset of grid from 0 position.'),