ToolQRCode.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # File Author: Marius Adrian Stanciu (c) #
  4. # Date: 3/10/2019 #
  5. # MIT Licence #
  6. # ##########################################################
  7. from PyQt5 import QtWidgets, QtCore
  8. from FlatCAMTool import FlatCAMTool
  9. from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner
  10. from camlib import *
  11. from shapely.geometry import Point
  12. from shapely.geometry.base import *
  13. import math
  14. import io
  15. from datetime import datetime
  16. import logging
  17. import qrcode
  18. import qrcode.image.svg
  19. from lxml import etree as ET
  20. import gettext
  21. import FlatCAMTranslation as fcTranslate
  22. import builtins
  23. fcTranslate.apply_language('strings')
  24. if '_' not in builtins.__dict__:
  25. _ = gettext.gettext
  26. log = logging.getLogger('base')
  27. class QRCode(FlatCAMTool):
  28. toolName = _("QRCode Tool")
  29. def __init__(self, app):
  30. FlatCAMTool.__init__(self, app)
  31. self.app = app
  32. self.canvas = self.app.plotcanvas
  33. self.decimals = 4
  34. self.units = ''
  35. # ## Title
  36. title_label = QtWidgets.QLabel("%s" % self.toolName)
  37. title_label.setStyleSheet("""
  38. QLabel
  39. {
  40. font-size: 16px;
  41. font-weight: bold;
  42. }
  43. """)
  44. self.layout.addWidget(title_label)
  45. self.layout.addWidget(QtWidgets.QLabel(''))
  46. # ## Grid Layout
  47. i_grid_lay = QtWidgets.QGridLayout()
  48. self.layout.addLayout(i_grid_lay)
  49. i_grid_lay.setColumnStretch(0, 0)
  50. i_grid_lay.setColumnStretch(1, 1)
  51. self.grb_object_combo = QtWidgets.QComboBox()
  52. self.grb_object_combo.setModel(self.app.collection)
  53. self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  54. self.grb_object_combo.setCurrentIndex(1)
  55. self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
  56. self.grbobj_label.setToolTip(
  57. _("Gerber Object to which the QRCode will be added.")
  58. )
  59. i_grid_lay.addWidget(self.grbobj_label, 0, 0)
  60. i_grid_lay.addWidget(self.grb_object_combo, 0, 1, 1, 2)
  61. i_grid_lay.addWidget(QtWidgets.QLabel(''), 1, 0)
  62. # ## Grid Layout
  63. grid_lay = QtWidgets.QGridLayout()
  64. self.layout.addLayout(grid_lay)
  65. grid_lay.setColumnStretch(0, 0)
  66. grid_lay.setColumnStretch(1, 1)
  67. self.qrcode_label = QtWidgets.QLabel('<b>%s</b>' % _('QRCode Parameters'))
  68. self.qrcode_label.setToolTip(
  69. _("Contain the expected calibration points and the\n"
  70. "ones measured.")
  71. )
  72. grid_lay.addWidget(self.qrcode_label, 0, 0, 1, 2)
  73. # VERSION #
  74. self.version_label = QtWidgets.QLabel('%s:' % _("Version"))
  75. self.version_label.setToolTip(
  76. _("QRCode version can have values from 1 (21x21 boxes)\n"
  77. "to 40 (177x177 boxes).")
  78. )
  79. self.version_entry = FCSpinner()
  80. self.version_entry.set_range(1, 40)
  81. self.version_entry.setWrapping(True)
  82. grid_lay.addWidget(self.version_label, 1, 0)
  83. grid_lay.addWidget(self.version_entry, 1, 1)
  84. # ERROR CORRECTION #
  85. self.error_label = QtWidgets.QLabel('%s:' % _("Error correction"))
  86. self.error_label.setToolTip(
  87. _("Parameter that controls the error correction used for the QR Code.\n"
  88. "L = maximum 7% errors can be corrected\n"
  89. "M = maximum 15% errors can be corrected\n"
  90. "Q = maximum 25% errors can be corrected\n"
  91. "H = maximum 30% errors can be corrected.")
  92. )
  93. self.error_radio = RadioSet([{'label': 'L', 'value': 'L'},
  94. {'label': 'M', 'value': 'M'},
  95. {'label': 'Q', 'value': 'Q'},
  96. {'label': 'H', 'value': 'H'}])
  97. self.error_radio.setToolTip(
  98. _("Parameter that controls the error correction used for the QR Code.\n"
  99. "L = maximum 7% errors can be corrected\n"
  100. "M = maximum 15% errors can be corrected\n"
  101. "Q = maximum 25% errors can be corrected\n"
  102. "H = maximum 30% errors can be corrected.")
  103. )
  104. grid_lay.addWidget(self.error_label, 2, 0)
  105. grid_lay.addWidget(self.error_radio, 2, 1)
  106. # BOX SIZE #
  107. self.bsize_label = QtWidgets.QLabel('%s:' % _("Box Size"))
  108. self.bsize_label.setToolTip(
  109. _("Box size control the overall size of the QRcode\n"
  110. "by adjusting the size of each box in the code.")
  111. )
  112. self.bsize_entry = FCSpinner()
  113. self.bsize_entry.set_range(1, 9999)
  114. self.bsize_entry.setWrapping(True)
  115. grid_lay.addWidget(self.bsize_label, 3, 0)
  116. grid_lay.addWidget(self.bsize_entry, 3, 1)
  117. # BORDER SIZE #
  118. self.border_size_label = QtWidgets.QLabel('%s:' % _("Border Size"))
  119. self.border_size_label.setToolTip(
  120. _("Size of the QRCode border. How many boxes thick is the border.\n"
  121. "Default value is 4.")
  122. )
  123. self.border_size_entry = FCSpinner()
  124. self.border_size_entry.set_range(1, 9999)
  125. self.border_size_entry.setWrapping(True)
  126. self.border_size_entry.set_value(4)
  127. grid_lay.addWidget(self.border_size_label, 4, 0)
  128. grid_lay.addWidget(self.border_size_entry, 4, 1)
  129. # Text box
  130. self.text_label = QtWidgets.QLabel('%s:' % _("QRCode Data"))
  131. self.text_label.setToolTip(
  132. _("QRCode Data. Alphanumeric text to be encoded in the QRCode.")
  133. )
  134. self.text_data = FCTextArea()
  135. grid_lay.addWidget(self.text_label, 5, 0)
  136. grid_lay.addWidget(self.text_data, 6, 0, 1, 2)
  137. # ## Create QRCode
  138. self.qrcode_button = QtWidgets.QPushButton(_("Create QRCode"))
  139. self.qrcode_button.setToolTip(
  140. _("Create the QRCode object.")
  141. )
  142. grid_lay.addWidget(self.qrcode_button, 7, 0, 1, 2)
  143. grid_lay.addWidget(QtWidgets.QLabel(''), 8, 0)
  144. self.layout.addStretch()
  145. def run(self, toggle=True):
  146. self.app.report_usage("QRCode()")
  147. if toggle:
  148. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  149. if self.app.ui.splitter.sizes()[0] == 0:
  150. self.app.ui.splitter.setSizes([1, 1])
  151. else:
  152. try:
  153. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  154. # if tab is populated with the tool but it does not have the focus, focus on it
  155. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  156. # focus on Tool Tab
  157. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  158. else:
  159. self.app.ui.splitter.setSizes([0, 1])
  160. except AttributeError:
  161. pass
  162. else:
  163. if self.app.ui.splitter.sizes()[0] == 0:
  164. self.app.ui.splitter.setSizes([1, 1])
  165. FlatCAMTool.run(self)
  166. self.set_tool_ui()
  167. self.app.ui.notebook.setTabText(2, _("QRCode Tool"))
  168. def install(self, icon=None, separator=None, **kwargs):
  169. FlatCAMTool.install(self, icon, separator, shortcut='ALT+Q', **kwargs)
  170. def set_tool_ui(self):
  171. self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value()
  172. self.version_entry.set_value(1)
  173. self.error_radio.set_value('M')
  174. self.bsize_entry.set_value(3)
  175. self.border_size_entry.set_value(4)
  176. # Signals #
  177. self.qrcode_button.clicked.connect(self.execute)
  178. def execute(self):
  179. text_data = self.text_data.get_value()
  180. if text_data == '':
  181. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Cancelled. There is no QRCode Data in the text box."))
  182. return 'fail'
  183. svg_file = io.BytesIO()
  184. error_code = {
  185. 'L': qrcode.constants.ERROR_CORRECT_L,
  186. 'M': qrcode.constants.ERROR_CORRECT_M,
  187. 'Q': qrcode.constants.ERROR_CORRECT_Q,
  188. 'H': qrcode.constants.ERROR_CORRECT_H
  189. }[self.error_radio.get_value()]
  190. qr = qrcode.QRCode(
  191. version=self.version_entry.get_value(),
  192. error_correction=error_code,
  193. box_size=self.bsize_entry.get_value(),
  194. border=self.border_size_entry.get_value(),
  195. image_factory=qrcode.image.svg.SvgFragmentImage
  196. )
  197. qr.add_data(text_data)
  198. qr.make()
  199. img = qr.make_image()
  200. img.save(svg_file)
  201. svg_text = StringIO(svg_file.getvalue().decode('UTF-8'))
  202. def obj_init(geo_obj, app_obj):
  203. geo_obj.import_svg(svg_text, units=self.units)
  204. geo_obj.solid_geometry = unary_union(geo_obj.solid_geometry).buffer(0.0000001)
  205. geo_obj.solid_geometry = geo_obj.solid_geometry.buffer(-0.0000001)
  206. with self.app.proc_container.new(_("Generating QRCode...")):
  207. # Object creation
  208. self.app.new_object('gerber', 'QRCode', obj_init, plot=True)
  209. def make(self):
  210. pass
  211. def utility_geo(self):
  212. pass
  213. def on_mouse_move(self, event):
  214. pass
  215. def on_mouse_release(self, event):
  216. pass