appDatabase.py 139 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255
  1. from PyQt5 import QtGui, QtCore, QtWidgets
  2. from appGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
  3. FCTree, RadioSet, FCFileSaveDialog
  4. from camlib import to_dict
  5. import sys
  6. import json
  7. from copy import deepcopy
  8. from datetime import datetime
  9. import math
  10. import gettext
  11. import appTranslation as fcTranslate
  12. import builtins
  13. fcTranslate.apply_language('strings')
  14. if '_' not in builtins.__dict__:
  15. _ = gettext.gettext
  16. class ToolsDB(QtWidgets.QWidget):
  17. mark_tools_rows = QtCore.pyqtSignal()
  18. def __init__(self, app, callback_on_edited, callback_on_tool_request, parent=None):
  19. super(ToolsDB, self).__init__(parent)
  20. self.app = app
  21. self.decimals = 4
  22. self.callback_app = callback_on_edited
  23. self.on_tool_request = callback_on_tool_request
  24. self.offset_item_options = ["Path", "In", "Out", "Custom"]
  25. self.type_item_options = ["Iso", "Rough", "Finish"]
  26. self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
  27. '''
  28. dict to hold all the tools in the Tools DB
  29. format:
  30. {
  31. tool_id: {
  32. 'name': 'new_tool'
  33. 'tooldia': self.app.defaults["geometry_cnctooldia"]
  34. 'offset': 'Path'
  35. 'offset_value': 0.0
  36. 'type': _('Rough'),
  37. 'tool_type': 'C1'
  38. 'data': dict()
  39. }
  40. }
  41. '''
  42. self.db_tool_dict = {}
  43. # layouts
  44. layout = QtWidgets.QVBoxLayout()
  45. self.setLayout(layout)
  46. table_hlay = QtWidgets.QHBoxLayout()
  47. layout.addLayout(table_hlay)
  48. self.table_widget = FCTable(drag_drop=True)
  49. self.table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  50. table_hlay.addWidget(self.table_widget)
  51. # set the number of columns and the headers tool tips
  52. self.configure_table()
  53. # pal = QtGui.QPalette()
  54. # pal.setColor(QtGui.QPalette.Background, Qt.white)
  55. # New Bookmark
  56. new_vlay = QtWidgets.QVBoxLayout()
  57. layout.addLayout(new_vlay)
  58. # new_tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Tool"))
  59. # new_vlay.addWidget(new_tool_lbl, alignment=QtCore.Qt.AlignBottom)
  60. self.buttons_frame = QtWidgets.QFrame()
  61. self.buttons_frame.setContentsMargins(0, 0, 0, 0)
  62. layout.addWidget(self.buttons_frame)
  63. self.buttons_box = QtWidgets.QHBoxLayout()
  64. self.buttons_box.setContentsMargins(0, 0, 0, 0)
  65. self.buttons_frame.setLayout(self.buttons_box)
  66. self.buttons_frame.show()
  67. add_entry_btn = FCButton(_("Add Geometry Tool in DB"))
  68. add_entry_btn.setToolTip(
  69. _("Add a new tool in the Tools Database.\n"
  70. "It will be used in the Geometry UI.\n"
  71. "You can edit it after it is added.")
  72. )
  73. self.buttons_box.addWidget(add_entry_btn)
  74. # add_fct_entry_btn = FCButton(_("Add Paint/NCC Tool in DB"))
  75. # add_fct_entry_btn.setToolTip(
  76. # _("Add a new tool in the Tools Database.\n"
  77. # "It will be used in the Paint/NCC Tools UI.\n"
  78. # "You can edit it after it is added.")
  79. # )
  80. # self.buttons_box.addWidget(add_fct_entry_btn)
  81. remove_entry_btn = FCButton(_("Delete Tool from DB"))
  82. remove_entry_btn.setToolTip(
  83. _("Remove a selection of tools in the Tools Database.")
  84. )
  85. self.buttons_box.addWidget(remove_entry_btn)
  86. export_db_btn = FCButton(_("Export DB"))
  87. export_db_btn.setToolTip(
  88. _("Save the Tools Database to a custom text file.")
  89. )
  90. self.buttons_box.addWidget(export_db_btn)
  91. import_db_btn = FCButton(_("Import DB"))
  92. import_db_btn.setToolTip(
  93. _("Load the Tools Database information's from a custom text file.")
  94. )
  95. self.buttons_box.addWidget(import_db_btn)
  96. self.add_tool_from_db = FCButton(_("Transfer the Tool"))
  97. self.add_tool_from_db.setToolTip(
  98. _("Add a new tool in the Tools Table of the\n"
  99. "active Geometry object after selecting a tool\n"
  100. "in the Tools Database.")
  101. )
  102. self.add_tool_from_db.hide()
  103. self.cancel_tool_from_db = FCButton(_("Cancel"))
  104. self.cancel_tool_from_db.hide()
  105. hlay = QtWidgets.QHBoxLayout()
  106. layout.addLayout(hlay)
  107. hlay.addWidget(self.add_tool_from_db)
  108. hlay.addWidget(self.cancel_tool_from_db)
  109. hlay.addStretch()
  110. # ##############################################################################
  111. # ######################## SIGNALS #############################################
  112. # ##############################################################################
  113. add_entry_btn.clicked.connect(self.on_tool_add)
  114. remove_entry_btn.clicked.connect(self.on_tool_delete)
  115. export_db_btn.clicked.connect(self.on_export_tools_db_file)
  116. import_db_btn.clicked.connect(self.on_import_tools_db_file)
  117. # closebtn.clicked.connect(self.accept)
  118. self.add_tool_from_db.clicked.connect(self.on_tool_requested_from_app)
  119. self.cancel_tool_from_db.clicked.connect(self.on_cancel_tool)
  120. self.setup_db_ui()
  121. def configure_table(self):
  122. self.table_widget.setColumnCount(27)
  123. # self.table_widget.setColumnWidth(0, 20)
  124. self.table_widget.setHorizontalHeaderLabels(
  125. [
  126. '#',
  127. _("Tool Name"),
  128. _("Tool Dia"),
  129. _("Tool Offset"),
  130. _("Custom Offset"),
  131. _("Tool Type"),
  132. _("Tool Shape"),
  133. _("Cut Z"),
  134. _("MultiDepth"),
  135. _("DPP"),
  136. _("V-Dia"),
  137. _("V-Angle"),
  138. _("Travel Z"),
  139. _("FR"),
  140. _("FR Z"),
  141. _("FR Rapids"),
  142. _("Spindle Speed"),
  143. _("Dwell"),
  144. _("Dwelltime"),
  145. _("Preprocessor"),
  146. _("ExtraCut"),
  147. _("E-Cut Length"),
  148. _("Toolchange"),
  149. _("Toolchange XY"),
  150. _("Toolchange Z"),
  151. _("Start Z"),
  152. _("End Z"),
  153. ]
  154. )
  155. self.table_widget.horizontalHeaderItem(0).setToolTip(
  156. _("Tool Index."))
  157. self.table_widget.horizontalHeaderItem(1).setToolTip(
  158. _("Tool name.\n"
  159. "This is not used in the app, it's function\n"
  160. "is to serve as a note for the user."))
  161. self.table_widget.horizontalHeaderItem(2).setToolTip(
  162. _("Tool Diameter."))
  163. self.table_widget.horizontalHeaderItem(3).setToolTip(
  164. _("Tool Offset.\n"
  165. "Can be of a few types:\n"
  166. "Path = zero offset\n"
  167. "In = offset inside by half of tool diameter\n"
  168. "Out = offset outside by half of tool diameter\n"
  169. "Custom = custom offset using the Custom Offset value"))
  170. self.table_widget.horizontalHeaderItem(4).setToolTip(
  171. _("Custom Offset.\n"
  172. "A value to be used as offset from the current path."))
  173. self.table_widget.horizontalHeaderItem(5).setToolTip(
  174. _("Tool Type.\n"
  175. "Can be:\n"
  176. "Iso = isolation cut\n"
  177. "Rough = rough cut, low feedrate, multiple passes\n"
  178. "Finish = finishing cut, high feedrate"))
  179. self.table_widget.horizontalHeaderItem(6).setToolTip(
  180. _("Tool Shape. \n"
  181. "Can be:\n"
  182. "C1 ... C4 = circular tool with x flutes\n"
  183. "B = ball tip milling tool\n"
  184. "V = v-shape milling tool"))
  185. self.table_widget.horizontalHeaderItem(7).setToolTip(
  186. _("Cutting Depth.\n"
  187. "The depth at which to cut into material."))
  188. self.table_widget.horizontalHeaderItem(8).setToolTip(
  189. _("Multi Depth.\n"
  190. "Selecting this will allow cutting in multiple passes,\n"
  191. "each pass adding a DPP parameter depth."))
  192. self.table_widget.horizontalHeaderItem(9).setToolTip(
  193. _("DPP. Depth per Pass.\n"
  194. "The value used to cut into material on each pass."))
  195. self.table_widget.horizontalHeaderItem(10).setToolTip(
  196. _("V-Dia.\n"
  197. "Diameter of the tip for V-Shape Tools."))
  198. self.table_widget.horizontalHeaderItem(11).setToolTip(
  199. _("V-Agle.\n"
  200. "Angle at the tip for the V-Shape Tools."))
  201. self.table_widget.horizontalHeaderItem(12).setToolTip(
  202. _("Clearance Height.\n"
  203. "Height at which the milling bit will travel between cuts,\n"
  204. "above the surface of the material, avoiding all fixtures."))
  205. self.table_widget.horizontalHeaderItem(13).setToolTip(
  206. _("FR. Feedrate\n"
  207. "The speed on XY plane used while cutting into material."))
  208. self.table_widget.horizontalHeaderItem(14).setToolTip(
  209. _("FR Z. Feedrate Z\n"
  210. "The speed on Z plane."))
  211. self.table_widget.horizontalHeaderItem(15).setToolTip(
  212. _("FR Rapids. Feedrate Rapids\n"
  213. "Speed used while moving as fast as possible.\n"
  214. "This is used only by some devices that can't use\n"
  215. "the G0 g-code command. Mostly 3D printers."))
  216. self.table_widget.horizontalHeaderItem(16).setToolTip(
  217. _("Spindle Speed.\n"
  218. "If it's left empty it will not be used.\n"
  219. "The speed of the spindle in RPM."))
  220. self.table_widget.horizontalHeaderItem(17).setToolTip(
  221. _("Dwell.\n"
  222. "Check this if a delay is needed to allow\n"
  223. "the spindle motor to reach its set speed."))
  224. self.table_widget.horizontalHeaderItem(18).setToolTip(
  225. _("Dwell Time.\n"
  226. "A delay used to allow the motor spindle reach its set speed."))
  227. self.table_widget.horizontalHeaderItem(19).setToolTip(
  228. _("Preprocessor.\n"
  229. "A selection of files that will alter the generated G-code\n"
  230. "to fit for a number of use cases."))
  231. self.table_widget.horizontalHeaderItem(20).setToolTip(
  232. _("Extra Cut.\n"
  233. "If checked, after a isolation is finished an extra cut\n"
  234. "will be added where the start and end of isolation meet\n"
  235. "such as that this point is covered by this extra cut to\n"
  236. "ensure a complete isolation."))
  237. self.table_widget.horizontalHeaderItem(21).setToolTip(
  238. _("Extra Cut length.\n"
  239. "If checked, after a isolation is finished an extra cut\n"
  240. "will be added where the start and end of isolation meet\n"
  241. "such as that this point is covered by this extra cut to\n"
  242. "ensure a complete isolation. This is the length of\n"
  243. "the extra cut."))
  244. self.table_widget.horizontalHeaderItem(22).setToolTip(
  245. _("Toolchange.\n"
  246. "It will create a toolchange event.\n"
  247. "The kind of toolchange is determined by\n"
  248. "the preprocessor file."))
  249. self.table_widget.horizontalHeaderItem(23).setToolTip(
  250. _("Toolchange XY.\n"
  251. "A set of coordinates in the format (x, y).\n"
  252. "Will determine the cartesian position of the point\n"
  253. "where the tool change event take place."))
  254. self.table_widget.horizontalHeaderItem(24).setToolTip(
  255. _("Toolchange Z.\n"
  256. "The position on Z plane where the tool change event take place."))
  257. self.table_widget.horizontalHeaderItem(25).setToolTip(
  258. _("Start Z.\n"
  259. "If it's left empty it will not be used.\n"
  260. "A position on Z plane to move immediately after job start."))
  261. self.table_widget.horizontalHeaderItem(26).setToolTip(
  262. _("End Z.\n"
  263. "A position on Z plane to move immediately after job stop."))
  264. def setup_db_ui(self):
  265. filename = self.app.data_path + '/tools_db.FlatDB'
  266. # load the database tools from the file
  267. try:
  268. with open(filename) as f:
  269. tools = f.read()
  270. except IOError:
  271. self.app.log.error("Could not load tools DB file.")
  272. self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
  273. return
  274. try:
  275. self.db_tool_dict = json.loads(tools)
  276. except Exception:
  277. e = sys.exc_info()[0]
  278. self.app.log.error(str(e))
  279. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  280. return
  281. self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
  282. self.build_db_ui()
  283. self.table_widget.setupContextMenu()
  284. self.table_widget.addContextMenu(
  285. _("Add to DB"), self.on_tool_add, icon=QtGui.QIcon(self.app.resource_location + "/plus16.png"))
  286. self.table_widget.addContextMenu(
  287. _("Copy from DB"), self.on_tool_copy, icon=QtGui.QIcon(self.app.resource_location + "/copy16.png"))
  288. self.table_widget.addContextMenu(
  289. _("Delete from DB"), self.on_tool_delete, icon=QtGui.QIcon(self.app.resource_location + "/delete32.png"))
  290. def build_db_ui(self):
  291. self.ui_disconnect()
  292. self.table_widget.setRowCount(len(self.db_tool_dict))
  293. nr_crt = 0
  294. for toolid, dict_val in self.db_tool_dict.items():
  295. row = nr_crt
  296. nr_crt += 1
  297. t_name = dict_val['name']
  298. try:
  299. self.add_tool_table_line(row, name=t_name, widget=self.table_widget, tooldict=dict_val)
  300. except Exception as e:
  301. self.app.log.debug("ToolDB.build_db_ui.add_tool_table_line() --> %s" % str(e))
  302. vertical_header = self.table_widget.verticalHeader()
  303. vertical_header.hide()
  304. horizontal_header = self.table_widget.horizontalHeader()
  305. horizontal_header.setMinimumSectionSize(10)
  306. horizontal_header.setDefaultSectionSize(70)
  307. self.table_widget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
  308. for x in range(27):
  309. self.table_widget.resizeColumnToContents(x)
  310. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  311. # horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
  312. # horizontal_header.setSectionResizeMode(13, QtWidgets.QHeaderView.Fixed)
  313. horizontal_header.resizeSection(0, 20)
  314. # horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
  315. # horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
  316. self.ui_connect()
  317. def add_tool_table_line(self, row, name, widget, tooldict):
  318. data = tooldict['data']
  319. nr_crt = row + 1
  320. id_item = QtWidgets.QTableWidgetItem('%d' % int(nr_crt))
  321. # id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  322. flags = id_item.flags() & ~QtCore.Qt.ItemIsEditable
  323. id_item.setFlags(flags)
  324. widget.setItem(row, 0, id_item) # Tool name/id
  325. tool_name_item = QtWidgets.QTableWidgetItem(name)
  326. widget.setItem(row, 1, tool_name_item)
  327. dia_item = FCDoubleSpinner()
  328. dia_item.set_precision(self.decimals)
  329. dia_item.setSingleStep(0.1)
  330. dia_item.set_range(0.0, 9999.9999)
  331. dia_item.set_value(float(tooldict['tooldia']))
  332. widget.setCellWidget(row, 2, dia_item)
  333. tool_offset_item = FCComboBox()
  334. for item in self.offset_item_options:
  335. tool_offset_item.addItem(item)
  336. tool_offset_item.set_value(tooldict['offset'])
  337. widget.setCellWidget(row, 3, tool_offset_item)
  338. c_offset_item = FCDoubleSpinner()
  339. c_offset_item.set_precision(self.decimals)
  340. c_offset_item.setSingleStep(0.1)
  341. c_offset_item.set_range(-9999.9999, 9999.9999)
  342. c_offset_item.set_value(float(tooldict['offset_value']))
  343. widget.setCellWidget(row, 4, c_offset_item)
  344. tt_item = FCComboBox()
  345. for item in self.type_item_options:
  346. tt_item.addItem(item)
  347. tt_item.set_value(tooldict['type'])
  348. widget.setCellWidget(row, 5, tt_item)
  349. tshape_item = FCComboBox()
  350. for item in self.tool_type_item_options:
  351. tshape_item.addItem(item)
  352. tshape_item.set_value(tooldict['tool_type'])
  353. widget.setCellWidget(row, 6, tshape_item)
  354. cutz_item = FCDoubleSpinner()
  355. cutz_item.set_precision(self.decimals)
  356. cutz_item.setSingleStep(0.1)
  357. if self.app.defaults['global_machinist_setting']:
  358. cutz_item.set_range(-9999.9999, 9999.9999)
  359. else:
  360. cutz_item.set_range(-9999.9999, -0.0000)
  361. cutz_item.set_value(float(data['cutz']))
  362. widget.setCellWidget(row, 7, cutz_item)
  363. multidepth_item = FCCheckBox()
  364. multidepth_item.set_value(data['multidepth'])
  365. widget.setCellWidget(row, 8, multidepth_item)
  366. # to make the checkbox centered but it can no longer have it's value accessed - needs a fix using findchild()
  367. # multidepth_item = QtWidgets.QWidget()
  368. # cb = FCCheckBox()
  369. # cb.set_value(data['multidepth'])
  370. # qhboxlayout = QtWidgets.QHBoxLayout(multidepth_item)
  371. # qhboxlayout.addWidget(cb)
  372. # qhboxlayout.setAlignment(QtCore.Qt.AlignCenter)
  373. # qhboxlayout.setContentsMargins(0, 0, 0, 0)
  374. # widget.setCellWidget(row, 8, multidepth_item)
  375. depth_per_pass_item = FCDoubleSpinner()
  376. depth_per_pass_item.set_precision(self.decimals)
  377. depth_per_pass_item.setSingleStep(0.1)
  378. depth_per_pass_item.set_range(0.0, 9999.9999)
  379. depth_per_pass_item.set_value(float(data['depthperpass']))
  380. widget.setCellWidget(row, 9, depth_per_pass_item)
  381. vtip_dia_item = FCDoubleSpinner()
  382. vtip_dia_item.set_precision(self.decimals)
  383. vtip_dia_item.setSingleStep(0.1)
  384. vtip_dia_item.set_range(0.0, 9999.9999)
  385. vtip_dia_item.set_value(float(data['vtipdia']))
  386. widget.setCellWidget(row, 10, vtip_dia_item)
  387. vtip_angle_item = FCDoubleSpinner()
  388. vtip_angle_item.set_precision(self.decimals)
  389. vtip_angle_item.setSingleStep(0.1)
  390. vtip_angle_item.set_range(-360.0, 360.0)
  391. vtip_angle_item.set_value(float(data['vtipangle']))
  392. widget.setCellWidget(row, 11, vtip_angle_item)
  393. travelz_item = FCDoubleSpinner()
  394. travelz_item.set_precision(self.decimals)
  395. travelz_item.setSingleStep(0.1)
  396. if self.app.defaults['global_machinist_setting']:
  397. travelz_item.set_range(-9999.9999, 9999.9999)
  398. else:
  399. travelz_item.set_range(0.0000, 9999.9999)
  400. travelz_item.set_value(float(data['travelz']))
  401. widget.setCellWidget(row, 12, travelz_item)
  402. fr_item = FCDoubleSpinner()
  403. fr_item.set_precision(self.decimals)
  404. fr_item.set_range(0.0, 9999.9999)
  405. fr_item.set_value(float(data['feedrate']))
  406. widget.setCellWidget(row, 13, fr_item)
  407. frz_item = FCDoubleSpinner()
  408. frz_item.set_precision(self.decimals)
  409. frz_item.set_range(0.0, 9999.9999)
  410. frz_item.set_value(float(data['feedrate_z']))
  411. widget.setCellWidget(row, 14, frz_item)
  412. frrapids_item = FCDoubleSpinner()
  413. frrapids_item.set_precision(self.decimals)
  414. frrapids_item.set_range(0.0, 9999.9999)
  415. frrapids_item.set_value(float(data['feedrate_rapid']))
  416. widget.setCellWidget(row, 15, frrapids_item)
  417. spindlespeed_item = FCSpinner()
  418. spindlespeed_item.set_range(0, 1000000)
  419. spindlespeed_item.set_value(int(data['spindlespeed']))
  420. spindlespeed_item.set_step(100)
  421. widget.setCellWidget(row, 16, spindlespeed_item)
  422. dwell_item = FCCheckBox()
  423. dwell_item.set_value(data['dwell'])
  424. widget.setCellWidget(row, 17, dwell_item)
  425. dwelltime_item = FCDoubleSpinner()
  426. dwelltime_item.set_precision(self.decimals)
  427. dwelltime_item.set_range(0.0000, 9999.9999)
  428. dwelltime_item.set_value(float(data['dwelltime']))
  429. widget.setCellWidget(row, 18, dwelltime_item)
  430. pp_item = FCComboBox()
  431. for item in self.app.preprocessors:
  432. pp_item.addItem(item)
  433. pp_item.set_value(data['ppname_g'])
  434. widget.setCellWidget(row, 19, pp_item)
  435. ecut_item = FCCheckBox()
  436. ecut_item.set_value(data['extracut'])
  437. widget.setCellWidget(row, 20, ecut_item)
  438. ecut_length_item = FCDoubleSpinner()
  439. ecut_length_item.set_precision(self.decimals)
  440. ecut_length_item.set_range(0.0000, 9999.9999)
  441. ecut_length_item.set_value(data['extracut_length'])
  442. widget.setCellWidget(row, 21, ecut_length_item)
  443. toolchange_item = FCCheckBox()
  444. toolchange_item.set_value(data['toolchange'])
  445. widget.setCellWidget(row, 22, toolchange_item)
  446. toolchangexy_item = QtWidgets.QTableWidgetItem(str(data['toolchangexy']) if data['toolchangexy'] else '')
  447. widget.setItem(row, 23, toolchangexy_item)
  448. toolchangez_item = FCDoubleSpinner()
  449. toolchangez_item.set_precision(self.decimals)
  450. toolchangez_item.setSingleStep(0.1)
  451. if self.app.defaults['global_machinist_setting']:
  452. toolchangez_item.set_range(-9999.9999, 9999.9999)
  453. else:
  454. toolchangez_item.set_range(0.0000, 9999.9999)
  455. toolchangez_item.set_value(float(data['toolchangez']))
  456. widget.setCellWidget(row, 24, toolchangez_item)
  457. startz_item = QtWidgets.QTableWidgetItem(str(data['startz']) if data['startz'] else '')
  458. widget.setItem(row, 25, startz_item)
  459. endz_item = FCDoubleSpinner()
  460. endz_item.set_precision(self.decimals)
  461. endz_item.setSingleStep(0.1)
  462. if self.app.defaults['global_machinist_setting']:
  463. endz_item.set_range(-9999.9999, 9999.9999)
  464. else:
  465. endz_item.set_range(0.0000, 9999.9999)
  466. endz_item.set_value(float(data['endz']))
  467. widget.setCellWidget(row, 26, endz_item)
  468. def on_tool_add(self):
  469. """
  470. Add a tool in the DB Tool Table
  471. :return: None
  472. """
  473. default_data = {}
  474. default_data.update({
  475. "cutz": float(self.app.defaults["geometry_cutz"]),
  476. "multidepth": self.app.defaults["geometry_multidepth"],
  477. "depthperpass": float(self.app.defaults["geometry_depthperpass"]),
  478. "vtipdia": float(self.app.defaults["geometry_vtipdia"]),
  479. "vtipangle": float(self.app.defaults["geometry_vtipangle"]),
  480. "travelz": float(self.app.defaults["geometry_travelz"]),
  481. "feedrate": float(self.app.defaults["geometry_feedrate"]),
  482. "feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
  483. "feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
  484. "spindlespeed": self.app.defaults["geometry_spindlespeed"],
  485. "dwell": self.app.defaults["geometry_dwell"],
  486. "dwelltime": float(self.app.defaults["geometry_dwelltime"]),
  487. "ppname_g": self.app.defaults["geometry_ppname_g"],
  488. "extracut": self.app.defaults["geometry_extracut"],
  489. "extracut_length": float(self.app.defaults["geometry_extracut_length"]),
  490. "toolchange": self.app.defaults["geometry_toolchange"],
  491. "toolchangexy": self.app.defaults["geometry_toolchangexy"],
  492. "toolchangez": float(self.app.defaults["geometry_toolchangez"]),
  493. "startz": self.app.defaults["geometry_startz"],
  494. "endz": float(self.app.defaults["geometry_endz"])
  495. })
  496. dict_elem = {}
  497. dict_elem['name'] = 'new_tool'
  498. if type(self.app.defaults["geometry_cnctooldia"]) == float:
  499. dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
  500. else:
  501. try:
  502. tools_string = self.app.defaults["geometry_cnctooldia"].split(",")
  503. tools_diameters = [eval(a) for a in tools_string if a != '']
  504. dict_elem['tooldia'] = tools_diameters[0] if tools_diameters else 0.0
  505. except Exception as e:
  506. self.app.log.debug("ToolDB.on_tool_add() --> %s" % str(e))
  507. return
  508. dict_elem['offset'] = 'Path'
  509. dict_elem['offset_value'] = 0.0
  510. dict_elem['type'] = 'Rough'
  511. dict_elem['tool_type'] = 'C1'
  512. dict_elem['data'] = default_data
  513. new_toolid = len(self.db_tool_dict) + 1
  514. self.db_tool_dict[new_toolid] = deepcopy(dict_elem)
  515. # add the new entry to the Tools DB table
  516. self.build_db_ui()
  517. self.callback_on_edited()
  518. self.app.inform.emit('[success] %s' % _("Tool added to DB."))
  519. def on_tool_copy(self):
  520. """
  521. Copy a selection of Tools in the Tools DB table
  522. :return:
  523. """
  524. new_tool_id = self.table_widget.rowCount() + 1
  525. for model_index in self.table_widget.selectionModel().selectedRows():
  526. # index = QtCore.QPersistentModelIndex(model_index)
  527. old_tool_id = self.table_widget.item(model_index.row(), 0).text()
  528. new_tool_id += 1
  529. for toolid, dict_val in list(self.db_tool_dict.items()):
  530. if int(old_tool_id) == int(toolid):
  531. self.db_tool_dict.update({
  532. new_tool_id: deepcopy(dict_val)
  533. })
  534. self.build_db_ui()
  535. self.callback_on_edited()
  536. self.app.inform.emit('[success] %s' % _("Tool copied from Tools DB."))
  537. def on_tool_delete(self):
  538. """
  539. Delete a selection of Tools in the Tools DB table
  540. :return:
  541. """
  542. for model_index in self.table_widget.selectionModel().selectedRows():
  543. # index = QtCore.QPersistentModelIndex(model_index)
  544. toolname_to_remove = self.table_widget.item(model_index.row(), 0).text()
  545. for toolid, dict_val in list(self.db_tool_dict.items()):
  546. if int(toolname_to_remove) == int(toolid):
  547. # remove from the storage
  548. self.db_tool_dict.pop(toolid, None)
  549. self.build_db_ui()
  550. self.callback_on_edited()
  551. self.app.inform.emit('[success] %s' % _("Tool removed from Tools DB."))
  552. def on_export_tools_db_file(self):
  553. self.app.defaults.report_usage("on_export_tools_db_file")
  554. self.app.log.debug("on_export_tools_db_file()")
  555. date = str(datetime.today()).rpartition('.')[0]
  556. date = ''.join(c for c in date if c not in ':-')
  557. date = date.replace(' ', '_')
  558. filter__ = "Text File (*.TXT);;All Files (*.*)"
  559. filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Tools Database"),
  560. directory='{l_save}/FlatCAM_{n}_{date}'.format(
  561. l_save=str(self.app.get_last_save_folder()),
  562. n=_("Tools_Database"),
  563. date=date),
  564. ext_filter=filter__)
  565. filename = str(filename)
  566. if filename == "":
  567. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
  568. return
  569. else:
  570. try:
  571. f = open(filename, 'w')
  572. f.close()
  573. except PermissionError:
  574. self.app.inform.emit('[WARNING] %s' %
  575. _("Permission denied, saving not possible.\n"
  576. "Most likely another app is holding the file open and not accessible."))
  577. return
  578. except IOError:
  579. self.app.log.debug('Creating a new Tools DB file ...')
  580. f = open(filename, 'w')
  581. f.close()
  582. except Exception:
  583. e = sys.exc_info()[0]
  584. self.app.log.error("Could not load Tools DB file.")
  585. self.app.log.error(str(e))
  586. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  587. return
  588. # Save update options
  589. try:
  590. # Save Tools DB in a file
  591. try:
  592. with open(filename, "w") as f:
  593. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  594. except Exception as e:
  595. self.app.log.debug("App.on_save_tools_db() --> %s" % str(e))
  596. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  597. return
  598. except Exception:
  599. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  600. return
  601. self.app.inform.emit('[success] %s: %s' % (_("Exported Tools DB to"), filename))
  602. def on_import_tools_db_file(self):
  603. self.app.defaults.report_usage("on_import_tools_db_file")
  604. self.app.log.debug("on_import_tools_db_file()")
  605. filter__ = "Text File (*.TXT);;All Files (*.*)"
  606. filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Tools DB"), filter=filter__)
  607. if filename == "":
  608. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
  609. else:
  610. try:
  611. with open(filename) as f:
  612. tools_in_db = f.read()
  613. except IOError:
  614. self.app.log.error("Could not load Tools DB file.")
  615. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  616. return
  617. try:
  618. self.db_tool_dict = json.loads(tools_in_db)
  619. except Exception:
  620. e = sys.exc_info()[0]
  621. self.app.log.error(str(e))
  622. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  623. return
  624. self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
  625. self.build_db_ui()
  626. self.callback_on_edited()
  627. def on_save_tools_db(self, silent=False):
  628. self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
  629. filename = self.app.data_path + "/tools_db.FlatDB"
  630. # Preferences save, update the color of the Tools DB Tab text
  631. for idx in range(self.app_ui.plot_tab_area.count()):
  632. if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
  633. self.app_ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
  634. # Save Tools DB in a file
  635. try:
  636. f = open(filename, "w")
  637. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  638. f.close()
  639. except Exception as e:
  640. self.app.log.debug("ToolsDB.on_save_tools_db() --> %s" % str(e))
  641. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  642. return
  643. if not silent:
  644. self.app.inform.emit('[success] %s' % _("Saved Tools DB."))
  645. def ui_connect(self):
  646. try:
  647. try:
  648. self.table_widget.itemChanged.disconnect(self.callback_on_edited)
  649. except (TypeError, AttributeError):
  650. pass
  651. self.table_widget.itemChanged.connect(self.callback_on_edited)
  652. except AttributeError:
  653. pass
  654. for row in range(self.table_widget.rowCount()):
  655. for col in range(self.table_widget.columnCount()):
  656. # ComboBox
  657. try:
  658. try:
  659. self.table_widget.cellWidget(row, col).currentIndexChanged.disconnect(self.callback_on_edited)
  660. except (TypeError, AttributeError):
  661. pass
  662. self.table_widget.cellWidget(row, col).currentIndexChanged.connect(self.callback_on_edited)
  663. except AttributeError:
  664. pass
  665. # CheckBox
  666. try:
  667. try:
  668. self.table_widget.cellWidget(row, col).toggled.disconnect(self.callback_on_edited)
  669. except (TypeError, AttributeError):
  670. pass
  671. self.table_widget.cellWidget(row, col).toggled.connect(self.callback_on_edited)
  672. except AttributeError:
  673. pass
  674. # SpinBox, DoubleSpinBox
  675. try:
  676. try:
  677. self.table_widget.cellWidget(row, col).valueChanged.disconnect(self.callback_on_edited)
  678. except (TypeError, AttributeError):
  679. pass
  680. self.table_widget.cellWidget(row, col).valueChanged.connect(self.callback_on_edited)
  681. except AttributeError:
  682. pass
  683. def ui_disconnect(self):
  684. try:
  685. self.table_widget.itemChanged.disconnect(self.callback_on_edited)
  686. except (TypeError, AttributeError):
  687. pass
  688. for row in range(self.table_widget.rowCount()):
  689. for col in range(self.table_widget.columnCount()):
  690. # ComboBox
  691. try:
  692. self.table_widget.cellWidget(row, col).currentIndexChanged.disconnect(self.callback_on_edited)
  693. except (TypeError, AttributeError):
  694. pass
  695. # CheckBox
  696. try:
  697. self.table_widget.cellWidget(row, col).toggled.disconnect(self.callback_on_edited)
  698. except (TypeError, AttributeError):
  699. pass
  700. # SpinBox, DoubleSpinBox
  701. try:
  702. self.table_widget.cellWidget(row, col).valueChanged.disconnect(self.callback_on_edited)
  703. except (TypeError, AttributeError):
  704. pass
  705. def callback_on_edited(self):
  706. # update the dictionary storage self.db_tool_dict
  707. self.db_tool_dict.clear()
  708. dict_elem = {}
  709. default_data = {}
  710. for row in range(self.table_widget.rowCount()):
  711. new_toolid = row + 1
  712. for col in range(self.table_widget.columnCount()):
  713. column_header_text = self.table_widget.horizontalHeaderItem(col).text()
  714. if column_header_text == _('Tool Name'):
  715. dict_elem['name'] = self.table_widget.item(row, col).text()
  716. elif column_header_text == _('Tool Dia'):
  717. dict_elem['tooldia'] = self.table_widget.cellWidget(row, col).get_value()
  718. elif column_header_text == _('Tool Offset'):
  719. dict_elem['offset'] = self.table_widget.cellWidget(row, col).get_value()
  720. elif column_header_text == _('Custom Offset'):
  721. dict_elem['offset_value'] = self.table_widget.cellWidget(row, col).get_value()
  722. elif column_header_text == _('Tool Type'):
  723. dict_elem['type'] = self.table_widget.cellWidget(row, col).get_value()
  724. elif column_header_text == _('Tool Shape'):
  725. dict_elem['tool_type'] = self.table_widget.cellWidget(row, col).get_value()
  726. else:
  727. if column_header_text == _('Cut Z'):
  728. default_data['cutz'] = self.table_widget.cellWidget(row, col).get_value()
  729. elif column_header_text == _('MultiDepth'):
  730. default_data['multidepth'] = self.table_widget.cellWidget(row, col).get_value()
  731. elif column_header_text == _('DPP'):
  732. default_data['depthperpass'] = self.table_widget.cellWidget(row, col).get_value()
  733. elif column_header_text == _('V-Dia'):
  734. default_data['vtipdia'] = self.table_widget.cellWidget(row, col).get_value()
  735. elif column_header_text == _('V-Angle'):
  736. default_data['vtipangle'] = self.table_widget.cellWidget(row, col).get_value()
  737. elif column_header_text == _('Travel Z'):
  738. default_data['travelz'] = self.table_widget.cellWidget(row, col).get_value()
  739. elif column_header_text == _('FR'):
  740. default_data['feedrate'] = self.table_widget.cellWidget(row, col).get_value()
  741. elif column_header_text == _('FR Z'):
  742. default_data['feedrate_z'] = self.table_widget.cellWidget(row, col).get_value()
  743. elif column_header_text == _('FR Rapids'):
  744. default_data['feedrate_rapid'] = self.table_widget.cellWidget(row, col).get_value()
  745. elif column_header_text == _('Spindle Speed'):
  746. default_data['spindlespeed'] = self.table_widget.cellWidget(row, col).get_value()
  747. elif column_header_text == _('Dwell'):
  748. default_data['dwell'] = self.table_widget.cellWidget(row, col).get_value()
  749. elif column_header_text == _('Dwelltime'):
  750. default_data['dwelltime'] = self.table_widget.cellWidget(row, col).get_value()
  751. elif column_header_text == _('Preprocessor'):
  752. default_data['ppname_g'] = self.table_widget.cellWidget(row, col).get_value()
  753. elif column_header_text == _('ExtraCut'):
  754. default_data['extracut'] = self.table_widget.cellWidget(row, col).get_value()
  755. elif column_header_text == _("E-Cut Length"):
  756. default_data['extracut_length'] = self.table_widget.cellWidget(row, col).get_value()
  757. elif column_header_text == _('Toolchange'):
  758. default_data['toolchange'] = self.table_widget.cellWidget(row, col).get_value()
  759. elif column_header_text == _('Toolchange XY'):
  760. default_data['toolchangexy'] = self.table_widget.item(row, col).text()
  761. elif column_header_text == _('Toolchange Z'):
  762. default_data['toolchangez'] = self.table_widget.cellWidget(row, col).get_value()
  763. elif column_header_text == _('Start Z'):
  764. default_data['startz'] = float(self.table_widget.item(row, col).text()) \
  765. if self.table_widget.item(row, col).text() != '' else None
  766. elif column_header_text == _('End Z'):
  767. default_data['endz'] = self.table_widget.cellWidget(row, col).get_value()
  768. dict_elem['data'] = default_data
  769. self.db_tool_dict.update(
  770. {
  771. new_toolid: deepcopy(dict_elem)
  772. }
  773. )
  774. self.callback_app()
  775. def on_tool_requested_from_app(self):
  776. if not self.table_widget.selectionModel().selectedRows():
  777. self.app.inform.emit('[WARNING_NOTCL] %s...' % _("No Tool/row selected in the Tools Database table"))
  778. return
  779. model_index_list = self.table_widget.selectionModel().selectedRows()
  780. for model_index in model_index_list:
  781. selected_row = model_index.row()
  782. tool_uid = selected_row + 1
  783. for key in self.db_tool_dict.keys():
  784. if str(key) == str(tool_uid):
  785. selected_tool = self.db_tool_dict[key]
  786. self.on_tool_request(tool=selected_tool)
  787. def on_cancel_tool(self):
  788. for idx in range(self.app_ui.plot_tab_area.count()):
  789. if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
  790. wdg = self.app_ui.plot_tab_area.widget(idx)
  791. wdg.deleteLater()
  792. self.app_ui.plot_tab_area.removeTab(idx)
  793. self.app.inform.emit('%s' % _("Cancelled adding tool from DB."))
  794. # def resize_new_tool_table_widget(self, min_size, max_size):
  795. # """
  796. # Resize the table widget responsible for adding new tool in the Tool Database
  797. #
  798. # :param min_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  799. # :param max_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  800. # :return:
  801. # """
  802. # t_height = self.t_height
  803. # if max_size > min_size:
  804. # t_height = self.t_height + self.new_tool_table_widget.verticalScrollBar().height()
  805. #
  806. # self.new_tool_table_widget.setMaximumHeight(t_height)
  807. def closeEvent(self, QCloseEvent):
  808. super().closeEvent(QCloseEvent)
  809. class ToolsDB2UI:
  810. def __init__(self, app, grid_layout):
  811. self.app = app
  812. self.decimals = self.app.decimals
  813. settings = QtCore.QSettings("Open Source", "FlatCAM")
  814. if settings.contains("machinist"):
  815. self.machinist_setting = settings.value('machinist', type=int)
  816. else:
  817. self.machinist_setting = 0
  818. self.g_lay = grid_layout
  819. tree_layout = QtWidgets.QVBoxLayout()
  820. self.g_lay.addLayout(tree_layout, 0, 0)
  821. self.tree_widget = FCTree(columns=2, header_hidden=False, protected_column=[0])
  822. self.tree_widget.setHeaderLabels(["ID", "Tool Name"])
  823. self.tree_widget.setIndentation(0)
  824. self.tree_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
  825. self.tree_widget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
  826. # set alternating colors
  827. # self.tree_widget.setAlternatingRowColors(True)
  828. # p = QtGui.QPalette()
  829. # p.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(226, 237, 253) )
  830. # self.tree_widget.setPalette(p)
  831. self.tree_widget.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
  832. tree_layout.addWidget(self.tree_widget)
  833. param_hlay = QtWidgets.QHBoxLayout()
  834. param_area = QtWidgets.QScrollArea()
  835. param_widget = QtWidgets.QWidget()
  836. param_widget.setLayout(param_hlay)
  837. param_area.setWidget(param_widget)
  838. param_area.setWidgetResizable(True)
  839. self.g_lay.addWidget(param_area, 0, 1)
  840. # ###########################################################################
  841. # ############## The UI form ################################################
  842. # ###########################################################################
  843. # Tool description box
  844. self.tool_description_box = QtWidgets.QGroupBox()
  845. self.tool_description_box.setStyleSheet("""
  846. QGroupBox
  847. {
  848. font-size: 16px;
  849. font-weight: bold;
  850. }
  851. """)
  852. self.description_vlay = QtWidgets.QVBoxLayout()
  853. self.tool_description_box.setTitle(_("Tool Description"))
  854. self.tool_description_box.setFixedWidth(250)
  855. # Milling box
  856. self.milling_box = QtWidgets.QGroupBox()
  857. self.milling_box.setStyleSheet("""
  858. QGroupBox
  859. {
  860. font-size: 16px;
  861. font-weight: bold;
  862. }
  863. """)
  864. self.milling_vlay = QtWidgets.QVBoxLayout()
  865. self.milling_box.setTitle(_("Milling Parameters"))
  866. self.milling_box.setFixedWidth(250)
  867. # NCC TOOL BOX
  868. self.ncc_box = QtWidgets.QGroupBox()
  869. self.ncc_box.setStyleSheet("""
  870. QGroupBox
  871. {
  872. font-size: 16px;
  873. font-weight: bold;
  874. }
  875. """)
  876. self.ncc_vlay = QtWidgets.QVBoxLayout()
  877. self.ncc_box.setTitle(_("NCC Parameters"))
  878. self.ncc_box.setFixedWidth(250)
  879. # PAINT TOOL BOX
  880. self.paint_box = QtWidgets.QGroupBox()
  881. self.paint_box.setStyleSheet("""
  882. QGroupBox
  883. {
  884. font-size: 16px;
  885. font-weight: bold;
  886. }
  887. """)
  888. self.paint_vlay = QtWidgets.QVBoxLayout()
  889. self.paint_box.setTitle(_("Paint Parameters"))
  890. self.paint_box.setFixedWidth(250)
  891. # ISOLATION TOOL BOX
  892. self.iso_box = QtWidgets.QGroupBox()
  893. self.iso_box.setStyleSheet("""
  894. QGroupBox
  895. {
  896. font-size: 16px;
  897. font-weight: bold;
  898. }
  899. """)
  900. self.iso_vlay = QtWidgets.QVBoxLayout()
  901. self.iso_box.setTitle(_("Isolation Parameters"))
  902. self.iso_box.setFixedWidth(250)
  903. # DRILLING TOOL BOX
  904. self.drill_box = QtWidgets.QGroupBox()
  905. self.drill_box.setStyleSheet("""
  906. QGroupBox
  907. {
  908. font-size: 16px;
  909. font-weight: bold;
  910. }
  911. """)
  912. self.drill_vlay = QtWidgets.QVBoxLayout()
  913. self.drill_box.setTitle(_("Drilling Parameters"))
  914. self.drill_box.setFixedWidth(250)
  915. # Layout Constructor
  916. self.tool_description_box.setLayout(self.description_vlay)
  917. self.milling_box.setLayout(self.milling_vlay)
  918. self.ncc_box.setLayout(self.ncc_vlay)
  919. self.paint_box.setLayout(self.paint_vlay)
  920. self.iso_box.setLayout(self.iso_vlay)
  921. self.drill_box.setLayout(self.drill_vlay)
  922. tools_vlay = QtWidgets.QVBoxLayout()
  923. tools_vlay.addWidget(self.iso_box)
  924. tools_vlay.addWidget(self.paint_box)
  925. tools_vlay.addWidget(self.ncc_box)
  926. tools_vlay.addStretch()
  927. descript_vlay = QtWidgets.QVBoxLayout()
  928. descript_vlay.addWidget(self.tool_description_box)
  929. descript_vlay.addLayout(tools_vlay)
  930. descript_vlay.addStretch()
  931. milling_vlay = QtWidgets.QVBoxLayout()
  932. milling_vlay.addWidget(self.milling_box)
  933. milling_vlay.addStretch()
  934. drilling_vlay = QtWidgets.QVBoxLayout()
  935. drilling_vlay.addWidget(self.drill_box)
  936. param_hlay.addLayout(descript_vlay)
  937. param_hlay.addLayout(milling_vlay)
  938. param_hlay.addLayout(drilling_vlay)
  939. param_hlay.addLayout(tools_vlay)
  940. # always visible, always to be included last
  941. param_hlay.addLayout(milling_vlay)
  942. param_hlay.addStretch()
  943. # ###########################################################################
  944. # ################ Tool UI form #############################################
  945. # ###########################################################################
  946. self.grid_tool = QtWidgets.QGridLayout()
  947. self.description_vlay.addLayout(self.grid_tool)
  948. self.grid_tool.setColumnStretch(0, 0)
  949. self.grid_tool.setColumnStretch(1, 1)
  950. self.description_vlay.addStretch()
  951. # Tool Name
  952. self.name_label = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('Name'))
  953. self.name_label.setToolTip(
  954. _("Tool name.\n"
  955. "This is not used in the app, it's function\n"
  956. "is to serve as a note for the user."))
  957. self.name_entry = FCEntry()
  958. self.name_entry.setObjectName('gdb_name')
  959. self.grid_tool.addWidget(self.name_label, 0, 0)
  960. self.grid_tool.addWidget(self.name_entry, 0, 1)
  961. # Tool Dia
  962. self.dia_label = QtWidgets.QLabel('%s:' % _('Diameter'))
  963. self.dia_label.setToolTip(
  964. _("Tool Diameter."))
  965. self.dia_entry = FCDoubleSpinner()
  966. self.dia_entry.set_range(-9999.9999, 9999.9999)
  967. self.dia_entry.set_precision(self.decimals)
  968. self.dia_entry.setObjectName('gdb_dia')
  969. self.grid_tool.addWidget(self.dia_label, 1, 0)
  970. self.grid_tool.addWidget(self.dia_entry, 1, 1)
  971. # Tool Tolerance
  972. self.tol_label = QtWidgets.QLabel("<b>%s:</b>" % _("Diameter Tolerance"))
  973. self.tol_label.setToolTip(
  974. _("Tool tolerance. If there is a tool in the targeted tools table with\n"
  975. "the value within the limits then this tool from DB will be used.")
  976. )
  977. self.grid_tool.addWidget(self.tol_label, 2, 0, 1, 2)
  978. # Tolerance Min Limit
  979. self.min_limit_label = QtWidgets.QLabel('%s:' % _("Min"))
  980. self.min_limit_label.setToolTip(
  981. _("Set the tool tolerance minimum.")
  982. )
  983. self.tol_min_entry = FCDoubleSpinner(callback=self.confirmation_message)
  984. self.tol_min_entry.set_precision(self.decimals)
  985. self.tol_min_entry.set_range(0, 9999.9999)
  986. self.tol_min_entry.setSingleStep(0.1)
  987. self.tol_min_entry.setObjectName("gdb_tol_min")
  988. self.grid_tool.addWidget(self.min_limit_label, 4, 0)
  989. self.grid_tool.addWidget(self.tol_min_entry, 4, 1)
  990. # Tolerance Min Limit
  991. self.max_limit_label = QtWidgets.QLabel('%s:' % _("Max"))
  992. self.max_limit_label.setToolTip(
  993. _("Set the tool tolerance maximum.")
  994. )
  995. self.tol_max_entry = FCDoubleSpinner(callback=self.confirmation_message)
  996. self.tol_max_entry.set_precision(self.decimals)
  997. self.tol_max_entry.set_range(0, 9999.9999)
  998. self.tol_max_entry.setSingleStep(0.1)
  999. self.tol_max_entry.setObjectName("gdb_tol_max")
  1000. self.grid_tool.addWidget(self.max_limit_label, 6, 0)
  1001. self.grid_tool.addWidget(self.tol_max_entry, 6, 1)
  1002. # Tool Object Type
  1003. self.tool_op_label = QtWidgets.QLabel('<b>%s:</b>' % _('Operation'))
  1004. self.tool_op_label.setToolTip(
  1005. _("The kind of Application Tool where this tool is to be used."))
  1006. self.tool_op_combo = FCComboBox()
  1007. self.tool_op_combo.addItems([_("General"), _("Milling"), _("Drilling"), _('Isolation'), _('Paint'), _('NCC')])
  1008. self.tool_op_combo.setObjectName('gdb_tool_target')
  1009. self.grid_tool.addWidget(self.tool_op_label, 8, 0)
  1010. self.grid_tool.addWidget(self.tool_op_combo, 8, 1)
  1011. # ###########################################################################
  1012. # ############### MILLING UI form ###########################################
  1013. # ###########################################################################
  1014. self.grid0 = QtWidgets.QGridLayout()
  1015. self.milling_vlay.addLayout(self.grid0)
  1016. self.grid0.setColumnStretch(0, 0)
  1017. self.grid0.setColumnStretch(1, 1)
  1018. self.milling_vlay.addStretch()
  1019. # Tool Shape
  1020. self.shape_label = QtWidgets.QLabel('%s:' % _('Shape'))
  1021. self.shape_label.setToolTip(
  1022. _("Tool Shape. \n"
  1023. "Can be:\n"
  1024. "C1 ... C4 = circular tool with x flutes\n"
  1025. "B = ball tip milling tool\n"
  1026. "V = v-shape milling tool"))
  1027. self.shape_combo = FCComboBox()
  1028. self.shape_combo.addItems(["C1", "C2", "C3", "C4", "B", "V"])
  1029. self.shape_combo.setObjectName('gdb_shape')
  1030. self.grid0.addWidget(self.shape_label, 2, 0)
  1031. self.grid0.addWidget(self.shape_combo, 2, 1)
  1032. # V-Dia
  1033. self.vdia_label = QtWidgets.QLabel('%s:' % _("V-Dia"))
  1034. self.vdia_label.setToolTip(
  1035. _("V-Dia.\n"
  1036. "Diameter of the tip for V-Shape Tools."))
  1037. self.vdia_entry = FCDoubleSpinner()
  1038. self.vdia_entry.set_range(0.0000, 9999.9999)
  1039. self.vdia_entry.set_precision(self.decimals)
  1040. self.vdia_entry.setObjectName('gdb_vdia')
  1041. self.grid0.addWidget(self.vdia_label, 4, 0)
  1042. self.grid0.addWidget(self.vdia_entry, 4, 1)
  1043. # V-Angle
  1044. self.vangle_label = QtWidgets.QLabel('%s:' % _("V-Angle"))
  1045. self.vangle_label.setToolTip(
  1046. _("V-Agle.\n"
  1047. "Angle at the tip for the V-Shape Tools."))
  1048. self.vangle_entry = FCDoubleSpinner()
  1049. self.vangle_entry.set_range(-360.0, 360.0)
  1050. self.vangle_entry.set_precision(self.decimals)
  1051. self.vangle_entry.setObjectName('gdb_vangle')
  1052. self.grid0.addWidget(self.vangle_label, 6, 0)
  1053. self.grid0.addWidget(self.vangle_entry, 6, 1)
  1054. separator_line = QtWidgets.QFrame()
  1055. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1056. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1057. self.grid0.addWidget(separator_line, 8, 0, 1, 2)
  1058. # Tool Type
  1059. self.type_label = QtWidgets.QLabel('%s:' % _("Tool Type"))
  1060. self.type_label.setToolTip(
  1061. _("Tool Type.\n"
  1062. "Can be:\n"
  1063. "Iso = isolation cut\n"
  1064. "Rough = rough cut, low feedrate, multiple passes\n"
  1065. "Finish = finishing cut, high feedrate"))
  1066. self.type_combo = FCComboBox()
  1067. self.type_combo.addItems(["Iso", "Rough", "Finish"])
  1068. self.type_combo.setObjectName('gdb_type')
  1069. self.grid0.addWidget(self.type_label, 10, 0)
  1070. self.grid0.addWidget(self.type_combo, 10, 1)
  1071. # Tool Offset
  1072. self.tooloffset_label = QtWidgets.QLabel('%s:' % _('Tool Offset'))
  1073. self.tooloffset_label.setToolTip(
  1074. _("Tool Offset.\n"
  1075. "Can be of a few types:\n"
  1076. "Path = zero offset\n"
  1077. "In = offset inside by half of tool diameter\n"
  1078. "Out = offset outside by half of tool diameter\n"
  1079. "Custom = custom offset using the Custom Offset value"))
  1080. self.tooloffset_combo = FCComboBox()
  1081. self.tooloffset_combo.addItems(["Path", "In", "Out", "Custom"])
  1082. self.tooloffset_combo.setObjectName('gdb_tool_offset')
  1083. self.grid0.addWidget(self.tooloffset_label, 12, 0)
  1084. self.grid0.addWidget(self.tooloffset_combo, 12, 1)
  1085. # Custom Offset
  1086. self.custom_offset_label = QtWidgets.QLabel('%s:' % _("Custom Offset"))
  1087. self.custom_offset_label.setToolTip(
  1088. _("Custom Offset.\n"
  1089. "A value to be used as offset from the current path."))
  1090. self.custom_offset_entry = FCDoubleSpinner()
  1091. self.custom_offset_entry.set_range(-9999.9999, 9999.9999)
  1092. self.custom_offset_entry.set_precision(self.decimals)
  1093. self.custom_offset_entry.setObjectName('gdb_custom_offset')
  1094. self.grid0.addWidget(self.custom_offset_label, 14, 0)
  1095. self.grid0.addWidget(self.custom_offset_entry, 14, 1)
  1096. separator_line = QtWidgets.QFrame()
  1097. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1098. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1099. self.grid0.addWidget(separator_line, 16, 0, 1, 2)
  1100. # Cut Z
  1101. self.cutz_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
  1102. self.cutz_label.setToolTip(
  1103. _("Cutting Depth.\n"
  1104. "The depth at which to cut into material."))
  1105. self.cutz_entry = FCDoubleSpinner()
  1106. self.cutz_entry.set_range(-9999.9999, 9999.9999)
  1107. self.cutz_entry.set_precision(self.decimals)
  1108. self.cutz_entry.setObjectName('gdb_cutz')
  1109. self.grid0.addWidget(self.cutz_label, 18, 0)
  1110. self.grid0.addWidget(self.cutz_entry, 18, 1)
  1111. # Multi Depth
  1112. self.multidepth_label = QtWidgets.QLabel('%s:' % _("MultiDepth"))
  1113. self.multidepth_label.setToolTip(
  1114. _("Multi Depth.\n"
  1115. "Selecting this will allow cutting in multiple passes,\n"
  1116. "each pass adding a DPP parameter depth."))
  1117. self.multidepth_cb = FCCheckBox()
  1118. self.multidepth_cb.setObjectName('gdb_multidepth')
  1119. self.grid0.addWidget(self.multidepth_label, 20, 0)
  1120. self.grid0.addWidget(self.multidepth_cb, 20, 1)
  1121. # Depth Per Pass
  1122. self.dpp_label = QtWidgets.QLabel('%s:' % _("DPP"))
  1123. self.dpp_label.setToolTip(
  1124. _("DPP. Depth per Pass.\n"
  1125. "The value used to cut into material on each pass."))
  1126. self.multidepth_entry = FCDoubleSpinner()
  1127. self.multidepth_entry.set_range(-9999.9999, 9999.9999)
  1128. self.multidepth_entry.set_precision(self.decimals)
  1129. self.multidepth_entry.setObjectName('gdb_multidepth_entry')
  1130. self.grid0.addWidget(self.dpp_label, 22, 0)
  1131. self.grid0.addWidget(self.multidepth_entry, 22, 1)
  1132. # Travel Z
  1133. self.travelz_label = QtWidgets.QLabel('%s:' % _("Travel Z"))
  1134. self.travelz_label.setToolTip(
  1135. _("Clearance Height.\n"
  1136. "Height at which the milling bit will travel between cuts,\n"
  1137. "above the surface of the material, avoiding all fixtures."))
  1138. self.travelz_entry = FCDoubleSpinner()
  1139. self.travelz_entry.set_range(-9999.9999, 9999.9999)
  1140. self.travelz_entry.set_precision(self.decimals)
  1141. self.travelz_entry.setObjectName('gdb_travelz')
  1142. self.grid0.addWidget(self.travelz_label, 24, 0)
  1143. self.grid0.addWidget(self.travelz_entry, 24, 1)
  1144. # Extra Cut
  1145. self.ecut_label = QtWidgets.QLabel('%s:' % _("ExtraCut"))
  1146. self.ecut_label.setToolTip(
  1147. _("Extra Cut.\n"
  1148. "If checked, after a isolation is finished an extra cut\n"
  1149. "will be added where the start and end of isolation meet\n"
  1150. "such as that this point is covered by this extra cut to\n"
  1151. "ensure a complete isolation."))
  1152. self.ecut_cb = FCCheckBox()
  1153. self.ecut_cb.setObjectName('gdb_ecut')
  1154. self.grid0.addWidget(self.ecut_label, 26, 0)
  1155. self.grid0.addWidget(self.ecut_cb, 26, 1)
  1156. # Extra Cut Length
  1157. self.ecut_length_label = QtWidgets.QLabel('%s:' % _("E-Cut Length"))
  1158. self.ecut_length_label.setToolTip(
  1159. _("Extra Cut length.\n"
  1160. "If checked, after a isolation is finished an extra cut\n"
  1161. "will be added where the start and end of isolation meet\n"
  1162. "such as that this point is covered by this extra cut to\n"
  1163. "ensure a complete isolation. This is the length of\n"
  1164. "the extra cut."))
  1165. self.ecut_length_entry = FCDoubleSpinner()
  1166. self.ecut_length_entry.set_range(0.0000, 9999.9999)
  1167. self.ecut_length_entry.set_precision(self.decimals)
  1168. self.ecut_length_entry.setObjectName('gdb_ecut_length')
  1169. self.grid0.addWidget(self.ecut_length_label, 28, 0)
  1170. self.grid0.addWidget(self.ecut_length_entry, 28, 1)
  1171. separator_line = QtWidgets.QFrame()
  1172. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1173. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1174. self.grid0.addWidget(separator_line, 30, 0, 1, 2)
  1175. # Feedrate X-Y
  1176. self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
  1177. self.frxy_label.setToolTip(
  1178. _("Feedrate X-Y. Feedrate\n"
  1179. "The speed on XY plane used while cutting into material."))
  1180. self.frxy_entry = FCDoubleSpinner()
  1181. self.frxy_entry.set_range(-999999.9999, 999999.9999)
  1182. self.frxy_entry.set_precision(self.decimals)
  1183. self.frxy_entry.setObjectName('gdb_frxy')
  1184. self.grid0.addWidget(self.frxy_label, 32, 0)
  1185. self.grid0.addWidget(self.frxy_entry, 32, 1)
  1186. # Feedrate Z
  1187. self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
  1188. self.frz_label.setToolTip(
  1189. _("Feedrate Z\n"
  1190. "The speed on Z plane."))
  1191. self.frz_entry = FCDoubleSpinner()
  1192. self.frz_entry.set_range(-999999.9999, 999999.9999)
  1193. self.frz_entry.set_precision(self.decimals)
  1194. self.frz_entry.setObjectName('gdb_frz')
  1195. self.grid0.addWidget(self.frz_label, 34, 0)
  1196. self.grid0.addWidget(self.frz_entry, 34, 1)
  1197. # Feedrate Rapids
  1198. self.frapids_label = QtWidgets.QLabel('%s:' % _("FR Rapids"))
  1199. self.frapids_label.setToolTip(
  1200. _("FR Rapids. Feedrate Rapids\n"
  1201. "Speed used while moving as fast as possible.\n"
  1202. "This is used only by some devices that can't use\n"
  1203. "the G0 g-code command. Mostly 3D printers."))
  1204. self.frapids_entry = FCDoubleSpinner()
  1205. self.frapids_entry.set_range(0.0000, 9999.9999)
  1206. self.frapids_entry.set_precision(self.decimals)
  1207. self.frapids_entry.setObjectName('gdb_frapids')
  1208. self.grid0.addWidget(self.frapids_label, 36, 0)
  1209. self.grid0.addWidget(self.frapids_entry, 36, 1)
  1210. separator_line = QtWidgets.QFrame()
  1211. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1212. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1213. self.grid0.addWidget(separator_line, 38, 0, 1, 2)
  1214. # Spindle Spped
  1215. self.spindle_label = QtWidgets.QLabel('%s:' % _("Spindle Speed"))
  1216. self.spindle_label.setToolTip(
  1217. _("Spindle Speed.\n"
  1218. "If it's left empty it will not be used.\n"
  1219. "The speed of the spindle in RPM."))
  1220. self.spindle_entry = FCDoubleSpinner()
  1221. self.spindle_entry.set_range(-999999.9999, 999999.9999)
  1222. self.spindle_entry.set_precision(self.decimals)
  1223. self.spindle_entry.setObjectName('gdb_spindle')
  1224. self.grid0.addWidget(self.spindle_label, 40, 0)
  1225. self.grid0.addWidget(self.spindle_entry, 40, 1)
  1226. # Dwell
  1227. self.dwell_label = QtWidgets.QLabel('%s:' % _("Dwell"))
  1228. self.dwell_label.setToolTip(
  1229. _("Dwell.\n"
  1230. "Check this if a delay is needed to allow\n"
  1231. "the spindle motor to reach its set speed."))
  1232. self.dwell_cb = FCCheckBox()
  1233. self.dwell_cb.setObjectName('gdb_dwell')
  1234. self.grid0.addWidget(self.dwell_label, 42, 0)
  1235. self.grid0.addWidget(self.dwell_cb, 42, 1)
  1236. # Dwell Time
  1237. self.dwelltime_label = QtWidgets.QLabel('%s:' % _("Dwelltime"))
  1238. self.dwelltime_label.setToolTip(
  1239. _("Dwell Time.\n"
  1240. "A delay used to allow the motor spindle reach its set speed."))
  1241. self.dwelltime_entry = FCDoubleSpinner()
  1242. self.dwelltime_entry.set_range(0.0000, 9999.9999)
  1243. self.dwelltime_entry.set_precision(self.decimals)
  1244. self.dwelltime_entry.setObjectName('gdb_dwelltime')
  1245. self.grid0.addWidget(self.dwelltime_label, 44, 0)
  1246. self.grid0.addWidget(self.dwelltime_entry, 44, 1)
  1247. # ###########################################################################
  1248. # ############### NCC UI form ###############################################
  1249. # ###########################################################################
  1250. self.grid2 = QtWidgets.QGridLayout()
  1251. self.ncc_vlay.addLayout(self.grid2)
  1252. self.grid2.setColumnStretch(0, 0)
  1253. self.grid2.setColumnStretch(1, 1)
  1254. self.ncc_vlay.addStretch()
  1255. # Operation
  1256. op_label = QtWidgets.QLabel('%s:' % _('Operation'))
  1257. op_label.setToolTip(
  1258. _("The 'Operation' can be:\n"
  1259. "- Isolation -> will ensure that the non-copper clearing is always complete.\n"
  1260. "If it's not successful then the non-copper clearing will fail, too.\n"
  1261. "- Clear -> the regular non-copper clearing.")
  1262. )
  1263. self.op_radio = RadioSet([
  1264. {"label": _("Clear"), "value": "clear"},
  1265. {"label": _("Isolation"), "value": "iso"}
  1266. ], orientation='horizontal', stretch=False)
  1267. self.op_radio.setObjectName("gdb_n_operation")
  1268. self.grid2.addWidget(op_label, 13, 0)
  1269. self.grid2.addWidget(self.op_radio, 13, 1)
  1270. # Milling Type Radio Button
  1271. self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
  1272. self.milling_type_label.setToolTip(
  1273. _("Milling type when the selected tool is of type: 'iso_op':\n"
  1274. "- climb / best for precision milling and to reduce tool usage\n"
  1275. "- conventional / useful when there is no backlash compensation")
  1276. )
  1277. self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
  1278. {'label': _('Conventional'), 'value': 'cv'}])
  1279. self.milling_type_radio.setToolTip(
  1280. _("Milling type when the selected tool is of type: 'iso_op':\n"
  1281. "- climb / best for precision milling and to reduce tool usage\n"
  1282. "- conventional / useful when there is no backlash compensation")
  1283. )
  1284. self.milling_type_radio.setObjectName("gdb_n_milling_type")
  1285. self.grid2.addWidget(self.milling_type_label, 14, 0)
  1286. self.grid2.addWidget(self.milling_type_radio, 14, 1)
  1287. # Overlap Entry
  1288. nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
  1289. nccoverlabel.setToolTip(
  1290. _("How much (percentage) of the tool width to overlap each tool pass.\n"
  1291. "Adjust the value starting with lower values\n"
  1292. "and increasing it if areas that should be cleared are still \n"
  1293. "not cleared.\n"
  1294. "Lower values = faster processing, faster execution on CNC.\n"
  1295. "Higher values = slow processing and slow execution on CNC\n"
  1296. "due of too many paths.")
  1297. )
  1298. self.ncc_overlap_entry = FCDoubleSpinner(suffix='%')
  1299. self.ncc_overlap_entry.set_precision(self.decimals)
  1300. self.ncc_overlap_entry.setWrapping(True)
  1301. self.ncc_overlap_entry.setRange(0.000, 99.9999)
  1302. self.ncc_overlap_entry.setSingleStep(0.1)
  1303. self.ncc_overlap_entry.setObjectName("gdb_n_overlap")
  1304. self.grid2.addWidget(nccoverlabel, 15, 0)
  1305. self.grid2.addWidget(self.ncc_overlap_entry, 15, 1)
  1306. # Margin
  1307. nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
  1308. nccmarginlabel.setToolTip(
  1309. _("Bounding box margin.")
  1310. )
  1311. self.ncc_margin_entry = FCDoubleSpinner()
  1312. self.ncc_margin_entry.set_precision(self.decimals)
  1313. self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
  1314. self.ncc_margin_entry.setObjectName("gdb_n_margin")
  1315. self.grid2.addWidget(nccmarginlabel, 16, 0)
  1316. self.grid2.addWidget(self.ncc_margin_entry, 16, 1)
  1317. # Method
  1318. methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
  1319. methodlabel.setToolTip(
  1320. _("Algorithm for copper clearing:\n"
  1321. "- Standard: Fixed step inwards.\n"
  1322. "- Seed-based: Outwards from seed.\n"
  1323. "- Line-based: Parallel lines.")
  1324. )
  1325. self.ncc_method_combo = FCComboBox()
  1326. self.ncc_method_combo.addItems(
  1327. [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
  1328. )
  1329. self.ncc_method_combo.setObjectName("gdb_n_method")
  1330. self.grid2.addWidget(methodlabel, 17, 0)
  1331. self.grid2.addWidget(self.ncc_method_combo, 17, 1)
  1332. # Connect lines
  1333. self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
  1334. self.ncc_connect_cb.setObjectName("gdb_n_connect")
  1335. self.ncc_connect_cb.setToolTip(
  1336. _("Draw lines between resulting\n"
  1337. "segments to minimize tool lifts.")
  1338. )
  1339. self.grid2.addWidget(self.ncc_connect_cb, 18, 0)
  1340. # Contour
  1341. self.ncc_contour_cb = FCCheckBox('%s' % _("Contour"))
  1342. self.ncc_contour_cb.setObjectName("gdb_n_contour")
  1343. self.ncc_contour_cb.setToolTip(
  1344. _("Cut around the perimeter of the polygon\n"
  1345. "to trim rough edges.")
  1346. )
  1347. self.grid2.addWidget(self.ncc_contour_cb, 18, 1)
  1348. # ## NCC Offset choice
  1349. self.ncc_choice_offset_cb = FCCheckBox('%s' % _("Offset"))
  1350. self.ncc_choice_offset_cb.setObjectName("gdb_n_offset")
  1351. self.ncc_choice_offset_cb.setToolTip(
  1352. _("If used, it will add an offset to the copper features.\n"
  1353. "The copper clearing will finish to a distance\n"
  1354. "from the copper features.\n"
  1355. "The value can be between 0 and 10 FlatCAM units.")
  1356. )
  1357. self.grid2.addWidget(self.ncc_choice_offset_cb, 19, 0)
  1358. # ## NCC Offset Entry
  1359. self.ncc_offset_spinner = FCDoubleSpinner()
  1360. self.ncc_offset_spinner.set_range(0.00, 10.00)
  1361. self.ncc_offset_spinner.set_precision(4)
  1362. self.ncc_offset_spinner.setWrapping(True)
  1363. self.ncc_offset_spinner.setObjectName("gdb_n_offset_value")
  1364. units = self.app.defaults['units'].upper()
  1365. if units == 'MM':
  1366. self.ncc_offset_spinner.setSingleStep(0.1)
  1367. else:
  1368. self.ncc_offset_spinner.setSingleStep(0.01)
  1369. self.grid2.addWidget(self.ncc_offset_spinner, 19, 1)
  1370. # ###########################################################################
  1371. # ############### Paint UI form #############################################
  1372. # ###########################################################################
  1373. self.grid3 = QtWidgets.QGridLayout()
  1374. self.paint_vlay.addLayout(self.grid3)
  1375. self.grid3.setColumnStretch(0, 0)
  1376. self.grid3.setColumnStretch(1, 1)
  1377. self.paint_vlay.addStretch()
  1378. # Overlap
  1379. ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
  1380. ovlabel.setToolTip(
  1381. _("How much (percentage) of the tool width to overlap each tool pass.\n"
  1382. "Adjust the value starting with lower values\n"
  1383. "and increasing it if areas that should be painted are still \n"
  1384. "not painted.\n"
  1385. "Lower values = faster processing, faster execution on CNC.\n"
  1386. "Higher values = slow processing and slow execution on CNC\n"
  1387. "due of too many paths.")
  1388. )
  1389. self.paintoverlap_entry = FCDoubleSpinner(suffix='%')
  1390. self.paintoverlap_entry.set_precision(3)
  1391. self.paintoverlap_entry.setWrapping(True)
  1392. self.paintoverlap_entry.setRange(0.0000, 99.9999)
  1393. self.paintoverlap_entry.setSingleStep(0.1)
  1394. self.paintoverlap_entry.setObjectName('gdb_p_overlap')
  1395. self.grid3.addWidget(ovlabel, 1, 0)
  1396. self.grid3.addWidget(self.paintoverlap_entry, 1, 1)
  1397. # Margin
  1398. marginlabel = QtWidgets.QLabel('%s:' % _('Offset'))
  1399. marginlabel.setToolTip(
  1400. _("Distance by which to avoid\n"
  1401. "the edges of the polygon to\n"
  1402. "be painted.")
  1403. )
  1404. self.paint_offset_entry = FCDoubleSpinner()
  1405. self.paint_offset_entry.set_precision(self.decimals)
  1406. self.paint_offset_entry.set_range(-9999.9999, 9999.9999)
  1407. self.paint_offset_entry.setObjectName('gdb_p_offset')
  1408. self.grid3.addWidget(marginlabel, 2, 0)
  1409. self.grid3.addWidget(self.paint_offset_entry, 2, 1)
  1410. # Method
  1411. methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
  1412. methodlabel.setToolTip(
  1413. _("Algorithm for painting:\n"
  1414. "- Standard: Fixed step inwards.\n"
  1415. "- Seed-based: Outwards from seed.\n"
  1416. "- Line-based: Parallel lines.\n"
  1417. "- Laser-lines: Active only for Gerber objects.\n"
  1418. "Will create lines that follow the traces.\n"
  1419. "- Combo: In case of failure a new method will be picked from the above\n"
  1420. "in the order specified.")
  1421. )
  1422. self.paintmethod_combo = FCComboBox()
  1423. self.paintmethod_combo.addItems(
  1424. [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
  1425. )
  1426. idx = self.paintmethod_combo.findText(_("Laser_lines"))
  1427. self.paintmethod_combo.model().item(idx).setEnabled(False)
  1428. self.paintmethod_combo.setObjectName('gdb_p_method')
  1429. self.grid3.addWidget(methodlabel, 7, 0)
  1430. self.grid3.addWidget(self.paintmethod_combo, 7, 1)
  1431. # Connect lines
  1432. self.pathconnect_cb = FCCheckBox('%s' % _("Connect"))
  1433. self.pathconnect_cb.setObjectName('gdb_p_connect')
  1434. self.pathconnect_cb.setToolTip(
  1435. _("Draw lines between resulting\n"
  1436. "segments to minimize tool lifts.")
  1437. )
  1438. self.paintcontour_cb = FCCheckBox('%s' % _("Contour"))
  1439. self.paintcontour_cb.setObjectName('gdb_p_contour')
  1440. self.paintcontour_cb.setToolTip(
  1441. _("Cut around the perimeter of the polygon\n"
  1442. "to trim rough edges.")
  1443. )
  1444. self.grid3.addWidget(self.pathconnect_cb, 10, 0)
  1445. self.grid3.addWidget(self.paintcontour_cb, 10, 1)
  1446. # ###########################################################################
  1447. # ############### Isolation UI form #########################################
  1448. # ###########################################################################
  1449. self.grid4 = QtWidgets.QGridLayout()
  1450. self.iso_vlay.addLayout(self.grid4)
  1451. self.grid4.setColumnStretch(0, 0)
  1452. self.grid4.setColumnStretch(1, 1)
  1453. self.iso_vlay.addStretch()
  1454. # Passes
  1455. passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
  1456. passlabel.setToolTip(
  1457. _("Width of the isolation gap in\n"
  1458. "number (integer) of tool widths.")
  1459. )
  1460. self.passes_entry = FCSpinner()
  1461. self.passes_entry.set_range(1, 999)
  1462. self.passes_entry.setObjectName("gdb_i_passes")
  1463. self.grid4.addWidget(passlabel, 0, 0)
  1464. self.grid4.addWidget(self.passes_entry, 0, 1)
  1465. # Overlap Entry
  1466. overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
  1467. overlabel.setToolTip(
  1468. _("How much (percentage) of the tool width to overlap each tool pass.")
  1469. )
  1470. self.iso_overlap_entry = FCDoubleSpinner(suffix='%')
  1471. self.iso_overlap_entry.set_precision(self.decimals)
  1472. self.iso_overlap_entry.setWrapping(True)
  1473. self.iso_overlap_entry.set_range(0.0000, 99.9999)
  1474. self.iso_overlap_entry.setSingleStep(0.1)
  1475. self.iso_overlap_entry.setObjectName("gdb_i_overlap")
  1476. self.grid4.addWidget(overlabel, 2, 0)
  1477. self.grid4.addWidget(self.iso_overlap_entry, 2, 1)
  1478. # Milling Type Radio Button
  1479. self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
  1480. self.milling_type_label.setToolTip(
  1481. _("Milling type when the selected tool is of type: 'iso_op':\n"
  1482. "- climb / best for precision milling and to reduce tool usage\n"
  1483. "- conventional / useful when there is no backlash compensation")
  1484. )
  1485. self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
  1486. {'label': _('Conventional'), 'value': 'cv'}])
  1487. self.milling_type_radio.setToolTip(
  1488. _("Milling type when the selected tool is of type: 'iso_op':\n"
  1489. "- climb / best for precision milling and to reduce tool usage\n"
  1490. "- conventional / useful when there is no backlash compensation")
  1491. )
  1492. self.milling_type_radio.setObjectName("gdb_i_milling_type")
  1493. self.grid4.addWidget(self.milling_type_label, 4, 0)
  1494. self.grid4.addWidget(self.milling_type_radio, 4, 1)
  1495. # Follow
  1496. self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
  1497. self.follow_label.setToolTip(
  1498. _("Generate a 'Follow' geometry.\n"
  1499. "This means that it will cut through\n"
  1500. "the middle of the trace.")
  1501. )
  1502. self.follow_cb = FCCheckBox()
  1503. self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
  1504. "This means that it will cut through\n"
  1505. "the middle of the trace."))
  1506. self.follow_cb.setObjectName("gdb_i_follow")
  1507. self.grid4.addWidget(self.follow_label, 6, 0)
  1508. self.grid4.addWidget(self.follow_cb, 6, 1)
  1509. # Isolation Type
  1510. self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
  1511. self.iso_type_label.setToolTip(
  1512. _("Choose how the isolation will be executed:\n"
  1513. "- 'Full' -> complete isolation of polygons\n"
  1514. "- 'Ext' -> will isolate only on the outside\n"
  1515. "- 'Int' -> will isolate only on the inside\n"
  1516. "'Exterior' isolation is almost always possible\n"
  1517. "(with the right tool) but 'Interior'\n"
  1518. "isolation can be done only when there is an opening\n"
  1519. "inside of the polygon (e.g polygon is a 'doughnut' shape).")
  1520. )
  1521. self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
  1522. {'label': _('Ext'), 'value': 'ext'},
  1523. {'label': _('Int'), 'value': 'int'}])
  1524. self.iso_type_radio.setObjectName("gdb_i_iso_type")
  1525. self.grid4.addWidget(self.iso_type_label, 8, 0)
  1526. self.grid4.addWidget(self.iso_type_radio, 8, 1)
  1527. # ###########################################################################
  1528. # ################ DRILLING UI form #########################################
  1529. # ###########################################################################
  1530. self.grid5 = QtWidgets.QGridLayout()
  1531. self.drill_vlay.addLayout(self.grid5)
  1532. self.grid5.setColumnStretch(0, 0)
  1533. self.grid5.setColumnStretch(1, 1)
  1534. self.drill_vlay.addStretch()
  1535. # Cut Z
  1536. self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
  1537. self.cutzlabel.setToolTip(
  1538. _("Drill depth (negative)\n"
  1539. "below the copper surface.")
  1540. )
  1541. self.cutz_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1542. self.cutz_drill_entry.set_precision(self.decimals)
  1543. if self.machinist_setting == 0:
  1544. self.cutz_drill_entry.set_range(-9999.9999, 0.0000)
  1545. else:
  1546. self.cutz_drill_entry.set_range(-9999.9999, 9999.9999)
  1547. self.cutz_drill_entry.setSingleStep(0.1)
  1548. self.cutz_drill_entry.setObjectName("gdb_e_cutz")
  1549. self.grid5.addWidget(self.cutzlabel, 4, 0)
  1550. self.grid5.addWidget(self.cutz_drill_entry, 4, 1)
  1551. # Tool Offset
  1552. self.tool_offset_label = QtWidgets.QLabel('%s:' % _('Offset Z'))
  1553. self.tool_offset_label.setToolTip(
  1554. _("Some drill bits (the larger ones) need to drill deeper\n"
  1555. "to create the desired exit hole diameter due of the tip shape.\n"
  1556. "The value here can compensate the Cut Z parameter.")
  1557. )
  1558. self.offset_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1559. self.offset_drill_entry.set_precision(self.decimals)
  1560. self.offset_drill_entry.set_range(-9999.9999, 9999.9999)
  1561. self.offset_drill_entry.setObjectName("gdb_e_offset")
  1562. self.grid5.addWidget(self.tool_offset_label, 6, 0)
  1563. self.grid5.addWidget(self.offset_drill_entry, 6, 1)
  1564. # Multi-Depth
  1565. self.multidepth_drill_label = QtWidgets.QLabel('%s:' % _("MultiDepth"))
  1566. self.multidepth_drill_label.setToolTip(
  1567. _(
  1568. "Use multiple passes to limit\n"
  1569. "the cut depth in each pass. Will\n"
  1570. "cut multiple times until Cut Z is\n"
  1571. "reached."
  1572. )
  1573. )
  1574. self.mpass_drill_cb = FCCheckBox()
  1575. self.mpass_drill_cb.setObjectName("gdb_e_multidepth")
  1576. self.grid5.addWidget(self.multidepth_drill_label, 7, 0)
  1577. self.grid5.addWidget(self.mpass_drill_cb, 7, 1)
  1578. # Depth Per Pass
  1579. self.dpp_drill_label = QtWidgets.QLabel('%s:' % _("DPP"))
  1580. self.dpp_drill_label.setToolTip(
  1581. _("DPP. Depth per Pass.\n"
  1582. "The value used to cut into material on each pass."))
  1583. self.maxdepth_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1584. self.maxdepth_drill_entry.set_precision(self.decimals)
  1585. self.maxdepth_drill_entry.set_range(0, 9999.9999)
  1586. self.maxdepth_drill_entry.setSingleStep(0.1)
  1587. self.maxdepth_drill_entry.setToolTip(_("Depth of each pass (positive)."))
  1588. self.maxdepth_drill_entry.setObjectName("gdb_e_depthperpass")
  1589. self.grid5.addWidget(self.dpp_drill_label, 8, 0)
  1590. self.grid5.addWidget(self.maxdepth_drill_entry, 8, 1)
  1591. # Travel Z (z_move)
  1592. self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
  1593. self.travelzlabel.setToolTip(
  1594. _("Tool height when travelling\n"
  1595. "across the XY plane.")
  1596. )
  1597. self.travelz_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1598. self.travelz_drill_entry.set_precision(self.decimals)
  1599. if self.machinist_setting == 0:
  1600. self.travelz_drill_entry.set_range(0.00001, 9999.9999)
  1601. else:
  1602. self.travelz_drill_entry.set_range(-9999.9999, 9999.9999)
  1603. self.travelz_drill_entry.setSingleStep(0.1)
  1604. self.travelz_drill_entry.setObjectName("gdb_e_travelz")
  1605. self.grid5.addWidget(self.travelzlabel, 10, 0)
  1606. self.grid5.addWidget(self.travelz_drill_entry, 10, 1)
  1607. separator_line = QtWidgets.QFrame()
  1608. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1609. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1610. self.grid5.addWidget(separator_line, 12, 0, 1, 2)
  1611. # Excellon Feedrate Z
  1612. self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
  1613. self.frzlabel.setToolTip(
  1614. _("Tool speed while drilling\n"
  1615. "(in units per minute).\n"
  1616. "So called 'Plunge' feedrate.\n"
  1617. "This is for linear move G01.")
  1618. )
  1619. self.feedrate_z_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1620. self.feedrate_z_drill_entry.set_precision(self.decimals)
  1621. self.feedrate_z_drill_entry.set_range(0.0, 99999.9999)
  1622. self.feedrate_z_drill_entry.setSingleStep(0.1)
  1623. self.feedrate_z_drill_entry.setObjectName("gdb_e_feedratez")
  1624. self.grid5.addWidget(self.frzlabel, 14, 0)
  1625. self.grid5.addWidget(self.feedrate_z_drill_entry, 14, 1)
  1626. # Excellon Rapid Feedrate
  1627. self.feedrate_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
  1628. self.feedrate_rapid_label.setToolTip(
  1629. _("Tool speed while drilling\n"
  1630. "(in units per minute).\n"
  1631. "This is for the rapid move G00.\n"
  1632. "It is useful only for Marlin,\n"
  1633. "ignore for any other cases.")
  1634. )
  1635. self.feedrate_rapid_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1636. self.feedrate_rapid_drill_entry.set_precision(self.decimals)
  1637. self.feedrate_rapid_drill_entry.set_range(0.0, 99999.9999)
  1638. self.feedrate_rapid_drill_entry.setSingleStep(0.1)
  1639. self.feedrate_rapid_drill_entry.setObjectName("gdb_e_fr_rapid")
  1640. self.grid5.addWidget(self.feedrate_rapid_label, 16, 0)
  1641. self.grid5.addWidget(self.feedrate_rapid_drill_entry, 16, 1)
  1642. separator_line = QtWidgets.QFrame()
  1643. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1644. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1645. self.grid5.addWidget(separator_line, 18, 0, 1, 2)
  1646. # Spindlespeed
  1647. self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
  1648. self.spindle_label.setToolTip(
  1649. _("Speed of the spindle\n"
  1650. "in RPM (optional)")
  1651. )
  1652. self.spindlespeed_drill_entry = FCSpinner(callback=self.confirmation_message_int)
  1653. self.spindlespeed_drill_entry.set_range(0, 1000000)
  1654. self.spindlespeed_drill_entry.set_step(100)
  1655. self.spindlespeed_drill_entry.setObjectName("gdb_e_spindlespeed")
  1656. self.grid5.addWidget(self.spindle_label, 20, 0)
  1657. self.grid5.addWidget(self.spindlespeed_drill_entry, 20, 1)
  1658. # Dwell
  1659. self.dwell_drill_label = QtWidgets.QLabel('%s:' % _("Dwell"))
  1660. self.dwell_drill_label.setToolTip(
  1661. _("Dwell.\n"
  1662. "Check this if a delay is needed to allow\n"
  1663. "the spindle motor to reach its set speed."))
  1664. self.dwell_drill_cb = FCCheckBox()
  1665. self.dwell_drill_cb.setObjectName("gdb_e_dwell")
  1666. self.grid5.addWidget(self.dwell_drill_label, 21, 0)
  1667. self.grid5.addWidget(self.dwell_drill_cb, 21, 1)
  1668. # Dwelltime
  1669. self.dwelltime_drill_lbl = QtWidgets.QLabel('%s:' % _('Dwelltime'))
  1670. self.dwelltime_drill_lbl.setToolTip(
  1671. _("Dwell Time.\n"
  1672. "A delay used to allow the motor spindle reach its set speed."))
  1673. self.dwelltime_drill_entry = FCDoubleSpinner(callback=self.confirmation_message)
  1674. self.dwelltime_drill_entry.set_precision(self.decimals)
  1675. self.dwelltime_drill_entry.set_range(0.0, 9999.9999)
  1676. self.dwelltime_drill_entry.setSingleStep(0.1)
  1677. self.dwelltime_drill_entry.setObjectName("gdb_e_dwelltime")
  1678. self.grid5.addWidget(self.dwelltime_drill_lbl, 22, 0)
  1679. self.grid5.addWidget(self.dwelltime_drill_entry, 22, 1)
  1680. separator_line = QtWidgets.QFrame()
  1681. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  1682. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  1683. self.grid5.addWidget(separator_line, 24, 0, 1, 2)
  1684. # Drill slots
  1685. self.drill_slots_drill_lbl = QtWidgets.QLabel('%s:' % _('Drill slots'))
  1686. self.drill_slots_drill_lbl.setToolTip(
  1687. _("If the selected tool has slots then they will be drilled.")
  1688. )
  1689. self.drill_slots_drill_cb = FCCheckBox()
  1690. self.drill_slots_drill_cb.setObjectName("gdb_e_drill_slots")
  1691. self.grid5.addWidget(self.drill_slots_drill_lbl, 26, 0,)
  1692. self.grid5.addWidget(self.drill_slots_drill_cb, 26, 1)
  1693. # Drill Overlap
  1694. self.drill_overlap_label = QtWidgets.QLabel('%s:' % _('Overlap'))
  1695. self.drill_overlap_label.setToolTip(
  1696. _("How much (percentage) of the tool diameter to overlap previous drill hole.")
  1697. )
  1698. self.drill_overlap_drill_entry = FCDoubleSpinner(suffix='%', callback=self.confirmation_message)
  1699. self.drill_overlap_drill_entry.set_precision(self.decimals)
  1700. self.drill_overlap_drill_entry.set_range(0.0, 100.0000)
  1701. self.drill_overlap_drill_entry.setSingleStep(0.1)
  1702. self.drill_overlap_drill_entry.setObjectName("gdb_e_drill_slots_over")
  1703. self.grid5.addWidget(self.drill_overlap_label, 28, 0)
  1704. self.grid5.addWidget(self.drill_overlap_drill_entry, 28, 1)
  1705. # Last drill in slot
  1706. self.last_drill_drill_lbl = QtWidgets.QLabel('%s:' % _('Last drill'))
  1707. self.last_drill_drill_lbl.setToolTip(
  1708. _("If the slot length is not completely covered by drill holes,\n"
  1709. "add a drill hole on the slot end point.")
  1710. )
  1711. self.last_drill_drill_cb = FCCheckBox()
  1712. self.last_drill_drill_cb.setObjectName("gdb_e_drill_last_drill")
  1713. self.grid5.addWidget(self.last_drill_drill_lbl, 30, 0, 1, 2)
  1714. self.grid5.addWidget(self.last_drill_drill_cb, 30, 1)
  1715. # ####################################################################
  1716. # ####################################################################
  1717. # GUI for the lower part of the window
  1718. # ####################################################################
  1719. # ####################################################################
  1720. new_vlay = QtWidgets.QVBoxLayout()
  1721. self.g_lay.addLayout(new_vlay, 1, 0, 1, 2)
  1722. self.buttons_frame = QtWidgets.QFrame()
  1723. self.buttons_frame.setContentsMargins(0, 0, 0, 0)
  1724. new_vlay.addWidget(self.buttons_frame)
  1725. self.buttons_box = QtWidgets.QHBoxLayout()
  1726. self.buttons_box.setContentsMargins(0, 0, 0, 0)
  1727. self.buttons_frame.setLayout(self.buttons_box)
  1728. self.buttons_frame.show()
  1729. self.add_entry_btn = FCButton(_("Add Tool in DB"))
  1730. self.add_entry_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
  1731. self.add_entry_btn.setToolTip(
  1732. _("Add a new tool in the Tools Database.\n"
  1733. "It will be used in the Geometry UI.\n"
  1734. "You can edit it after it is added.")
  1735. )
  1736. self.buttons_box.addWidget(self.add_entry_btn)
  1737. # add_fct_entry_btn = FCButton(_("Add Paint/NCC Tool in DB"))
  1738. # add_fct_entry_btn.setToolTip(
  1739. # _("Add a new tool in the Tools Database.\n"
  1740. # "It will be used in the Paint/NCC Tools UI.\n"
  1741. # "You can edit it after it is added.")
  1742. # )
  1743. # self.buttons_box.addWidget(add_fct_entry_btn)
  1744. self.remove_entry_btn = FCButton(_("Delete Tool from DB"))
  1745. self.remove_entry_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
  1746. self.remove_entry_btn.setToolTip(
  1747. _("Remove a selection of tools in the Tools Database.")
  1748. )
  1749. self.buttons_box.addWidget(self.remove_entry_btn)
  1750. self.export_db_btn = FCButton(_("Export DB"))
  1751. self.export_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/export.png'))
  1752. self.export_db_btn.setToolTip(
  1753. _("Save the Tools Database to a custom text file.")
  1754. )
  1755. self.buttons_box.addWidget(self.export_db_btn)
  1756. self.import_db_btn = FCButton(_("Import DB"))
  1757. self.import_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/import.png'))
  1758. self.import_db_btn.setToolTip(
  1759. _("Load the Tools Database information's from a custom text file.")
  1760. )
  1761. self.buttons_box.addWidget(self.import_db_btn)
  1762. self.save_db_btn = FCButton(_("Save DB"))
  1763. self.save_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  1764. self.save_db_btn.setToolTip(
  1765. _("Save the Tools Database information's.")
  1766. )
  1767. self.buttons_box.addWidget(self.save_db_btn)
  1768. self.add_tool_from_db = FCButton(_("Transfer the Tool"))
  1769. self.add_tool_from_db.setToolTip(
  1770. _("Insert a new tool in the Tools Table of the\n"
  1771. "object/application tool after selecting a tool\n"
  1772. "in the Tools Database.")
  1773. )
  1774. self.add_tool_from_db.setStyleSheet("""
  1775. QPushButton
  1776. {
  1777. font-weight: bold;
  1778. color: green;
  1779. }
  1780. """)
  1781. self.add_tool_from_db.hide()
  1782. self.cancel_tool_from_db = FCButton(_("Cancel"))
  1783. self.cancel_tool_from_db.hide()
  1784. hlay = QtWidgets.QHBoxLayout()
  1785. tree_layout.addLayout(hlay)
  1786. hlay.addWidget(self.add_tool_from_db)
  1787. hlay.addWidget(self.cancel_tool_from_db)
  1788. # hlay.addStretch()
  1789. # ############################ FINSIHED GUI ###################################
  1790. # #############################################################################
  1791. def confirmation_message(self, accepted, minval, maxval):
  1792. if accepted is False:
  1793. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  1794. self.decimals,
  1795. minval,
  1796. self.decimals,
  1797. maxval), False)
  1798. else:
  1799. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  1800. def confirmation_message_int(self, accepted, minval, maxval):
  1801. if accepted is False:
  1802. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  1803. (_("Edited value is out of range"), minval, maxval), False)
  1804. else:
  1805. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  1806. class ToolsDB2(QtWidgets.QWidget):
  1807. mark_tools_rows = QtCore.pyqtSignal()
  1808. def __init__(self, app, callback_on_edited, callback_on_tool_request, parent=None):
  1809. super(ToolsDB2, self).__init__(parent)
  1810. self.app = app
  1811. self.app_ui = self.app.ui
  1812. self.decimals = self.app.decimals
  1813. self.callback_app = callback_on_edited
  1814. self.on_tool_request = callback_on_tool_request
  1815. self.offset_item_options = ["Path", "In", "Out", "Custom"]
  1816. self.type_item_options = ["Iso", "Rough", "Finish"]
  1817. self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
  1818. '''
  1819. dict to hold all the tools in the Tools DB
  1820. format:
  1821. {
  1822. tool_id: {
  1823. 'name': 'new_tool'
  1824. 'tooldia': self.app.defaults["geometry_cnctooldia"]
  1825. 'offset': 'Path'
  1826. 'offset_value': 0.0
  1827. 'type': _('Rough'),
  1828. 'tool_type': 'C1'
  1829. 'data': dict()
  1830. }
  1831. }
  1832. '''
  1833. self.db_tool_dict = {}
  1834. # ##############################################################################
  1835. # ##############################################################################
  1836. # TOOLS DATABASE UI
  1837. # ##############################################################################
  1838. # ##############################################################################
  1839. layout = QtWidgets.QGridLayout()
  1840. layout.setColumnStretch(0, 0)
  1841. layout.setColumnStretch(1, 1)
  1842. self.setLayout(layout)
  1843. self.ui = ToolsDB2UI(app=self.app, grid_layout=layout)
  1844. # ##############################################################################
  1845. # ##############################################################################
  1846. # ########## SETUP THE DICTIONARIES THAT HOLD THE WIDGETS #####################
  1847. # ##############################################################################
  1848. # ##############################################################################
  1849. self.form_fields = {
  1850. "tool_target": self.ui.tool_op_combo,
  1851. "tol_min": self.ui.tol_min_entry,
  1852. "tol_max": self.ui.tol_max_entry,
  1853. "name": self.ui.name_entry,
  1854. "tooldia": self.ui.dia_entry,
  1855. # Milling
  1856. "tool_type": self.ui.shape_combo,
  1857. "cutz": self.ui.cutz_entry,
  1858. "multidepth": self.ui.multidepth_cb,
  1859. "depthperpass": self.ui.multidepth_entry,
  1860. "travelz": self.ui.travelz_entry,
  1861. "feedrate": self.ui.frxy_entry,
  1862. "feedrate_z": self.ui.frz_entry,
  1863. "spindlespeed": self.ui.spindle_entry,
  1864. "dwell": self.ui.dwell_cb,
  1865. "dwelltime": self.ui.dwelltime_entry,
  1866. "type": self.ui.type_combo,
  1867. "offset": self.ui.tooloffset_combo,
  1868. "offset_value": self.ui.custom_offset_entry,
  1869. "vtipdia": self.ui.vdia_entry,
  1870. "vtipangle": self.ui.vangle_entry,
  1871. "feedrate_rapid": self.ui.frapids_entry,
  1872. "extracut": self.ui.ecut_cb,
  1873. "extracut_length": self.ui.ecut_length_entry,
  1874. # NCC
  1875. "tools_nccoperation": self.ui.op_radio,
  1876. "tools_nccmilling_type": self.ui.milling_type_radio,
  1877. "tools_nccoverlap": self.ui.ncc_overlap_entry,
  1878. "tools_nccmargin": self.ui.ncc_margin_entry,
  1879. "tools_nccmethod": self.ui.ncc_method_combo,
  1880. "tools_nccconnect": self.ui.ncc_connect_cb,
  1881. "tools_ncccontour": self.ui.ncc_contour_cb,
  1882. "tools_ncc_offset_choice": self.ui.ncc_choice_offset_cb,
  1883. "tools_ncc_offset_value": self.ui.ncc_offset_spinner,
  1884. # Paint
  1885. "tools_paintoverlap": self.ui.paintoverlap_entry,
  1886. "tools_paintoffset": self.ui.paint_offset_entry,
  1887. "tools_paintmethod": self.ui.paintmethod_combo,
  1888. "tools_pathconnect": self.ui.pathconnect_cb,
  1889. "tools_paintcontour": self.ui.paintcontour_cb,
  1890. # Isolation
  1891. "tools_iso_passes": self.ui.passes_entry,
  1892. "tools_iso_overlap": self.ui.iso_overlap_entry,
  1893. "tools_iso_milling_type": self.ui.milling_type_radio,
  1894. "tools_iso_follow": self.ui.follow_cb,
  1895. "tools_iso_isotype": self.ui.iso_type_radio,
  1896. # Drilling
  1897. "tools_drill_cutz": self.ui.cutz_drill_entry,
  1898. "tools_drill_multidepth": self.ui.mpass_drill_cb,
  1899. "tools_drill_depthperpass": self.ui.maxdepth_drill_entry,
  1900. "tools_drill_travelz": self.ui.travelz_drill_entry,
  1901. "tools_drill_feedrate_z": self.ui.feedrate_z_drill_entry,
  1902. "tools_drill_feedrate_rapid": self.ui.feedrate_rapid_drill_entry,
  1903. "tools_drill_spindlespeed": self.ui.spindlespeed_drill_entry,
  1904. "tools_drill_dwell": self.ui.dwell_drill_cb,
  1905. "tools_drill_dwelltime": self.ui.dwelltime_drill_entry,
  1906. "tools_drill_offset": self.ui.offset_drill_entry,
  1907. "tools_drill_drill_slots": self.ui.drill_slots_drill_cb,
  1908. "tools_drill_drill_overlap": self.ui.drill_overlap_drill_entry,
  1909. "tools_drill_last_drill": self.ui.last_drill_drill_cb,
  1910. }
  1911. self.name2option = {
  1912. "gdb_tool_target": "tool_target",
  1913. "gdb_tol_min": "tol_min",
  1914. "gdb_tol_max": "tol_max",
  1915. "gdb_name": "name",
  1916. "gdb_dia": "tooldia",
  1917. # Milling
  1918. "gdb_shape": "tool_type",
  1919. "gdb_cutz": "cutz",
  1920. "gdb_multidepth": "multidepth",
  1921. "gdb_multidepth_entry": "depthperpass",
  1922. "gdb_travelz": "travelz",
  1923. "gdb_frxy": "feedrate",
  1924. "gdb_frz": "feedrate_z",
  1925. "gdb_spindle": "spindlespeed",
  1926. "gdb_dwell": "dwell",
  1927. "gdb_dwelltime": "dwelltime",
  1928. "gdb_type": "type",
  1929. "gdb_tool_offset": "offset",
  1930. "gdb_custom_offset": "offset_value",
  1931. "gdb_vdia": "vtipdia",
  1932. "gdb_vangle": "vtipangle",
  1933. "gdb_frapids": "feedrate_rapid",
  1934. "gdb_ecut": "extracut",
  1935. "gdb_ecut_length": "extracut_length",
  1936. # NCC
  1937. "gdb_n_operation": "tools_nccoperation",
  1938. "gdb_n_overlap": "tools_nccoverlap",
  1939. "gdb_n_margin": "tools_nccmargin",
  1940. "gdb_n_method": "tools_nccmethod",
  1941. "gdb_n_connect": "tools_nccconnect",
  1942. "gdb_n_contour": "tools_ncccontour",
  1943. "gdb_n_offset": "tools_ncc_offset_choice",
  1944. "gdb_n_offset_value": "tools_ncc_offset_value",
  1945. "gdb_n_milling_type": "tools_nccmilling_type",
  1946. # Paint
  1947. 'gdb_p_overlap': "tools_paintoverlap",
  1948. 'gdb_p_offset': "tools_paintoffset",
  1949. 'gdb_p_method': "tools_paintmethod",
  1950. 'gdb_p_connect': "tools_pathconnect",
  1951. 'gdb_p_contour': "tools_paintcontour",
  1952. # Isolation
  1953. "gdb_i_passes": "tools_iso_passes",
  1954. "gdb_i_overlap": "tools_iso_overlap",
  1955. "gdb_i_milling_type": "tools_iso_milling_type",
  1956. "gdb_i_follow": "tools_iso_follow",
  1957. "gdb_i_iso_type": "tools_iso_isotype",
  1958. # Drilling
  1959. "gdb_e_cutz": "tools_drill_cutz",
  1960. "gdb_e_multidepth": "tools_drill_multidepth",
  1961. "gdb_e_depthperpass": "tools_drill_depthperpass",
  1962. "gdb_e_travelz": "tools_drill_travelz",
  1963. "gdb_e_feedratez": "tools_drill_feedrate_z",
  1964. "gdb_e_fr_rapid": "tools_drill_feedrate_rapid",
  1965. "gdb_e_spindlespeed": "tools_drill_spindlespeed",
  1966. "gdb_e_dwell": "tools_drill_dwell",
  1967. "gdb_e_dwelltime": "tools_drill_dwelltime",
  1968. "gdb_e_offset": "tools_drill_offset",
  1969. "gdb_e_drill_slots": "tools_drill_drill_slots",
  1970. "gdb_e_drill_slots_over": "tools_drill_drill_overlap",
  1971. "gdb_e_drill_last_drill": "tools_drill_last_drill",
  1972. }
  1973. self.current_toolid = None
  1974. # variable to show if double clicking and item will trigger adding a tool from DB
  1975. self.ok_to_add = False
  1976. # ##############################################################################
  1977. # ######################## SIGNALS #############################################
  1978. # ##############################################################################
  1979. self.ui.add_entry_btn.clicked.connect(self.on_tool_add)
  1980. self.ui.remove_entry_btn.clicked.connect(self.on_tool_delete)
  1981. self.ui.export_db_btn.clicked.connect(self.on_export_tools_db_file)
  1982. self.ui.import_db_btn.clicked.connect(self.on_import_tools_db_file)
  1983. self.ui.save_db_btn.clicked.connect(self.on_save_db_btn_click)
  1984. # closebtn.clicked.connect(self.accept)
  1985. self.ui.add_tool_from_db.clicked.connect(self.on_tool_requested_from_app)
  1986. self.ui.cancel_tool_from_db.clicked.connect(self.on_cancel_tool)
  1987. # self.ui.tree_widget.selectionModel().selectionChanged.connect(self.on_list_selection_change)
  1988. self.ui.tree_widget.currentItemChanged.connect(self.on_list_selection_change)
  1989. self.ui.tree_widget.itemChanged.connect(self.on_list_item_edited)
  1990. self.ui.tree_widget.customContextMenuRequested.connect(self.on_menu_request)
  1991. self.ui.tree_widget.itemDoubleClicked.connect(self.on_item_double_clicked)
  1992. self.ui.tool_op_combo.currentIndexChanged.connect(self.on_tool_target_changed)
  1993. self.setup_db_ui()
  1994. def on_menu_request(self, pos):
  1995. menu = QtWidgets.QMenu()
  1996. add_tool = menu.addAction(QtGui.QIcon(self.app.resource_location + '/plus16.png'), _("Add to DB"))
  1997. add_tool.triggered.connect(self.on_tool_add)
  1998. copy_tool = menu.addAction(QtGui.QIcon(self.app.resource_location + '/copy16.png'), _("Copy from DB"))
  1999. copy_tool.triggered.connect(self.on_tool_copy)
  2000. delete_tool = menu.addAction(QtGui.QIcon(self.app.resource_location + '/delete32.png'), _("Delete from DB"))
  2001. delete_tool.triggered.connect(self.on_tool_delete)
  2002. # sep = menu.addSeparator()
  2003. save_changes = menu.addAction(QtGui.QIcon(self.app.resource_location + '/save_as.png'), _("Save changes"))
  2004. save_changes.triggered.connect(self.on_save_changes)
  2005. # tree_item = self.ui.tree_widget.itemAt(pos)
  2006. menu.exec(self.ui.tree_widget.viewport().mapToGlobal(pos))
  2007. def on_save_changes(self):
  2008. widget_name = self.app_ui.plot_tab_area.currentWidget().objectName()
  2009. if widget_name == 'database_tab':
  2010. # Tools DB saved, update flag
  2011. self.app.tools_db_changed_flag = False
  2012. self.app.tools_db_tab.on_save_tools_db()
  2013. def on_item_double_clicked(self, item, column):
  2014. if column == 0 and self.ok_to_add is True:
  2015. self.ok_to_add = False
  2016. self.on_tool_requested_from_app()
  2017. def on_list_selection_change(self, current, previous):
  2018. self.ui_disconnect()
  2019. self.current_toolid = int(current.text(0))
  2020. self.storage_to_form(self.db_tool_dict[current.text(0)])
  2021. self.ui_connect()
  2022. def on_list_item_edited(self, item, column):
  2023. if column == 0:
  2024. return
  2025. self.ui.name_entry.set_value(item.text(1))
  2026. def storage_to_form(self, dict_storage):
  2027. self.ui_disconnect()
  2028. for form_key in self.form_fields:
  2029. for storage_key in dict_storage:
  2030. if form_key == storage_key:
  2031. try:
  2032. self.form_fields[form_key].set_value(dict_storage[form_key])
  2033. except Exception as e:
  2034. print(str(e))
  2035. if storage_key == 'data':
  2036. for data_key in dict_storage[storage_key]:
  2037. if form_key == data_key:
  2038. try:
  2039. self.form_fields[form_key].set_value(dict_storage['data'][data_key])
  2040. except Exception as e:
  2041. print(str(e))
  2042. self.ui_connect()
  2043. def form_to_storage(self, tool):
  2044. self.ui_disconnect()
  2045. widget_changed = self.sender()
  2046. wdg_objname = widget_changed.objectName()
  2047. option_changed = self.name2option[wdg_objname]
  2048. tooluid_item = int(tool)
  2049. for tooluid_key, tooluid_val in self.db_tool_dict.items():
  2050. if int(tooluid_key) == tooluid_item:
  2051. new_option_value = self.form_fields[option_changed].get_value()
  2052. if option_changed in tooluid_val:
  2053. tooluid_val[option_changed] = new_option_value
  2054. if option_changed in tooluid_val['data']:
  2055. tooluid_val['data'][option_changed] = new_option_value
  2056. self.ui_connect()
  2057. def setup_db_ui(self):
  2058. filename = self.app.data_path + '\\tools_db.FlatDB'
  2059. # load the database tools from the file
  2060. try:
  2061. with open(filename) as f:
  2062. tools = f.read()
  2063. except IOError:
  2064. self.app.log.error("Could not load tools DB file.")
  2065. self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
  2066. return
  2067. try:
  2068. self.db_tool_dict = json.loads(tools)
  2069. except Exception:
  2070. e = sys.exc_info()[0]
  2071. self.app.log.error(str(e))
  2072. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  2073. return
  2074. self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
  2075. self.build_db_ui()
  2076. def build_db_ui(self):
  2077. self.ui_disconnect()
  2078. nr_crt = 0
  2079. parent = self.ui.tree_widget
  2080. self.ui.tree_widget.blockSignals(True)
  2081. self.ui.tree_widget.clear()
  2082. self.ui.tree_widget.blockSignals(False)
  2083. for toolid, dict_val in self.db_tool_dict.items():
  2084. row = nr_crt
  2085. nr_crt += 1
  2086. t_name = dict_val['name']
  2087. try:
  2088. # self.add_tool_table_line(row, name=t_name, tooldict=dict_val)
  2089. self.ui.tree_widget.blockSignals(True)
  2090. try:
  2091. self.ui.tree_widget.addParentEditable(parent=parent, title=[str(row+1), t_name], editable=True)
  2092. except Exception as e:
  2093. print('FlatCAMCoomn.ToolDB2.build_db_ui() -> ', str(e))
  2094. self.ui.tree_widget.blockSignals(False)
  2095. except Exception as e:
  2096. self.app.log.debug("ToolDB.build_db_ui.add_tool_table_line() --> %s" % str(e))
  2097. if self.current_toolid is None or self.current_toolid < 1:
  2098. if self.db_tool_dict:
  2099. self.storage_to_form(self.db_tool_dict['1'])
  2100. # Enable appGUI
  2101. try:
  2102. self.on_tool_target_changed(val=self.db_tool_dict['1']['data']['tool_target'])
  2103. except KeyError:
  2104. self.on_tool_target_changed(val=_("General"))
  2105. self.ui.tree_widget.setCurrentItem(self.ui.tree_widget.topLevelItem(0))
  2106. # self.ui.tree_widget.setFocus()
  2107. else:
  2108. # Disable appGUI
  2109. self.ui.tool_description_box.show()
  2110. self.ui.milling_box.show()
  2111. self.ui.ncc_box.show()
  2112. self.ui.paint_box.show()
  2113. self.ui.iso_box.show()
  2114. self.ui.drill_box.show()
  2115. self.ui.tool_description_box.setEnabled(False)
  2116. self.ui.milling_box.setEnabled(False)
  2117. self.ui.ncc_box.setEnabled(False)
  2118. self.ui.paint_box.setEnabled(False)
  2119. self.ui.iso_box.setEnabled(False)
  2120. self.ui.drill_box.setEnabled(False)
  2121. else:
  2122. self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
  2123. self.ui_connect()
  2124. def on_tool_target_changed(self, index=None, val=None):
  2125. if val is None:
  2126. tool_target = self.ui.tool_op_combo.get_value()
  2127. else:
  2128. tool_target = val
  2129. self.ui.tool_description_box.setEnabled(True)
  2130. if self.db_tool_dict:
  2131. if tool_target == _("General"):
  2132. self.ui.milling_box.setEnabled(True)
  2133. self.ui.ncc_box.setEnabled(True)
  2134. self.ui.paint_box.setEnabled(True)
  2135. self.ui.iso_box.setEnabled(True)
  2136. self.ui.drill_box.setEnabled(True)
  2137. self.ui.milling_box.show()
  2138. self.ui.ncc_box.show()
  2139. self.ui.paint_box.show()
  2140. self.ui.iso_box.show()
  2141. self.ui.drill_box.show()
  2142. else:
  2143. self.ui.milling_box.hide()
  2144. self.ui.ncc_box.hide()
  2145. self.ui.paint_box.hide()
  2146. self.ui.iso_box.hide()
  2147. self.ui.drill_box.hide()
  2148. if tool_target == _("Milling"):
  2149. self.ui.milling_box.setEnabled(True)
  2150. self.ui.milling_box.show()
  2151. if tool_target == _("Drilling"):
  2152. self.ui.drill_box.setEnabled(True)
  2153. self.ui.drill_box.show()
  2154. if tool_target == _("Isolation"):
  2155. self.ui.iso_box.setEnabled(True)
  2156. self.ui.iso_box.show()
  2157. self.ui.milling_box.setEnabled(True)
  2158. self.ui.milling_box.show()
  2159. if tool_target == _("Paint"):
  2160. self.ui.paint_box.setEnabled(True)
  2161. self.ui.paint_box.show()
  2162. self.ui.milling_box.setEnabled(True)
  2163. self.ui.milling_box.show()
  2164. if tool_target == _("NCC"):
  2165. self.ui.ncc_box.setEnabled(True)
  2166. self.ui.ncc_box.show()
  2167. self.ui.milling_box.setEnabled(True)
  2168. self.ui.milling_box.show()
  2169. def on_tool_add(self):
  2170. """
  2171. Add a tool in the DB Tool Table
  2172. :return: None
  2173. """
  2174. default_data = {}
  2175. default_data.update({
  2176. "plot": True,
  2177. "tool_target": _("General"),
  2178. "tol_min": 0.0,
  2179. "tol_max": 0.0,
  2180. # Milling
  2181. "cutz": float(self.app.defaults["geometry_cutz"]),
  2182. "multidepth": self.app.defaults["geometry_multidepth"],
  2183. "depthperpass": float(self.app.defaults["geometry_depthperpass"]),
  2184. "vtipdia": float(self.app.defaults["geometry_vtipdia"]),
  2185. "vtipangle": float(self.app.defaults["geometry_vtipangle"]),
  2186. "travelz": float(self.app.defaults["geometry_travelz"]),
  2187. "feedrate": float(self.app.defaults["geometry_feedrate"]),
  2188. "feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
  2189. "feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
  2190. "spindlespeed": self.app.defaults["geometry_spindlespeed"],
  2191. "dwell": self.app.defaults["geometry_dwell"],
  2192. "dwelltime": float(self.app.defaults["geometry_dwelltime"]),
  2193. "ppname_g": self.app.defaults["geometry_ppname_g"],
  2194. "extracut": self.app.defaults["geometry_extracut"],
  2195. "extracut_length": float(self.app.defaults["geometry_extracut_length"]),
  2196. "toolchange": self.app.defaults["geometry_toolchange"],
  2197. "toolchangexy": self.app.defaults["geometry_toolchangexy"],
  2198. "toolchangez": float(self.app.defaults["geometry_toolchangez"]),
  2199. "startz": self.app.defaults["geometry_startz"],
  2200. "endz": float(self.app.defaults["geometry_endz"]),
  2201. # NCC
  2202. "tools_nccoperation": self.app.defaults["tools_nccoperation"],
  2203. "tools_nccmilling_type": self.app.defaults["tools_nccmilling_type"],
  2204. "tools_nccoverlap": float(self.app.defaults["tools_nccoverlap"]),
  2205. "tools_nccmargin": float(self.app.defaults["tools_nccmargin"]),
  2206. "tools_nccmethod": self.app.defaults["tools_nccmethod"],
  2207. "tools_nccconnect": self.app.defaults["tools_nccconnect"],
  2208. "tools_ncccontour": self.app.defaults["tools_ncccontour"],
  2209. "tools_ncc_offset_choice": self.app.defaults["tools_ncc_offset_choice"],
  2210. "tools_ncc_offset_value": float(self.app.defaults["tools_ncc_offset_value"]),
  2211. # Paint
  2212. "tools_paintoverlap": float(self.app.defaults["tools_paintoverlap"]),
  2213. "tools_paintoffset": float(self.app.defaults["tools_paintoffset"]),
  2214. "tools_paintmethod": self.app.defaults["tools_paintmethod"],
  2215. "tools_pathconnect": self.app.defaults["tools_pathconnect"],
  2216. "tools_paintcontour": self.app.defaults["tools_paintcontour"],
  2217. # Isolation
  2218. "tools_iso_passes": int(self.app.defaults["tools_iso_passes"]),
  2219. "tools_iso_overlap": float(self.app.defaults["tools_iso_overlap"]),
  2220. "tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"],
  2221. "tools_iso_follow": self.app.defaults["tools_iso_follow"],
  2222. "tools_iso_isotype": self.app.defaults["tools_iso_isotype"],
  2223. # Drilling
  2224. "tools_drill_cutz": float(self.app.defaults["tools_drill_cutz"]),
  2225. "tools_drill_multidepth": self.app.defaults["tools_drill_multidepth"],
  2226. "tools_drill_depthperpass": float(self.app.defaults["tools_drill_depthperpass"]),
  2227. "tools_drill_travelz": float(self.app.defaults["tools_drill_travelz"]),
  2228. "tools_drill_feedrate_z": float(self.app.defaults["tools_drill_feedrate_z"]),
  2229. "tools_drill_feedrate_rapid": float(self.app.defaults["tools_drill_feedrate_rapid"]),
  2230. "tools_drill_spindlespeed": float(self.app.defaults["tools_drill_spindlespeed"]),
  2231. "tools_drill_dwell": self.app.defaults["tools_drill_dwell"],
  2232. "tools_drill_offset": float(self.app.defaults["tools_drill_offset"]),
  2233. "tools_drill_drill_slots": self.app.defaults["tools_drill_drill_slots"],
  2234. "tools_drill_drill_overlap": float(self.app.defaults["tools_drill_drill_overlap"]),
  2235. "tools_drill_last_drill": self.app.defaults["tools_drill_last_drill"],
  2236. })
  2237. temp = []
  2238. for k, v in self.db_tool_dict.items():
  2239. if "new_tool_" in v['name']:
  2240. temp.append(float(v['name'].rpartition('_')[2]))
  2241. if temp:
  2242. new_name = "new_tool_%d" % int(max(temp) + 1)
  2243. else:
  2244. new_name = "new_tool_1"
  2245. dict_elem = {}
  2246. dict_elem['name'] = new_name
  2247. if type(self.app.defaults["geometry_cnctooldia"]) == float:
  2248. dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
  2249. else:
  2250. try:
  2251. tools_string = self.app.defaults["geometry_cnctooldia"].split(",")
  2252. tools_diameters = [eval(a) for a in tools_string if a != '']
  2253. dict_elem['tooldia'] = tools_diameters[0] if tools_diameters else 0.0
  2254. except Exception as e:
  2255. self.app.log.debug("ToolDB.on_tool_add() --> %s" % str(e))
  2256. return
  2257. dict_elem['offset'] = 'Path'
  2258. dict_elem['offset_value'] = 0.0
  2259. dict_elem['type'] = 'Rough'
  2260. dict_elem['tool_type'] = 'C1'
  2261. dict_elem['data'] = default_data
  2262. new_toolid = len(self.db_tool_dict) + 1
  2263. self.db_tool_dict[str(new_toolid)] = deepcopy(dict_elem)
  2264. # add the new entry to the Tools DB table
  2265. self.update_storage()
  2266. self.build_db_ui()
  2267. # select the last Tree item just added
  2268. nr_items = self.ui.tree_widget.topLevelItemCount()
  2269. if nr_items:
  2270. last_item = self.ui.tree_widget.topLevelItem(nr_items - 1)
  2271. self.ui.tree_widget.setCurrentItem(last_item)
  2272. last_item.setSelected(True)
  2273. self.on_tool_target_changed(val=dict_elem['data']['tool_target'])
  2274. self.app.inform.emit('[success] %s' % _("Tool added to DB."))
  2275. def on_tool_copy(self):
  2276. """
  2277. Copy a selection of Tools in the Tools DB table
  2278. :return:
  2279. """
  2280. new_tool_id = len(self.db_tool_dict)
  2281. for item in self.ui.tree_widget.selectedItems():
  2282. old_tool_id = item.data(0, QtCore.Qt.DisplayRole)
  2283. for toolid, dict_val in list(self.db_tool_dict.items()):
  2284. if int(old_tool_id) == int(toolid):
  2285. new_tool_id += 1
  2286. new_key = str(new_tool_id)
  2287. self.db_tool_dict.update({
  2288. new_key: deepcopy(dict_val)
  2289. })
  2290. self.current_toolid = new_tool_id
  2291. self.update_storage()
  2292. self.build_db_ui()
  2293. # select the last Tree item just added
  2294. nr_items = self.ui.tree_widget.topLevelItemCount()
  2295. if nr_items:
  2296. last_item = self.ui.tree_widget.topLevelItem(nr_items - 1)
  2297. self.ui.tree_widget.setCurrentItem(last_item)
  2298. last_item.setSelected(True)
  2299. self.callback_app()
  2300. self.app.inform.emit('[success] %s' % _("Tool copied from Tools DB."))
  2301. def on_tool_delete(self):
  2302. """
  2303. Delete a selection of Tools in the Tools DB table
  2304. :return:
  2305. """
  2306. for item in self.ui.tree_widget.selectedItems():
  2307. toolname_to_remove = item.data(0, QtCore.Qt.DisplayRole)
  2308. for toolid, dict_val in list(self.db_tool_dict.items()):
  2309. if int(toolname_to_remove) == int(toolid):
  2310. # remove from the storage
  2311. self.db_tool_dict.pop(toolid, None)
  2312. self.current_toolid -= 1
  2313. self.update_storage()
  2314. self.build_db_ui()
  2315. # select the first Tree item
  2316. nr_items = self.ui.tree_widget.topLevelItemCount()
  2317. if nr_items:
  2318. first_item = self.ui.tree_widget.topLevelItem(0)
  2319. self.ui.tree_widget.setCurrentItem(first_item)
  2320. first_item.setSelected(True)
  2321. self.app.inform.emit('[success] %s' % _("Tool removed from Tools DB."))
  2322. def on_export_tools_db_file(self):
  2323. self.app.defaults.report_usage("on_export_tools_db_file")
  2324. self.app.log.debug("on_export_tools_db_file()")
  2325. date = str(datetime.today()).rpartition('.')[0]
  2326. date = ''.join(c for c in date if c not in ':-')
  2327. date = date.replace(' ', '_')
  2328. filter__ = "Text File (*.TXT);;All Files (*.*)"
  2329. filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Tools Database"),
  2330. directory='{l_save}/FlatCAM_{n}_{date}'.format(
  2331. l_save=str(self.app.get_last_save_folder()),
  2332. n=_("Tools_Database"),
  2333. date=date),
  2334. ext_filter=filter__)
  2335. filename = str(filename)
  2336. if filename == "":
  2337. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
  2338. return
  2339. else:
  2340. try:
  2341. f = open(filename, 'w')
  2342. f.close()
  2343. except PermissionError:
  2344. self.app.inform.emit('[WARNING] %s' %
  2345. _("Permission denied, saving not possible.\n"
  2346. "Most likely another app is holding the file open and not accessible."))
  2347. return
  2348. except IOError:
  2349. self.app.log.debug('Creating a new Tools DB file ...')
  2350. f = open(filename, 'w')
  2351. f.close()
  2352. except Exception:
  2353. e = sys.exc_info()[0]
  2354. self.app.log.error("Could not load Tools DB file.")
  2355. self.app.log.error(str(e))
  2356. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  2357. return
  2358. # Save update options
  2359. try:
  2360. # Save Tools DB in a file
  2361. try:
  2362. with open(filename, "w") as f:
  2363. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  2364. except Exception as e:
  2365. self.app.log.debug("App.on_save_tools_db() --> %s" % str(e))
  2366. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  2367. return
  2368. except Exception:
  2369. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  2370. return
  2371. self.app.inform.emit('[success] %s: %s' % (_("Exported Tools DB to"), filename))
  2372. def on_import_tools_db_file(self):
  2373. self.app.defaults.report_usage("on_import_tools_db_file")
  2374. self.app.log.debug("on_import_tools_db_file()")
  2375. filter__ = "Text File (*.TXT);;All Files (*.*)"
  2376. filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Tools DB"), filter=filter__)
  2377. if filename == "":
  2378. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
  2379. else:
  2380. try:
  2381. with open(filename) as f:
  2382. tools_in_db = f.read()
  2383. except IOError:
  2384. self.app.log.error("Could not load Tools DB file.")
  2385. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  2386. return
  2387. try:
  2388. self.db_tool_dict = json.loads(tools_in_db)
  2389. except Exception:
  2390. e = sys.exc_info()[0]
  2391. self.app.log.error(str(e))
  2392. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  2393. return
  2394. self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
  2395. self.build_db_ui()
  2396. self.update_storage()
  2397. def on_save_tools_db(self, silent=False):
  2398. self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
  2399. filename = self.app.data_path + "/tools_db.FlatDB"
  2400. # Preferences save, update the color of the Tools DB Tab text
  2401. for idx in range(self.app_ui.plot_tab_area.count()):
  2402. if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
  2403. self.app_ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
  2404. self.ui.save_db_btn.setStyleSheet("")
  2405. # clean the dictionary and leave only keys of interest
  2406. for tool_id in self.db_tool_dict.keys():
  2407. if self.db_tool_dict[tool_id]['data']['tool_target'] != _('General'):
  2408. continue
  2409. if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Milling'):
  2410. for k in list(self.db_tool_dict[tool_id]['data'].keys()):
  2411. if str(k).startswith('tools_'):
  2412. self.db_tool_dict[tool_id]['data'].pop(k, None)
  2413. if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Drilling'):
  2414. for k in list(self.db_tool_dict[tool_id]['data'].keys()):
  2415. if str(k).startswith('tools_'):
  2416. if str(k).startswith('tools_drill'):
  2417. pass
  2418. else:
  2419. self.db_tool_dict[tool_id]['data'].pop(k, None)
  2420. if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Isolation'):
  2421. for k in list(self.db_tool_dict[tool_id]['data'].keys()):
  2422. if str(k).startswith('tools_'):
  2423. if str(k).startswith('tools_iso'):
  2424. pass
  2425. else:
  2426. self.db_tool_dict[tool_id]['data'].pop(k, None)
  2427. if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Paint'):
  2428. for k in list(self.db_tool_dict[tool_id]['data'].keys()):
  2429. if str(k).startswith('tools_'):
  2430. if str(k).startswith('tools_paint'):
  2431. pass
  2432. else:
  2433. self.db_tool_dict[tool_id]['data'].pop(k, None)
  2434. if self.db_tool_dict[tool_id]['data']['tool_target'] == _('NCC'):
  2435. for k in list(self.db_tool_dict[tool_id]['data'].keys()):
  2436. if str(k).startswith('tools_'):
  2437. if str(k).startswith('tools_ncc'):
  2438. pass
  2439. else:
  2440. self.db_tool_dict[tool_id]['data'].pop(k, None)
  2441. # Save Tools DB in a file
  2442. try:
  2443. f = open(filename, "w")
  2444. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  2445. f.close()
  2446. except Exception as e:
  2447. self.app.log.debug("ToolsDB.on_save_tools_db() --> %s" % str(e))
  2448. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  2449. return
  2450. if not silent:
  2451. self.app.inform.emit('[success] %s' % _("Saved Tools DB."))
  2452. def on_save_db_btn_click(self):
  2453. self.app.tools_db_changed_flag = False
  2454. self.on_save_tools_db()
  2455. def on_calculate_tooldia(self):
  2456. if self.ui.shape_combo.get_value() == 'V':
  2457. tip_dia = float(self.ui.vdia_entry.get_value())
  2458. half_tip_angle = float(self.ui.vangle_entry.get_value()) / 2.0
  2459. cut_z = float(self.ui.cutz_entry.get_value())
  2460. cut_z = -cut_z if cut_z < 0 else cut_z
  2461. # calculated tool diameter so the cut_z parameter is obeyed
  2462. tool_dia = tip_dia + (2 * cut_z * math.tan(math.radians(half_tip_angle)))
  2463. self.ui.dia_entry.set_value(tool_dia)
  2464. def ui_connect(self):
  2465. # make sure that we don't make multiple connections to the widgets
  2466. self.ui_disconnect()
  2467. self.ui.name_entry.editingFinished.connect(self.update_tree_name)
  2468. for key in self.form_fields:
  2469. wdg = self.form_fields[key]
  2470. # FCEntry
  2471. if isinstance(wdg, FCEntry):
  2472. wdg.textChanged.connect(self.update_storage)
  2473. # ComboBox
  2474. if isinstance(wdg, FCComboBox):
  2475. wdg.currentIndexChanged.connect(self.update_storage)
  2476. # CheckBox
  2477. if isinstance(wdg, FCCheckBox):
  2478. wdg.toggled.connect(self.update_storage)
  2479. # FCRadio
  2480. if isinstance(wdg, RadioSet):
  2481. wdg.activated_custom.connect(self.update_storage)
  2482. # SpinBox, DoubleSpinBox
  2483. if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
  2484. wdg.valueChanged.connect(self.update_storage)
  2485. # connect the calculate tooldia method to the controls
  2486. # if the tool shape is 'V' the tool dia will be calculated to obey Cut Z parameter
  2487. self.ui.shape_combo.currentIndexChanged.connect(self.on_calculate_tooldia)
  2488. self.ui.cutz_entry.valueChanged.connect(self.on_calculate_tooldia)
  2489. self.ui.vdia_entry.valueChanged.connect(self.on_calculate_tooldia)
  2490. self.ui.vangle_entry.valueChanged.connect(self.on_calculate_tooldia)
  2491. def ui_disconnect(self):
  2492. try:
  2493. self.ui.name_entry.editingFinished.disconnect(self.update_tree_name)
  2494. except (TypeError, AttributeError):
  2495. pass
  2496. try:
  2497. self.ui.shape_combo.currentIndexChanged.disconnect(self.on_calculate_tooldia)
  2498. except (TypeError, AttributeError):
  2499. pass
  2500. try:
  2501. self.ui.cutz_entry.valueChanged.disconnect(self.on_calculate_tooldia)
  2502. except (TypeError, AttributeError):
  2503. pass
  2504. try:
  2505. self.ui.vdia_entry.valueChanged.disconnect(self.on_calculate_tooldia)
  2506. except (TypeError, AttributeError):
  2507. pass
  2508. try:
  2509. self.ui.vangle_entry.valueChanged.disconnect(self.on_calculate_tooldia)
  2510. except (TypeError, AttributeError):
  2511. pass
  2512. for key in self.form_fields:
  2513. wdg = self.form_fields[key]
  2514. # FCEntry
  2515. if isinstance(wdg, FCEntry):
  2516. try:
  2517. wdg.textChanged.disconnect(self.update_storage)
  2518. except (TypeError, AttributeError):
  2519. pass
  2520. # ComboBox
  2521. if isinstance(wdg, FCComboBox):
  2522. try:
  2523. wdg.currentIndexChanged.disconnect(self.update_storage)
  2524. except (TypeError, AttributeError):
  2525. pass
  2526. # CheckBox
  2527. if isinstance(wdg, FCCheckBox):
  2528. try:
  2529. wdg.toggled.disconnect(self.update_storage)
  2530. except (TypeError, AttributeError):
  2531. pass
  2532. # FCRadio
  2533. if isinstance(wdg, RadioSet):
  2534. try:
  2535. wdg.activated_custom.disconnect(self.update_storage)
  2536. except (TypeError, AttributeError):
  2537. pass
  2538. # SpinBox, DoubleSpinBox
  2539. if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
  2540. try:
  2541. wdg.valueChanged.disconnect(self.update_storage)
  2542. except (TypeError, AttributeError):
  2543. pass
  2544. def update_tree_name(self):
  2545. val = self.ui.name_entry.get_value()
  2546. item = self.ui.tree_widget.currentItem()
  2547. if item is None:
  2548. return
  2549. # I'm setting the value for the second column (designated by 1) because first column holds the ID
  2550. # and second column holds the Name (this behavior is set in the build_ui method)
  2551. item.setData(1, QtCore.Qt.DisplayRole, val)
  2552. def update_storage(self):
  2553. """
  2554. Update the dictionary that is the storage of the tools 'database'
  2555. :return:
  2556. """
  2557. tool_id = str(self.current_toolid)
  2558. try:
  2559. wdg = self.sender()
  2560. assert isinstance(wdg, QtWidgets.QWidget) or isinstance(wdg, QtWidgets.QAction), \
  2561. "Expected a QWidget got %s" % type(wdg)
  2562. if wdg is None:
  2563. return
  2564. wdg_name = wdg.objectName()
  2565. val = wdg.get_value()
  2566. except AttributeError:
  2567. return
  2568. # #############################################################################################################
  2569. # this might change in the future; it makes sense to change values at once for all tools
  2570. # for now change values only for one tool at once
  2571. sel_rows = []
  2572. for item in self.ui.tree_widget.selectedItems():
  2573. sel_rows.append(item.data(0, QtCore.Qt.DisplayRole))
  2574. len_sel_rows = len(sel_rows)
  2575. if len_sel_rows > 1:
  2576. msg = '[ERROR_NOTCL] %s: %s' % \
  2577. (_("To change tool properties select only one tool. Tools currently selected"), str(len_sel_rows))
  2578. self.app.inform.emit(msg)
  2579. old_value = self.db_tool_dict[tool_id]['data'][self.name2option[wdg_name]]
  2580. wdg.set_value(old_value)
  2581. wdg.clearFocus()
  2582. return
  2583. # #############################################################################################################
  2584. if wdg_name == "gdb_name":
  2585. self.db_tool_dict[tool_id]['name'] = val
  2586. elif wdg_name == "gdb_dia":
  2587. self.db_tool_dict[tool_id]['tooldia'] = val
  2588. elif wdg_name == "gdb_tool_offset":
  2589. self.db_tool_dict[tool_id]['offset'] = val
  2590. elif wdg_name == "gdb_custom_offset":
  2591. self.db_tool_dict[tool_id]['offset_value'] = val
  2592. elif wdg_name == "gdb_type":
  2593. self.db_tool_dict[tool_id]['type'] = val
  2594. elif wdg_name == "gdb_shape":
  2595. self.db_tool_dict[tool_id]['tool_type'] = val
  2596. else:
  2597. # Milling Tool
  2598. if wdg_name == "gdb_tool_target":
  2599. self.db_tool_dict[tool_id]['data']['tool_target'] = val
  2600. elif wdg_name == "gdb_tol_min":
  2601. self.db_tool_dict[tool_id]['data']['tol_min'] = val
  2602. elif wdg_name == "gdb_tol_max":
  2603. self.db_tool_dict[tool_id]['data']['tol_max'] = val
  2604. elif wdg_name == "gdb_cutz":
  2605. self.db_tool_dict[tool_id]['data']['cutz'] = val
  2606. elif wdg_name == "gdb_multidepth":
  2607. self.db_tool_dict[tool_id]['data']['multidepth'] = val
  2608. elif wdg_name == "gdb_multidepth_entry":
  2609. self.db_tool_dict[tool_id]['data']['depthperpass'] = val
  2610. elif wdg_name == "gdb_travelz":
  2611. self.db_tool_dict[tool_id]['data']['travelz'] = val
  2612. elif wdg_name == "gdb_frxy":
  2613. self.db_tool_dict[tool_id]['data']['feedrate'] = val
  2614. elif wdg_name == "gdb_frz":
  2615. self.db_tool_dict[tool_id]['data']['feedrate_z'] = val
  2616. elif wdg_name == "gdb_spindle":
  2617. self.db_tool_dict[tool_id]['data']['spindlespeed'] = val
  2618. elif wdg_name == "gdb_dwell":
  2619. self.db_tool_dict[tool_id]['data']['dwell'] = val
  2620. elif wdg_name == "gdb_dwelltime":
  2621. self.db_tool_dict[tool_id]['data']['dwelltime'] = val
  2622. elif wdg_name == "gdb_vdia":
  2623. self.db_tool_dict[tool_id]['data']['vtipdia'] = val
  2624. elif wdg_name == "gdb_vangle":
  2625. self.db_tool_dict[tool_id]['data']['vtipangle'] = val
  2626. elif wdg_name == "gdb_frapids":
  2627. self.db_tool_dict[tool_id]['data']['feedrate_rapid'] = val
  2628. elif wdg_name == "gdb_ecut":
  2629. self.db_tool_dict[tool_id]['data']['extracut'] = val
  2630. elif wdg_name == "gdb_ecut_length":
  2631. self.db_tool_dict[tool_id]['data']['extracut_length'] = val
  2632. # NCC Tool
  2633. elif wdg_name == "gdb_n_operation":
  2634. self.db_tool_dict[tool_id]['data']['tools_nccoperation'] = val
  2635. elif wdg_name == "gdb_n_overlap":
  2636. self.db_tool_dict[tool_id]['data']['tools_nccoverlap'] = val
  2637. elif wdg_name == "gdb_n_margin":
  2638. self.db_tool_dict[tool_id]['data']['tools_nccmargin'] = val
  2639. elif wdg_name == "gdb_n_method":
  2640. self.db_tool_dict[tool_id]['data']['tools_nccmethod'] = val
  2641. elif wdg_name == "gdb_n_connect":
  2642. self.db_tool_dict[tool_id]['data']['tools_nccconnect'] = val
  2643. elif wdg_name == "gdb_n_contour":
  2644. self.db_tool_dict[tool_id]['data']['tools_ncccontour'] = val
  2645. elif wdg_name == "gdb_n_offset":
  2646. self.db_tool_dict[tool_id]['data']['tools_ncc_offset_choice'] = val
  2647. elif wdg_name == "gdb_n_offset_value":
  2648. self.db_tool_dict[tool_id]['data']['tools_ncc_offset_value'] = val
  2649. elif wdg_name == "gdb_n_milling_type":
  2650. self.db_tool_dict[tool_id]['data']['tools_nccmilling_type'] = val
  2651. # Paint Tool
  2652. elif wdg_name == "gdb_p_overlap":
  2653. self.db_tool_dict[tool_id]['data']['tools_paintoverlap'] = val
  2654. elif wdg_name == "gdb_p_offset":
  2655. self.db_tool_dict[tool_id]['data']['tools_paintoffset'] = val
  2656. elif wdg_name == "gdb_p_method":
  2657. self.db_tool_dict[tool_id]['data']['tools_paintmethod'] = val
  2658. elif wdg_name == "gdb_p_connect":
  2659. self.db_tool_dict[tool_id]['data']['tools_pathconnect'] = val
  2660. elif wdg_name == "gdb_p_contour":
  2661. self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val
  2662. # Isolation Tool
  2663. elif wdg_name == "gdb_i_passes":
  2664. self.db_tool_dict[tool_id]['data']['tools_iso_passes'] = val
  2665. elif wdg_name == "gdb_i_overlap":
  2666. self.db_tool_dict[tool_id]['data']['tools_iso_overlap'] = val
  2667. elif wdg_name == "gdb_i_milling_type":
  2668. self.db_tool_dict[tool_id]['data']['tools_iso_milling_type'] = val
  2669. elif wdg_name == "gdb_i_follow":
  2670. self.db_tool_dict[tool_id]['data']['tools_iso_follow'] = val
  2671. elif wdg_name == "gdb_i_iso_type":
  2672. self.db_tool_dict[tool_id]['data']['tools_iso_isotype'] = val
  2673. # Drilling Tool
  2674. elif wdg_name == "gdb_e_cutz":
  2675. self.db_tool_dict[tool_id]['data']['tools_drill_cutz'] = val
  2676. elif wdg_name == "gdb_e_multidepth":
  2677. self.db_tool_dict[tool_id]['data']['tools_drill_multidepth'] = val
  2678. elif wdg_name == "gdb_e_depthperpass":
  2679. self.db_tool_dict[tool_id]['data']['tools_drill_depthperpass'] = val
  2680. elif wdg_name == "gdb_e_travelz":
  2681. self.db_tool_dict[tool_id]['data']['tools_drill_travelz'] = val
  2682. elif wdg_name == "gdb_e_feedratez":
  2683. self.db_tool_dict[tool_id]['data']['tools_drill_feedrate_z'] = val
  2684. elif wdg_name == "gdb_e_fr_rapid":
  2685. self.db_tool_dict[tool_id]['data']['tools_drill_feedrate_rapid'] = val
  2686. elif wdg_name == "gdb_e_spindlespeed":
  2687. self.db_tool_dict[tool_id]['data']['tools_drill_spindlespeed'] = val
  2688. elif wdg_name == "gdb_e_dwell":
  2689. self.db_tool_dict[tool_id]['data']['tools_drill_dwell'] = val
  2690. elif wdg_name == "gdb_e_dwelltime":
  2691. self.db_tool_dict[tool_id]['data']['tools_drill_dwelltime'] = val
  2692. elif wdg_name == "gdb_e_offset":
  2693. self.db_tool_dict[tool_id]['data']['tools_drill_offset'] = val
  2694. elif wdg_name == "gdb_e_drill_slots":
  2695. self.db_tool_dict[tool_id]['data']['tools_drill_drill_slots'] = val
  2696. elif wdg_name == "gdb_e_drill_slots_over":
  2697. self.db_tool_dict[tool_id]['data']['tools_drill_drill_overlap'] = val
  2698. elif wdg_name == "gdb_e_drill_last_drill":
  2699. self.db_tool_dict[tool_id]['data']['tools_drill_last_drill'] = val
  2700. self.callback_app()
  2701. def on_tool_requested_from_app(self):
  2702. if not self.ui.tree_widget.selectedItems():
  2703. self.app.inform.emit('[WARNING_NOTCL] %s...' % _("No Tool/row selected in the Tools Database table"))
  2704. return
  2705. for item in self.ui.tree_widget.selectedItems():
  2706. tool_uid = item.data(0, QtCore.Qt.DisplayRole)
  2707. for key in self.db_tool_dict.keys():
  2708. if str(key) == str(tool_uid):
  2709. selected_tool = self.db_tool_dict[key]
  2710. self.on_tool_request(tool=selected_tool)
  2711. def on_cancel_tool(self):
  2712. for idx in range(self.app_ui.plot_tab_area.count()):
  2713. if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
  2714. wdg = self.app_ui.plot_tab_area.widget(idx)
  2715. wdg.deleteLater()
  2716. self.app_ui.plot_tab_area.removeTab(idx)
  2717. self.app.inform.emit('%s' % _("Cancelled adding tool from DB."))
  2718. # def resize_new_tool_table_widget(self, min_size, max_size):
  2719. # """
  2720. # Resize the table widget responsible for adding new tool in the Tool Database
  2721. #
  2722. # :param min_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  2723. # :param max_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  2724. # :return:
  2725. # """
  2726. # t_height = self.t_height
  2727. # if max_size > min_size:
  2728. # t_height = self.t_height + self.new_tool_table_widget.verticalScrollBar().height()
  2729. #
  2730. # self.new_tool_table_widget.setMaximumHeight(t_height)
  2731. def closeEvent(self, QCloseEvent):
  2732. super().closeEvent(QCloseEvent)