diff --git a/uberwriter/FormatShortcuts.py b/uberwriter/FormatShortcuts.py index 28711ac..ade4e01 100644 --- a/uberwriter/FormatShortcuts.py +++ b/uberwriter/FormatShortcuts.py @@ -1,221 +1,190 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- -### BEGIN LICENSE +# BEGIN LICENSE # Copyright (C) 2012, Wolf Vollprecht -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see . -### END LICENSE +# END LICENSE -import gettext - -import re -from gi.repository import Gtk, Gdk # pylint: disable=E0611 -from gi.repository import Pango # pylint: disable=E0611 - from gettext import gettext as _ from . MarkupBuffer import MarkupBuffer + class FormatShortcuts(): + """Manage the insertion of formatting for insert them using shortcuts + """ - def __init__(self, textbuffer, texteditor): - self.TextBuffer = textbuffer - self.TextEditor = texteditor - def rule(self): - self.TextBuffer.insert_at_cursor("\n\n-------\n") - self.TextEditor.scroll_mark_onscreen(self.TextBuffer.get_insert()) - self.regex = MarkupBuffer.regex + def __init__(self, textbuffer, texteditor): + self.text_buffer = textbuffer + self.text_editor = texteditor + self.regex = MarkupBuffer.regex - def bold(self): - self.apply_format("**") - - def italic(self): - self.apply_format("*") + def rule(self): + """insert ruler at cursor + """ - def strikeout(self): - self.apply_format("~~") + self.text_buffer.insert_at_cursor("\n\n-------\n") + self.text_editor.scroll_mark_onscreen(self.text_buffer.get_insert()) - def apply_format(self, wrap = "*"): - if self.TextBuffer.get_has_selection(): - ## Find current highlighting + def bold(self): + """set selected text as bold + """ - (start, end) = self.TextBuffer.get_selection_bounds() - moved = False - if ( - start.get_offset() >= len(wrap) and - end.get_offset() <= self.TextBuffer.get_char_count() - len(wrap) - ): - moved = True - ext_start = start.copy() - ext_start.backward_chars(len(wrap)) - ext_end = end.copy() - ext_end.forward_chars(len(wrap)) - text = self.TextBuffer.get_text(ext_start, ext_end, True) - else: - text = self.TextBuffer.get_text(start, end, True) - - if moved and text.startswith(wrap) and text.endswith(wrap): - text = text[len(wrap):-len(wrap)] - new_text = text - self.TextBuffer.delete(ext_start, ext_end) - move_back = 0 - else: - if moved: - text = text[len(wrap):-len(wrap)] - new_text = text.lstrip().rstrip() - text = text.replace(new_text, wrap + new_text + wrap) + self.apply_format("**") - self.TextBuffer.delete(start, end) - move_back = len(wrap) - - self.TextBuffer.insert_at_cursor(text) - text_length = len(new_text) + def italic(self): + """set selected text as italic + """ + self.apply_format("*") - else: - helptext = "" - if wrap == "*": - helptext = _("emphasized text") - elif wrap == "**": - helptext = _("strong text") - elif wrap == "~~": - helptext = _("striked out text") + def strikeout(self): + """set selected text as stricked out + """ + self.apply_format("~~") - self.TextBuffer.insert_at_cursor(wrap + helptext + wrap) - text_length = len(helptext) - move_back = len(wrap) + def apply_format(self, wrap="*"): + """apply the given wrap to a selected text, or insert a helper text wraped + if nothing is selected - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) - cursor_iter.backward_chars(move_back) - self.TextBuffer.move_mark_by_name('selection_bound', cursor_iter) - cursor_iter.backward_chars(text_length) - self.TextBuffer.move_mark_by_name('insert', cursor_iter) + Keyword Arguments: + wrap {str} -- [the format mark] (default: {"*"}) + """ - def unordered_list_item(self): - helptext = _("List item") - text_length = len(helptext) - move_back = 0 - if self.TextBuffer.get_has_selection(): - (start, end) = self.TextBuffer.get_selection_bounds() - if start.starts_line(): - text = self.TextBuffer.get_text(start, end, False) - if text.startswith(("- ", "* ", "+ ")): - delete_end = start.forward_chars(2) - self.TextBuffer.delete(start, delete_end) - else: - self.TextBuffer.insert(start, "- ") - else: - move_back = 0 - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) + if self.text_buffer.get_has_selection(): + # Find current highlighting + (start, end) = self.text_buffer.get_selection_bounds() + moved = False + if (start.get_offset() >= len(wrap) and + end.get_offset() <= self.text_buffer.get_char_count() - len(wrap)): + moved = True + ext_start = start.copy() + ext_start.backward_chars(len(wrap)) + ext_end = end.copy() + ext_end.forward_chars(len(wrap)) + text = self.text_buffer.get_text(ext_start, ext_end, True) + else: + text = self.text_buffer.get_text(start, end, True) - start_ext = cursor_iter.copy() - start_ext.backward_lines(3) - text = self.TextBuffer.get_text(cursor_iter, start_ext, False) - lines = text.splitlines() + if moved and text.startswith(wrap) and text.endswith(wrap): + text = text[len(wrap):-len(wrap)] + new_text = text + self.text_buffer.delete(ext_start, ext_end) + move_back = 0 + else: + if moved: + text = text[len(wrap):-len(wrap)] + new_text = text.lstrip().rstrip() + text = text.replace(new_text, wrap + new_text + wrap) - for line in reversed(lines): - if len(line) and line.startswith(("- ", "* ", "+ ")): - if cursor_iter.starts_line(): - self.TextBuffer.insert_at_cursor(line[:2] + helptext) - else: - self.TextBuffer.insert_at_cursor("\n" + line[:2] + helptext) - break - else: - if len(lines[-1]) == 0 and len(lines[-2]) == 0: - self.TextBuffer.insert_at_cursor("- " + helptext) - elif len(lines[-1]) == 0: - if cursor_iter.starts_line(): - self.TextBuffer.insert_at_cursor("- " + helptext) - else: - self.TextBuffer.insert_at_cursor("\n- " + helptext) - else: - self.TextBuffer.insert_at_cursor("\n\n- " + helptext) - break + self.text_buffer.delete(start, end) + move_back = len(wrap) - self.select_edit(move_back, text_length) + self.text_buffer.insert_at_cursor(text) + text_length = len(new_text) - def ordered_list_item(self): - pass + else: + helptext = "" + if wrap == "*": + helptext = _("emphasized text") + elif wrap == "**": + helptext = _("strong text") + elif wrap == "~~": + helptext = _("striked out text") - def select_edit(self, move_back, text_length): - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) - cursor_iter.backward_chars(move_back) - self.TextBuffer.move_mark_by_name('selection_bound', cursor_iter) - cursor_iter.backward_chars(text_length) - self.TextBuffer.move_mark_by_name('insert', cursor_iter) - self.TextEditor.scroll_mark_onscreen(self.TextBuffer.get_insert()) + self.text_buffer.insert_at_cursor(wrap + helptext + wrap) + text_length = len(helptext) + move_back = len(wrap) - def above(self, linestart = ""): - if not cursor_iter.starts_line(): - return "" - else: - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) + cursor_mark = self.text_buffer.get_insert() + cursor_iter = self.text_buffer.get_iter_at_mark(cursor_mark) + cursor_iter.backward_chars(move_back) + self.text_buffer.move_mark_by_name('selection_bound', cursor_iter) + cursor_iter.backward_chars(text_length) + self.text_buffer.move_mark_by_name('insert', cursor_iter) - start_ext = cursor_iter.copy() - start_ext.backward_lines(2) - text = self.TextBuffer.get_text(cursor_iter, start_ext, False) - lines = text.splitlines() + def unordered_list_item(self): + """insert unordered list items or mark a selection as + an item in an unordered list + """ - #if line[-1].startswith + helptext = _("List item") + text_length = len(helptext) + move_back = 0 + if self.text_buffer.get_has_selection(): + (start, end) = self.text_buffer.get_selection_bounds() + if start.starts_line(): + text = self.text_buffer.get_text(start, end, False) + if text.startswith(("- ", "* ", "+ ")): + delete_end = start.forward_chars(2) + self.text_buffer.delete(start, delete_end) + else: + self.text_buffer.insert(start, "- ") + else: + move_back = 0 + cursor_mark = self.text_buffer.get_insert() + cursor_iter = self.text_buffer.get_iter_at_mark(cursor_mark) - def get_lines(self, cursor_iter): + start_ext = cursor_iter.copy() + start_ext.backward_lines(3) + text = self.text_buffer.get_text(cursor_iter, start_ext, False) + lines = text.splitlines() - start_ext = cursor_iter.copy() - start_ext.backward_lines(2) - text = self.TextBuffer.get_text(cursor_iter, start_ext, False) - lines = text.splitlines() + for line in reversed(lines): + if line and line.startswith(("- ", "* ", "+ ")): + if cursor_iter.starts_line(): + self.text_buffer.insert_at_cursor(line[:2] + helptext) + else: + self.text_buffer.insert_at_cursor( + "\n" + line[:2] + helptext) + break + else: + if not lines[-1] and not lines[-2]: + self.text_buffer.insert_at_cursor("- " + helptext) + elif not lines[-1]: + if cursor_iter.starts_line(): + self.text_buffer.insert_at_cursor("- " + helptext) + else: + self.text_buffer.insert_at_cursor("\n- " + helptext) + else: + self.text_buffer.insert_at_cursor("\n\n- " + helptext) + break - abs_line = cursor_iter.get_line() + self.select_edit(move_back, text_length) - return reversed(lines) + def ordered_list_item(self): + # TODO: implement ordered lists + pass - def heading(self, level = 0): - helptext = _("Heading") - before = "" - if self.TextBuffer.get_has_selection(): - (start, end) = self.TextBuffer.get_selection_bounds() - text = self.TextBuffer.get_text(start, end, False) - self.TextBuffer.delete(start, end) - else: - text = helptext + def select_edit(self, move_back, text_length): + cursor_mark = self.text_buffer.get_insert() + cursor_iter = self.text_buffer.get_iter_at_mark(cursor_mark) + cursor_iter.backward_chars(move_back) + self.text_buffer.move_mark_by_name('selection_bound', cursor_iter) + cursor_iter.backward_chars(text_length) + self.text_buffer.move_mark_by_name('insert', cursor_iter) + self.text_editor.scroll_mark_onscreen(self.text_buffer.get_insert()) - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) + def heading(self): + """insert heading at cursor position or set selected text as one + """ + helptext = _("Heading") + if self.text_buffer.get_has_selection(): + (start, end) = self.text_buffer.get_selection_bounds() + text = self.text_buffer.get_text(start, end, False) + self.text_buffer.delete(start, end) + else: + text = helptext - #lines = self.get_lines(cursor_iter) - - #if cursor_iter.starts_line(): - # if lines[1] != '': - # before = before + "\n" - #else: - # match = re.match(r'([\#]+ )(.+)', lines[0]) - # if match: - # if match.group(1): - # - # print match.group(0) - # if len(match.group(0)) < 6: - # before = before + "#" * (len(match.group(0)) + 1) - # else: - # before = before + "#" - # else: - # before = before + "\n\n" - # - # - # check_text = self.TextBuffer.get_text(start, cursor_iter, False).decode("utf-8") - # print check_text - - self.TextBuffer.insert_at_cursor("#" + " " + text) - self.select_edit(0, len(text)) + self.text_buffer.insert_at_cursor("#" + " " + text) + self.select_edit(0, len(text)) diff --git a/uberwriter/MarkupBuffer.py b/uberwriter/MarkupBuffer.py index c17fd63..7b7a133 100644 --- a/uberwriter/MarkupBuffer.py +++ b/uberwriter/MarkupBuffer.py @@ -102,7 +102,7 @@ class MarkupBuffer(): # self.ftag = self.TextBuffer.create_tag("pix_front", pixels_above_lines = 100) regex = { "ITALIC": re.compile(r"(\*|_)(.*?)\1", re.UNICODE), # *asdasd* // _asdasd asd asd_ - "STRONG": re.compile(r"(\*\*|__)(.*?)\1", re.UNICODE), # **as das** // __asdasdasd asd ad a__ + "STRONG": re.compile(r"(\*\*|__)(.*?)\1", re.UNICODE), # **as das** // __asdasd asd ad a__ "STRONGITALIC": re.compile(r"(\*\*\*|___)(.*?)\1"), "BLOCKQUOTE": re.compile(r"^([\>]+ )", re.MULTILINE), "STRIKETHROUGH": re.compile(r"~~[^ `~\n].+?~~"), diff --git a/uberwriter/Settings.py b/uberwriter/Settings.py index 1495e6a..58c44b0 100644 --- a/uberwriter/Settings.py +++ b/uberwriter/Settings.py @@ -13,16 +13,14 @@ # with this program. If not, see . ### END LICENSE -from gi.repository import Gtk, Gdk, GLib, Gio - -from gettext import gettext as _ +from gi.repository import Gio class Settings(Gio.Settings): """ UberWriter Settings """ - + def __init__(self): """ Init Settings @@ -35,4 +33,4 @@ class Settings(Gio.Settings): """ settings = Gio.Settings.new("de.wolfvollprecht.UberWriter") settings.__class__ = Settings - return settings \ No newline at end of file + return settings diff --git a/uberwriter/UberwriterAutoCorrect.py b/uberwriter/UberwriterAutoCorrect.py index b70146a..d11eaea 100644 --- a/uberwriter/UberwriterAutoCorrect.py +++ b/uberwriter/UberwriterAutoCorrect.py @@ -43,8 +43,8 @@ class UberwriterAutoCorrect: if self.bubble: self.bubble_label.set_text(suggestion) else: - pos = self.TextView.get_iter_location(iterator) - pos_adjusted = self.TextView.buffer_to_window_coords( + pos = self.text_view.get_iter_location(iterator) + pos_adjusted = self.text_view.buffer_to_window_coords( Gtk.TextWindowType.TEXT, pos.x, pos.y + pos.height) self.bubble_eventbox = Gtk.EventBox.new() self.bubble = Gtk.Grid.new() @@ -52,7 +52,7 @@ class UberwriterAutoCorrect: self.bubble_eventbox.add(self.bubble) self.bubble_eventbox.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.bubble_eventbox.connect("button_press_event", self.clicked_bubble) - self.TextView.add_child_in_window(self.bubble_eventbox, + self.text_view.add_child_in_window(self.bubble_eventbox, Gtk.TextWindowType.TEXT, pos_adjusted[0], pos_adjusted[1]) @@ -196,12 +196,12 @@ class UberwriterAutoCorrect: self.enchant_dict = enchant.Dict(self.language) def __init__(self, textview, textbuffer): - self.TextView = textview + self.text_view = textview self.buffer = textbuffer self.suggestion = "" self.bubble = self.bubble_label = None self.buffer.connect_after('insert-text', self.text_insert) - self.TextView.connect('key-press-event', self.key_pressed) + self.text_view.connect('key-press-event', self.key_pressed) self.language = "en_US" self.frequency_dict = {} diff --git a/uberwriter/UberwriterInlinePreview.py b/uberwriter/UberwriterInlinePreview.py index ecf18c3..bf59507 100644 --- a/uberwriter/UberwriterInlinePreview.py +++ b/uberwriter/UberwriterInlinePreview.py @@ -1,31 +1,30 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- -### BEGIN LICENSE +# BEGIN LICENSE # Copyright (C) 2012, Wolf Vollprecht -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see . -### END LICENSE +# END LICENSE -import os import re -import http.client import urllib -from urllib.error import URLError, HTTPError +from urllib.error import URLError import webbrowser -import gettext import subprocess import tempfile - +import logging import threading -from pprint import pprint +import telnetlib + +from gettext import gettext as _ from gi.repository import Gtk, Gdk, GdkPixbuf, GObject from uberwriter_lib import LatexToPNG @@ -35,12 +34,9 @@ from .FixTable import FixTable from .MarkupBuffer import MarkupBuffer -from gettext import gettext as _ +LOGGER = logging.getLogger('uberwriter') -import logging -logger = logging.getLogger('uberwriter') - -GObject.threads_init() # Still needed? +GObject.threads_init() # Still needed? # TODO: # - Don't insert a span with id, it breaks the text to often @@ -48,78 +44,77 @@ GObject.threads_init() # Still needed? # A jumping URL from that (for preview) # Also, after going to preview, set cursor back to where it was -import telnetlib -import subprocess +class DictAccessor(): -class DictAccessor(object): - - def __init__( self, host='pan.alephnull.com', port=2628, timeout=60 ): - self.tn = telnetlib.Telnet( host, port ) + def __init__(self, host='pan.alephnull.com', port=2628, timeout=60): + self.telnet = telnetlib.Telnet(host, port) self.timeout = timeout - self.login_response = self.tn.expect( [ self.reEndResponse ], self.timeout )[ 2 ] + self.login_response = self.telnet.expect( + [self.reEndResponse], self.timeout)[2] + def get_online(self, word): + process = subprocess.Popen(['dict', '-d', 'wn', word], + stdout=subprocess.PIPE) + return process.communicate()[0] - def getOnline(self, word): - p = subprocess.Popen(['dict', '-d', 'wn', word], stdout=subprocess.PIPE) - return p.communicate()[0] + def run_command(self, cmd): + self.telnet.write(cmd.encode('utf-8') + b'\r\n') + return self.telnet.expect([self.reEndResponse], self.timeout)[2] - def runCommand( self, cmd ): - self.tn.write( cmd.encode('utf-8') + b'\r\n' ) - return self.tn.expect( [ self.reEndResponse ], self.timeout )[ 2 ] - - def getMatches( self, database, strategy, word ): - if database in [ '', 'all' ]: + def get_matches(self, database, strategy, word): + if database in ['', 'all']: d = '*' else: d = database - if strategy in [ '', 'default' ]: + if strategy in ['', 'default']: s = '.' else: s = strategy - w = word.replace( '"', r'\"' ) - tsplit = self.runCommand( 'MATCH %s %s "%s"' % ( d, s, w ) ).splitlines( ) - mlist = list( ) - if tsplit[ -1 ].startswith( b'250 ok' ) and tsplit[ 0 ].startswith( b'1' ): - mlines = tsplit[ 1:-2 ] + w = word.replace('"', r'\"') + tsplit = self.run_command('MATCH %s %s "%s"' % (d, s, w)).splitlines() + mlist = list() + if tsplit[-1].startswith(b'250 ok') and tsplit[0].startswith(b'1'): + mlines = tsplit[1:-2] for line in mlines: - lsplit = line.strip( ).split( ) - db = lsplit[ 0 ] - word = unquote( ' '.join( lsplit[ 1: ] ) ) - mlist.append( ( db, word ) ) + lsplit = line.strip().split() + db = lsplit[0] + word = unquote(' '.join(lsplit[1:])) + mlist.append((db, word)) return mlist - reEndResponse = re.compile( br'^[2-5][0-58][0-9] .*\r\n$', re.DOTALL + re.MULTILINE ) - reDefinition = re.compile( br'^151(.*?)^\.', re.DOTALL + re.MULTILINE ) + reEndResponse = re.compile( + br'^[2-5][0-58][0-9] .*\r\n$', re.DOTALL + re.MULTILINE) + reDefinition = re.compile(br'^151(.*?)^\.', re.DOTALL + re.MULTILINE) - def getDefinition( self, database, word ): - if database in [ '', 'all' ]: + def get_definition(self, database, word): + if database in ['', 'all']: d = '*' else: d = database - w = word.replace( '"', r'\"' ) - dsplit = self.runCommand( 'DEFINE %s "%s"' % ( d, w ) ).splitlines( True ) + w = word.replace('"', r'\"') + dsplit = self.run_command('DEFINE %s "%s"' % (d, w)).splitlines(True) # dsplit = self.getOnline(word).splitlines() - dlist = list( ) - if dsplit[ -1 ].startswith( b'250 ok' ) and dsplit[ 0 ].startswith( b'1' ): - dlines = dsplit[ 1:-1 ] - dtext = b''.join( dlines ) - dlist = self.reDefinition.findall( dtext ) + dlist = list() + if dsplit[-1].startswith(b'250 ok') and dsplit[0].startswith(b'1'): + dlines = dsplit[1:-1] + dtext = b''.join(dlines) + dlist = self.reDefinition.findall(dtext) # print(dlist) - dlist = [ dtext ] + dlist = [dtext] # dlist = dsplit # not using the localhost telnet connection return dlist - def close( self ): - t = self.runCommand( 'QUIT' ) - self.tn.close( ) + def close(self): + t = self.run_command('QUIT') + self.telnet.close() return t def parse_wordnet(self, response): # consisting of group (n,v,adj,adv) # number, description, examples, synonyms, antonyms - capture = {} + lines = response.splitlines() lines = lines[2:] lines = ' '.join(lines) @@ -129,9 +124,9 @@ class DictAccessor(object): act_res = {'defs': [], 'class': 'none', 'num': 'None'} for l in lines: l = l.strip() - if len(l) == 0: + if not l: continue - if l in ['adv', 'adj', 'n', 'v']: + if l in ['adv', 'adj', 'n', 'v']: if act_res: res.append(act_res.copy()) act_res = {} @@ -160,7 +155,7 @@ class DictAccessor(object): act_def['description'] = [] for llll in lll: llll = llll.strip() - if(llll.strip().startswith('"')): + if llll.strip().startswith('"'): act_def['examples'].append(llll) else: act_def['description'].append(llll) @@ -172,8 +167,9 @@ class DictAccessor(object): res.append(act_res.copy()) return res + def check_url(url, item, spinner): - logger.debug("thread started, checking url") + LOGGER.debug("thread started, checking url") error = False try: response = urllib.request.urlopen(url) @@ -183,25 +179,27 @@ def check_url(url, item, spinner): if not error: if (response.code / 100) >= 4: - logger.debug("Website not available") + LOGGER.debug("Website not available") text = _("Website is not available") else: text = _("Website is available") - logger.debug("Response: %s" % text) + LOGGER.debug("Response: %s" % text) spinner.destroy() item.set_label(text) + def get_dictionary(term): da = DictAccessor() - output = da.getDefinition('wn', term) - if(len(output)): + output = da.get_definition('wn', term) + if output: output = output[0] else: return None return da.parse_wordnet(output.decode(encoding='UTF-8')) + def get_web_thumbnail(url, item, spinner): - logger.debug("thread started, generating thumb") + LOGGER.debug("thread started, generating thumb") # error = False @@ -211,9 +209,10 @@ def get_web_thumbnail(url, item, spinner): filename = tempfile.mktemp(suffix='.png') thumb_size = '256' # size can only be 32, 64, 96, 128 or 256! - args = ['gnome-web-photo', '--mode=thumbnail', '-s', thumb_size, url, filename] - p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = p.communicate()[0] + args = ['gnome-web-photo', '--mode=thumbnail', + '-s', thumb_size, url, filename] + process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + _output = process.communicate()[0] image = Gtk.Image.new_from_file(filename) image.show() @@ -229,6 +228,7 @@ def get_web_thumbnail(url, item, spinner): item.add(image) item.show() + def fill_lexikon_bubble(vocab, lexikon_dict): grid = Gtk.Grid.new() i = 0 @@ -259,31 +259,30 @@ def fill_lexikon_bubble(vocab, lexikon_dict): i = i + 1 grid.show_all() return grid - else: - return None - + return None class UberwriterInlinePreview(): def __init__(self, view, text_buffer): - self.TextView = view - self.TextBuffer = text_buffer - self.LatexConverter = LatexToPNG.LatexToPNG() - cursor_mark = self.TextBuffer.get_insert() - cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark) - self.ClickMark = self.TextBuffer.create_mark('click', cursor_iter) + self.text_view = view + self.text_buffer = text_buffer + self.latex_converter = LatexToPNG.LatexToPNG() + cursor_mark = self.text_buffer.get_insert() + cursor_iter = self.text_buffer.get_iter_at_mark(cursor_mark) + self.click_mark = self.text_buffer.create_mark('click', cursor_iter) # Events for popup menu # self.TextView.connect_after('populate-popup', self.populate_popup) # self.TextView.connect_after('popup-menu', self.move_popup) - self.TextView.connect('button-press-event', self.click_move_button) + self.text_view.connect('button-press-event', self.click_move_button) self.popover = None self.settings = Settings.new() def open_popover_with_widget(self, widget): - a = self.TextBuffer.create_child_anchor(self.TextBuffer.get_iter_at_mark(self.ClickMark)) + a = self.text_buffer.create_child_anchor( + self.text_buffer.get_iter_at_mark(self.click_mark)) lbl = Gtk.Label('') - self.TextView.add_child_at_anchor(lbl, a) + self.text_view.add_child_at_anchor(lbl, a) lbl.show() # a = Gtk.Window.new(Gtk.WindowType.POPUP) # a.set_transient_for(self.TextView.get_toplevel()) @@ -296,7 +295,6 @@ class UberwriterInlinePreview(): # if(event.keyval == Gdk.KEY_Escape): # widget.destroy() # a.connect('key-press-event', close) - b = Gtk.Grid.new() alignment = Gtk.Alignment() alignment.props.margin_bottom = 5 alignment.props.margin_top = 5 @@ -310,7 +308,7 @@ class UberwriterInlinePreview(): self.popover.get_style_context().add_class("QuickPreviewPopup") self.popover.add(alignment) # a.add(alignment) - dismiss, rect = self.popover.get_pointing_to() + _dismiss, rect = self.popover.get_pointing_to() rect.y = rect.y - 20 self.popover.set_pointing_to(rect) # widget = Gtk.Label.new("testasds a;12j3 21 lk3j213") @@ -322,21 +320,21 @@ class UberwriterInlinePreview(): # print(self.popover) self.popover.set_property('width-request', 50) - def click_move_button(self, widget, event): + def click_move_button(self, _widget, event): if event.button == 1 and event.state & Gdk.ModifierType.CONTROL_MASK: - x, y = self.TextView.window_to_buffer_coords(2, - int(event.x), - int(event.y)) - self.TextBuffer.move_mark(self.ClickMark, - self.TextView.get_iter_at_location(x, y).iter) - self.populate_popup(self.TextView) + x, y = self.text_view.window_to_buffer_coords(2, + int(event.x), + int(event.y)) + self.text_buffer.move_mark(self.click_mark, + self.text_view.get_iter_at_location(x, y).iter) + self.populate_popup(self.text_view) - def fix_table(self, widget, data=None): - logger.debug('fixing that table') - f = FixTable(self.TextBuffer) - f.fix_table() + def fix_table(self, _widget, _data=None): + LOGGER.debug('fixing that table') + fix_table = FixTable(self.text_buffer) + fix_table.fix_table() - def populate_popup(self, editor, data=None): + def populate_popup(self, _editor, _data=None): # popover = Gtk.Popover.new(editor) # pop_cont = Gtk.Container.new() # popover.add(pop_cont) @@ -354,14 +352,14 @@ class UberwriterInlinePreview(): # menu.prepend(table_item) # menu.show() - start_iter = self.TextBuffer.get_iter_at_mark(self.ClickMark) + start_iter = self.text_buffer.get_iter_at_mark(self.click_mark) # Line offset of click mark line_offset = start_iter.get_line_offset() end_iter = start_iter.copy() start_iter.set_line_offset(0) end_iter.forward_to_line_end() - text = self.TextBuffer.get_text(start_iter, end_iter, False) + text = self.text_buffer.get_text(start_iter, end_iter, False) math = MarkupBuffer.regex["MATH"] link = MarkupBuffer.regex["LINK"] @@ -369,22 +367,18 @@ class UberwriterInlinePreview(): footnote = re.compile(r'\[\^([^\s]+?)\]') image = re.compile(r"!\[(.*?)\]\((.+?)\)") - buf = self.TextBuffer - context_offset = 0 - - matchlist = [] - found_match = False matches = re.finditer(math, text) for match in matches: - logger.debug(match.group(1)) - if match.start() < line_offset and match.end() > line_offset: - success, result = self.LatexConverter.generatepng(match.group(1)) + LOGGER.debug(match.group(1)) + if match.start() < line_offset < match.end(): + success, result = self.latex_converter.generatepng( + match.group(1)) if success: image = Gtk.Image.new_from_file(result) image.show() - logger.debug("logging image") + LOGGER.debug("logging image") # item.add(image) self.open_popover_with_widget(image) else: @@ -407,12 +401,12 @@ class UberwriterInlinePreview(): # Links matches = re.finditer(link, text) for match in matches: - if match.start() < line_offset and match.end() > line_offset: + if match.start() < line_offset < match.end(): text = text[text.find("http://"):-1] item.connect("activate", lambda w: webbrowser.open(text)) - logger.debug(text) + LOGGER.debug(text) statusitem = Gtk.MenuItem.new() statusitem.show() @@ -421,9 +415,9 @@ class UberwriterInlinePreview(): spinner.start() statusitem.add(spinner) spinner.show() - - thread = threading.Thread(target=check_url, - args=(text, statusitem, spinner)) + + thread = threading.Thread(target=check_url, + args=(text, statusitem, spinner)) thread.start() webphoto_item = Gtk.MenuItem.new() @@ -433,15 +427,15 @@ class UberwriterInlinePreview(): webphoto_item.add(spinner_2) spinner_2.show() - thread_image = threading.Thread(target=get_web_thumbnail, - args=(text, webphoto_item, spinner_2)) + thread_image = threading.Thread(target=get_web_thumbnail, + args=(text, webphoto_item, spinner_2)) thread_image.start() item.set_label(_("Open Link in Webbrowser")) item.show() self.open_popover_with_widget(webphoto_item) - + # menu.prepend(separator) # separator.show() @@ -450,25 +444,25 @@ class UberwriterInlinePreview(): # menu.prepend(item) # menu.show() - found_match = True break if not found_match: matches = re.finditer(image, text) for match in matches: - if match.start() < line_offset and match.end() > line_offset: + if match.start() < line_offset < match.end(): path = match.group(2) if path.startswith("file://"): path = path[7:] elif not path.startswith("/"): # then the path is relative - base_path = self.settings.get_value("open-file-path").get_string() + base_path = self.settings.get_value( + "open-file-path").get_string() path = base_path + "/" + path - - logger.info(path) - pb = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 400, 300) - image = Gtk.Image.new_from_pixbuf(pb) + + LOGGER.info(path) + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 400, 300) + image = Gtk.Image.new_from_pixbuf(pixbuf) image.show() self.open_popover_with_widget(image) item.set_property('width-request', 50) @@ -486,15 +480,18 @@ class UberwriterInlinePreview(): if not found_match: matches = re.finditer(footnote, text) for match in matches: - if match.start() < line_offset and match.end() > line_offset: - logger.debug(match.group(1)) - footnote_match = re.compile(r"\[\^" + match.group(1) + r"\]: (.+(?:\n|\Z)(?:^[\t].+(?:\n|\Z))*)", re.MULTILINE) + if match.start() < line_offset < match.end(): + LOGGER.debug(match.group(1)) + footnote_match = re.compile( + r"\[\^" + match.group(1) + r"\]: (.+(?:\n|\Z)(?:^[\t].+(?:\n|\Z))*)", + re.MULTILINE) replace = re.compile(r"^\t", re.MULTILINE) - start, end = self.TextBuffer.get_bounds() - fn_match = re.search(footnote_match, self.TextBuffer.get_text(start, end, False)) + start, end = self.text_buffer.get_bounds() + fn_match = re.search( + footnote_match, self.text_buffer.get_text(start, end, False)) label = Gtk.Label() label.set_alignment(0.0, 0.5) - logger.debug(fn_match) + LOGGER.debug(fn_match) if fn_match: result = re.sub(replace, "", fn_match.group(1)) if result.endswith("\n"): @@ -517,21 +514,19 @@ class UberwriterInlinePreview(): break if not found_match: - start_iter = self.TextBuffer.get_iter_at_mark(self.ClickMark) + start_iter = self.text_buffer.get_iter_at_mark(self.click_mark) start_iter.backward_word_start() end_iter = start_iter.copy() end_iter.forward_word_end() - word = self.TextBuffer.get_text(start_iter, end_iter, False) + word = self.text_buffer.get_text(start_iter, end_iter, False) terms = get_dictionary(word) if terms: - sc = Gtk.ScrolledWindow.new() - sc.add(fill_lexikon_bubble(word, terms)) - sc.props.width_request = 500 - sc.props.height_request = 400 - sc.show_all() - self.open_popover_with_widget(sc) - - return + scrolled_window = Gtk.ScrolledWindow.new() + scrolled_window.add(fill_lexikon_bubble(word, terms)) + scrolled_window.props.width_request = 500 + scrolled_window.props.height_request = 400 + scrolled_window.show_all() + self.open_popover_with_widget(scrolled_window) def move_popup(self): pass diff --git a/uberwriter/UberwriterTextEditor.py b/uberwriter/UberwriterTextEditor.py index 8b4afe5..d2f7a1d 100644 --- a/uberwriter/UberwriterTextEditor.py +++ b/uberwriter/UberwriterTextEditor.py @@ -44,8 +44,6 @@ Extending A TextEditor is Gtk.TextView """ - - try: from gi.repository import Gtk from gi.repository import Gdk @@ -55,8 +53,6 @@ try: except: print("couldn't load depencies") - - import logging LOGGER = logging.getLogger('uberwriter') @@ -104,7 +100,6 @@ class TextEditor(Gtk.TextView): 'redo': (GObject.SIGNAL_ACTION, None, ()) } - # TODO: delete this? def scroll_to_iter(self, iterable, *args): self.get_buffer().place_cursor(iterable) diff --git a/uberwriter/UberwriterWindow.py b/uberwriter/UberwriterWindow.py index 709062d..f00a285 100644 --- a/uberwriter/UberwriterWindow.py +++ b/uberwriter/UberwriterWindow.py @@ -20,7 +20,6 @@ import os import codecs import webbrowser import urllib -import pickle import logging import mimetypes @@ -195,6 +194,8 @@ class UberwriterWindow(Window): self.check_scroll(self.text_buffer.get_insert()) if self.spellcheck: self.spell_checker._misspelled.set_property('underline', 0) + self.click_event = self.text_editor.connect("button-release-event", + self.on_focusmode_click) else: self.remove_typewriter() self.focusmode = False @@ -210,7 +211,14 @@ class UberwriterWindow(Window): self.update_line_and_char_count() self.check_scroll() if self.spellcheck: - self.spell_checker._misspelled.set_property('underline', 4) + self.SpellChecker._misspelled.set_property('underline', 4) + _click_event = self.text_editor.disconnect(self.click_event) + + def on_focusmode_click(self, *_args): + """call MarkupBuffer to mark as bold the line where the cursor is + """ + + self.markup_buffer.markup_buffer(1) def scroll_smoothly(self, widget, frame_clock, _data=None): if self.smooth_scroll_data['target_pos'] == -1: diff --git a/uberwriter_lib/.pylintrc b/uberwriter_lib/.pylintrc new file mode 100644 index 0000000..2dc4fd2 --- /dev/null +++ b/uberwriter_lib/.pylintrc @@ -0,0 +1,568 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +# disable=print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + non-ascii-bytes-literal, + raw-checker-failed, + bad-inline-option, + locally-disabled, + locally-enabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + deprecated-itertools-function, + deprecated-types-field, + next-method-defined, + dict-items-not-iterating, + dict-keys-not-iterating, + dict-values-not-iterating, + deprecated-operator-function, + deprecated-urllib-function, + xreadlines-attribute, + deprecated-sys-function, + exception-escape, + comprehension-escape + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=msvs + +# Tells whether to display a full report or only the messages. +reports=yes + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma, + dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: he (hspell), en_IE +# (hunspell), es_BO (hunspell), en_NZ (hunspell), es_DO (hunspell), es_MX +# (hunspell), en_ZA (hunspell), en_IN (hunspell), en_TT (hunspell), ca +# (aspell), ca_FR (hunspell), es_HN (hunspell), ca_AD (hunspell), es_SV +# (hunspell), es_PA (hunspell), en_DK (hunspell), es_NI (hunspell), es_PE +# (hunspell), en_SG (hunspell), es_UY (hunspell), en_BS (hunspell), en_BW +# (hunspell), es_CL (hunspell), es_AR (hunspell), en_BZ (hunspell), es_CO +# (hunspell), en_ZW (hunspell), en_HK (hunspell), es_CR (hunspell), en_NA +# (hunspell), es_PR (hunspell), en_JM (hunspell), es_VE (hunspell), en_AG +# (hunspell), es_CU (hunspell), en_NG (hunspell), ca_ES (hunspell), es_ES +# (hunspell), es_ANY (hunspell), es_EC (hunspell), es_GT (hunspell), en_PH +# (hunspell), en_GB (hunspell), en_US (hunspell), ca_IT (hunspell), es_PY +# (hunspell), en_GH (hunspell).. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=yes + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement. +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception". +overgeneral-exceptions=Exception diff --git a/uberwriter_lib/AppWindow.py b/uberwriter_lib/AppWindow.py index 4aa5c91..d8b2089 100644 --- a/uberwriter_lib/AppWindow.py +++ b/uberwriter_lib/AppWindow.py @@ -1,32 +1,29 @@ -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. -import sys import argparse - -import gettext -import os +from gettext import gettext as _ import gi -gi.require_version('Gtk', '3.0') +gi.require_version('Gtk', '3.0') # pylint: disable=wrong-import-position from gi.repository import GLib, Gio, Gtk, GdkPixbuf -from . helpers import get_builder, show_uri, get_help_uri, get_media_path from uberwriter import UberwriterWindow from uberwriter.Settings import Settings from uberwriter_lib import set_up_logging from uberwriter_lib.PreferencesDialog import PreferencesDialog +from . helpers import get_builder, get_media_path + -from gettext import gettext as _ class Window(Gtk.ApplicationWindow): @@ -35,26 +32,26 @@ class Window(Gtk.ApplicationWindow): # This will be in the windows group and have the "win" prefix max_action = Gio.SimpleAction.new_stateful("maximize", None, - GLib.Variant.new_boolean(False)) + GLib.Variant.new_boolean(False)) max_action.connect("change-state", self.on_maximize_toggle) self.add_action(max_action) # Keep it in sync with the actual state self.connect("notify::is-maximized", - lambda obj, pspec: max_action.set_state( - GLib.Variant.new_boolean(obj.props.is_maximized))) - - self.set_default_size(850,500) - + lambda obj, pspec: max_action.set_state( + GLib.Variant.new_boolean(obj.props.is_maximized))) + + self.set_default_size(850, 500) + icon_file = get_media_path("uberwriter.svg") self.set_icon_from_file(icon_file) - + builder = get_builder('UberwriterWindow') new_object = builder.get_object("FullscreenOverlay") - + self.contents = new_object self.add(self.contents) - + self.finish_initializing(builder) def on_maximize_toggle(self, action, value): @@ -63,7 +60,7 @@ class Window(Gtk.ApplicationWindow): self.maximize() else: self.unmaximize() - + def finish_initializing(self, builder): """Called while initializing this instance in __new__ @@ -74,11 +71,10 @@ class Window(Gtk.ApplicationWindow): # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self, True) - self.PreferencesDialog = None # class - self.preferences_dialog = None # instance - self.AboutDialog = None # class + self.PreferencesDialog = None # class + self.preferences_dialog = None # instance + self.AboutDialog = None # class - # self.settings = Gio.Settings("net.launchpad.uberwriter") # self.settings.connect('changed', self.on_preferences_changed) @@ -94,7 +90,7 @@ class Window(Gtk.ApplicationWindow): self.indicator = indicator.new_application_indicator(self) except ImportError: pass - + class Application(Gtk.Application): @@ -104,7 +100,7 @@ class Application(Gtk.Application): **kwargs) self.window = None self.settings = Settings.new() - + def do_startup(self): Gtk.Application.do_startup(self) @@ -113,11 +109,11 @@ class Application(Gtk.Application): action = Gio.SimpleAction.new("help", None) action.connect("activate", self.on_help) self.add_action(action) - + action = Gio.SimpleAction.new("shortcuts", None) action.connect("activate", self.on_shortcuts) self.add_action(action) - + action = Gio.SimpleAction.new("about", None) action.connect("activate", self.on_about) self.add_action(action) @@ -136,26 +132,26 @@ class Application(Gtk.Application): set_dark_mode = self.settings.get_value("dark-mode") action = Gio.SimpleAction.new_stateful("dark_mode", - None, - GLib.Variant.new_boolean(set_dark_mode)) + None, + GLib.Variant.new_boolean(set_dark_mode)) action.connect("change-state", self.on_dark_mode) self.add_action(action) action = Gio.SimpleAction.new_stateful("focus_mode", - None, - GLib.Variant.new_boolean(False)) + None, + GLib.Variant.new_boolean(False)) action.connect("change-state", self.on_focus_mode) self.add_action(action) action = Gio.SimpleAction.new_stateful("fullscreen", - None, - GLib.Variant.new_boolean(False)) + None, + GLib.Variant.new_boolean(False)) action.connect("change-state", self.on_fullscreen) self.add_action(action) action = Gio.SimpleAction.new_stateful("preview", - None, - GLib.Variant.new_boolean(False)) + None, + GLib.Variant.new_boolean(False)) action.connect("change-state", self.on_preview) self.add_action(action) @@ -164,8 +160,8 @@ class Application(Gtk.Application): self.add_action(action) action = Gio.SimpleAction.new_stateful("spellcheck", - None, - GLib.Variant.new_boolean(True)) + None, + GLib.Variant.new_boolean(True)) action.connect("change-state", self.on_spellcheck) self.add_action(action) @@ -207,20 +203,18 @@ class Application(Gtk.Application): action.connect("activate", self.on_preferences) self.add_action(action) - '''Shortcuts''' - self.set_accels_for_action("app.focus_mode",["d"]) - self.set_accels_for_action("app.fullscreen",["F11"]) - self.set_accels_for_action("app.preview",["p"]) - self.set_accels_for_action("app.search",["f"]) - self.set_accels_for_action("app.spellcheck",["F7"]) - - self.set_accels_for_action("app.new",["n"]) - self.set_accels_for_action("app.open",["o"]) - self.set_accels_for_action("app.save",["s"]) - self.set_accels_for_action("app.save_as",["s"]) + self.set_accels_for_action("app.focus_mode", ["d"]) + self.set_accels_for_action("app.fullscreen", ["F11"]) + self.set_accels_for_action("app.preview", ["p"]) + self.set_accels_for_action("app.search", ["f"]) + self.set_accels_for_action("app.spellcheck", ["F7"]) + self.set_accels_for_action("app.new", ["n"]) + self.set_accels_for_action("app.open", ["o"]) + self.set_accels_for_action("app.save", ["s"]) + self.set_accels_for_action("app.save_as", ["s"]) def do_activate(self): # We only allow a single window and raise any existing ones @@ -228,17 +222,16 @@ class Application(Gtk.Application): # Windows are associated with the application # when the last one is closed the application shuts down # self.window = Window(application=self, title="UberWriter") - self.window = UberwriterWindow.UberwriterWindow(application=self, title="UberWriter") - if len(self.args) > 0: - self.window.load_file(self.args[0]) + self.window = UberwriterWindow.UberwriterWindow( + application=self, title="UberWriter") + if self.args: + self.window.load_file(self.args[0]) if self.options.experimental_features: self.window.use_experimental_features(True) - self.window.present() - def do_command_line(self, command_line): - + def do_command_line(self, _command_line): """Support for command line options""" parser = argparse.ArgumentParser() parser.add_argument( @@ -247,37 +240,36 @@ class Application(Gtk.Application): parser.add_argument( "-e", "--experimental-features", help=_("Use experimental features"), action='store_true' - ) + ) (self.options, self.args) = parser.parse_known_args() set_up_logging(self.options) - + self.activate() return 0 - - def on_about(self, action, param): + def on_about(self, _action, _param): builder = get_builder('About') about_dialog = builder.get_object("AboutDialog") about_dialog.set_transient_for(self.window) logo_file = get_media_path("uberwriter.svg") logo = GdkPixbuf.Pixbuf.new_from_file(logo_file) - + about_dialog.set_logo(logo) - + about_dialog.present() - - def on_help(self, action, param): + + def on_help(self, _action, _param): self.window.open_pandoc_markdown(self) - def on_translate(self, action, param): + def on_translate(self, _action, _param): self.window.open_translation() - def on_donate(self, action, param): + def on_donate(self, _action, _param): self.window.open_donation() - - def on_shortcuts(self, action, param): + + def on_shortcuts(self, _action, _param): builder = get_builder('Shortcuts') builder.get_object("shortcuts").set_transient_for(self.window) builder.get_object("shortcuts").show() @@ -285,12 +277,13 @@ class Application(Gtk.Application): def on_dark_mode(self, action, value): action.set_state(value) self.settings.set_value("dark-mode", - GLib.Variant("b", value)) + GLib.Variant("b", value)) self.window.toggle_dark_mode(value) - #this changes the headerbar theme accordingly + # this changes the headerbar theme accordingly self.dark_setting = Gtk.Settings.get_default() - self.dark_setting.set_property("gtk-application-prefer-dark-theme", value) + self.dark_setting.set_property( + "gtk-application-prefer-dark-theme", value) def on_focus_mode(self, action, value): action.set_state(value) @@ -304,44 +297,44 @@ class Application(Gtk.Application): action.set_state(value) self.window.toggle_preview(value) - def on_search(self, action, value): + def on_search(self, _action, _value): self.window.open_search_and_replace() def on_spellcheck(self, action, value): action.set_state(value) self.window.toggle_spellcheck(value) - def on_new(self, action, value): + def on_new(self, _action, _value): self.window.new_document() - def on_open(self, action, value): + def on_open(self, _action, _value): self.window.open_document() - + def on_open_recent(self, action, value): pass - - def on_example(self, action, value): + + def on_example(self, _action, _value): self.window.open_uberwriter_markdown() - - def on_save(self, action, value): + + def on_save(self, _action, _value): self.window.save_document() - - def on_save_as(self, action, value): + + def on_save_as(self, _action, _value): self.window.save_document_as() - - def on_export(self, action, value): + + def on_export(self, _action, _value): self.window.open_advanced_export() - - def on_html_copy(self, action, value): + + def on_html_copy(self, _action, _value): self.window.copy_html_to_clipboard() - def on_preferences(self, action, value): + def on_preferences(self, _action, _value): PreferencesWindow = PreferencesDialog() PreferencesWindow.set_application(self) PreferencesWindow.set_transient_for(self.window) PreferencesWindow.show() - - def on_quit(self, action, param): + + def on_quit(self, _action, _param): self.quit() # ~ if __name__ == "__main__": diff --git a/uberwriter_lib/Builder.py b/uberwriter_lib/Builder.py index 96c7bab..123acd3 100644 --- a/uberwriter_lib/Builder.py +++ b/uberwriter_lib/Builder.py @@ -1,34 +1,35 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- -### BEGIN LICENSE +# BEGIN LICENSE # Copyright (C) 2012, Wolf Vollprecht -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see . -### END LICENSE +# END LICENSE ### DO NOT EDIT THIS FILE ### '''Enhances builder connections, provides object to access glade objects''' -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import GObject, Gtk # pylint: disable=E0611 import inspect import functools import logging -logger = logging.getLogger('uberwriter_lib') - from xml.etree.cElementTree import ElementTree +import gi +gi.require_version('Gtk', '3.0') # pylint: disable=wrong-import-position +from gi.repository import GObject, Gtk # pylint: disable=E0611 + +LOGGER = logging.getLogger('uberwriter_lib') + # this module is big so uses some conventional prefixes and postfixes # *s list, except self.widgets is a dictionary # *_dict dictionary @@ -57,7 +58,7 @@ class Builder(Gtk.Builder): # pylint: disable=R0201 # this is a method so that a subclass of Builder can redefine it def default_handler(self, - handler_name, filename, *args, **kwargs): + handler_name, filename, *args, **kwargs): '''helps the apprentice guru glade defined handlers that do not exist come here instead. @@ -65,7 +66,7 @@ class Builder(Gtk.Builder): now he can define any likely candidates in glade and notice which ones get triggered when he plays with the project. this method does not appear in Gtk.Builder''' - logger.debug('''tried to call non-existent function:%s() + LOGGER.debug('''tried to call non-existent function:%s() expected in %s args:%s kwargs:%s''', handler_name, filename, args, kwargs) @@ -101,8 +102,8 @@ class Builder(Gtk.Builder): connections = [ (name, - ele_signal.attrib['name'], - ele_signal.attrib['handler']) for ele_signal in ele_signals] + ele_signal.attrib['name'], + ele_signal.attrib['handler']) for ele_signal in ele_signals] if connections: self.connections.extend(connections) @@ -110,7 +111,7 @@ class Builder(Gtk.Builder): ele_signals = tree.getiterator("signal") for ele_signal in ele_signals: self.glade_handler_dict.update( - {ele_signal.attrib["handler"]: None}) + {ele_signal.attrib["handler"]: None}) def connect_signals(self, callback_obj): '''connect the handlers defined in glade @@ -131,8 +132,8 @@ class Builder(Gtk.Builder): connection_dict[item[0]] = handler # replace the run time warning - logger.warn("expected handler '%s' in %s", - item[0], filename) + LOGGER.warning("expected handler '%s' in %s", + item[0], filename) # connect glade define handlers Gtk.Builder.connect_signals(self, connection_dict) @@ -140,8 +141,8 @@ class Builder(Gtk.Builder): # let's tell the user how we applied the glade design for connection in self.connections: widget_name, signal_name, handler_name = connection - logger.debug("connect builder by design '%s', '%s', '%s'", - widget_name, signal_name, handler_name) + LOGGER.debug("connect builder by design '%s', '%s', '%s'", + widget_name, signal_name, handler_name) def get_ui(self, callback_obj=None, by_name=True): '''Creates the ui object with widgets as attributes @@ -167,6 +168,7 @@ class Builder(Gtk.Builder): # apart from the glade widgets class UiFactory(): ''' provides an object with attributes as glade widgets''' + def __init__(self, widget_dict): self._widget_dict = widget_dict for (widget_name, widget) in widget_dict.items(): @@ -177,14 +179,14 @@ class UiFactory(): cannot_message = """cannot bind ui.%s, name already exists consider using a pythonic name instead of design name '%s'""" consider_message = """consider using a pythonic name instead of design name '%s'""" - + for (widget_name, widget) in widget_dict.items(): pyname = make_pyname(widget_name) if pyname != widget_name: if hasattr(self, pyname): - logger.debug(cannot_message, pyname, widget_name) + LOGGER.debug(cannot_message, pyname, widget_name) else: - logger.debug(consider_message, widget_name) + LOGGER.debug(consider_message, widget_name) setattr(self, pyname, widget) def iterator(): @@ -203,14 +205,14 @@ def make_pyname(name): pyname = '' for character in name: if (character.isalpha() or character == '_' or - (pyname and character.isdigit())): + (pyname and character.isdigit())): pyname += character else: pyname += '_' return pyname -# Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we +# Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we # need to reimplement inspect.getmembers. GObject introspection doesn't # play nice with it. def getmembers(obj, check): @@ -233,10 +235,10 @@ def dict_from_callback_obj(callback_obj): aliased_methods = [x[1] for x in methods if hasattr(x[1], 'aliases')] # a method may have several aliases - #~ @alias('on_btn_foo_clicked') - #~ @alias('on_tool_foo_activate') - #~ on_menu_foo_activate(): - #~ pass + # ~ @alias('on_btn_foo_clicked') + # ~ @alias('on_tool_foo_activate') + # ~ on_menu_foo_activate(): + # ~ pass alias_groups = [(x.aliases, x) for x in aliased_methods] aliases = [] @@ -287,13 +289,13 @@ def auto_connect_by_name(callback_obj, builder): handler_names.append("on_%s" % sig) do_connect(item, sig, handler_names, - callback_handler_dict, builder.connections) + callback_handler_dict, builder.connections) log_unconnected_functions(callback_handler_dict, builder.connections) def do_connect(item, signal_name, handler_names, - callback_handler_dict, connections): + callback_handler_dict, connections): '''connect this signal to an unused handler''' widget_name, widget = item @@ -305,8 +307,8 @@ def do_connect(item, signal_name, handler_names, widget.connect(signal_name, callback_handler_dict[handler_name]) connections.append(connection) - logger.debug("connect builder by name '%s','%s', '%s'", - widget_name, signal_name, handler_name) + LOGGER.debug("connect builder by name '%s','%s', '%s'", + widget_name, signal_name, handler_name) def log_unconnected_functions(callback_handler_dict, connections): @@ -324,4 +326,4 @@ def log_unconnected_functions(callback_handler_dict, connections): pass for handler_name in unconnected: - logger.debug("Not connected to builder '%s'", handler_name) + LOGGER.debug("Not connected to builder '%s'", handler_name) diff --git a/uberwriter_lib/LatexToPNG.py b/uberwriter_lib/LatexToPNG.py index 858962e..10dea51 100644 --- a/uberwriter_lib/LatexToPNG.py +++ b/uberwriter_lib/LatexToPNG.py @@ -16,11 +16,15 @@ COPYING granted under the terms of the MIT License. """ -import os, sys, tempfile, subprocess +import os +import sys +import tempfile +import subprocess + class LatexToPNG(): - TEX_HEADER = r'''\documentclass{article} + TEX_HEADER = r'''\documentclass{article} \usepackage{amsmath} \usepackage{amsthm} \usepackage{amssymb} @@ -30,79 +34,79 @@ class LatexToPNG(): \newcommand{\T}{\text{T}} % Transpose \pagestyle{empty} \begin{document}''' - - TEX_FOOTER = r'''\end{document}''' - def __init__(self): - self.temp_result = tempfile.NamedTemporaryFile(suffix='.png') + TEX_FOOTER = r'''\end{document}''' - def latex2png(self, tex, outfile, dpi, modified): - '''Convert LaTeX input file infile to PNG file named outfile.''' - outfile = os.path.abspath(outfile) - outdir = os.path.dirname(outfile) - texfile = tempfile.mktemp(suffix='.tex', dir=os.path.dirname(outfile)) - basefile = os.path.splitext(texfile)[0] - dvifile = basefile + '.dvi' - temps = [basefile + ext for ext in ('.tex','.dvi', '.aux', '.log')] - skip = False - - tex = '%s\n%s\n%s\n' % (self.TEX_HEADER, tex.strip(), self.TEX_FOOTER) + def __init__(self): + self.temp_result = tempfile.NamedTemporaryFile(suffix='.png') - open(texfile, 'w').write(tex) - saved_pwd = os.getcwd() + def latex2png(self, tex, outfile, dpi, modified): + '''Convert LaTeX input file infile to PNG file named outfile.''' + outfile = os.path.abspath(outfile) + outdir = os.path.dirname(outfile) + texfile = tempfile.mktemp(suffix='.tex', dir=os.path.dirname(outfile)) + basefile = os.path.splitext(texfile)[0] + dvifile = basefile + '.dvi' + temps = [basefile + ext for ext in ('.tex', '.dvi', '.aux', '.log')] + skip = False - os.chdir(outdir) - - args = ['latex', '-halt-on-error', texfile] - p = subprocess.Popen(args, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE) + tex = '%s\n%s\n%s\n' % (self.TEX_HEADER, tex.strip(), self.TEX_FOOTER) - output = p.stdout - output_lines = output.readlines() - if os.path.isfile(dvifile): # DVI File exists - # Convert DVI file to PNG. - args = ['dvipng', - '-D', str(dpi), - '-T', 'tight', - '-x', '1000', - '-z', '9', - '-bg', 'Transparent', - '-o', outfile, - dvifile] + open(texfile, 'w').write(tex) + saved_pwd = os.getcwd() - p = subprocess.Popen(args) - p.communicate() - - else: - self.clean_up(temps) - ''' + os.chdir(outdir) + + args = ['latex', '-halt-on-error', texfile] + p = subprocess.Popen(args, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE) + + output = p.stdout + output_lines = output.readlines() + if os.path.isfile(dvifile): # DVI File exists + # Convert DVI file to PNG. + args = ['dvipng', + '-D', str(dpi), + '-T', 'tight', + '-x', '1000', + '-z', '9', + '-bg', 'Transparent', + '-o', outfile, + dvifile] + + p = subprocess.Popen(args) + p.communicate() + + else: + self.clean_up(temps) + ''' Errors in Latex output start with "! " Stripping exclamation marks and superflous newlines and telling the user what he's done wrong. ''' - i = [] - error = "" - for line in output_lines: - line = line.decode('utf-8') - if line.startswith("!"): - error += line[2:] # removing "! " - if error.endswith("\n"): - error = error[:-1] - raise Exception(error) + i = [] + error = "" + for line in output_lines: + line = line.decode('utf-8') + if line.startswith("!"): + error += line[2:] # removing "! " + if error.endswith("\n"): + error = error[:-1] + raise Exception(error) - def generatepng(self, formula): - try: - self.temp_result = tempfile.NamedTemporaryFile(suffix='.png') - formula = "$" + formula + "$" - self.latex2png(formula, self.temp_result.name, 300, False) - return (True, self.temp_result.name) + def generatepng(self, formula): + try: + self.temp_result = tempfile.NamedTemporaryFile(suffix='.png') + formula = "$" + formula + "$" + self.latex2png(formula, self.temp_result.name, 300, False) + return (True, self.temp_result.name) - except Exception as e: - self.temp_result.close() - return (False, e.args[0]) + except Exception as e: + self.temp_result.close() + return (False, e.args[0]) - def clean_up(self, files): - for f in files: - if os.path.isfile(f): - os.remove(f) \ No newline at end of file + def clean_up(self, files): + for f in files: + if os.path.isfile(f): + os.remove(f) diff --git a/uberwriter_lib/helpers.py b/uberwriter_lib/helpers.py index 7d8abfd..efd4cde 100644 --- a/uberwriter_lib/helpers.py +++ b/uberwriter_lib/helpers.py @@ -1,39 +1,34 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- -### BEGIN LICENSE +# BEGIN LICENSE # Copyright (C) 2012, Wolf Vollprecht -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see . -### END LICENSE +# END LICENSE ### DO NOT EDIT THIS FILE ### -"""Helpers for an Ubuntu application.""" +"""Helpers for the application.""" import logging import os -import sys import shutil from . uberwriterconfig import get_data_file from . Builder import Builder -import locale -import gettext - -from gettext import gettext as _ def get_builder(builder_file_name): - """Return a fully-instantiated Gtk.Builder instance from specified ui + """Return a fully-instantiated Gtk.Builder instance from specified ui file - + :param builder_file_name: The name of the builder file, without extension. Assumed to be in the 'ui' directory under the data path. """ @@ -50,35 +45,50 @@ def get_builder(builder_file_name): # Owais Lone : To get quick access to icons and stuff. def get_media_file(media_file_name): + """Return the full path of a given filename under the media dir + (starts with file:///) + """ + media_filename = get_data_file('media', '%s' % (media_file_name,)) if not os.path.exists(media_filename): media_filename = None - return "file:///"+media_filename + return "file:///" + media_filename + def get_media_path(media_file_name): + """Return the full path of a given filename under the media dir + (doesn't start with file:///) + """ + media_filename = get_data_file('media', '%s' % (media_file_name,)) if not os.path.exists(media_filename): media_filename = None return media_filename + def get_script_path(script_file_name): + """Return the full path of a given filename under the script dir + """ script_filename = get_data_file('lua', '%s' % (script_file_name,)) if not os.path.exists(script_filename): script_filename = None return script_filename + class NullHandler(logging.Handler): def emit(self, record): pass + def set_up_logging(opts): # add a handler to prevent basicConfig root = logging.getLogger() null_handler = NullHandler() root.addHandler(null_handler) - formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s() '%(message)s'") + formatter = logging.Formatter( + "%(levelname)s:%(name)s: %(funcName)s() '%(message)s'") logger = logging.getLogger('uberwriter') logger_sh = logging.StreamHandler() @@ -97,6 +107,7 @@ def set_up_logging(opts): if opts.verbose and opts.verbose > 1: lib_logger.setLevel(logging.DEBUG) + def get_help_uri(page=None): # help_uri from source tree - default language here = os.path.dirname(__file__) @@ -112,11 +123,13 @@ def get_help_uri(page=None): return help_uri + def show_uri(parent, link): - from gi.repository import Gtk # pylint: disable=E0611 + from gi.repository import Gtk # pylint: disable=E0611 screen = parent.get_screen() Gtk.show_uri(screen, link, Gtk.get_current_event_time()) + def alias(alternative_function_name): '''see http://www.drdobbs.com/web-development/184406073#l9''' def decorator(function): @@ -127,5 +140,15 @@ def alias(alternative_function_name): return function return decorator + def exist_executable(command): - return shutil.which(command) is not None \ No newline at end of file + """return if a command can be executed in the SO + + Arguments: + command {str} -- a command + + Returns: + {bool} -- if the given command exists in the system + """ + + return shutil.which(command) is not None diff --git a/uberwriter_lib/uberwriterconfig.py b/uberwriter_lib/uberwriterconfig.py index 38e694c..7347106 100644 --- a/uberwriter_lib/uberwriterconfig.py +++ b/uberwriter_lib/uberwriterconfig.py @@ -1,16 +1,16 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- ### BEGIN LICENSE # Copyright (C) 2012, Wolf Vollprecht -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along +# +# You should have received a copy of the GNU General Public License along # with this program. If not, see . ### END LICENSE @@ -30,9 +30,7 @@ __version__ = 'VERSION' import os -from locale import gettext as _ - -class project_path_not_found(Exception): +class ProjectPathNotFound(Exception): """Raised when we can't find the project directory.""" @@ -68,7 +66,7 @@ def get_data_path(): if not os.path.exists(abs_data_path): abs_data_path = '/opt/uberwriter/data/' elif not os.path.exists(abs_data_path): - raise project_path_not_found + raise ProjectPathNotFound return abs_data_path