2010-02-23 11 views
7

Voglio rendere parti di un file SVG per nome ma per la vita di me non riesco a capire come farlo (usando python + gtk).Come posso rendere * le parti * di un file SVG?

Ecco il file in formato SVG in questione: http://david.bellot.free.fr/svg-cards/files/SVG-cards-2.0.1.tar.gz (Aggiornamento: questo file non esiste più, ma è possibile rintracciarlo in http://svg-cards.sourceforge.net/)

Sul suo sito, David, dice:

È possibile disegnare una scheda tramite rendendo il file su una pixmap e ritagliando ciascuna scheda manualmente o tramite utilizzando il nome della scheda tramite un'interfaccia DOM . Tutte le carte sono incorporate in un gruppo SVG.

Non so cosa intenda per interfaccia DOM. Ho fatto qualche ricerca e il miglior risultato ho trovato che sembra adattarsi quello che voglio fare è:

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")); 

Avviso però che è per Qt e non è nemmeno scritto in python.

Questo è quello che ho finora:

#!/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() 

non funziona, naturalmente.

Cosa mi manca?

risposta

9

La libreria GTK per il rendering SVG è chiamato rsvg. Ha collegamenti Python, ma non sono documentati e non avvolgono le funzioni rsvg_handle_get_pixbuf_sub() e rsvg_handle_render_cairo_sub() che normalmente userete a tale scopo in C. Ecco cosa dovete fare per quanto posso dire. Estrai il nodo XML come suggerito da Adam Crossland. Per renderlo, devi fare qualcosa di simile:

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()) 

Ecco, se si desidera in un gtk.Image, altrimenti fare qualcosa di diverso con il pixbuf. Puoi anche renderlo in un contesto Cairo con handle.render_cairo(cr) dove cr è il tuo contesto Cairo.

EDIT:

Spiacente, non ho letto i binding Python abbastanza da vicino in un primo momento.I _sub() funzioni sono implementate utilizzando l'argomento id=, in modo che il programma può ridursi a questo:

#!/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() 

Ho provato questo e funziona. Tuttavia, la finestra ha le dimensioni dell'intero canvas SVG ed è ritagliata alle dimensioni dello schermo (che è il motivo per cui ho reso il 3 di quadri invece dell'asso di fiori che si trova nell'angolo.) Quindi avrai ancora per trovare un modo per ritagliare il pixbuf attorno alla carta che vuoi, ma non dovrebbe essere troppo difficile.

+0

In realtà ho provato anche in questo modo e non sono riuscito a capire perché stava facendo quello che stava facendo (di conseguenza, ho provato altre soluzioni per vedere se avrei ottenuto un risultato simile). Nonostante la dimensione della finestra sia la dimensione dell'intero canvas SVG, questa soluzione risponde alla mia domanda (mentre, naturalmente, sollevando un'altra domanda). :) –

+0

Questo è stato super-utile, anche se più aiuto sarebbe stato utile. Per eseguire il rendering di parte di un'immagine SVG puoi usare handle.render_cairo (cr = c, id = "# layer_5") dove "c" è un contesto cairo. La parte che per me non era affatto ovvia è che è necessario il "#" all'inizio dell'ID. Il mio SVG ha id come 'id = "layer_1"' e posso renderizzare singoli layer con render_cairo (cr = c, id = "# layer_1") – bodgesoc

2

Credo che ciò che intende per "attraverso un'interfaccia DOM" sia che poiché SVG è XML, è possibile caricare il file SVG nel minidom o in un altro parser XML Python ed estrarre il nodo XML con lo specifico nome che stai cercando. Quel nodo XML dovrebbe rappresentare un elemento che può essere reso.

+0

Come si esegue il rendering del nodo XML? –

0

È possibile farlo modificando il tag. Modifica larghezza e altezza, imposta l'attributo viewBox sull'elemento svg principale sul rettangolo che desideri, renderizza, ripeti.

Vedi How to show a subsection or "slice" of an SVG graphic? e http://dingoskidneys.com/~dholth/svg/

+0

Questo non aiuta a recuperare parti del file SVG in base al nome e quindi a renderle. –

+0

Potrebbe essere possibile ottenere il riquadro di delimitazione per le parti nominate del file SVG e impostare il ViewBox di conseguenza. – joeforker

1

Ecco la mia risposta al problema del ritaglio spazio vuoto. È un trucco ruvido ma ha funzionato alla grande. Questo sarebbe anche un buon punto di partenza per ottenere carte per chiunque realizzi un gioco di carte in 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", {}) 
Problemi correlati