Copy-Pasteable Text in pygtk's TreeView - gtk

I came across this rather unexpected problem. I have a gtk.TreeView with a single text column, that is rendered by gtk.CellRendererText. What I want is that the user can mark the displayed text using the mouse and get it into the clipboard by pressing ctrl+c. (I am referring to the very basic feature present in every webbrowser and texteditor). However, gtk won't let me do it. I have a simple example here, with non-markable / non-highlightable text:
import gtk
class TreeViewExample(gtk.TreeView):
def __init__(self):
gtk.TreeView.__init__(self)
self.get_selection().set_mode(gtk.SELECTION_NONE)
self.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
# create model
self.list_store = gtk.ListStore(str)
self.list_store.append(['Hello, this is some \n multiline text.'])
self.list_store.append(['Another text.'])
self.set_model(self.list_store)
# create text column
col = gtk.TreeViewColumn('Text Column')
self.append_column(col)
cell = gtk.CellRendererText()
col.pack_start(cell, True)
col.add_attribute(cell, 'text', 0)
class MasterWindow(object):
def destroy(self, widget, data=None):
gtk.main_quit()
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
self.window.connect("destroy", self.destroy)
self.window.add(TreeViewExample())
self.window.show_all()
if __name__ == '__main__':
mw = MasterWindow()
gtk.main()
I could of course make the cell editable, because the editable mode provides the feature. But that is far from elegant, because it is some kind of popup, that breaks the line wrap and, well, edits the text. What i need is a cell, that is not selectable, editable or anything, but has text, that can be copied.
Does anyone have a solution? Thanks!

I made some changes in the code.
Try the code below, this example works well and I am sure that is the answer to your question.
Hope this helps you in the future of their knowledge and coding.
import gtk
class TreeViewExample():
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
model = gtk.ListStore(str)
model.append(['a'])
model.append(['b'])
model.append(['c'])
treeview = gtk.TreeView(model)
self.renderer = gtk.CellRendererText()
self.renderer.set_property('editable', True)
treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=0)
self.window.add(treeview)
self.window.show_all()
if __name__ == '__main__':
TreeViewExample()
gtk.main()

I made some changes in the code. Trying to answer questions of #Flimm.I hpe Helps you #Flimm.
import gtk
class TreeViewExample():
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
self.model = gtk.ListStore(str,str)
self.model.append(['a','b'])
self.model.append(['c','a'])
self.model.append(['d','a'])
self.model.append(['e','a'])
self.treeview = gtk.TreeView(self.model)
self.renderer = gtk.CellRendererText()
self.renderer.set_property('editable', True)
self.renderer.connect('edited', self._text_changed, 0)
self.treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=0)
self.treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=1)
self.window.add(self.treeview)
self.window.show_all()
def _text_changed( self, w, row, new_value, column):
self.model[row][column] = new_value
if __name__ == '__main__':
TreeViewExample()
gtk.main()

Related

Editable label in Python GTK+ 3?

I'm new to GTK programming. I want to have a Label widget whose text can be edited, kind of like this: https://docs.gtk.org/gtk4/class.EditableLabel.html.
The problem is I have no idea how to implement this. I understand that Gtk.Button has a set_label() function, though I don't know how to use it to make an editable label.
You can do that with Gtk.Entry which you can set_editable based on "something". Example with check button would look something like this:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
def on_editable_toggled(button, entry):
value = button.get_active()
entry.set_editable(value)
entry.set_sensitive(value)
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
win.add(vbox)
entry = Gtk.Entry()
entry.set_text("Hello World")
vbox.pack_start(entry, True, True, 0)
check_editable = Gtk.CheckButton(label="Editable")
check_editable.connect("toggled", on_editable_toggled, entry)
check_editable.set_active(True)
vbox.pack_start(check_editable, True, True, 0)
win.show_all()
Gtk.main()
It doesn't really look like a label, but you can use the Gtk CSS styling to change the background and border colors to make it look like one when it is set to non-editable.

Kivy button press to next page

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.submit = Button(text='Order', font_size=40)
self.add_widget(self.submit)
self.submit.bind(on_press=self.pressed)
def pressed(self, instance):
pressing = Secondtab()
pressing()
class Secondtab(GridLayout):
def __init__(self, **kwargs):
super(Secondtab, self).__init__(**kwargs)
self.cols = 5
self.submit = Button(text='Drinks', font_size=40)
self.add_widget(self.submit)
print('check')
def __call__(self):
print('new tab')
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
So I want the window to change to a new layout when I press the first button. I tought making a new grid class and calling it would work but it didnt work as I expected. I dont want the button to open a new window tho I want the current window to refresh to the new layout.
As #John Anderson suggested. Take a look at the screen manager widget. The 'current window' must be a populated screen widget and you could change the current screen by assigning the next screen's name to the current attribute.
This will help you I guess. Using ScreenManager:
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.submit = Button(text='Order', font_size=40)
self.add_widget(self.submit)
self.submit.bind(on_press=self.pressed)
def pressed(self, instance):
pressing = Secondtab()
pressing()
myapp.screen_manager.transition = SlideTransition(direction='left', duration=.25) #You can change transition speed or you could just remove it to set it on default
myapp.screen_manager.current = 'Secondtab'
class MyApp(App):
def build(self):
return LoginScreen()
self.screen_manager = ScreenManager()
self.loginscreen = LoginScreen()
screen = Screen(name='LoginScreen')
screen.add_widget(self.loginscreen)
self.screen_manager.add_widget(screen)
self.secondtab = Secondtab()
screen = Screen(name='Secondtab')
screen.add_widget(self.secondtab)
self.screen_manager.add_widget(screen)
return self.screen_manager

