diff --git a/changelog b/changelog index 92f4992e15f1f65e4130de836c7345bfc9930107..502fe4e937facadca1bdb938717353f7b7158278 100644 --- a/changelog +++ b/changelog @@ -5,6 +5,26 @@ Note: This changelog does not comprise development of pyspread for Python2 It starts with the first Alpha release 1.99.0.0 +2.1.1 +----- + +This is a bugfix release + +Dependencies: + * Mandatory: Python (≥ 3.6), numpy (>=1.1), PyQt5 (≥ 5.10, requires PyQt5.Svg), setuptools (>=40.0), markdown2 (>= 2.3) + * Recommended: matplotlib (>=1.1.1), pyenchant (>=1.1), pip (>=18), python-dateutil (>= 2.7.0), py-moneyed (>=2.0) + * For building the apidocs with Sphinx see apidocs/requirements.txt + +Bug fixes: + * Toolbar buttons now are correctly updated when a new cell is selected. + * Application icon is now shown in Wayland. + * The tarball now includes a metainfo file share/applications/io.gitlab.pyspread.pyspread.desktop (see Debian packaging guidelines) + * .desktop file fixed + * Menu buttons are now updated correctly when selecting a cell. + * Border menu toggle actions are now updated correctly. + * requirements.txt now includes optional dependency py-moneyed + * Entry line and macro editor now use a monospace font family + 2.1 --- diff --git a/pyspread/__init__.py b/pyspread/__init__.py index b8f10d8bfc5416b691a1c4966ddff674485ac4ae..86659bf5e0c1a2eca45fa862093469d652f9d174 100644 --- a/pyspread/__init__.py +++ b/pyspread/__init__.py @@ -3,4 +3,4 @@ APP_NAME = "pyspread" # Current pyspread version -VERSION = "2.1" +VERSION = "2.1.1" diff --git a/pyspread/entryline.py b/pyspread/entryline.py index e52e489a435daf6c33b3a6871585572ecece3164..144e3d5281382a762552175e391e88b904b1dcfb 100644 --- a/pyspread/entryline.py +++ b/pyspread/entryline.py @@ -52,7 +52,8 @@ class Entryline(SpellTextEdit): """ - super().__init__(line_numbers=False) + font_family = main_window.settings.entry_line_font_family + super().__init__(line_numbers=False, font_family=font_family) self.main_window = main_window diff --git a/pyspread/lib/spelltextedit.py b/pyspread/lib/spelltextedit.py index f3f15dbffb20a85a7df57c6438612cc2067f4039..bf98db7f3c9984dadfcbe3d9fa0c62014f99e25d 100644 --- a/pyspread/lib/spelltextedit.py +++ b/pyspread/lib/spelltextedit.py @@ -92,7 +92,9 @@ """ +import keyword from math import log10 +import re import sys from warnings import warn @@ -215,7 +217,8 @@ class SpellTextEdit(QPlainTextEdit): max_suggestions = 20 spaces_per_tab = 4 - def __init__(self, parent=None, line_numbers=True): + def __init__(self, parent=None, line_numbers=True, + font_family="Monospace"): self.line_numbers = line_numbers @@ -223,6 +226,11 @@ class SpellTextEdit(QPlainTextEdit): self.actions = SpellTextEditActions(self) + # Set default font to font_family + font = self.document().defaultFont() + font.setFamily(font_family) + self.document().setDefaultFont(font) + # If a <Tab> is present then it should be of width 4 try: _distance = QFontMetricsF(self.font()).horizontalAdvance(" ") @@ -496,14 +504,7 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): err_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) # Python keywords - keywords = [ - 'and', 'assert', 'break', 'class', 'continue', 'def', - 'del', 'elif', 'else', 'except', 'exec', 'finally', - 'for', 'from', 'global', 'if', 'import', 'in', - 'is', 'lambda', 'not', 'or', 'pass', 'print', - 'raise', 'return', 'try', 'while', 'yield', - 'None', 'True', 'False', - ] + keywords = keyword.kwlist # Python operators operators = [ @@ -533,8 +534,8 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): # Multi-line strings (expression, flag, style) # FIXME: The triple-quotes in these two lines will mess up the # syntax highlighting from this point onward - self.tri_single = (QRegExp("'''"), 1, STYLES['string2']) - self.tri_double = (QRegExp('"""'), 2, STYLES['string2']) + self.tri_single = ("'''", 1, STYLES['string2']) + self.tri_double = ('"""', 2, STYLES['string2']) rules = [] @@ -568,13 +569,13 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): STYLES['numbers']), ] - # Build a QRegExp for each pattern - self.rules = [(QRegExp(pat), index, fmt) + # Build a regex for each pattern + self.rules = [(re.compile(pat, re.U), index, fmt) for (pat, index, fmt) in rules] def match_multiline(self, text, delimiter, in_state, style): """Do highlighting of multi-line strings. ``delimiter`` should be a - ``QRegExp`` for triple-single-quotes or triple-double-quotes, and + regex for triple-single-quotes or triple-double-quotes, and ``in_state`` should be a unique integer to represent the corresponding state changes when inside those strings. Returns True if we're still inside a multi-line string when this function is finished. @@ -586,17 +587,17 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): add = 0 # Otherwise, look for the delimiter on this line else: - start = delimiter.indexIn(text) + start = text.find(delimiter) # Move past this match - add = delimiter.matchedLength() + add = len(delimiter) # As long as there's a delimiter match on this line... while start >= 0: # Look for the ending delimiter - end = delimiter.indexIn(text, start + add) + end = text.find(delimiter, start + add) # Ending delimiter on this line? if end >= add: - length = end - start + add + delimiter.matchedLength() + length = end - start + add + len(delimiter) self.setCurrentBlockState(0) # No; multi-line string else: @@ -609,11 +610,11 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): # Apply formatting self.setFormat(start, length, style) # Look for the next match - start = delimiter.indexIn(text, start + length) + start = text.find(delimiter, start + length) - # Return True if still inside a multi-line string, False otherwise + # Return state if still inside a multi-line string, False otherwise if self.currentBlockState() == in_state: - return True + return in_state else: return False @@ -675,21 +676,24 @@ class PythonEnchantHighlighter(QSyntaxHighlighter): # Do other syntax formatting for expression, nth, format in self.rules: - index = expression.indexIn(text, 0) + for match in expression.finditer(text): + length = match.end() - match.start() + self.setFormat(match.start(), length, format) + + # index = expression.indexIn(text, 0) - while index >= 0: - # We actually want the index of the nth match - index = expression.pos(nth) - length = len(expression.cap(nth)) - self.setFormat(index, length, format) - index = expression.indexIn(text, index + length) + # while index >= 0: + # # We actually want the index of the nth match + # index = expression.pos(nth) + # length = len(expression.cap(nth)) + # self.setFormat(index, length, format) + # index = expression.indexIn(text, index + length) self.setCurrentBlockState(0) # Do multi-line strings in_multiline = self.match_multiline(text, *self.tri_single) - if not in_multiline: - in_multiline = self.match_multiline(text, *self.tri_double) + in_multiline = self.match_multiline(text, *self.tri_double) def highlightBlock(self, text): """Overridden QSyntaxHighlighter method to apply the highlight""" diff --git a/pyspread/main_window.py b/pyspread/main_window.py index 4e51ba125fee008c3704cf720f8197ad2e7912a9..e2749aea2dd97161c0a9494f7b1808746e4eaef4 100644 --- a/pyspread/main_window.py +++ b/pyspread/main_window.py @@ -713,29 +713,35 @@ class MainWindow(QMainWindow): is_bold = attributes.fontweight == QFont.Bold self.main_window_actions.bold.setChecked(is_bold) + self.main_window_toolbar_actions.bold.setChecked(is_bold) is_italic = attributes.fontstyle == QFont.StyleItalic self.main_window_actions.italics.setChecked(is_italic) + self.main_window_toolbar_actions.italics.setChecked(is_italic) - underline_action = self.main_window_actions.underline - underline_action.setChecked(attributes.underline) + self.main_window_actions.underline.setChecked(attributes.underline) + self.main_window_toolbar_actions.underline.setChecked(attributes.underline) - strikethrough_action = self.main_window_actions.strikethrough - strikethrough_action.setChecked(attributes.strikethrough) + self.main_window_actions.strikethrough.setChecked( + attributes.strikethrough) + self.main_window_toolbar_actions.strikethrough.setChecked( + attributes.strikethrough) renderer = attributes.renderer widgets.renderer_button.set_current_action(renderer) widgets.renderer_button.set_menu_checked(renderer) - freeze_action = self.main_window_actions.freeze_cell - freeze_action.setChecked(attributes.frozen) + self.main_window_actions.freeze_cell.setChecked(attributes.frozen) + self.main_window_toolbar_actions.freeze_cell.setChecked(attributes.frozen) - lock_action = self.main_window_actions.lock_cell - lock_action.setChecked(attributes.locked) + self.main_window_actions.lock_cell.setChecked(attributes.locked) + self.main_window_toolbar_actions.lock_cell.setChecked(attributes.locked) self.entry_line.setReadOnly(attributes.locked) - button_action = self.main_window_actions.button_cell - button_action.setChecked(attributes.button_cell is not False) + self.main_window_actions.button_cell.setChecked( + attributes.button_cell is not False) + self.main_window_toolbar_actions.button_cell.setChecked( + attributes.button_cell is not False) rotation = f"rotate_{int(attributes.angle)}" widgets.rotate_button.set_current_action(rotation) @@ -782,5 +788,7 @@ class MainWindow(QMainWindow): widgets.font_combo.font = attributes.textfont widgets.font_size_combo.size = attributes.pointsize - merge_cells_action = self.main_window_actions.merge_cells - merge_cells_action.setChecked(attributes.merge_area is not None) + self.main_window_actions.merge_cells.setChecked( + attributes.merge_area is not None) + self.main_window_toolbar_actions.merge_cells.setChecked( + attributes.merge_area is not None) diff --git a/pyspread/menus.py b/pyspread/menus.py index d9b700355ba201cf3cc71d72ade2f92823e1da99..1f9d4823b69951025eeed7e01d71e6cd39544802 100644 --- a/pyspread/menus.py +++ b/pyspread/menus.py @@ -38,6 +38,7 @@ from functools import partial from pathlib import Path +from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( QMenuBar, QMenu, QAction, QMainWindow, QWidget, QToolBar) @@ -352,6 +353,8 @@ class BorderChoiceMenu(QMenu): """ + self.actions = actions + super().__init__() self.setTitle("Formatted borders") @@ -366,6 +369,14 @@ class BorderChoiceMenu(QMenu): self.addAction(actions.format_borders_inner) self.addAction(actions.format_borders_top_bottom) + self.triggered.connect(self.on_selection) + + def on_selection(self, event): + """Event handler""" + + self.actions.parent.format_toolbar.border_menu_button.setAttribute( + Qt.WA_UnderMouse, False) + class BorderWidthMenu(QMenu): """QMenu for choosing the cell border width""" @@ -376,6 +387,8 @@ class BorderWidthMenu(QMenu): """ + self.actions = actions + super().__init__() self.setTitle("Border width") @@ -390,6 +403,14 @@ class BorderWidthMenu(QMenu): self.addAction(actions.format_borders_32) self.addAction(actions.format_borders_64) + self.triggered.connect(self.on_selection) + + def on_selection(self, event): + """Event handler""" + + self.actions.parent.format_toolbar.border_menu_button.setAttribute( + Qt.WA_UnderMouse, False) + class GridContextMenu(QMenu): """Context menu for grid""" diff --git a/pyspread/panels.py b/pyspread/panels.py index 33e187e420f191dcd4814a6690a4b1d31f54aca1..62b319c34926f0a7790132807a65b31d5e3bf429 100644 --- a/pyspread/panels.py +++ b/pyspread/panels.py @@ -66,7 +66,8 @@ class MacroPanel(QDialog): def _init_widgets(self): """Inititialize widgets""" - self.macro_editor = SpellTextEdit(self) + font_family = self.parent.settings.macro_editor_font_family + self.macro_editor = SpellTextEdit(self, font_family=font_family) self.result_viewer = QTextEdit(self) self.result_viewer.setReadOnly(True) diff --git a/pyspread/pyspread.py b/pyspread/pyspread.py index 471b77f49d387c1589a2af2a43770d340cc1efb3..3e61bb42d08261138f2785c5f2b57d0856a15872 100755 --- a/pyspread/pyspread.py +++ b/pyspread/pyspread.py @@ -75,6 +75,7 @@ def main(): args, _ = parser.parse_known_args() app = QApplication(sys.argv) + app.setDesktopFileName("io.gitlab.pyspread.pyspread") main_window = MainWindow(args.file, default_settings=args.default_settings) main_window.show() diff --git a/pyspread/requirements.txt b/pyspread/requirements.txt index 7c25cb474dc96c9bf27bfa5e7db6248c842c5d8d..e08834eba248d54a4245cbf23ee1e6c14bb3c683 100644 --- a/pyspread/requirements.txt +++ b/pyspread/requirements.txt @@ -9,3 +9,4 @@ matplotlib (>=1.1.1) pyenchant (>=1.1) pip (>=18) python-dateutil (>=2.7.0) +py-moneyed (>=2.0) diff --git a/pyspread/settings.py b/pyspread/settings.py index 1116b2ee8732b59938838fdd3a44722077789e80..e3c76005f793a77c59481199523b4e97d8c98ced 100644 --- a/pyspread/settings.py +++ b/pyspread/settings.py @@ -92,6 +92,12 @@ class Settings: file_history = [] """Files in file history""" + entry_line_font_family = "Monospace" + """Font family for entry line widget""" + + macro_editor_font_family = "Monospace" + """Font family for macro editor widget""" + digest_types = None """List of default digest types for preprocessing values from CSV import""" diff --git a/pyspread/share/applications/io.gitlab.pyspread.pyspread.desktop b/pyspread/share/applications/io.gitlab.pyspread.pyspread.desktop index 435a688c1e6e760d4754bf6689ac4b20da479400..2bfb5b1c020548b69de478fd3283dbc78da4311b 100755 --- a/pyspread/share/applications/io.gitlab.pyspread.pyspread.desktop +++ b/pyspread/share/applications/io.gitlab.pyspread.pyspread.desktop @@ -1,17 +1,17 @@ -#!/usr/bin/env xdg-open [Desktop Entry] Categories=Office;Spreadsheet; Comment[en_US]=pyspread is a non-traditional spreadsheet application that is based on and written in the programming language Python Comment=pyspread is a non-traditional spreadsheet application that is based on and written in the programming language Python -Exec=pyspread +Exec=pyspread %U GenericName[en_US]=Python based Spreadsheet GenericName=Python based Spreadsheet Keywords=python;spreadsheet;matplotlib;math; -Icon=pyspread +Icon=pyspread.svg MimeType=application/x-pyspread-spreadsheet;application/x-pyspread-bz-spreadsheet Name[en_US]=pyspread Name=pyspread StartupNotify=false +StartupWMClass=io.gitlab.pyspread.pyspread Terminal=false Type=Application X-DBUS-ServiceName= diff --git a/setup.py b/setup.py index 2396f4f4b701b508ad28403428fa1df9e5d748ec..3013b980251ac5abd619706a3ef207c4df066260 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,9 @@ setup( 'share/*/*/*/*/*', ] }, + data_files = [ + ('pyspread/share/applications', ['pyspread/share/applications/io.gitlab.pyspread.pyspread.desktop']), + ], license='GPL v3 :: GNU General Public License', keywords=['spreadsheet', 'pyspread'], python_requires='>=3.6',