Undo with GTK TextView - python

I'm trying to keep dependencies to a minimum for a program I contribute to, it's a small text editor.
GTK Textview doesn't seem to come with a built-in undo function. Is there any reference implementation I've been missing on so far? Is everyone writing their own undo function for their TextView widgets?
I'll be happy about any sample code - most happy about python sample code, as our project is in python.

as a follwow-up: I ported gtksourceview's undo mechanism to python: http://bitbucket.org/tiax/gtk-textbuffer-with-undo/
serves as a drop-in replacement for gtksourceview's undo
(OP here, but launchpad open-id doesn't work anymore)

As far as I know, GTK TextView doesn't include an undo function. So while I am not familiar with Python's GTK library, I would think it doesn't have one.
The Ruby-GNOME2 project has a sample text editor that has undo/redo functionality. Basically they are connecting to the insert_text and delete_range signals of the TextView widget and recording the events and associated data in a list.

Depending on just how dependency-averse you are, and what kind of text editor you're building, GtkSourceView adds undo/redo among many other things. Very worth looking at if you want some of the other features it offers.

Use GtkSource
https://wiki.gnome.org/Projects/GtkSourceView
https://lazka.github.io/pgi-docs/GtkSource-3.0/
https://lazka.github.io/pgi-docs/GtkSource-3.0/classes.html
.
[Cmnd] + [Z] for undo (default)
[Cmnd] + [Shift] + [Z] for redo (default)
[Cmnd] + [Y] for redo (added manually)
example:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
gi.require_version('GtkSource', '3.0')
from gi.repository import GtkSource
import os
class TreeviewWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="TreeviewWindow")
self.set_size_request(300, 300)
self.connect("key-press-event", self._key_press_event)
self.mainbox = Gtk.VBox(spacing=10)
self.add(self.mainbox)
self.textbuffer = GtkSource.Buffer()
textview = GtkSource.View(buffer=self.textbuffer)
textview.set_editable(True)
textview.set_cursor_visible(True)
textview.set_show_line_numbers(True)
self.mainbox.pack_start(textview, True, True, 0)
self.show_all()
def _key_press_event(self, widget, event):
keyval_name = Gdk.keyval_name(event.keyval)
ctrl = (event.state & Gdk.ModifierType.CONTROL_MASK)
if ctrl and keyval_name == 'y':
if self.textbuffer.can_redo():
self.textbuffer.do_redo(self.textbuffer)
def main(self):
Gtk.main()
if __name__ == "__main__":
base = TreeviewWindow()
base.main()

Related

How do I get parameter value from a GParam object