is there a way to take numerical inputs in gtk+, pygobject?

I need to implement a input dialog box that takes in numerical values. How is it done in Pygobject?. It is similar to excel taking numerical inputs.
If you're looking for simple numerical values, you can use Gtk.SpinButton.
The more general way getting input from the user in the UI is Gtk.Entry. That widget does not support built-in validation, but it can be trivially implemented, similarly as shown in this stackoverflow answer (by overriding the insert_text method).
If you want to show validation errors to your user, you can use either CSS styling, or for example set the secondary icon (with e.g. the secondary_icon_name property) to a warning sign, with an accompanying label explaining what is wrong.
With some help from Ricardo Silva Veloso, this is basically the code I was suggesting to write in my first comment (also considering #nielsdg secondary icon suggestion):
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MyEntry(Gtk.Entry, Gtk.Editable):
__gtype_name__ = 'MyEntry'
def __init__(self, **kwargs):
super(MyEntry, self).__init__(**kwargs)
def do_insert_text(self, text, length, position):
if text.isnumeric():
self.props.secondary_icon_name = None
self.get_buffer().insert_text(position, text, length)
return length + position
else:
self.props.secondary_icon_name = "dialog-error"
self.props.secondary_icon_tooltip_text = "Only numbers are allowed"
return position
win = Gtk.Window()
myentry = MyEntry()
win.add(myentry)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

How do I pause a loop with a button in PyGObject?

I have a button that runs a loop and updates the window accordingly. I want another "pause" button that pauses this loop but it doesn't seem like I can do that while the loop is running. Perhaps threading is the solution (I tried GLib.timeout_add_seconds without success) - what is the easiest way to make this work?
I have attached my faulty code:
import sys
from time import sleep
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class LoopButton(Gtk.Box):
def __init__(self, GUI):
Gtk.Box.__init__(self)
self.GUI = GUI
self.set_border_width(10)
self.message = "1"
button = Gtk.Button.new_with_label("Run")
button.connect("clicked", self.on_click)
self.pack_start(button, True, True, 0)
def on_click(self, widget):
msg = int(self.message)
while self.GUI.is_paused == False:
self.GUI.restart_window(str(msg))
msg += 1
while Gtk.events_pending():
Gtk.main_iteration()
sleep(1)
self.GUI.is_paused = True
class PauseButton(Gtk.Box):
def __init__(self, GUI):
Gtk.Box.__init__(self)
self.GUI = GUI
self.set_border_width(10)
button = Gtk.Button.new_with_label("Pause")
button.connect("clicked", self.on_click)
self.pack_start(button, True, True, 0)
def on_click(self, widget):
self.GUI.is_paused = True
class GUI:
def __init__(self):
self.is_paused = False
self.win = Gtk.Window()
self.window_grid = Gtk.Grid()
self.box = Gtk.Box(spacing=10)
self.label = Gtk.Label("Default label")
self.win.connect("delete-event", Gtk.main_quit)
self.start_window()
def start_window(self):
self.box.pack_start(LoopButton(self), True, True, 0)
self.box.pack_start(PauseButton(self), True, True, 0)
self.window_grid.add(self.box)
self.window_grid.add(self.label)
self.win.add(self.window_grid)
self.win.show_all()
def restart_window(self, label="Default label"):
self.window_grid.destroy()
self.window_grid = Gtk.Grid()
self.box = Gtk.Box(spacing=10)
self.label = Gtk.Label(label)
self.start_window()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
Depending on what you really do in the loop there may be more necessary details, but below is an example of how looping is normally avoided in GLib/Gtk. The caveats of this method are: A) one iteration must be fast enough to not affect UI updates (a few milliseconds at most). B) The timing is not exact: the timeout function might not be called exactly 2 seconds after the previous call.
import gi
from gi.repository import GLib
class GUI:
def __init__(self):
# initialize the UI here...
# setup updates every two seconds
GLib.timeout_add_seconds(2, self.timeout_update)
def timeout_update(self):
# update your widgets here
print ("Updating UI now...")
# yes, we want to be called again
return True

set up the entry of a combobox (gtk)

How could I set up the text of a combobox without knowing its id? I have a combobox populated with a list of name ('Jack','Emily','Paul',...). By default, the combo is set on -1 but I want it to be set on "Paul".
Here is my code to declare and populate the combo with tuple (id,fabName) :
self.cmbFabricant = builder.get_object("cmbFabricant")
self.cmbFabricant.set_model(lstStore)
self.cmbFabricant.set_entry_text_column(1)
Now, I'd like to set the combobox on the item called "Paul". I thought I could write :
self.cmbFabricant.set_active_id('Paul')
I was wrong.
I could be wrong, but I think set_active_id is new in GTK+ 3, and PyGTK is GTK+ 2. If you want to use GTK+ 3, you have to switch to PyGObject.
But if you're stuck on PyGTK, you can easily work around it by doing something like this:
import gtk
def set_active_name(combobox, col, name):
liststore = combobox.get_model()
for i in xrange(len(liststore)):
row = liststore[i]
if row[col] == name:
combobox.set_active(i)
window = gtk.Window()
window.connect("destroy", gtk.main_quit)
liststore = gtk.ListStore(int, str)
liststore.append([0, 'Jack'])
liststore.append([1, 'Emily'])
liststore.append([2, 'Paul'])
combobox = gtk.ComboBox()
cell = gtk.CellRendererText()
combobox.pack_start(cell)
combobox.add_attribute(cell, 'text', 1)
combobox.set_model(liststore)
set_active_name(combobox, 1, 'Paul')
window.add(combobox)
window.show_all()
gtk.main()
I'm not sure if there is a more elegant/efficient way, but this works at least.