Просмотр исходного кода

- fixed the Tcl Command Help to work as expected; made the text of the commands to be colored in Red color and bold
- added a 'Close' menu entry in the Tcl Shell context menu that will close (hide) the Tcl Shell Dock widget
- on launching the Tcl Shell the Edit line will take focus immediately
- in App.on_mouse_move_over_plot() method no longer will be done a setFocus() on every move, only when it is needed

Marius Stanciu 5 лет назад
Родитель
Сommit
3735753a93
7 измененных файлов с 105 добавлено и 64 удалено
  1. 7 0
      CHANGELOG.md
  2. 52 34
      FlatCAMApp.py
  3. 1 1
      flatcamGUI/FlatCAMGUI.py
  4. 8 1
      flatcamGUI/GUIElements.py
  5. 22 10
      flatcamTools/ToolShell.py
  6. 14 16
      tclCommands/TclCommandHelp.py
  7. 1 2
      tclCommands/__init__.py

+ 7 - 0
CHANGELOG.md

@@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
+23.04.2020 
+
+- fixed the Tcl Command Help to work as expected; made the text of the commands to be colored in Red color and bold
+- added a 'Close' menu entry in the Tcl Shell context menu that will close (hide) the Tcl Shell Dock widget
+- on launching the Tcl Shell the Edit line will take focus immediately 
+- in App.on_mouse_move_over_plot() method no longer will be done a setFocus() on every move, only when it is needed
+
 22.04.2020 
 22.04.2020 
 
 
 - added a new feature, project auto-saving controlled from Edit -> Preferences -> General -> APP. Preferences -> Enable Auto Save checkbox
 - added a new feature, project auto-saving controlled from Edit -> Preferences -> General -> APP. Preferences -> Enable Auto Save checkbox

+ 52 - 34
FlatCAMApp.py

@@ -2105,7 +2105,7 @@ class App(QtCore.QObject):
         self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis)
         self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis)
         self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_toggle)
         self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_toggle)
 
 
-        self.ui.menutoolshell.triggered.connect(self.on_toggle_shell)
+        self.ui.menutoolshell.triggered.connect(self.toggle_shell)
 
 
         self.ui.menuhelp_about.triggered.connect(self.on_about)
         self.ui.menuhelp_about.triggered.connect(self.on_about)
         self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.manual_url))
         self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.manual_url))
@@ -3326,7 +3326,7 @@ class App(QtCore.QObject):
         # re-add the TCL Shell action to the Tools menu and reconnect it to ist slot function
         # re-add the TCL Shell action to the Tools menu and reconnect it to ist slot function
         self.ui.menutoolshell = self.ui.menutool.addAction(QtGui.QIcon(self.resource_location + '/shell16.png'),
         self.ui.menutoolshell = self.ui.menutool.addAction(QtGui.QIcon(self.resource_location + '/shell16.png'),
                                                            '&Command Line\tS')
                                                            '&Command Line\tS')
-        self.ui.menutoolshell.triggered.connect(self.on_toggle_shell)
+        self.ui.menutoolshell.triggered.connect(self.toggle_shell)
 
 
         # third install all of them
         # third install all of them
         try:
         try:
@@ -3377,7 +3377,7 @@ class App(QtCore.QObject):
         self.ui.jmp_btn.triggered.connect(self.on_jump_to)
         self.ui.jmp_btn.triggered.connect(self.on_jump_to)
         self.ui.locate_btn.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active()))
         self.ui.locate_btn.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active()))
 
 
-        self.ui.shell_btn.triggered.connect(self.on_toggle_shell)
+        self.ui.shell_btn.triggered.connect(self.toggle_shell)
         self.ui.new_script_btn.triggered.connect(self.on_filenewscript)
         self.ui.new_script_btn.triggered.connect(self.on_filenewscript)
         self.ui.open_script_btn.triggered.connect(self.on_fileopenscript)
         self.ui.open_script_btn.triggered.connect(self.on_fileopenscript)
         self.ui.run_script_btn.triggered.connect(self.on_filerunscript)
         self.ui.run_script_btn.triggered.connect(self.on_filerunscript)
