diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 659cc6c5..2c6101e8 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -172,10 +172,18 @@ class App(QtCore.QObject): self.load_defaults() chars = 'abcdefghijklmnopqrstuvwxyz0123456789' - if self.defaults['serial'] == 0: + if self.defaults['serial'] == 0 or len(str(self.defaults['serial'])) < 10: self.defaults['serial'] = ''.join([random.choice(chars) for i in range(20)]) self.save_defaults() + def auto_save_defaults(): + try: + self.save_defaults() + finally: + QtCore.QTimer.singleShot(20000, auto_save_defaults) + + QtCore.QTimer.singleShot(20000, auto_save_defaults) + self.options_form = GlobalOptionsUI() self.options_form_fields = { "units": self.options_form.units_radio, @@ -400,6 +408,20 @@ class App(QtCore.QObject): # Send to worker self.worker_task.emit({'fcn': worker_task, 'params': [self]}) + def report_usage(self, resource): + """ + Increments usage counter for the given resource + in self.defaults['stats']. + + :param resource: Name of the resource. + :return: None + """ + + if resource in self.defaults['stats']: + self.defaults['stats'][resource] += 1 + else: + self.defaults['stats'][resource] = 1 + def exec_command(self, text): """ Hadles input from the shell. See FlatCAMApp.setup_shell for shell commands. @@ -407,6 +429,8 @@ class App(QtCore.QObject): :param text: Input command :return: None """ + self.report_usage('exec_command') + text = str(text) try: @@ -606,6 +630,12 @@ class App(QtCore.QObject): self.log.error("options_write_form(): No field for: %s" % option) def on_about(self): + """ + Displays the "about" dialog. + + :return: None + """ + self.report_usage("on_about") class AboutDialog(QtGui.QDialog): def __init__(self, parent=None): @@ -655,6 +685,15 @@ class App(QtCore.QObject): self.save_defaults() def save_defaults(self): + """ + Saves application default options + ``self.defaults`` to defaults.json. + + :return: None + """ + + self.report_usage("save_defaults") + # Read options from file try: f = open("defaults.json") @@ -697,6 +736,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_options_app2project") + self.defaults_read_form() self.options.update(self.defaults) self.options_write_form() @@ -709,6 +750,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_options_project2app") + self.options_read_form() self.defaults.update(self.options) self.defaults_write_form() @@ -721,6 +764,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_options_project2object") + self.options_read_form() obj = self.collection.get_active() if obj is None: @@ -740,6 +785,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_options_object2project") + obj = self.collection.get_active() if obj is None: self.inform.emit("WARNING: No object selected.") @@ -758,6 +805,9 @@ class App(QtCore.QObject): :return: None """ + + self.report_usage("on_options_object2app") + obj = self.collection.get_active() if obj is None: self.inform.emit("WARNING: No object selected.") @@ -777,6 +827,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_options_app2object") + self.defaults_read_form() obj = self.collection.get_active() if obj is None: @@ -798,6 +850,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_toggle_units") + if self.toggle_units_ignore: return @@ -904,6 +958,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_delete") + # Keep this for later try: name = copy(self.collection.get_active().options["name"]) @@ -924,6 +980,12 @@ class App(QtCore.QObject): self.inform.emit("Object deleted: %s" % name) def on_plots_updated(self): + """ + Callback used to report when the plots have changed. + Adjust axes and zooms to fit. + + :return: None + """ self.plotcanvas.auto_adjust_axes() self.on_zoom_fit(None) @@ -933,6 +995,8 @@ class App(QtCore.QObject): :return: None """ + + self.report_usage("on_toolbar_replot") self.log.debug("on_toolbar_replot()") try: @@ -947,6 +1011,12 @@ class App(QtCore.QObject): self.ui.notebook.setCurrentWidget(self.ui.selected_tab) def on_object_created(self, obj): + """ + Event callback for object creation. + + :param obj: The newly created FlatCAM object. + :return: None + """ self.log.debug("on_object_created()") self.inform.emit("Object (%s) created: %s" % (obj.kind, obj.options['name'])) self.collection.append(obj) @@ -962,6 +1032,7 @@ class App(QtCore.QObject): :param event: Ignored. :return: None """ + xmin, ymin, xmax, ymax = self.collection.get_bounds() width = xmax - xmin height = ymax - ymin @@ -1062,6 +1133,9 @@ class App(QtCore.QObject): :return: None """ + + self.report_usage("on_file_new") + # Remove everything from memory App.log.debug("on_file_new()") @@ -1077,18 +1151,16 @@ class App(QtCore.QObject): # Re-fresh project options self.on_options_app2project() - def on_options_app2project(self): + def on_fileopengerber(self): """ - Callback for Options->Transfer Options->App=>Project. Copies options - from application defaults to project defaults. + File menu callback for opening a Gerber. :return: None """ - self.options.update(self.defaults) - - def on_fileopengerber(self): + self.report_usage("on_fileopengerber") App.log.debug("on_fileopengerber()") + try: filename = QtGui.QFileDialog.getOpenFileName(caption="Open Gerber", directory=self.last_folder) @@ -1107,7 +1179,15 @@ class App(QtCore.QObject): 'params': [filename]}) def on_fileopenexcellon(self): + """ + File menu callback for opening an Excellon file. + + :return: None + """ + + self.report_usage("on_fileopenexcellon") App.log.debug("on_fileopenexcellon()") + try: filename = QtGui.QFileDialog.getOpenFileName(caption="Open Excellon", directory=self.last_folder) @@ -1126,6 +1206,13 @@ class App(QtCore.QObject): 'params': [filename]}) def on_fileopengcode(self): + """ + File menu call back for opening gcode. + + :return: None + """ + + self.report_usage("on_fileopengcode") App.log.debug("on_fileopengcode()") try: @@ -1146,6 +1233,13 @@ class App(QtCore.QObject): 'params': [filename]}) def on_file_openproject(self): + """ + File menu callback for opening a project. + + :return: None + """ + + self.report_usage("on_file_openproject") App.log.debug("on_file_openproject()") try: @@ -1174,6 +1268,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_file_saveproject") + if self.project_filename is None: self.on_file_saveprojectas() else: @@ -1190,6 +1286,8 @@ class App(QtCore.QObject): :return: None """ + self.report_usage("on_file_saveprojectas") + try: filename = QtGui.QFileDialog.getSaveFileName(caption="Save Project As ...", directory=self.last_folder) @@ -1472,7 +1570,7 @@ class App(QtCore.QObject): def setup_shell(self): """ - Creates shell functions. + Creates shell functions. Runs once at startup. :return: None """ @@ -1994,13 +2092,16 @@ class App(QtCore.QObject): """ Checks for the latest version of the program. Alerts the user if theirs is outdated. This method is meant to be run - in a saeparate thread. + in a separate thread. :return: None """ self.log.debug("version_check()") - full_url = App.version_url + "?s=" + str(self.defaults['serial']) + "&v=" + str(self.version) + full_url = App.version_url + \ + "?s=" + str(self.defaults['serial']) + \ + "&v=" + str(self.version) + \ + "&" + urllib.urlencode(self.defaults["stats"]) App.log.debug("Checking for updates @ %s" % full_url) try: diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 6acefcd6..533a28cc 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -71,12 +71,15 @@ class FlatCAMObj(QtCore.QObject): self.app.info("Name changed from %s to %s" % (old_name, new_name)) def on_offset_button_click(self): + self.app.report_usage("obj_on_offset_button") + self.read_form() vect = self.ui.offsetvector_entry.get_value() self.offset(vect) self.plot() def on_scale_button_click(self): + self.app.report_usage("obj_on_scale_button") self.read_form() factor = self.ui.scale_entry.get_value() self.scale(factor) @@ -317,6 +320,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click) def on_generatenoncopper_button_click(self, *args): + self.app.report_usage("gerber_on_generatenoncopper_button") + self.read_form() name = self.options["name"] + "_noncopper" @@ -332,6 +337,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.app.new_object("geometry", name, geo_init) def on_generatebb_button_click(self, *args): + self.app.report_usage("gerber_on_generatebb_button") self.read_form() name = self.options["name"] + "_bbox" @@ -346,6 +352,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.app.new_object("geometry", name, geo_init) def on_generatecutout_button_click(self, *args): + self.app.report_usage("gerber_on_generatecutout_button") self.read_form() name = self.options["name"] + "_cutout" @@ -387,6 +394,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.app.new_object("geometry", name, geo_init) def on_iso_button_click(self, *args): + self.app.report_usage("gerber_on_iso_button") self.read_form() self.isolate() @@ -615,6 +623,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click) def on_create_cncjob_button_click(self, *args): + self.app.report_usage("excellon_on_create_cncjob_button") self.read_form() # Get the tools from the list @@ -796,6 +805,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.plot() def on_exportgcode_button_click(self, *args): + self.app.report_usage("cncjob_on_exportgcode_button") try: filename = QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...", @@ -895,6 +905,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.ui.generate_paint_button.clicked.connect(self.on_paint_button_click) def on_paint_button_click(self, *args): + self.app.report_usage("geometry_on_paint_button") + self.app.info("Click inside the desired polygon.") self.read_form() tooldia = self.options["painttooldia"] @@ -926,6 +938,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.app.new_object("geometry", name, gen_paintarea) def on_generatecnc_button_click(self, *args): + self.app.report_usage("geometry_on_generatecnc_button") self.read_form() self.generatecncjob() diff --git a/defaults.json b/defaults.json index 378dacf0..9e26dfee 100644 --- a/defaults.json +++ b/defaults.json @@ -1 +1 @@ -{"gerber_noncopperrounded": false, "geometry_paintoverlap": 0.15, "cncjob_append": "", "excellon_feedrate": 3.0, "gerber_plot": true, "geometry_plot": true, "excellon_drillz": -0.1, "geometry_feedrate": 3.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "gerber_isopasses": 1, "excellon_plot": true, "gerber_isotooldia": 0.016, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.1, "excellon_solid": true, "geometry_paintmargin": 0.0, "geometry_cutz": -0.002, "geometry_cnctooldia": 0.016, "gerber_cutouttooldia": 0.07, "gerber_gaps": "4", "gerber_bboxmargin": 0.0, "cncjob_plot": true, "gerber_cutoutgapsize": 0.15, "gerber_isooverlap": 0.15, "gerber_bboxrounded": false, "gerber_noncoppermargin": 0.0, "geometry_painttooldia": 0.07} \ No newline at end of file +{} \ No newline at end of file diff --git a/recent.json b/recent.json index 5b633ad4..0637a088 100644 --- a/recent.json +++ b/recent.json @@ -1 +1 @@ -[{"kind": "gerber", "filename": "tests/CBS-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/maitest.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\maitest.gtl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/CBS-F_Cu.gtl"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/CBS.drl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Gerbers/AVR_Transistor_Tester_copper_top.GTL"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/LockController_v1.0_pcb-RoundHoles.TXT/LockController_v1.0_pcb-RoundHoles.TXT"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Gerbers/AVR_Transistor_Tester.DRL"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/FlatCam_Drilling_Test/FlatCam_Drilling_Test.drl"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Excellon_Planck/X-Y CONTROLLER - Drill Data - Through Hole.drl"}] \ No newline at end of file +[] \ No newline at end of file