What:
Gtk+ 3 with PyGObject bindings and Python 2.7.
Question:
How can I get the value of a GParamObject? I assume it has a value, (otherwise what would be the point of it?) but I have spent several hours googling and experimenting and have not been able find the answer to this seemingly simple question. Maybe my assumption that is has a value is unfounded.
Background: I have a Gtk.Stack, and I have connected a callback to the "notify::visible-child" signals that does some stuff when the visible-child changes. Something like this
stack.connect("notify::visible-child", on_stack_changed)
def on_stack_changed(stack, param):
print "stack's visible child changed"
print param # Prints: <GParamObject 'visible-child'>
Now, since the GTK.Stack docs indicate that the visible_child property's value is the visible child widget, I expect to be able to get a reference to the widget from the param passed to my callback. But I have not been able to figure out how to get any value from the GParam object.
I have tried every variation of things like param.value, param.get_value() etc. that I could think of, but to no avail.
Of course, since the stack object is also passed to my callback, I could always do stack. get_visible_child(), but that is no fun, is it? :D
So, any explanation of the GParamObject, its intended use, or links to examples of its use would be greatly appreciated.
Edit: Here is a standalone example.
#!/usr/bin/env python
import os, gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, Gdk
def on_stack_changed(stack, param):
for attr in dir(param):
print "{}: {}".format(attr, getattr(param, attr))
stack = Gtk.Stack()
stack.add_titled(Gtk.Label('Child 1'), 'child1', 'Child 1')
stack.add_titled(Gtk.Label('Child 2'), 'child2', 'Child 2')
stack.connect("notify::visible-child", on_stack_changed)
stack_switcher = Gtk.StackSwitcher(stack=stack)
header_bar = Gtk.HeaderBar(custom_title=stack_switcher, show_close_button=True)
window = Gtk.Window()
window.set_default_size(500, 250)
window.set_titlebar(header_bar)
window.add(stack)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
As suggested by liberforce, I tried print dir(param) which results in this output:
__doc__: The widget currently visible in the stack
__gtype__: <GType GParamObject (94151103218704)>
blurb: The widget currently visible in the stack
flags: 1073742051
name: visible-child
nick: Visible child
owner_type: <GType GtkStack (94151105445744)>
value_type: <GType GtkWidget (94151104851072)>
This helps a great deal in understanding what information is available from the GParamObject. As expected the value_type is a GtkWidget, but I still don't see how to get the value itself.
I think it's because it doesn't exist there. What you get in param seems to be a GParamSpec which is just metadata about a parameter, holding its name, type, flags... I can't find a reference on GParamObject in the PyGObject documentation. But If I look the C documentation of the notify signal, indeed, thats a GParamSpec that is passed there, so that may be a GObject.ParamSpecObject
So I think your get_visible_child is the way to go. You may get the same result in a more flexible way using using the GParamSpec with stack.get_property(param.name).
UPDATE:
Here's the standalone example modified:
In the callback, I use 3 different ways of getting the visible child, all working. They all return the same object instance, as confirmed by id.
#!/usr/bin/env python
import os, gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, Gdk
def on_stack_changed(stack, param):
print 'stack.get_visible_child(): {}'.format(id(stack.get_visible_child()))
print 'stack.get_property(param.name): {}'.format(id(stack.get_property(param.name)))
print 'stack.props.visible_child: {}'.format(id(stack.props.visible_child))
stack = Gtk.Stack()
stack.add_titled(Gtk.Label('Child 1'), 'child1', 'Child 1')
stack.add_titled(Gtk.Label('Child 2'), 'child2', 'Child 2')
stack.connect("notify::visible-child", on_stack_changed)
stack_switcher = Gtk.StackSwitcher(stack=stack)
header_bar = Gtk.HeaderBar(custom_title=stack_switcher, show_close_button=True)
window = Gtk.Window()
window.set_default_size(500, 250)
window.set_titlebar(header_bar)
window.add(stack)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
Using print vars(param) may help you see what is really in your object.

Difference between these two window structure?

Both of the code-snips below will create an empty Gtk window using python. However they seem quite different.
What is the main benefits of one vs the other ?
Is there any performance, security or compatibility implications of choosing one versus the other?
1st code-snip:
#!/usr/bin/python
from gi.repository import Gtk
win = Gtk.Window()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
2nd code-snip:
from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys
class GUI:
def __init__(self):
window = Gtk.Window()
window.set_title ("Hello World")
window.connect_after('destroy', self.destroy)
window.show_all()
def destroy(self, window):
Gtk.main_quit()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
Ref1: 1st snip-code reference
Ref2: 2nd snip-code reference
The second snippet is more object-oriented, it defines a class GUI that you can extend for your application, which is a more elegant solution in my opinion. Additionally, it properly defines a main() function and calls it, this gives you more flexibility and allows you to import this module from elsewhere without instantiating the GUI on import. There are, as you asked, no real performance, compatibility or security implications.
In the 2nd snippet, I would, however, bind the window to self (self.window = Gtk.Window()), allowing you access from any method in the class.

Python interpreter, Pygtk and GtkGrid

