How to save textview content in gtk to pdf using python? - python

I want to save textview's buffer in pdf format. I can do it using reportlab if it's just a simple text. But, what if I want to save everything, from text with its tags and also images?
from gi.repository import Gtk, GdkPixbuf
import pango
from reportlab.pdfgen import canvas
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.box = Gtk.Box()
self.window.add(self.box)
self.textview = Gtk.TextView()
self.textbuffer = self.textview.get_buffer()
self.tag_bold = self.textbuffer.create_tag("bold",
weight=pango.WEIGHT_BOLD)
self.tag_italic = self.textbuffer.create_tag("italic",
style=pango.STYLE_ITALIC)
pix = GdkPixbuf.Pixbuf.new_from_file_at_size('baby.jpg', 50, 50)
tag = [self.tag_bold,
self.tag_italic]
self.textbuffer.insert_pixbuf(self.textbuffer.get_end_iter(), pix)
for i in range(20):
self.textbuffer.insert_with_tags(self.textbuffer.get_end_iter(),
'line%d\n' % (i+1),
tag[i % 2])
self.box.pack_start(self.textview, True, True, 0)
self.button = Gtk.Button(label='Start')
self.button.connect('clicked', self.on_button_clicked)
self.box.pack_start(self.button, True, True, 0)
self.window.show_all()
Gtk.main()
def on_button_clicked(self, widget):
canv = canvas.Canvas('tes.pdf')
for i in range(self.textbuffer.get_line_count()):
a = self.textbuffer.get_iter_at_line(i)
b = self.textbuffer.get_iter_at_line(i+1).get_offset()
c = self.textbuffer.get_iter_at_offset(b - 1)
t = self.textbuffer.get_text(a, b, True)
line = 750 - (15 * l)
canv.drawString(40, line, t)
canv.save()
if __name__ == '__main__':
gui = gui()
EDIT:
drahnr suggest to use cairo instead. Okay, I think it's a better idea since reportlab coordinate start from bottom left and cairo coordinate start from top left. Below is my code using cairo.
from gi.repository import Gtk, GdkPixbuf, Gdk
import pango
import cairo
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.textview = Gtk.TextView()
self.window.add(self.textview)
self.initText()
self.createPDF()
self.window.show_all()
Gtk.main()
def initText(self):
self.tag_bold = self.textview.get_buffer().create_tag("bold", weight=pango.WEIGHT_BOLD)
self.tag_italic = self.textview.get_buffer().create_tag("italic", style=pango.STYLE_ITALIC)
pix = GdkPixbuf.Pixbuf.new_from_file_at_size('baby.png', 50, 50)
tag = [self.tag_bold, self.tag_italic]
self.textview.get_buffer().insert_pixbuf(self.textview.get_buffer().get_end_iter(), pix)
self.textview.get_buffer().insert(self.textview.get_buffer().get_end_iter(), '\n')
for i in range(20):
self.textview.get_buffer().insert_with_tags(self.textview.get_buffer().get_end_iter(), 'line%d' % (i+1), tag[i % 2])
self.textview.get_buffer().insert(self.textview.get_buffer().get_end_iter(), '\n')
def createPDF(self):
line = 30
row = 5
pos = 0
ps = cairo.PDFSurface('tes.pdf', 600, 770)
cr = cairo.Context(ps)
while pos != self.textview.get_buffer().get_end_iter().get_offset():
if self.textview.get_buffer().get_iter_at_offset(pos).has_tag(self.tag_bold):
cr.select_font_face('Times', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
elif self.textview.get_buffer().get_iter_at_offset(pos).has_tag(self.tag_italic):
cr.select_font_face('Times', cairo.FONT_SLANT_ITALIC, cairo.FONT_WEIGHT_NORMAL)
else:
cr.select_font_face('Times', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
t = self.textview.get_buffer().get_slice(self.textview.get_buffer().get_iter_at_offset(pos), self.textview.get_buffer().get_iter_at_offset(pos+1), False)
if t == '\n':
line += 12
row = 5
elif t == unichr(0xFFFC):
pix = self.textview.get_buffer().get_iter_at_offset(pos).get_pixbuf()
Gdk.cairo_set_source_pixbuf(cr, pix, 8 * row, line)
line += pix.get_width()
cr.paint()
else:
cr.move_to(8 * row, line)
cr.show_text(t)
pos=pos+1
row += 1
if __name__ == '__main__':
gui()
literally it's still the same. I should hardcode it to draw everything. and drahnr suggest to use gtk_widget_draw to render it to cairo surface.

This is no python answer, but it might be applicable to your problem too:
Rendering to a cairo_surface_t and use the pdf surface API - how you render your text is up to you, a simple way would be to render the textview via gtk_widget_draw completely.
https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-draw.
http://cairographics.org/manual/cairo-PDF-Surfaces.html
Update #2:
The below is incomplete and requires a widget that does the rendering for you and thus does not replace the gtk_widget_draw call. You need to create ctype extension or do the drawing manually.
Source: http://www.stuartaxon.com/2010/02/03/using-cairo-to-generate-svg-in-django/
Update #1:
This function renders an image to a surface (PDF,SVG, whatever your compiled cairo library supports) and stores it to the file dest - for details refer to the manual of the specific function
def draw_render_to_file(dest, Widget, Surface = PDFSurface, width = 100, height = 100)
widget = Widget(Surface)
surface = widget.Surface(dest, width, height)
widget.draw(Context(surface), width, height)
surface.finish()

Related

How should widgets be resized in npyscreen (a Python curses wrapper)?

I want the height of some widgets in my npyscreen app to resize as the terminal is resized.
Specifically I want those two column widgets to continue to take up about the same fraction of the height of the terminal when the terminal is resized, with the row widget at the bottom to stay at the bottom of the terminal when the terminal is resized.
I've got something sorta working but when the terminal gets resized something is going wrong with the rendering overall, like the widgets don't know where I want them to be drawn. What might be going wrong? How should this be done properly?
import curses
import sys
import npyscreen
npyscreen.disableColor()
def main():
app = App()
app.run()
class App(npyscreen.NPSApp):
def main(self):
form = npyscreen.FormBaseNew(name = "COMMUNICATIONS")
column_height = terminal_dimensions()[0] - 9
widget_contacts = form.add(
Column,
name = "CONTACTS",
relx = 2,
rely = 2,
max_width = 20,
max_height = column_height
)
widget_messages = form.add(
Column,
name = "MESSAGES",
relx = 23,
rely = 2,
max_height = column_height
)
widget_input = form.add(
npyscreen.BoxTitle,
name = "INPUT",
max_height = 5
)
widget_input.resize
widget_contacts.values = ["alice", "bob"]
widget_messages.values = ["2018-09-19T2001Z a message", "2018-09-19T2002Z another message"]
widget_input.values = ["sup"]
#exit_button = form.add(
# ExitButton,
# name = "EXIT",
# #relx = 78,
# rely = 23
#)
widget_contacts.max_height = 5
form.edit()
class Column(npyscreen.BoxTitle):
def resize(self):
self.max_height = int(0.73 * terminal_dimensions()[0])
class ExitButton(npyscreen.ButtonPress):
def whenPressed(self):
sys.exit(0)
def terminal_dimensions():
return curses.initscr().getmaxyx()
if __name__ == "__main__":
main()

pyGtk: Image prevents window resize

So I am attempting to make a GTK application using python, and I have run into this issue where after I place an image on a window, I can increase the size of the window, but not decrease it. Given that the purpose of this particular window is to display a resizable image, this is rather bothersome.
I have extracted the relevant code which demonstrates this behavior below
#!/usr/bin/env python
from gi.repository import Gtk, GdkPixbuf
import sys
class ImageWindow(Gtk.Window):
def __init__(self, image_data):
Gtk.Window.__init__(self, title="image test")
if image_data and len(image_data) > 0:
self.loader = GdkPixbuf.PixbufLoader()
self.loader.write(image_data)
self.pixbuf = self.loader.get_pixbuf()
self.image = Gtk.Image.new_from_pixbuf(self.pixbuf)
else:
self.image = Gtk.Image.new()
self.add(self.image)
self.connect('delete-event', Gtk.main_quit)
win = ImageWindow(sys.stdin.read())
win.show_all()
Gtk.main()
If you pipe in nothing, the window resizes fine. Pipe in an image, and the form clicks to the size of the image, and can resize bigger, but cannot resize smaller.
So here is an example of a scaling image. The idea is that you put the image in a Gtk.ScrolledWindow() and resize the image as soon as the window is resized.:
#!/usr/bin/env python
from gi.repository import Gtk, GdkPixbuf, GLib
import sys
class ImageWindow(Gtk.Window):
def __init__(self, image_data):
Gtk.Window.__init__(self, title="image test")
self.connect('delete-event', Gtk.main_quit)
self.image = Gtk.Image()
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.add(self.image)
self.add(scrolled_window)
if len(image_data) == 0:
return
self.loader = GdkPixbuf.PixbufLoader()
self.loader.write(image_data)
self.loader.close()
self.pixbuf = self.loader.get_pixbuf()
self.image.set_from_pixbuf(self.pixbuf)
width = self.pixbuf.get_width()
height = self.pixbuf.get_height()
self.dimension = float(width) / height
self.set_default_size(width, height)
self.connect('check-resize', self.on_resize)
def on_resize(self, window):
width, height = self.get_size()
if float(width) / height > self.dimension:
self.pixbuf = self.pixbuf.scale_simple(
self.dimension * height,
height,
GdkPixbuf.InterpType.NEAREST
)
else:
self.pixbuf = self.pixbuf.scale_simple(
width,
width / self.dimension,
GdkPixbuf.InterpType.NEAREST
)
GLib.idle_add(self.image.set_from_pixbuf, self.pixbuf)
win = ImageWindow(sys.stdin.read())
win.show_all()
Gtk.main()
As an alternative, you can load the pixbuf again from the loader and scale it afterwards. This looks better if you make your image smaller and then larger again but needs more processing:
def on_resize(self, window):
width, height = self.get_size()
self.pixbuf = self.loader.get_pixbuf()
if float(width) / height > self.dimension:
self.pixbuf = self.pixbuf.scale_simple(
self.dimension * height,
height,
GdkPixbuf.InterpType.BILINEAR
)
else:
self.pixbuf = self.pixbuf.scale_simple(
width,
width / self.dimension,
GdkPixbuf.InterpType.BILINEAR
)
GLib.idle_add(self.image.set_from_pixbuf, self.pixbuf)

Python GTK Window Marquee like behaviour

i am very new to python. I want to open a Python GTK Window which should display a moving text just like a marquee in HTML. Kindly anyone suggest a solution
i am using this piece of code:
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class MyProgram:
def __init__(self):
# create a new window
app_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
app_window.set_size_request(800, 350)
app_window.set_border_width(15)
app_window.set_title("Volks Electronics")
app_window.connect("delete_event", lambda w,e: gtk.main_quit())
vbox_app = gtk.VBox(False, 0)
app_window.add(vbox_app)
vbox_app.show()
label_app = gtk.Label("Sample Page ")
label_app.show()
vbox_app.pack_start(label_app, False, False, 1)
# Draw Table() to layout text:
table_layout = gtk.Table(rows=5, columns=5, homogeneous=True)
label_a = gtk.Label("Train Name:")
label_a.show()
table_layout.attach(label_a, 0, 1, 0, 1, 0,0,0,0)
label_c = gtk.Label("Train No:")
label_c.show()
table_layout.attach(label_c, 0, 1, 2, 3, 0,0,0,0)
label_d = gtk.Label("Departed From:")
label_d.show()
table_layout.attach(label_d, 0, 1, 3, 4, 0,0,0,0)
label_b = gtk.Label("Next Station:")
label_b.show()
table_layout.attach(label_b, 0, 1, 1, 2, 0,0,0,0)
table_layout.show()
vbox_app.add(table_layout)
# Use HBox() to layout text and button next to each other:
hbox_close = gtk.HBox(False, 0)
label_close = gtk.Label("Close aplication: ")
hbox_close.pack_start(label_close, True, True, 0)
label_close.show()
button_close = gtk.Button(stock=gtk.STOCK_CLOSE)
button_close.connect("clicked", lambda w: gtk.main_quit())
button_close.set_flags(gtk.CAN_DEFAULT)
hbox_close.pack_start(button_close, True, True, 0)
button_close.show()
hbox_close.show()
vbox_app.add(hbox_close)
# Place after association to hbox/vbox to avoid the following error:
# GtkWarning: gtkwidget.c:5460: widget not within a GtkWindow
button_close.grab_default()
app_window.show()
return
def main():
gtk.main()
return 0
if __name__ == "__main__":
MyProgram()
main()
I want to put some text after that close button which should work like a moving banner(marquee in HTML). I couldn't find any code which works like a marquee in a GTK window. Please suggest any way to do this
I like this.
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.label = Gtk.Label()
self.box.pack_start(self.label, True, True, 0)
self.gibberish = "Spam and eggs, spam and eggs, spam and eggs!"
self.a = 0
self.z = 40
def marquee(self, text):
if self.a < len(text):
self.a = self.a + 1
self.z = self.z + 1
if self.a >= len(text):
self.a = 0
self.z = 40
return str(text[self.a:self.z])
# Displays Marquee
def displayMarquee(self):
# putting our text into our function and setting our label to the result.
# we need to return "True" to ensure the timer continues to run, otherwise it will only run once.
self.label.set_label(self.marquee(self.gibberish))
return True
# Initialize Marquee
def startMarquee(self):
# this takes 2 args: (how often to update in millisec, the method to run)
GObject.timeout_add(500, self.displayMarquee)
win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
win.startMarquee()
Gtk.main()

Gnomeapplet - Seeing only a white dot where an entry and a button should be

I have written a gnomeapplet for gnome-panel, and the corresponding server file. Everything seems to work fine when I use the "debug mode", but when I try to load the applet from the panel it shows only a little white dot.
Can anyone help me find the problem?
my code is:
#!/usr/bin/env python
import gnomeapplet
import gobject
import sys
import gtk
class Priberam(gnomeapplet.Applet):
def __init__(self, applet, iid):
hbox = gtk.HBox(False, 0)
image = gtk.Image()
pixbuf = gtk.gdk.pixbuf_new_from_file('1.png')
pixbuf = gtk.gdk.Pixbuf.add_alpha(pixbuf,255,255,255 ,255)
size = applet.get_size()-6
pixbuf = pixbuf.scale_simple(size,size,gtk.gdk.INTERP_BILINEAR)
image.set_from_pixbuf(pixbuf)
button_search = gtk.Button()
button_search.add(image)
entry = gtk.Entry()
hbox.pack_start(button_search, False, False, 0)
hbox.pack_end(entry, False, False, 0)
applet.add(hbox)
applet.show_all()
gobject.type_register(Priberam)
def priberam_factory(applet,iid):
Priberam(applet,iid)
return True
if len(sys.argv) > 1 and sys.argv[1] == '-d': # debugging
mainWindow = gtk.Window()
mainWindow.set_title('Applet window')
mainWindow.connect("destroy", lambda w: gtk.main_quit())
applet = gnomeapplet.Applet()
priberam_factory(applet, None)
applet.reparent(mainWindow)
mainWindow.show_all()
gtk.main()
sys.exit()
if __name__ == '__main__':
gnomeapplet.bonobo_factory('OAFIID:GNOME_Priberam_Factory', gnomeapplet.Applet.__gtype__, 'Priberam Applet', '0.1', priberam_factory)
Thanks in advance
Solved...The solution was so simple...I just have to change the path to the image file to the complete path...instead of pixbuf = gtk.gdk.pixbuf_new_from_file('1.png') I should use for example: pixbuf = gtk.gdk.pixbuf_new_from_file('/home/username/applet/1.png')
Better: pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(os.path.dirname(__file__), '1.png')), don't forget to import os

Using a loop to generate unique bitmap buttons with separate events when clicked

I'm pretty new to Python, so I'll hope you forgive me for such amateurish code. I've tried pouring over examples that do similar things but I'm having trouble figuring out what they're doing that is different. In examples I've seen each button generated with the loop had a different action, for mine only the last button in the loop is affected by the click, no matter which button I press. Here's the code:
import wx
import mmap
class pt:
Note = open('note.txt', "r+")
buf = mmap.mmap(Note.fileno(), 0)
TL = 0
readline = buf.readline
while readline():
TL += 1
class MainWindow(wx.Frame):
def __init__(self, parent, title):
w, h = wx.GetDisplaySize()
x = w * 0
y = h - bdepth
wx.Frame.__init__(self, parent, title = title, pos = (x, y), size = (200,bdepth), style = wx.STAY_ON_TOP)
self.__DoLayout()
self.Bind(wx.EVT_BUTTON, self.OnClick)
self.Show(True)
def __DoLayout(self):
self.__DoButtons(wx.Panel(self, size=(200,bdepth), pos=(0,0), name='panel'), 'Cheese')
def __DoButtons(self, panel, label):
for i, line in enumerate(pt.Note):
solid = wx.EmptyBitmap(200,50,-1)
dc = wx.MemoryDC()
dc.SelectObject(solid)
solidbrush = wx.Brush(wx.Colour(75,75,75),wx.SOLID)
solidpen = wx.Pen(wx.Colour(75,75,75),wx.SOLID)
dc.SetBrush(solidbrush)
dc.SetPen(solidpen)
dc.DrawRectangle(0, 0, 200, 50)
dc.SetTextForeground(wx.Colour(255, 255, 255))
dc.DrawText(line.rstrip(), 30, 17)
dc.SelectObject(wx.NullBitmap)
self.checked = wx.Image('buttonchecked.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
dc = wx.MemoryDC()
dc.SelectObject(self.checked)
dc.SetTextForeground(wx.Colour(200, 255, 0))
dc.DrawText(line.rstrip(), 30, 17)
dc.SelectObject(wx.NullBitmap)
self.b = wx.BitmapButton(panel, i + 800, solid, (0, i * 50), (solid.GetWidth(), solid.GetHeight()), style = wx.NO_BORDER, name=line.rstrip())
def OnClick(self, event):
self.b.SetBitmapDisabled(self.checked)
self.b.Enable(False)
print('cheese')
bdepth = pt.TL * 50
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()enter code here
Only the last button is working because each time you go through the __DoButtons loop you reassign self.b to a different button. So after the loop has finished self.b is only assigned to the last button. You can get the button pressed using the event.GetEventObject() method.
Change your OnClick method to:
def OnClick(self, event):
button = event.GetEventObject()
button.SetBitmapDisabled(self.checked)
button.Enable(False)
print('cheese')

Categories

Resources