@@ -5173,7 +5173,7 @@ class App(QtCore.QObject):
         # When the main event loop is not started yet in which case the qApp.quit() will do nothing
         # When the main event loop is not started yet in which case the qApp.quit() will do nothing
         # we use the following command
         # we use the following command
         # sys.exit(0)
         # sys.exit(0)
-        os._exit(0) # fix to work with Python 3.8
+        os._exit(0)     # fix to work with Python 3.8
 
 
     def kill_app(self):
     def kill_app(self):
         # QtCore.QCoreApplication.quit()
         # QtCore.QCoreApplication.quit()
@@ -5274,34 +5274,6 @@ class App(QtCore.QObject):
         with open(config_file, 'w') as f:
         with open(config_file, 'w') as f:
             f.writelines(data)
             f.writelines(data)
 
 
-    def on_toggle_shell(self):
-        """
-        Toggle shell: if is visible close it, if it is closed then open it
-        :return: None
-        """
-
-        self.report_usage("on_toggle_shell()")
-
-        if self.ui.shell_dock.isVisible():
-            self.ui.shell_dock.hide()
-        else:
-            self.ui.shell_dock.show()
-
-    def on_toggle_shell_from_settings(self, state):
-        """
-        Toggle shell: if is visible close it, if it is closed then open it
-        :return: None
-        """
-
-        self.report_usage("on_toggle_shell_from_settings()")
-
-        if state is True:
-            if not self.ui.shell_dock.isVisible():
-                self.ui.shell_dock.show()
-        else:
-            if self.ui.shell_dock.isVisible():
-                self.ui.shell_dock.hide()
-
     def on_register_files(self, obj_type=None):
     def on_register_files(self, obj_type=None):
         """
         """
         Called whenever there is a need to register file extensions with FlatCAM.
         Called whenever there is a need to register file extensions with FlatCAM.
@@ -8601,8 +8573,11 @@ class App(QtCore.QObject):
                 pan_button = 2
                 pan_button = 2
             self.event_is_dragging = self.plotcanvas.is_dragging
             self.event_is_dragging = self.plotcanvas.is_dragging
 
 
-        # So it can receive key presses
-        self.plotcanvas.native.setFocus()
+        # So it can receive key presses but not when the Tcl Shell is active
+        if not self.ui.shell_dock.isVisible():
+            if not self.plotcanvas.native.hasFocus():
+                self.plotcanvas.native.setFocus()
+
         self.pos_jump = event_pos
         self.pos_jump = event_pos
 
 
         self.ui.popMenu.mouse_is_panning = False
         self.ui.popMenu.mouse_is_panning = False
@@ -12813,6 +12788,49 @@ class App(QtCore.QObject):
         """
         """
         pass
         pass
 
 
+    def toggle_shell(self):
+        """
+        Toggle shell: if is visible close it, if it is closed then open it
+        :return: None
+        """
+
+        self.report_usage("toggle_shell()")
+
+        if self.ui.shell_dock.isVisible():
+            self.ui.shell_dock.hide()
+            self.plotcanvas.native.setFocus()
+        else:
+            self.ui.shell_dock.show()
+
+            # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run
+            # self.shell._edit.setFocus()
+            QtCore.QTimer.singleShot(0, lambda: self.ui.shell_dock.widget()._edit.setFocus())
+
+            # HACK - simulate a mouse click - alternative
+            # no_km = QtCore.Qt.KeyboardModifier(QtCore.Qt.NoModifier)    # no KB modifier
+            # pos = QtCore.QPoint((self.shell._edit.width() - 40), (self.shell._edit.height() - 2))
+            # e = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
+            #                       no_km)
+            # QtWidgets.qApp.sendEvent(self.shell._edit, e)
+            # f = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
+            #                       no_km)
+            # QtWidgets.qApp.sendEvent(self.shell._edit, f)
+
+    def on_toggle_shell_from_settings(self, state):
+        """
+        Toggle shell: if is visible close it, if it is closed then open it
+        :return: None
+        """
+
+        self.report_usage("on_toggle_shell_from_settings()")
+
+        if state is True:
+            if not self.ui.shell_dock.isVisible():
+                self.ui.shell_dock.show()
+        else:
+            if self.ui.shell_dock.isVisible():
+                self.ui.shell_dock.hide()
+
     def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False):
     def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False):
         """
         """
         Shows a message on the FlatCAM Shell
         Shows a message on the FlatCAM Shell

