Я хочу представить части svg файла по имени, но ни за что в жизни я не могу выяснить, как сделать так (использующий Python + gtk).
Вот svg рассматриваемый файл: http://david.bellot.free.fr/svg-cards/files/SVG-cards-2.0.1.tar.gz (Обновление: этот файл больше не существует, но можно разыскать его по http://svg-cards.sourceforge.net/),
На его сайте, David, говорит:
Можно потянуть карту или путем рендеринга файла на пиксельную карту и отсечения каждой карты вручную или при помощи имени карты через интерфейс DOM. Все карты встраиваются в группу SVG.
Я не знаю то, что он подразумевает под интерфейсом DOM. Я сделал некоторый поиск и лучший результат, я нашел, что это, кажется, соответствует тому, что я хочу сделать:
QSvgRenderer *renderer = new QSvgRenderer(QLatin1String("SvgCardDeck.svg"));
QGraphicsSvgItem *black = new QGraphicsSvgItem();
QGraphicsSvgItem *red = new QGraphicsSvgItem();
black->setSharedRenderer(renderer);
black->setElementId(QLatin1String("black_joker"));
red->setSharedRenderer(renderer);
red->setElementId(QLatin1String("red_joker"));
Заметьте однако, что это для QT и даже не записано в Python.
Это - то, что я имею до сих пор:
#!/usr/bin/env python
from __future__ import absolute_import
import cairo
import gtk
import rsvg
from xml import xpath
from xml.dom import minidom
window = gtk.Window()
window.set_title("Foo")
window.set_size_request(256, 256)
window.set_property("resizable", False)
window.set_position(gtk.WIN_POS_CENTER)
window.connect("destroy", gtk.main_quit)
window.show()
document = minidom.parse("cards.svg")
element = xpath.Evaluate("//*[@id='1_club']", document)[0]
xml = element.toxml()
svg = rsvg.Handle()
svg.write(xml)
pixbuf = svg.get_pixbuf()
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()
window.add(image)
gtk.main()
Это не работает, конечно.
Что я пропускаю?
Библиотека GTK для рендеринга SVG называется RSVG. У нее есть привязки к python, но они недокументированы, и в них не обернуты функции rsvg_handle_get_pixbuf_sub()
и rsvg_handle_render_cairo_sub()
, которые вы обычно используете для этой цели на C. Вот что вам нужно сделать, насколько я могу судить. Вы извлекаете узел XML, как предложил Адам Кроссланд. Для его рендеринга нужно сделать что-то вроде этого:
import gtk
import rsvg
handle = rsvg.Handle()
handle.write(buffer=xml_data)
# xml_data is the XML string for the object you want
image = gtk.Image()
image.set_from_pixbuf(handle.get_pixbuf())
Это если вы хотите получить его в gtk.Image
, в противном случае сделайте что-нибудь другое с pixbuf. Вы также можете перевести его в контекст Cairo с помощью handle.render_cairo(cr)
, где cr
- это ваш контекст Cairo.
EDIT:
Извините, сначала я недостаточно внимательно прочитал привязку к python. Функции _sub()
реализованы с использованием аргумента id=
, так что ваша программа может сводиться к следующему:
#!/usr/bin/env python
import gtk
import rsvg
window = gtk.Window()
window.set_title("Foo")
window.connect("destroy", gtk.main_quit)
window.show()
svg = rsvg.Handle(file='cards.svg')
pixbuf = svg.get_pixbuf(id='#3_diamond')
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()
window.add(image)
gtk.main()
Я протестировал это и это работает. Однако окно имеет размер всего холста SVG и обрезается по размеру экрана (вот почему я отобразил 3 бубны вместо туза треф, который находится в углу). Поэтому вам все равно придется найти способ обрезать pixbuf вокруг нужной карты, но это не должно быть слишком сложно.
Я считаю, что он имеет в виду «через интерфейс DOM» так, поскольку SVG XML, вы можете загрузить файл SVG в minidom или какой-либо другой синтаксический анализатор Python XML и извлечь узел XML с конкретным именем, которое вы ищете. Этот узел XML должен представлять элемент, который можно отобразить.
Вы можете сделать это, отредактировав тег. Измените ширину и высоту, установите атрибут viewBox в основном элементе svg на нужный прямоугольник, отрендерите, повторите.
См. Как показать подраздел или «фрагмент» SVG-графики? и http://dingoskidneys.com/~dholth/svg/
Вот мой ответ на проблему обрезки пустого пространства. Это грубый хакер, но он отлично сработал. Это также послужит хорошей отправной точкой для получения карточек для всех, кто делает карточную игру на Python.
import gtk
import rsvg
svg = rsvg.Handle(file="/usr/share/gnome-games-common/cards/gnomangelo_bitmap.svg")
w, h = 202.5, 315
card_names = map(str, range(1,11)) + ["jack", "queen", "king"]
suites = ["club", "diamond", "heart", "spade"]
specials = [{"name":"black_joker","x":0, "y":4}, {"name":"red_joker","x":1, "y":4}, {"name":"back","x":2, "y":4}]
for suite_number, suite in enumerate(suites):
for card_number, card in enumerate(card_names):
print "processing", suite, card, '#'+card+'_'+suite
pixbuf = svg.get_pixbuf(id='#'+card+'_'+suite)
pixbuf.subpixbuf(int(w*card_number), int(h*suite_number), int(w), int(h)).save("./"+card+"_"+suite+".png","png", {})
for special in specials:
print "processing", special["name"]
pixbuf = svg.get_pixbuf(id='#'+special["name"])
card_number = special["x"]
suite_number = special["y"]
pixbuf.subpixbuf(int(w*card_number), int(h*suite_number), int(w), int(h)).save("./"+special["name"]+".png","png", {})