Заранее предупреждаю - всё тут немного сумбурно...

Графический редактор Inkscape (если пользователю и Inkscape повезёт с установкой) может запускать Python-скрипты рисующие всякие линии, круги, прямоугольники и даже текст.

Текст плагина для поиска совпадений в двух текстовых файлах (textfa.txt и textfb.txt):

#!/usr/bin/env python
# coding=utf-8

# импортируем графическую магию и прочую требуху
import inkex
from inkex import TextElement, Group, Layer, Tspan, PathElement
FntSz = 3.175    # будем выводить текст - так вот это размер текста 
FntHt = 1.6      # расстояние между строками
FilenameA = '/home/user_name/textfa.txt'    # файл первый
FilenameB = '/home/user_name/textfb.txt'    # файл второй
sxA = 0       # координаты для вывода текста из первого файла
syA = 0
sxB = 1000       # координаты для вывода текста из второго файла
syB = 0
class Comparator(inkex.EffectExtension):     # ну что сказать... 
#--#--#-----------I want to distincs the exact "SourceText" in this group somehow -------------------------------
 def effect(self):                # этот метод класса Comparator запускается из Инкскейпа
    
    ar23A = ['nothing','here']   # подготавливаем массив для чтения из первого файла
    ar23B = ['nothing','here']   # подготавливаем массив для чтения из второго файла 

    Layer = self.PrepareThelayer('Habr23')  # приготавливаем пустой Графич_слой для рисования

    with open(FilenameA, 'r') as file:
       BigTxA = file.read()                  # читаем текст из первого файла
       ar23A = BigTxA.split('\n')           # разбиваем текст в массив на строки 

    self.drawText(Layer, sxA, syA, ar23A)   # выводим массив строк в Графич_файл

    with open(FilenameB, 'r') as file:
       BigTxB = file.read()                  # читаем текст из второго файла
       ar23B = BigTxB.split('\n')           # разбиваем текст в массив на строки

    self.drawText(Layer, sxB, syB, ar23B)   # выводим массив строк в Графич_файл

    for nA in range(0, len(ar23A)):         # перебираем оба массива со строками в поисках...
      for nB in range(0, len(ar23B)):
         if (ar23A[nA] == ar23B[nB]):       # ...в поисках одинаковых строк
           if(len(ar23A[nA]) > 1):          # избегаем пустых строк
            yA = nA * FntHt * FntSz         # подготавливаем координаты для линии между совпадающими строками
            yB = nB * FntHt * FntSz

            self.drawLine(Layer, sxA+200,syA+yA, sxB, syB+yB)   # рисуем линию между совпадающими строками
            
#--#--#---------------------------------------------------------------
 def drawText(self, Layer, x, y, Tmass):
        # начинаем церемонию создания текст_блока
        elem = TextElement(x=str(x), y=str(y))
        
        elem.style = {    # задаём внешний вид нашего текст_блока
            'font-size':str(FntSz),    # размер шрифта
            'line-height':str(FntHt),  # расстояние между строк
            'font-family':'courier',   # и т.д.
            '-inkscape-font-specification':'courier',
            'stroke-width':'0.264583'}

        # ещё немного церемониальных заклинаний
        elem.set('xml:space',"preserve")
        # и начинаем построчно выводить текст
        for n in range(0, len(Tmass)):
          tx23 = Tspan(Tmass[n])       # создаем строку
          tx23.set('sodipodi:role',"line")  # магия...
          tx23.set('x' ,x)
          tx23.set('y' , y + (n * FntHt * FntSz))
          elem.add(tx23)  # присоединяя каждую строку к текстовому блоку

        # выводим текстовый блок в Графич_слой (то есть выводим на экран)
        Layer.add(elem)   # выводим созданный текстовый блок на экран

#--#--#---------------------------------------------------------------
 def PrepareThelayer(self, Lname):
         # сперва попробуем найти Графич_слой с желаемым имменем
         svg = self.document.getroot()  # ищем в документе 
         for g in svg.xpath('//svg:g', namespaces=inkex.NSS):
            if g.get(inkex.addNS('groupmode', 'inkscape')) == 'layer':
              t23 = g.get(inkex.addNS('label', 'inkscape'))
              if (t23 == Lname):    # совпадает ли имя Графич_слоя?
                for child in g:     # если это желаемый слой
                  g.remove(child)   # очищаем его содержимое
                return g
         # Графич_слой с желаемым именем мы не нашли
         # поэтому создаём новый Графич_слой с желаемым именем
         newlayer=self.svg.add(Layer.new(Lname))   
         return newlayer   
#--#--#---------------------------------------------------------------
 def drawLine(self, layer, x0,y0, x99, y99):
         # рисуем линию соблюдая все церемонии Инкскейпа
         # сперва создаем линию...
         line = PathElement()
         line.set('fill','none')
         line.set('stroke',"#4FF")       # цвет
         line.set('stroke-width','0.8')  # толщину
         line.set('d','M %f %f %f %f' % (x0, y0, x99, y99)) # координаты 
         # теперь выводим линию в Графич_слой (то есть выводим на экран)
         layer.append(line)