+ 1 - 1
flatcamGUI/FlatCAMGUI.py

@@ -3247,7 +3247,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
 
                 # Shell toggle
                 # Shell toggle
                 if key == QtCore.Qt.Key_S:
                 if key == QtCore.Qt.Key_S:
-                    self.app.on_toggle_shell()
+                    self.app.toggle_shell()
 
 
                 # Add a Tool from shortcut
                 # Add a Tool from shortcut
                 if key == QtCore.Qt.Key_T:
                 if key == QtCore.Qt.Key_T:

+ 8 - 1
flatcamGUI/GUIElements.py

@@ -2560,10 +2560,11 @@ class DialogBoxRadio(QtWidgets.QDialog):
 
 
 class _BrowserTextEdit(QTextEdit):
 class _BrowserTextEdit(QTextEdit):
 
 
-    def __init__(self, version):
+    def __init__(self, version, app=None):
         QTextEdit.__init__(self)
         QTextEdit.__init__(self)
         self.menu = None
         self.menu = None
         self.version = version
         self.version = version
+        self.app = app
 
 
     def contextMenuEvent(self, event):
     def contextMenuEvent(self, event):
         self.menu = self.createStandardContextMenu(event.pos())
         self.menu = self.createStandardContextMenu(event.pos())
@@ -2571,6 +2572,12 @@ class _BrowserTextEdit(QTextEdit):
         clear_action.setShortcut(QKeySequence(Qt.Key_Delete))   # it's not working, the shortcut
         clear_action.setShortcut(QKeySequence(Qt.Key_Delete))   # it's not working, the shortcut
         self.menu.addAction(clear_action)
         self.menu.addAction(clear_action)
         clear_action.triggered.connect(self.clear)
         clear_action.triggered.connect(self.clear)
+
+        if self.app:
+            close_action = QAction("Close", self)
+            self.menu.addAction(close_action)
+            close_action.triggered.connect(lambda: self.app.ui.shell_dock.hide())
+
         self.menu.exec_(event.globalPos())
         self.menu.exec_(event.globalPos())
 
 
     def clear(self):
     def clear(self):

+ 22 - 10
flatcamTools/ToolShell.py

@@ -33,10 +33,10 @@ class TermWidget(QWidget):
     User pressed Enter. Client class should decide, if command must be executed or user may continue edit it
     User pressed Enter. Client class should decide, if command must be executed or user may continue edit it
     """
     """
 
 
-    def __init__(self, version, *args):
+    def __init__(self, version, app, *args):
         QWidget.__init__(self, *args)
         QWidget.__init__(self, *args)
 
 