i've to use GtkGrid because i need to draw a rectangle for every item i have.
(i would add a Drawing area to every GtkGrid's cell and draw the rectangle by cairographics library)
But there was a problem: python 2.7 doesn't support GtkGrid, so i surfed the web and i simply changed the first line of my file (and installed python3.3).
#!/usr/bin/python3.3
try:
import pygtk
pygtk.require("2.0")
except:
print("PyGtk Not Availible")
sys.exit(1)
try:
import gtk
import gtk.glade
except:
print("GTK Not Availible")
sys.exit(1)
Now it cannot rescue anymore Pygtk or GTK libraries.. and with python2.7 all work fine..
Maybe the best solution would be to avoid newer python interpreters and change GtkGrid into something else..
Help me please
EDIT::
Just cut and pasted an example from Pygtk examples
#!/usr/bin/python3.3
# example drawingarea.py
import pygtk
pygtk.require('2.0')
import gtk
import operator
import time
import string
class DrawingAreaExample:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Drawing Area Example")
window.connect("destroy", lambda w: gtk.main_quit())
self.area = gtk.DrawingArea()
self.area.set_size_request(400, 300)
self.pangolayout = self.area.create_pango_layout("")
self.sw = gtk.ScrolledWindow()
...
With Python 2.7 it works, with Python 3.3: No module named 'pygtk'
Just to make this clear: You have to decide wether you want to use the gi.introspection bindings (which are up to date and mostly autogenerated) or the pygtk wrapper around gtk+-2.0 (hand crafted, as of now pygtk3 is still work in progress, correct me if I am wrong).
Mixing these will get you in trouble further or later.
Also your initial issue was, GtkGrid (part of gtk+-3.0) was part of the gi.introspection bindings which (usually) require Python 3.x.y, whereas you used pygtk2 with Python 2.7.x. Changing to Python 3.3 just made the bindings availiable to you.
In gtk2 GtkGrid was called GtkTable, see https://developer.gnome.org/gtk2/stable/GtkTable.html.
Try this:
#!/usr/bin/env python
# -*- coding: utf-8; -*-
from gi.repository import Gtk
grid = Gtk.Grid()
You need to use GTK+ 3, not python3.3 (in order to have a Gtk.Grid)... if i understand what you mean...
Edited.

Simple Pantheon panel applet on eOS?

I would like to make a simple applet for the Pantheon Panel on eOS Luna with Python. I can't find any documentation on any API. It's been suggested on some forum I should use the same procedure as Gnome or Unity. The applets I have tried, however (like the one on this answer), simply didn't work.
Could you guide me a little towards what I should be doing to have a simple applet icon + menu showing on the Pantheon panel?
It seems one has to use the App Indicator module as per Ubuntu documentation. The appindicator package of PyGtk didn't work out, but the PyGi AppIndicator3 does work fine as far as I can tell.
A simple example is:
#!/usr/env/bin/ python
from gi.repository import Gtk
from gi.repository import AppIndicator3 as appindicator
def menuitem_response(w, buf):
print buf
if __name__ == "__main__":
ind = appindicator.Indicator.new (
"example-simple-client",
"indicator-messages",
appindicator.IndicatorCategory.APPLICATION_STATUS)
ind.set_status (appindicator.IndicatorStatus.ACTIVE)
ind.set_attention_icon ("indicator-messages-new")
menu = Gtk.Menu()
for i in range(3):
buf = "Test-undermenu - %d" % i
menu_items = Gtk.MenuItem(buf)
menu.append(menu_items)
menu_items.show()
ind.set_menu(menu)
Gtk.main()
Example drawn from here.

How to make global keyboard shortcuts with python (and Gtk3)?

I want to make keyboard shortcuts like t, that would work, when the main window is closed (but process is running, as the programme has a unity appindicator). I saw a package keybinder, but it seems, one can't use it with Gtk3 and pygobject. Or can? Then how? If not, is there any other way to do that?
The application is for linux (ubuntu), I use python 2.7.
Keybinder works fine with python3, Gtk3, and pygi. There just wasn't a working example in the source tree.
#!/usr/bin/env python3
"""
example-gi-py3.py
Looked at a pull request that was built for py2.x, but
overwrote the original py instead of making a separate example.
I wouldn't have accepted that pull request either.
The keybinder.init() part wasn't in the original example.
aking1012.com#gmail.com
public domain
"""
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Keybinder', '3.0')
from gi.repository import Gtk
from gi.repository import Keybinder
def callback(keystr, user_data):
print ("Handling", user_data)
print ("Event time:", Keybinder.get_current_event_time())
Gtk.main_quit()
if __name__ == '__main__':
keystr = "<Ctrl><Alt>M"
Keybinder.init()
Keybinder.bind(keystr, callback, "keystring %s (user data)" % keystr)
print ("Press", keystr, "to handle keybinding and quit")
Gtk.main()
Notes:
Not thoroughly tested, but as a simple example it seems to work.
I use also Keybinder to activate a search entry field in a Gtk3 app:
from gi.repository import Keybinder
…
class MyApp:
…
Keybinder.init()
Keybinder.bind("<Ctrl>F", self.set_search_entry_focus)
…
def set_search_entry_focus(self, keystring):
self.search_entry.grab_focus()
http://lazka.github.io/pgi-docs/Keybinder-3.0/
But be aware, this will also steal focus if if you are using another app and your app is running in the background.

Categories

Resources