# Питонская ООП-магия
if __name__ == '__main__':
    Comparator().run()

Теперь будут некоторые пояснения. Пользователь редактора Inkscape может запускать Python-скрипты через меню.

Окно графического редактора Inkscape как его вижу я и мой LinuxMint
Окно графического редактора Inkscape как его вижу я и мой LinuxMint

В верхнем меню графического редактора Inkscape помимо всяческих File, Edit, View – есть волшебное подменю Extensions (Расширения) при нажатии на которое выводится список уже имеющихся в редакторе и готовых к запуску Python-скриптов.

...Создатели Inkscape предписывают Linux-поклоннику держать его самодельные Python-скрипты в районе “домашней” папки “/home/user_name/.config/inkscape/extensions/” (для Windows-пользователей там что-то своё).

В этой же папке следует расположить файл с расширением *.inx
В моём случае это был файл "proba01.inx"

<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
  <_name>proba-1</_name>  
  <id>something12345</id>
  <dependency type="executable" location="extensions">proba01.py</dependency>  
  <effect>
    <object-type>all</object-type>
  </effect>
  <script>
    <command reldir="extensions" interpreter="python">proba01.py</command>
  </script>
</inkscape-extension>

Я разместил в папке “/home/user_name/.config/inkscape/extensions/” два вышеупомянутых файла "proba01.py" и "proba01.inx", перезапустил Inkscape и с радостью обнаружил в подменю “Extensions” пункт “proba-1” который запускал скрипт “proba01.py”

И вот результат работы скрипта:

Выгдядит достаточно наукообразно.

Я планирую улучшить этот скрипт. С радостью выслушаю мнения.
(где .. тут кнопка "Сохранить" ?)