-        self._browser = _BrowserTextEdit(version=version)
+        self._browser = _BrowserTextEdit(version=version, app=app)
         self._browser.setStyleSheet("font: 9pt \"Courier\";")
         self._browser.setStyleSheet("font: 9pt \"Courier\";")
         self._browser.setReadOnly(True)
         self._browser.setReadOnly(True)
         self._browser.document().setDefaultStyleSheet(
         self._browser.document().setDefaultStyleSheet(
@@ -92,10 +92,14 @@ class TermWidget(QWidget):
         """
         """
         Convert text to HTML for inserting it to browser
         Convert text to HTML for inserting it to browser
         """
         """
-        assert style in ('in', 'out', 'err', 'warning', 'success', 'selected')
+        assert style in ('in', 'out', 'err', 'warning', 'success', 'selected', 'raw')
 
 
-        text = html.escape(text)
-        text = text.replace('\n', '<br/>')
+        if style != 'raw':
+            text = html.escape(text)
+            text = text.replace('\n', '<br/>')
+        else:
+            text = text.replace('\n', '<br>')
+            text = text.replace('\t', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
 
 
         if style == 'in':
         if style == 'in':
             text = '<span style="font-weight: bold;">%s</span>' % text
             text = '<span style="font-weight: bold;">%s</span>' % text
@@ -107,6 +111,8 @@ class TermWidget(QWidget):
             text = '<span style="font-weight: bold; color: #15b300;">%s</span>' % text
             text = '<span style="font-weight: bold; color: #15b300;">%s</span>' % text
         elif style == 'selected':
         elif style == 'selected':
             text = ''
             text = ''
+        elif style == 'raw':
+            text = text
         else:
         else:
             text = '<span>%s</span>' % text  # without span <br/> is ignored!!!
             text = '<span>%s</span>' % text  # without span <br/> is ignored!!!
 
 
@@ -177,23 +183,29 @@ class TermWidget(QWidget):
         """
         """
         self._append_to_browser('out', text)
         self._append_to_browser('out', text)
 
 
+    def append_raw(self, text):
+        """
+        Append text to output widget as it is
+        """
+        self._append_to_browser('raw', text)
+
     def append_success(self, text):
     def append_success(self, text):
-        """Appent text to output widget
+        """Append text to output widget
         """
         """
         self._append_to_browser('success', text)
         self._append_to_browser('success', text)
 
 
     def append_selected(self, text):
     def append_selected(self, text):
-        """Appent text to output widget
+        """Append text to output widget
         """
         """
         self._append_to_browser('selected', text)
         self._append_to_browser('selected', text)
 
 
     def append_warning(self, text):
     def append_warning(self, text):
-        """Appent text to output widget
+        """Append text to output widget
         """
         """
         self._append_to_browser('warning', text)
         self._append_to_browser('warning', text)
 
 
     def append_error(self, text):
     def append_error(self, text):
-        """Appent error text to output widget. Text is drawn with red background
+        """Append error text to output widget. Text is drawn with red background
         """
         """
         self._append_to_browser('err', text)
         self._append_to_browser('err', text)
 
 
@@ -235,7 +247,7 @@ class FCShell(TermWidget):
         :param version:     FlatCAM version string
         :param version:     FlatCAM version string
         :param args:        Parameters passed to the TermWidget parent class
         :param args:        Parameters passed to the TermWidget parent class
         """
         """
-        TermWidget.__init__(self, version, *args)
+        TermWidget.__init__(self, version, *args, app=sysShell)
         self._sysShell = sysShell
         self._sysShell = sysShell
 
 
     def is_command_complete(self, text):
     def is_command_complete(self, text):

+ 14 - 16
tclCommands/TclCommandHelp.py

@@ -22,10 +22,10 @@ if '_' not in builtins.__dict__:
 
 
 class TclCommandHelp(TclCommand):
 class TclCommandHelp(TclCommand):
     """
     """
-    Tcl shell command to get the value of a system variable
+    Tcl shell command to show Help
 
 
     example:
     example:
-        get_sys excellon_zeros
+        help add_circle
     """
     """
 
 
     # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
     # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
@@ -58,21 +58,21 @@ class TclCommandHelp(TclCommand):
     def execute(self, args, unnamed_args):
     def execute(self, args, unnamed_args):
         """
         """
 
 
-        :param args:
+        :param args:            Without any argument will display the list of commands. Can have as a argument
+        a tcl command name to display the help for that command.
         :param unnamed_args:
         :param unnamed_args:
         :return:
         :return:
         """
         """
-        print(self.app.tcl_commands_storage)
 
 
         if 'name' in args:
         if 'name' in args:
             name = args['name']
             name = args['name']
             if name not in self.app.tcl_commands_storage:
             if name not in self.app.tcl_commands_storage:
                 return "Unknown command: %s" % name
                 return "Unknown command: %s" % name
 
 
-            print(self.app.tcl_commands_storage[name]["help"])
+            self.app.shell.append_output(self.app.tcl_commands_storage[name]["help"])
         else:
         else:
-            if args is None:
-                cmd_enum = _("Available commands:\n")
+            if not args:
+                cmd_enum = '%s\n' % _("Available commands:")
 
 
                 displayed_text = []
                 displayed_text = []
                 try:
                 try:
@@ -85,14 +85,12 @@ class TclCommandHelp(TclCommand):
                     max_tabs = math.ceil(max_len / 8)
                     max_tabs = math.ceil(max_len / 8)
 
 
                     for cmd_name in sorted(self.app.tcl_commands_storage):
                     for cmd_name in sorted(self.app.tcl_commands_storage):
-                        cmd_description = self.app.tcl_commands_storage[cmd_name]['description']
+                        cmd_description = "<span>%s</span>" % self.app.tcl_commands_storage[cmd_name]['description']
 
 
                         curr_len = len(cmd_name)
                         curr_len = len(cmd_name)
                         tabs = '\t'
                         tabs = '\t'
 
 
-                        cmd_name_colored = "<span style=\" color:#ff0000;\" >"
-                        cmd_name_colored += str(cmd_name)
-                        cmd_name_colored += "</span>"
+                        cmd_name_colored = "<span style=\" font-weight: bold; color: red;\" >%s</span>" % str(cmd_name)
 
 
                         # make sure to add the right number of tabs (1 tab = 8 spaces) so all the commands
                         # make sure to add the right number of tabs (1 tab = 8 spaces) so all the commands
                         # descriptions are aligned
                         # descriptions are aligned
@@ -102,7 +100,7 @@ class TclCommandHelp(TclCommand):
                             nr_tabs = 0
                             nr_tabs = 0
 
 
                             for x in range(max_tabs):
                             for x in range(max_tabs):
-                                if curr_len <= (x * 8):
+                                if curr_len < (x * 8):
                                     nr_tabs += 1
                                     nr_tabs += 1
 
 
                             # nr_tabs = 2 if curr_len <= 8 else 1
                             # nr_tabs = 2 if curr_len <= 8 else 1
@@ -111,9 +109,9 @@ class TclCommandHelp(TclCommand):
                         displayed_text.append(cmd_line_txt)
                         displayed_text.append(cmd_line_txt)
                 except Exception as err:
                 except Exception as err:
                     self.app.log.debug("App.setup_shell.shelp() when run as 'help' --> %s" % str(err))
                     self.app.log.debug("App.setup_shell.shelp() when run as 'help' --> %s" % str(err))
-                    displayed_text = [' %s' % cmd for cmd in sorted(self.app.tcl_commands_storage)]
+                    displayed_text = ['> %s\n' % cmd for cmd in sorted(self.app.tcl_commands_storage)]
 
 
-                cmd_enum += '\n'.join(displayed_text)
-                cmd_enum += '\n\n%s\n%s' % (_("Type help <command_name> for usage."), _("Example: help open_gerber"))
+                cmd_enum += '<br>'.join(displayed_text)
+                cmd_enum += '<br><br>%s<br>%s' % (_("Type help <command_name> for usage."), _("Example: help open_gerber"))
 
 
-                print(cmd_enum)
+                self.app.shell.append_raw(cmd_enum)

+ 1 - 2
tclCommands/__init__.py

@@ -1,7 +1,6 @@
 import pkgutil
 import pkgutil
 import sys
 import sys
 
 
-# Todo: I think these imports are not needed.
 # allowed command modules (please append them alphabetically ordered)
 # allowed command modules (please append them alphabetically ordered)
 import tclCommands.TclCommandAddCircle
 import tclCommands.TclCommandAddCircle
 import tclCommands.TclCommandAddPolygon
 import tclCommands.TclCommandAddPolygon
@@ -27,6 +26,7 @@ import tclCommands.TclCommandGeoCutout
 import tclCommands.TclCommandGeoUnion
 import tclCommands.TclCommandGeoUnion
 import tclCommands.TclCommandGetNames
 import tclCommands.TclCommandGetNames
 import tclCommands.TclCommandGetSys
 import tclCommands.TclCommandGetSys
+import tclCommands.TclCommandHelp
 import tclCommands.TclCommandImportSvg
 import tclCommands.TclCommandImportSvg
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandIsolate
 import tclCommands.TclCommandIsolate
@@ -93,7 +93,6 @@ def register_all_commands(app, commands):
     tcl_modules = {k: v for k, v in list(sys.modules.items()) if k.startswith('tclCommands.TclCommand')}
     tcl_modules = {k: v for k, v in list(sys.modules.items()) if k.startswith('tclCommands.TclCommand')}
 
 
     for key, mod in list(tcl_modules.items()):
     for key, mod in list(tcl_modules.items()):
-        print(key)
         if key != 'tclCommands.TclCommand':
         if key != 'tclCommands.TclCommand':
             class_name = key.split('.')[1]
             class_name = key.split('.')[1]
             class_type = getattr(mod, class_name)
             class_type = getattr(mod, class_name)