diff --git a/FlatCAMApp.py b/FlatCAMApp.py index d082602a..5c1a9570 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1700,7 +1700,7 @@ class App(QtCore.QObject): """ pass - def shell_message(self, msg, show=False, error=False, warning=False, success=False): + def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False): """ Shows a message on the FlatCAM Shell @@ -1720,8 +1720,10 @@ class App(QtCore.QObject): else: if success: self.shell.append_success(msg + "\n") - else: - self.shell.append_output(msg + "\n") + if success: + self.shell.append_selected(msg + "\n") + else: + self.shell.append_output(msg + "\n") except AttributeError: log.debug("shell_message() is called before Shell Class is instantiated. The message is: %s", str(msg)) @@ -1894,13 +1896,19 @@ class App(QtCore.QObject): elif level.lower() == "error_notcl": self.shell_message(msg, error=True, show=False) + elif level.lower() == "warning_notcl": self.shell_message(msg, warning=True, show=False) elif level.lower() == "success": self.shell_message(msg, success=True, show=False) + + elif level.lower() == "selected": + self.shell_message(msg, selected=True, show=False) + else: self.shell_message(msg, show=False) + else: self.ui.fcinfo.set_status(str(msg), level="info") @@ -4440,6 +4448,9 @@ class App(QtCore.QObject): # and as a convenience move the focus to the Project tab because Selected tab is now empty self.ui.notebook.setCurrentWidget(self.ui.project_tab) + # delete any text in the status bar, implicitly the last object name that was selected + self.inform.emit("") + else: # case when there is only an object under the click and we toggle it if len(objects_under_the_click_list) == 1: @@ -4448,6 +4459,20 @@ class App(QtCore.QObject): # create the selection box around the selected object curr_sel_obj = self.collection.get_active() self.draw_selection_shape(curr_sel_obj) + + if curr_sel_obj.kind == 'gerber': + self.inform.emit('[selected]%s selected' % + ('green', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'excellon': + self.inform.emit('[selected]%s selected' % + ('brown', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'cncjob': + self.inform.emit('[selected]%s selected' % + ('blue', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'geometry': + self.inform.emit('[selected]%s selected' % + ('red', str(curr_sel_obj.options['name']))) + elif self.collection.get_active().options['name'] not in objects_under_the_click_list: self.collection.set_all_inactive() self.delete_selection_shape() @@ -4455,9 +4480,25 @@ class App(QtCore.QObject): # create the selection box around the selected object curr_sel_obj = self.collection.get_active() self.draw_selection_shape(curr_sel_obj) + + if curr_sel_obj.kind == 'gerber': + self.inform.emit('[selected]%s selected' % + ('green', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'excellon': + self.inform.emit('[selected]%s selected' % + ('brown', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'cncjob': + self.inform.emit('[selected]%s selected' % + ('blue', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'geometry': + self.inform.emit('[selected]%s selected' % + ('red', str(curr_sel_obj.options['name']))) + else: self.collection.set_all_inactive() self.delete_selection_shape() + self.inform.emit("") + else: # If there is no selected object # make active the first element of the overlapped objects list @@ -4482,6 +4523,19 @@ class App(QtCore.QObject): # create the selection box around the selected object self.draw_selection_shape(curr_sel_obj) + if curr_sel_obj.kind == 'gerber': + self.inform.emit('[selected]%s selected' % + ('green', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'excellon': + self.inform.emit('[selected]%s selected' % + ('brown', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'cncjob': + self.inform.emit('[selected]%s selected' % + ('blue', str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'geometry': + self.inform.emit('[selected]%s selected' % + ('red', str(curr_sel_obj.options['name']))) + # for obj in self.collection.get_list(): # obj.plot() # curr_sel_obj.plot(color=self.FC_dark_blue, face_color=self.FC_light_blue) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index b6bb0bef..024ef608 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -4016,9 +4016,11 @@ class FlatCAMInfoBar(QtWidgets.QWidget): layout.addStretch() - def set_text_(self, text): + def set_text_(self, text, color=None): self.text.setText(text) self.text.setToolTip(text) + if color: + self.text.setStyleSheet('color: %s' % str(color)) def set_status(self, text, level="info"): level = str(level) @@ -4029,9 +4031,11 @@ class FlatCAMInfoBar(QtWidgets.QWidget): self.pmap = QtGui.QPixmap('share/greenlight12.png') elif level == "WARNING" or level == "WARNING_NOTCL": self.pmap = QtGui.QPixmap('share/yellowlight12.png') + elif level == "selected" or level == "SELECTED": + self.pmap = QtGui.QPixmap('share/bluelight12.png') else: self.pmap = QtGui.QPixmap('share/graylight12.png') - self.icon.setPixmap(self.pmap) self.set_text_(text) + self.icon.setPixmap(self.pmap) # end of file diff --git a/GUIElements.py b/GUIElements.py index d2242559..54dc0c54 100644 --- a/GUIElements.py +++ b/GUIElements.py @@ -1,14 +1,18 @@ from PyQt5 import QtGui, QtCore, QtWidgets -from PyQt5.QtCore import pyqtSignal, pyqtSlot +from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot +from PyQt5.QtWidgets import QTextEdit, QCompleter, QAction +from PyQt5.QtGui import QColor, QKeySequence, QPalette, QTextCursor from copy import copy import re import logging +import html log = logging.getLogger('base') EDIT_SIZE_HINT = 70 + class RadioSet(QtWidgets.QWidget): activated_custom = QtCore.pyqtSignal() @@ -1218,3 +1222,158 @@ class Dialog_box(QtWidgets.QWidget): self.location, self.ok = dialog_box.getText(self, title, label) + +class _BrowserTextEdit(QTextEdit): + + def __init__(self, version): + QTextEdit.__init__(self) + self.menu = None + self.version = version + + def contextMenuEvent(self, event): + self.menu = self.createStandardContextMenu(event.pos()) + clear_action = QAction("Clear", self) + clear_action.setShortcut(QKeySequence(Qt.Key_Delete)) # it's not working, the shortcut + self.menu.addAction(clear_action) + clear_action.triggered.connect(self.clear) + self.menu.exec_(event.globalPos()) + + + def clear(self): + QTextEdit.clear(self) + text = "FlatCAM %s (c)2014-2019 Juan Pablo Caram (Type help to get started)\n\n" % self.version + text = html.escape(text) + text = text.replace('\n', '
') + self.moveCursor(QTextCursor.End) + self.insertHtml(text) + + +class _ExpandableTextEdit(QTextEdit): + """ + Class implements edit line, which expands themselves automatically + """ + + historyNext = pyqtSignal() + historyPrev = pyqtSignal() + + def __init__(self, termwidget, *args): + QTextEdit.__init__(self, *args) + self.setStyleSheet("font: 9pt \"Courier\";") + self._fittedHeight = 1 + self.textChanged.connect(self._fit_to_document) + self._fit_to_document() + self._termWidget = termwidget + + self.completer = MyCompleter() + + self.model = QtCore.QStringListModel() + self.completer.setModel(self.model) + self.set_model_data(keyword_list=[]) + self.completer.insertText.connect(self.insertCompletion) + + def set_model_data(self, keyword_list): + self.model.setStringList(keyword_list) + + def insertCompletion(self, completion): + tc = self.textCursor() + extra = (len(completion) - len(self.completer.completionPrefix())) + tc.movePosition(QTextCursor.Left) + tc.movePosition(QTextCursor.EndOfWord) + tc.insertText(completion[-extra:]) + self.setTextCursor(tc) + self.completer.popup().hide() + + def focusInEvent(self, event): + if self.completer: + self.completer.setWidget(self) + QTextEdit.focusInEvent(self, event) + + def keyPressEvent(self, event): + """ + Catch keyboard events. Process Enter, Up, Down + """ + if event.matches(QKeySequence.InsertParagraphSeparator): + text = self.toPlainText() + if self._termWidget.is_command_complete(text): + self._termWidget.exec_current_command() + return + elif event.matches(QKeySequence.MoveToNextLine): + text = self.toPlainText() + cursor_pos = self.textCursor().position() + textBeforeEnd = text[cursor_pos:] + + if len(textBeforeEnd.split('\n')) <= 1: + self.historyNext.emit() + return + elif event.matches(QKeySequence.MoveToPreviousLine): + text = self.toPlainText() + cursor_pos = self.textCursor().position() + text_before_start = text[:cursor_pos] + # lineCount = len(textBeforeStart.splitlines()) + line_count = len(text_before_start.split('\n')) + if len(text_before_start) > 0 and \ + (text_before_start[-1] == '\n' or text_before_start[-1] == '\r'): + line_count += 1 + if line_count <= 1: + self.historyPrev.emit() + return + elif event.matches(QKeySequence.MoveToNextPage) or \ + event.matches(QKeySequence.MoveToPreviousPage): + return self._termWidget.browser().keyPressEvent(event) + + tc = self.textCursor() + if event.key() == Qt.Key_Tab and self.completer.popup().isVisible(): + self.completer.insertText.emit(self.completer.getSelected()) + self.completer.setCompletionMode(QCompleter.PopupCompletion) + return + + QTextEdit.keyPressEvent(self, event) + tc.select(QTextCursor.WordUnderCursor) + cr = self.cursorRect() + + if len(tc.selectedText()) > 0: + self.completer.setCompletionPrefix(tc.selectedText()) + popup = self.completer.popup() + popup.setCurrentIndex(self.completer.completionModel().index(0, 0)) + + cr.setWidth(self.completer.popup().sizeHintForColumn(0) + + self.completer.popup().verticalScrollBar().sizeHint().width()) + self.completer.complete(cr) + else: + self.completer.popup().hide() + + def sizeHint(self): + """ + QWidget sizeHint impelemtation + """ + hint = QTextEdit.sizeHint(self) + hint.setHeight(self._fittedHeight) + return hint + + def _fit_to_document(self): + """ + Update widget height to fit all text + """ + documentsize = self.document().size().toSize() + self._fittedHeight = documentsize.height() + (self.height() - self.viewport().height()) + self.setMaximumHeight(self._fittedHeight) + self.updateGeometry() + + def insertFromMimeData(self, mime_data): + # Paste only plain text. + self.insertPlainText(mime_data.text()) + + +class MyCompleter(QCompleter): + insertText = pyqtSignal(str) + + def __init__(self, parent=None): + QCompleter.__init__(self) + self.setCompletionMode(QCompleter.PopupCompletion) + self.highlighted.connect(self.setHighlighted) + + def setHighlighted(self, text): + self.lastSelected = text + + def getSelected(self): + return self.lastSelected diff --git a/README.md b/README.md index 3363d5bf..8919f409 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ CAD program, and create G-Code for Isolation routing. - added some new icons in the help menu and reorganized this menu - added a new function and the shortcut 'leftquote' (left of Key 1) for toggle of the notebook section - changed the Shortcut list shortcut key to F3 +- moved some graphical classes out of Tool Shell to GUIElements.py where they belong +- when selecting an object on canvas by single click, it's name is displayed in status bar. When nothing is selected a blank message (nothing) it's displayed +- in Move Tool I've added the type of object that was moved in the status bar message +- color coded the status bar bullet to blue for selection +- the name of the selected objects are displayed in the status bar color coded: green for Gerber objects, Brown for Excellon, Red for Geometry and Blue for CNCJobs. + 6.02.2019 diff --git a/flatcamTools/ToolMove.py b/flatcamTools/ToolMove.py index 562aff80..9f920af4 100644 --- a/flatcamTools/ToolMove.py +++ b/flatcamTools/ToolMove.py @@ -139,12 +139,13 @@ class ToolMove(FlatCAMTool): proc.done() # delete the selection bounding box self.delete_shape() + self.app.inform.emit('[success]%s object was moved ...' % + str(sel_obj.kind).capitalize()) self.app.worker_task.emit({'fcn': job_move, 'params': [self]}) self.clicked_move = 0 self.toggle() - self.app.inform.emit("[success]Object was moved ...") return except TypeError: diff --git a/flatcamTools/ToolShell.py b/flatcamTools/ToolShell.py index 80e94437..22171252 100644 --- a/flatcamTools/ToolShell.py +++ b/flatcamTools/ToolShell.py @@ -6,165 +6,12 @@ # MIT Licence # ############################################################ +# from PyQt5.QtCore import pyqtSignal +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QTextCursor +from PyQt5.QtWidgets import QVBoxLayout, QWidget +from GUIElements import _BrowserTextEdit, _ExpandableTextEdit import html -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import Qt, QStringListModel -from PyQt5.QtGui import QColor, QKeySequence, QPalette, QTextCursor -from PyQt5.QtWidgets import QLineEdit, QSizePolicy, QTextEdit, QVBoxLayout, QWidget, QCompleter, QAction - -class _BrowserTextEdit(QTextEdit): - - def __init__(self, version): - QTextEdit.__init__(self) - self.menu = None - self.version = version - - def contextMenuEvent(self, event): - self.menu = self.createStandardContextMenu(event.pos()) - clear_action = QAction("Clear", self) - clear_action.setShortcut(QKeySequence(Qt.Key_Delete)) # it's not working, the shortcut - self.menu.addAction(clear_action) - clear_action.triggered.connect(self.clear) - self.menu.exec_(event.globalPos()) - - - def clear(self): - QTextEdit.clear(self) - text = "FlatCAM %s (c)2014-2019 Juan Pablo Caram (Type help to get started)\n\n" % self.version - text = html.escape(text) - text = text.replace('\n', '
') - self.moveCursor(QTextCursor.End) - self.insertHtml(text) - -class _ExpandableTextEdit(QTextEdit): - """ - Class implements edit line, which expands themselves automatically - """ - - historyNext = pyqtSignal() - historyPrev = pyqtSignal() - - def __init__(self, termwidget, *args): - QTextEdit.__init__(self, *args) - self.setStyleSheet("font: 9pt \"Courier\";") - self._fittedHeight = 1 - self.textChanged.connect(self._fit_to_document) - self._fit_to_document() - self._termWidget = termwidget - - self.completer = MyCompleter() - - self.model = QStringListModel() - self.completer.setModel(self.model) - self.set_model_data(keyword_list=[]) - self.completer.insertText.connect(self.insertCompletion) - - def set_model_data(self, keyword_list): - self.model.setStringList(keyword_list) - - def insertCompletion(self, completion): - tc = self.textCursor() - extra = (len(completion) - len(self.completer.completionPrefix())) - tc.movePosition(QTextCursor.Left) - tc.movePosition(QTextCursor.EndOfWord) - tc.insertText(completion[-extra:]) - self.setTextCursor(tc) - self.completer.popup().hide() - - def focusInEvent(self, event): - if self.completer: - self.completer.setWidget(self) - QTextEdit.focusInEvent(self, event) - - def keyPressEvent(self, event): - """ - Catch keyboard events. Process Enter, Up, Down - """ - if event.matches(QKeySequence.InsertParagraphSeparator): - text = self.toPlainText() - if self._termWidget.is_command_complete(text): - self._termWidget.exec_current_command() - return - elif event.matches(QKeySequence.MoveToNextLine): - text = self.toPlainText() - cursor_pos = self.textCursor().position() - textBeforeEnd = text[cursor_pos:] - - if len(textBeforeEnd.split('\n')) <= 1: - self.historyNext.emit() - return - elif event.matches(QKeySequence.MoveToPreviousLine): - text = self.toPlainText() - cursor_pos = self.textCursor().position() - text_before_start = text[:cursor_pos] - # lineCount = len(textBeforeStart.splitlines()) - line_count = len(text_before_start.split('\n')) - if len(text_before_start) > 0 and \ - (text_before_start[-1] == '\n' or text_before_start[-1] == '\r'): - line_count += 1 - if line_count <= 1: - self.historyPrev.emit() - return - elif event.matches(QKeySequence.MoveToNextPage) or \ - event.matches(QKeySequence.MoveToPreviousPage): - return self._termWidget.browser().keyPressEvent(event) - - tc = self.textCursor() - if event.key() == Qt.Key_Tab and self.completer.popup().isVisible(): - self.completer.insertText.emit(self.completer.getSelected()) - self.completer.setCompletionMode(QCompleter.PopupCompletion) - return - - QTextEdit.keyPressEvent(self, event) - tc.select(QTextCursor.WordUnderCursor) - cr = self.cursorRect() - - if len(tc.selectedText()) > 0: - self.completer.setCompletionPrefix(tc.selectedText()) - popup = self.completer.popup() - popup.setCurrentIndex(self.completer.completionModel().index(0, 0)) - - cr.setWidth(self.completer.popup().sizeHintForColumn(0) - + self.completer.popup().verticalScrollBar().sizeHint().width()) - self.completer.complete(cr) - else: - self.completer.popup().hide() - - def sizeHint(self): - """ - QWidget sizeHint impelemtation - """ - hint = QTextEdit.sizeHint(self) - hint.setHeight(self._fittedHeight) - return hint - - def _fit_to_document(self): - """ - Update widget height to fit all text - """ - documentsize = self.document().size().toSize() - self._fittedHeight = documentsize.height() + (self.height() - self.viewport().height()) - self.setMaximumHeight(self._fittedHeight) - self.updateGeometry() - - def insertFromMimeData(self, mime_data): - # Paste only plain text. - self.insertPlainText(mime_data.text()) - - -class MyCompleter(QCompleter): - insertText = pyqtSignal(str) - - def __init__(self, parent=None): - QCompleter.__init__(self) - self.setCompletionMode(QCompleter.PopupCompletion) - self.highlighted.connect(self.setHighlighted) - - def setHighlighted(self, text): - self.lastSelected = text - - def getSelected(self): - return self.lastSelected class TermWidget(QWidget): @@ -234,7 +81,7 @@ class TermWidget(QWidget): """ Convert text to HTML for inserting it to browser """ - assert style in ('in', 'out', 'err', 'warning', 'success') + assert style in ('in', 'out', 'err', 'warning', 'success', 'selected') text = html.escape(text) text = text.replace('\n', '
') @@ -247,6 +94,8 @@ class TermWidget(QWidget): text = '%s' % text elif style == 'success': text = '%s' % text + elif style == 'selected': + text = '%s' % text else: text = '%s' % text # without span
is ignored!!! @@ -313,6 +162,11 @@ class TermWidget(QWidget): """ self._append_to_browser('success', text) + def append_selected(self, text): + """Appent text to output widget + """ + self._append_to_browser('selected', text) + def append_warning(self, text): """Appent text to output widget """ @@ -352,6 +206,7 @@ class TermWidget(QWidget): self._edit.setPlainText(self._history[self._historyIndex]) self._edit.moveCursor(QTextCursor.End) + class FCShell(TermWidget): def __init__(self, sysShell, version, *args): TermWidget.__init__(self, version, *args) diff --git a/share/bluelight12.png b/share/bluelight12.png new file mode 100644 index 00000000..050aa906 Binary files /dev/null and b/share/bluelight12.png differ