Комментарии (10)


  1. ReadOnlySadUser
    26.07.2022 02:13
    +2

    С радостью выслушаю мнения

    По поводу чего? А то у меня сейчас в голове лишь два вопроса: "зачем?" и "зачем?".

    1. Зачем это вообще надо?
    2. Зачем это опубликовано на хабр?

    Дам немного пояснений. Более или менее "наукообразный" текст следует (явно или неявно) следующей структуре:

    1. Обозначение проблемы. Неплохо бы описать какая проблема у вас есть и почему вы хотите её решить.

    2. Краткий обзор на имеющиеся решения, с последующим обоснованием, почему эти решения вам не подходят. Положим проблему вы обозначили как "проблемы нет, хочу в целях обучения написать свой велосипед". Тогда вместо краткого обзора, в этом разделе должна быть теоретическая база происходящего.

    3. Собственно, описание вашего решения

    4. Сравнение ваших результатов с имеющимися решениями из п.2. Если аналогов нет, то неплохо бы описать просто результаты, примеры приложения вашей технологии к реальному миру, возможности к расширению. В общем всё, что будет доказываться, что вы придумали что-то нужное.

    5. Инструкции по воспроизведению результатов

    6. Выводы, вопросы к комьюнити, шутеечки и всё прочее.

    Судя по тому, что я вижу в статье, у вас сейчас есть п.3 и немножечко п.5. Об чём нам разговаривать?


  1. AlexanderAstafiev
    26.07.2022 07:41
    +4

    Простите, я не могу так больше. Я слишком хорошо знаю Python, чтобы молчать при виде такого кода.

    Зачем Вы указываете кодировку? Это имело смысл для Python 2.7, и то я не уверен, что это не пофиксили сегодня. Любые файлы с кодом в наше время должны быть по умолчанию в Unicode, и если какой-то софт имеет с этим проблемы, то этот софт - говно (c).

    Не надо комментировать импорты. Импорты всегда в начале файла. Они не должны быть в середине или конце, потому что можно словить падение в рантайме из-за проблем с импортами. Если в скрипте все импорты в начале И скрипт запускается, то он НЕ упадет в рантайме из-за импортов. Это же привычкой должно быть.

    Вы то импортируете inkex целиком, то выдергиваете из него классы. Не понял, зачем.

    Глобальные переменные - зло, особенно так, как это делаете Вы:

    FntSz = 3.175    # будем выводить текст - так вот это размер текста

    Что за FntSz? В интернете СТОЛЬКО статей, книг, лекций про "называйте переменные так, чтобы по названию было понятно назначение". Вы специально шифруете? Почему переменная названа с большой буквы? В CamelCase форме? В Python так делают только для имен классов. Хорошо, что есть очень понятный комментарий. Размер текста. В попугаях размер текста?

    Что это за sxA, sxB, ar23A? Я ничего не могу понять по именам переменных.

    # ну что сказать... 

    Шикарный комментарий. Допишите остальную часть припева.

    В Python методы визуально отделяются друг от друга пустыми строками, а не комментариями с дефисами.

    Впервые вижу, чтобы отступы были длиной в 1 пробел.

    Спасибо, что используете менеджеры контекста при чтении из файла, без сарказма! Только про DRY Вы ничего не знаете, да?

    Добавлять "\n" в str.split() необязательно, это дефолтный аргумент. И вообще, лучше использовать str.splitlines(), не везде разделитель строк "\n".

    range(0, len(ar23A))

    Почему нельзя написать так?

    enumerate(ar23A)

    Я устал. Я не могу это читать. Простите за токсичную критику, накипело.


    1. iig
      26.07.2022 08:04

      В pycharm так написать будет сложно. Разве что принципиально не обращать внимания на warningи.


    1. kpen Автор
      26.07.2022 16:04
      +2

      Ну что Вы, я очень рад Вашей информации. Времени не хватает на изучение всех программистских "поняток". А тут прямо череда конструктивных советов. В ближайшее время подправлю текст скрипта. Про DRY (на данный момент) ничего не знаю. Погуглю по дороге из мастерской.


    1. kale
      26.07.2022 18:56

      Мне нравится изучать исходники известных проектов, стиль, подходы.. Недавно рассматривал исходники библитекаря Calibre, и был немало удивлен обилием инструкций import внутри тел процедур, if-ов... Так как не считаю себя знатоком python, решил что если уж таких проектах так делают, то почему бы и нет..


      1. iig
        27.07.2022 17:53

        если уж таких проектах так делают

        Говнокоду найдётся место в любом проекте и на любом языке ;)


  1. economist75
    26.07.2022 10:20
    +6

    Статья понравилась. Не кодом, который почти что ужасен, а темой макросов в графических пакетах. И вангую рост потребности в них, геометрический. Видел воочию сколько кода на VBA в CorelDraw в немецких дизайн-студиях и рекламных агентствах. Его там много (в наших нет, можно сказать, вообще). Это GUI-утилиты полезняшки, в основном реализующие компоновочные решения при верстке, а также пакетные операции с фото, символами строк итп.

    Доля Корела в Германии высока, примерно 40% (остальное условно назовем AI, и это искусственный интеллект, а Adobe Illustrator). В нашей стране официально за CorelDraw не более 25%, но много пиратки в регионах, в каждой студии он имеется (дистр корела легко и бесследно прячется, в отличие от продукции Adobe). Не могу сказать про госструктуры, но на пиратке-кореле там тоже активно рисуют, видел в около-выставочном окружении двух наших главных мероприятий в стране. В условии цейтнота и оргсуматохи ам и не такое можно увидеть.

    Так вот, в труде граф. дизайнера оказалось неожиданного много около-текстовой рутины, которую VBA или Python могут взять на себя. Так как горячие клавиши недоступны части особо гордых людей - то только макросы могут повысить производительность труда до приемлемого европейского уровня. Да, он там выше. Я не видел в Германии дизайнеров, не умеющих печать вслепую или просто быстро. Ctrl+D они умеют все.

    InkScape - наиболее импортозамещающий свободный пакет из всей векторной графики. Дивизионы Corel и Adobe в РФ хвалились десятками миллионов и даже миллиардом выручки в долларах, так что вопрос не праздный. Python в InkScape, расширения на нем - позволяют делать очень многое, и скорее всего обеспечат технологическое лидерство тем компаниям, которые их освоят, а не будут продолжать платить по 1 млн. руб. за рабочее место на инструментах Adobe. Данная статья - хороший старт, чтобы знать что оно вообще возможно и даже работает.


    1. kpen Автор
      26.07.2022 18:45
      +2

      Да, Вы совершенно правы - эта заметка - про "макросы в графических пакетах". Связка Inkscape-Python заслуживает внимания. Я не люблю Inkscape (который очень уступает CorelDraw по удобству использования) и я не люблю вездессущий Python (многовато плясок вокруг empty spaces) но эти инструменты доступны сейчас и вряд ли перестанут быть доступными...

      Целевая аудитория для этой заметки мне и самому не понятна - кодеры не рисуют (особенно в Inkscape), дизайнеры не программируют (и тоже не используют Inkscape).

      У меня не получилось сходу забраться в эту несложную тему "Python-макросов для Inkscape". Пришлось много гуглить и сердиться :). Надеюсь что кому-нибудь этот неидеальный, кое-как работающий код сэкономит время и поможет сделать свой "генератор актуальной инфографики".

      В общем - спасибо Вам за понимание - зачем эта заметка вообще написана.


  1. nikolay_karelin
    27.07.2022 11:26

    А насколько сложно отлаживать такие расширения?

    По моему опыту - может быть самая дурная часть всей работы.


    1. kpen Автор
      27.07.2022 17:08

      У меня встречный вопрос - а какие действия подразумевает под собой "отладка"?
      (предположим - у нас есть некоторый код. Если в запускаемом коде синтаксические ошибки - в графической среде выскочит окно с извещением об ошибке. Вроде у питонских интерпретаторов приемлемые отчёты об ошибках - номер строки.. имёна функций и прочее.

      Или Вы имеете в виду "возможность пошаговой отладки с real time monitoring of variables"?)