Заранее предупреждаю - всё тут немного сумбурно...
Графический редактор 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 помимо всяческих 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)
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)
Я устал. Я не могу это читать. Простите за токсичную критику, накипело.
iig
26.07.2022 08:04В pycharm так написать будет сложно. Разве что принципиально не обращать внимания на warningи.
kpen Автор
26.07.2022 16:04+2Ну что Вы, я очень рад Вашей информации. Времени не хватает на изучение всех программистских "поняток". А тут прямо череда конструктивных советов. В ближайшее время подправлю текст скрипта. Про DRY (на данный момент) ничего не знаю. Погуглю по дороге из мастерской.
kale
26.07.2022 18:56Мне нравится изучать исходники известных проектов, стиль, подходы.. Недавно рассматривал исходники библитекаря Calibre, и был немало удивлен обилием инструкций import внутри тел процедур, if-ов... Так как не считаю себя знатоком python, решил что если уж таких проектах так делают, то почему бы и нет..
iig
27.07.2022 17:53если уж таких проектах так делают
Говнокоду найдётся место в любом проекте и на любом языке ;)
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. Данная статья - хороший старт, чтобы знать что оно вообще возможно и даже работает.
kpen Автор
26.07.2022 18:45+2Да, Вы совершенно правы - эта заметка - про "макросы в графических пакетах". Связка Inkscape-Python заслуживает внимания. Я не люблю Inkscape (который очень уступает CorelDraw по удобству использования) и я не люблю вездес
сущий Python (многовато плясок вокруг empty spaces) но эти инструменты доступны сейчас и вряд ли перестанут быть доступными...Целевая аудитория для этой заметки мне и самому не понятна - кодеры не рисуют (особенно в Inkscape), дизайнеры не программируют (и тоже не используют Inkscape).
У меня не получилось сходу забраться в эту несложную тему "Python-макросов для Inkscape". Пришлось много гуглить и сердиться :). Надеюсь что кому-нибудь этот неидеальный, кое-как работающий код сэкономит время и поможет сделать свой "генератор актуальной инфографики".
В общем - спасибо Вам за понимание - зачем эта заметка вообще написана.
nikolay_karelin
27.07.2022 11:26А насколько сложно отлаживать такие расширения?
По моему опыту - может быть самая дурная часть всей работы.
kpen Автор
27.07.2022 17:08У меня встречный вопрос - а какие действия подразумевает под собой "отладка"?
(предположим - у нас есть некоторый код. Если в запускаемом коде синтаксические ошибки - в графической среде выскочит окно с извещением об ошибке. Вроде у питонских интерпретаторов приемлемые отчёты об ошибках - номер строки.. имёна функций и прочее.Или Вы имеете в виду "возможность пошаговой отладки с real time monitoring of variables"?)
ReadOnlySadUser
По поводу чего? А то у меня сейчас в голове лишь два вопроса: "зачем?" и "зачем?".
1. Зачем это вообще надо?
2. Зачем это опубликовано на хабр?
Дам немного пояснений. Более или менее "наукообразный" текст следует (явно или неявно) следующей структуре:
Обозначение проблемы. Неплохо бы описать какая проблема у вас есть и почему вы хотите её решить.
Краткий обзор на имеющиеся решения, с последующим обоснованием, почему эти решения вам не подходят. Положим проблему вы обозначили как "проблемы нет, хочу в целях обучения написать свой велосипед". Тогда вместо краткого обзора, в этом разделе должна быть теоретическая база происходящего.
Собственно, описание вашего решения
Сравнение ваших результатов с имеющимися решениями из п.2. Если аналогов нет, то неплохо бы описать просто результаты, примеры приложения вашей технологии к реальному миру, возможности к расширению. В общем всё, что будет доказываться, что вы придумали что-то нужное.
Инструкции по воспроизведению результатов
Выводы, вопросы к комьюнити, шутеечки и всё прочее.
Судя по тому, что я вижу в статье, у вас сейчас есть п.3 и немножечко п.5. Об чём нам разговаривать?