How do I set the text attributes on an empty line in a PyGtk TextView? - pygtk

The first section of code is the original code posted. The second code is modification of Bob's answer who led me in the right direction.
I need the user, when they reach a specific line (where this is a new line, which of course is empty) in the gtk.TextBuffer to be typing in a bold font.
If possible, what change is necessary in the code below that would make the font bold from where the user begins to type?
import gtk, pango
class BoldTestWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.connect( "destroy", lambda *w: gtk.main_quit() )
self.set_default_size(280, 80)
# Create a text view and get it's buffer.
self.tv = gtk.TextView()
buffer = self.tv.get_buffer()
# Create the Tags and add them to the Tag Table.
tags = [["weight", pango.WEIGHT_BOLD], ]
for tag in tags:
newTag = gtk.TextTag(name=tag[0])
newTag.set_property(tag[0], tag[1])
textTable = buffer.get_tag_table()
textTable.add(newTag)
# Grab an Iter to insert text.
startIter = buffer.get_start_iter()
# Insert some text in which to apply the bold attribute.
buffer.insert(startIter, "A Bold Statement.")
# startIter must be asked for again, because text was inserted.
startIter = buffer.get_start_iter()
endIter = buffer.get_end_iter()
# Apply the tags to entire range of TextBuffer
tags = ["weight",]
for tag in tags:
buffer.apply_tag_by_name(tag, startIter, endIter)
# Let's see what happens.
self.add(self.tv)
self.tv.grab_focus()
self.set_position(gtk.WIN_POS_CENTER)
self.show_all()
if __name__ == "__main__":
bww = BoldTestWindow()
gtk.main()
Bob's answer was the expose event, accept the expose event floods with thousands of calls to change the attributes, where only one call is neccessary per character added.
In order to stop a flooding of events, I experimented with key events.
I tried using key-press-event, but the character was not modified until the next event round, which left no style changes for the last character pressed.
I tried key-release-event and this worked, but there was a small delay in adding the attributes. The character would be no attribute for a split second, then the bold attribute would be added.
Finally, I tried a mix of key-press-event with expose-event, but that bottle necked some, then Juhaz from irc #pygtk recommended changed event which seems to work well. I believe the anser is close, I will use the following section to post it when done. I still have to work out line justification properties, they are still buggy.
import gtk, pango
# TaggedCursorTextView attempts to only add one feature to gtk.TextView: make the cursor dynamically settable
# to a list of tags. This forces the current text being typed to the set of tags set.
class TaggedCursorTextView(gtk.TextView):
def __init__(self):
gtk.TextView.__init__(self)
# Create buffer variable that point to it's internal TextBuffer.
self.buffer = self.get_buffer()
# Listen for the changed event. (User types, deletes or pastes text, etc.)
self.connect("key-press-event", self.on_key_press_event)
# What ever tags are place in here determines the text attributes (font type, bold, etc.)
# That is being typed by the user at any given moment.
# Default should be kept empty, no styles.
self.cursorTags = []
self.handlerID = None
def addTags(self, tagList):
# Create the Tags and add them to the Tag Table. Ignores duplicate tag names.
for tag in tagList:
newTag = gtk.TextTag(name=tag[0])
textTable = self.buffer.get_tag_table()
tagNameFound = textTable.lookup(tag[0])
if not tagNameFound:
newTag.set_property(tag[1], tag[2])
textTable.add(newTag)
def removeTags(self, tagNameList):
pass
def setCursorTags(self, tagList):
self.cursorTags = tagList
def on_key_press_event(self, widget, event):
self.handlerID = self.buffer.connect("changed", self.on_changed_event)
def on_changed_event(self, widget):
"""This method updates the last character type to the cursor style."""
self.buffer.disconnect(self.handlerID)
# Get the iter that falls before and after the last char typed.
endIter = self.buffer.get_end_iter()
offset = endIter.get_offset() - 1
startIter = self.buffer.get_iter_at_offset(offset)
# Apply the tags to the newly typed char
for tag in self.cursorTags:
self.buffer.apply_tag_by_name(tag, startIter, endIter)
class TaggedCurserTextViewTestWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.connect( "destroy", lambda *w: gtk.main_quit() )
self.set_default_size(280, 80)
# Create a TaggedCursorTextView.
tctv = TaggedCursorTextView()
# Add some cursors tags that will be used at some point later in the app.
# Each tag element list is: [tag name, tag property, tag value]
tagList = [["Italic", "style", pango.STYLE_ITALIC], ["Bold", "weight", pango.WEIGHT_BOLD], ["Center", "justification", gtk.JUSTIFY_CENTER]]
tctv.addTags(tagList)
# Use the names of the tags in a list to set the cursor tags.
tctv.setCursorTags(["Italic", "Bold", "Center"]) # Comment out this line for no style.
# Let's see what happens.
self.add(tctv)
tctv.grab_focus()
self.set_position(gtk.WIN_POS_CENTER)
self.show_all()
if __name__ == "__main__":
TaggedCurserTextViewTestWindow()
gtk.main()

Here's a rather crude modification of your code. I've just created an expose-event handler for your textview and whenever expose-event is invoked (i.e. when your textview requests to redraw), it redraws everything with bold font.
I'm not sure, if this is what you wanted; please explain in more detail your purpose, if it's not (I'm in IRC).
import gtk, pango
class BoldTestWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.connect( "destroy", lambda *w: gtk.main_quit() )
self.set_default_size(280, 80)
# Create a text view and get it's buffer.
self.tv = gtk.TextView()
buffer = self.tv.get_buffer()
# Create the Tags and add them to the Tag Table.
tags = [["weight", pango.WEIGHT_BOLD], ]
for tag in tags:
newTag = gtk.TextTag(name=tag[0])
newTag.set_property(tag[0], tag[1])
textTable = buffer.get_tag_table()
textTable.add(newTag)
# Grab an Iter to insert text.
startIter = buffer.get_start_iter()
# Insert some text in which to apply the bold attribute.
buffer.insert(startIter, "A Bold Statement.")
# startIter must be asked for again, because text was inserted.
startIter = buffer.get_start_iter()
endIter = buffer.get_end_iter()
# Apply the tags to entire range of TextBuffer
tags = ["weight",]
for tag in tags:
buffer.apply_tag_by_name(tag, startIter, endIter)
# Let's see what happens.
self.add(self.tv)
self.tv.grab_focus()
self.set_position(gtk.WIN_POS_CENTER)
self.show_all()
def on_expose_event(self, widget, event, data=None):
buffer = self.tv.get_buffer()
startIter = buffer.get_start_iter()
endIter = buffer.get_end_iter()
# Apply the tags to entire range of TextBuffer
tags = ["weight",]
for tag in tags:
buffer.apply_tag_by_name(tag, startIter, endIter)
if __name__ == "__main__":
bww = BoldTestWindow()
bww.tv.connect("expose-event", bww.on_expose_event)
gtk.main()

Related

How to extract the [Documentation] text from Robot framework test case

I am trying to extract the content of the [Documentation] section as a string for comparision with other part in a Python script.
I was told to use Robot framework API https://robot-framework.readthedocs.io/en/stable/
to extract but I have no idea how.
However, I am required to work with version 3.1.2
Example:
*** Test Cases ***
ATC Verify that Sensor Battery can enable and disable manufacturing mode
[Documentation] E1: This is the description of the test 1
... E2: This is the description of the test 2
[Tags] E1 TRACE{Trace_of_E1}
... E2 TRACE{Trace_of_E2}
Extract the string as
E1: This is the description of the test 1
E2: This is the description of the test 2
Have a look at these examples. I did something similar to generate testplans descritio. I tried to adapt my code to your requirements and this could maybe work for you.
import os
import re
from robot.api.parsing import (
get_model, get_tokens, Documentation, EmptyLine, KeywordCall,
ModelVisitor, Token
)
class RobotParser(ModelVisitor):
def __init__(self):
# Create object with remarkup_text to store formated documentation
self.text = ''
def get_text(self):
return self.text
def visit_TestCase(self, node):
# The matched `TestCase` node is a block with `header` and
# `body` attributes. `header` is a statement with familiar
# `get_token` and `get_value` methods for getting certain
# tokens or their value.
for keyword in node.body:
# skip empty lines
if keyword.get_value(Token.DOCUMENTATION) == None:
continue
self.text += keyword.get_value(Token.ARGUMENT)
def visit_Documentation(self,node):
# The matched "Documentation" node with value
self.remarkup_text += node.value + self.new_line
def visit_File(self, node):
# Call `generic_visit` to visit also child nodes.
return self.generic_visit(node)
if __name__ == "__main__":
path = "../tests"
for filename in os.listdir(path):
if re.match(".*\.robot", filename):
model = get_model(os.path.join(path, filename))
robot_parser = RobotParser()
robot_parser.visit(model)
text=robot_parser._text()
The code marked as best answer didn't quite work for me and has a lot of redundancy but it inspired me enough to get into the parsing and write it in a much readable and efficient way that actually works as is. You just have to have your own way of generating & iterating through filesystem where you call the get_robot_metadata(filepath) function.
from robot.api.parsing import (get_model, ModelVisitor, Token)
class RobotParser(ModelVisitor):
def __init__(self):
self.testcases = {}
def visit_TestCase(self, node):
testcasename = (node.header.name)
self.testcases[testcasename] = {}
for section in node.body:
if section.get_value(Token.DOCUMENTATION) != None:
documentation = section.value
self.testcases[testcasename]['Documentation'] = documentation
elif section.get_value(Token.TAGS) != None:
tags = section.values
self.testcases[testcasename]['Tags'] = tags
def get_testcases(self):
return self.testcases
def get_robot_metadata(filepath):
if filepath.endswith('.robot'):
robot_parser = RobotParser()
model = get_model(filepath)
robot_parser.visit(model)
metadata = robot_parser.get_testcases()
return metadata
This function will be able to extract the [Documentation] section from the testcase:
def documentation_extractor(testcase):
documentation = []
for setting in testcase.settings:
if len(setting) > 2 and setting[1].lower() == "[documentation]":
for doc in setting[2:]:
if doc.startswith("#"):
# the start of a comment, so skip rest of the line
break
documentation.append(doc)
break
return "\n".join(documentation)

Telegram Bot Use Button in Query

I am creating my first ever Telegram Bot on Python. For now, I managed to get an inline menu, but I cannot understand how to get the information from the last button (aka the text on the button that was pushed) to use it in the query for google doc. For example, if they check Button 3, I want to somehow access text 'Button 3' and use it in the query (data[data['type']=='Button 3']). Here is the code to help with understanding:
########################## Open connection with Google Sheets #############################
client = gspread.authorize(creds)
sheet = client.open('YodissiDay').sheet1
############################### Bot ############################################
def start(bot, update):
bot.message.reply_text(main_menu_message(), reply_markup=main_menu_keyboard())
def main_menu(bot, update):
bot.callback_query.message.edit_text(main_menu_message(), reply_markup=main_menu_keyboard())
def first_menu(bot, update):
bot.callback_query.message.edit_text(first_menu_message(),
reply_markup=first_menu_keyboard())
def second_menu(bot, update):
bot.callback_query.message.edit_text(second_menu_message(),
reply_markup=second_menu_keyboard())
def third_menu(bot, update):
bot.callback_query.message.edit_text(second_menu_message(),
reply_markup=second_menu_keyboard())
def first_submenu(bot, update):
bot.callback_query.message.edit_text(first_submenu_message(),
reply_markup=first_submenu_keyboard(bot, update))
def second_submenu(bot, update):
pass
def error(update, context):
print(f'Update {update} caused error {context.error}')
############################ Keyboards #########################################
def main_menu_keyboard():
keyboard = [[InlineKeyboardButton('First Choice', callback_data='m1')],
[InlineKeyboardButton('Second Choice', callback_data='m2')],
[InlineKeyboardButton('Third Choice', callback_data='m3')]]
return InlineKeyboardMarkup(keyboard)
def first_menu_keyboard():
keyboard = [[InlineKeyboardButton('Beauty', callback_data='Beauty')],
[InlineKeyboardButton('One', callback_data='One')],
[InlineKeyboardButton('Two', callback_data='Two')],
[InlineKeyboardButton('Three', callback_data='Three')],
[InlineKeyboardButton('Main menu', callback_data='main')]]
return InlineKeyboardMarkup(keyboard)
def second_menu_keyboard():
pass
def first_submenu_keyboard(bot, update):
data = gspread_dataframe.get_as_dataframe(sheet)
data = data.astype({'Taken' : 'float64'})
data = data[data['Taken']==0]
gift_type = update.callback_query.data
gift_ideas = data[data['Type']==gift_type]
keyboard = []
for row in range(0,gift_ideas.shape[0]):
keyboard[row]=InlineKeyboardButton(text=gift_ideas['Name'][row], url=gift_ideas['Link'][row])
keyboard[gift_ideas.shape[0]+1]=InlineKeyboardButton('Main menu', callback_data='main')
return InlineKeyboardMarkup(keyboard)
############################# Messages #########################################
def main_menu_message():
return 'Hello 1:'
def first_menu_message():
return 'Hello 1_1:'
def second_menu_message():
return 'Hello 1_2:'
def third_menu_message():
return 'Hello 1_3:'
def first_submenu_message():
return 'Hello 1_1_1:'
############################# Handlers #########################################
#
Create bot object:
bot = telegram.Bot(token=BOT_KEY)
updater = Updater(BOT_KEY, use_context=True)
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(main_menu, pattern='main'))
updater.dispatcher.add_handler(CallbackQueryHandler(first_menu, pattern='m1'))
updater.dispatcher.add_handler(CallbackQueryHandler(second_menu, pattern='m2'))
updater.dispatcher.add_handler(CallbackQueryHandler(third_menu, pattern='m3'))
updater.dispatcher.add_handler(CallbackQueryHandler(first_submenu, pattern='Beauty'))
updater.dispatcher.add_error_handler(error)
# Start polling:
updater.start_polling()
updater.idle()```
All other recommendations/advices are also welcome! Keep in mind, that I try it step by step and for now I want to see how this 'Beauty' button would work! Other buttons lead to nowhere for now
Please see Creating a handler by clicking on dynamic inline buttons about accessing the text of the clicked button.
Two additional remarks:
You pass use_context=True to your Updater, but use old-style callback signatures (def callback(bot, update): instead of def callback(update, context):). Please see the v12 transition guide for details.
The names of four callbacks (main_menu, first_menu) imply that you are building some sort of interactive menu. If you add more functionality to your bot, this may become hard to maintain in this form. I suggest that you have a look at ConversationHandler and the conversationbot2.py example and see if that's helpful for your use case.
Disclaimer: I'm currently the maintainer of python-telegram-bot

GtkListBox, how to prevent auto-selecting a row on window show

I have a list box with several rows attached to a window.
list_box = Gtk.ListBox()
list_box.insert(Gtk.Label('foo'), -1)
list_box.insert(Gtk.Label('bar'), -1)
list_box.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
window = Gtk.Window()
window.add(list_box)
window.show_all()
When I call show_all(), the first row of the list is being selected automatically what I don't want to happen. How to prevent auto-selecting it?
I tried changing the order of the functions call
window.show_all()
window.add(list_box)
which broke the layout and the size of the window doesn't fit to the list.
I was running into this issue as well, I used the following code to do it:
listbox = Gtk.ListBox(margin=0)
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
I can still click on each row and do a callback with the following, as well:
listbox.connect("row-activated", self.callback)
The ListBox has a property selection-mode, which you can set to Gtk.SELECTION_NONE. In this case none of the rows will be selected (and cannot be selected later). I don't know if that is what you want.
You can also call the method unselect_all, which will unselect all rows. For this to work, the ListBox must be in SELECT_MULTIPLE or SELECT_SINGLE mode.
This example seems to work completely as expected (i.e. no selection at the start, and if a line is selected, the button can unselect it). If in your installation it doesn't work, I would try to update your packages:
from gi.repository import Gtk
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect("delete-event", self.on_delete)
self.listbox = Gtk.ListBox()
self.listbox.insert(Gtk.Label('foo'), -1)
self.listbox.insert(Gtk.Label('bar'), -1)
self.listbox.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
button = Gtk.Button("Clear selection")
button.connect("clicked", self.on_button_clicked)
vbox = Gtk.VBox()
vbox.pack_start(button, False, True, 0)
vbox.pack_start(self.listbox, False, True, 0)
self.add(vbox)
self.show_all()
Gtk.main()
def on_button_clicked(self, btn):
self.listbox.unselect_all()
def on_delete(self, win, event):
Gtk.main_quit()
def main():
w = MainWindow()
return 0
if __name__ == '__main__':
main()
A note about gtk3 themes: Some themes do not show selected rows at all. Particularly dark themes such as eg. FlatStudioDark, but also some light themes.
Add a dummy label to the top of your list_box and hide it:
dummyLabel = Gtk.Label('nothing') # workaround for focus of title entry
list_box.insert(dummyLabel, -1)
list_box.insert(Gtk.Label('foo'), -1)
list_box.insert(Gtk.Label('bar'), -1)
list_box.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
window = Gtk.Window()
window.add(list_box)
window.show_all()
dummyLabel.hide()
Gtk.main()
The result:
result

Python 3.x tkinter - can't get checkbox' variable value

Please help! As it says in the title - I can't get checkbox' variable value.
def init_widgets(self):
ttk.Button(self.root, command=self.insert_txt, text='Button', width='10').place(x=10, y=10)
...
cbcc = ttk.Checkbutton(root, text="Damage", onvalue="on", offvalue='off').place(x=235, y=12)
...
def insert_txt(self):
...
cbcd = StringVar()
cbcd.get()
print(cbcd)
if cbcd == "on":
self.damage
else:
Print delivers "PY_VAR2" and counting up from there with every time I click (PY_VAR3, etc.)
It seems in your code, the argument 'variable' in your Checkbutton is not bounded, furthermore to get the value from IntVar or StringVar you would use (IntVar.get(), StringVar().get()), you could use the next code as example to use Checkbutton widget.
'''
CheckBox Test
References:
http://effbot.org/tkinterbook/checkbutton.htm
'''
from tkinter import *
class App:
def __init__(self, master):
self.var = IntVar() #This is the variable bounded to checkbutton to
#get the checkbutton state value
#
frame = Frame(master)
frame.pack()
self.checkbutton = Checkbutton(frame, text="Hello Checkbutton",
command=self.say_hello, variable=self.var)
self.checkbutton.pack(side=LEFT)
#
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=LEFT)
def say_hello(self):
'''
Function Bounded to Checkbutton in command parameter, every click
either check or un-check print the current state of the checkbutton
'''
print("State Changed:", self.var.get())
if __name__ == '__main__':
root = Tk()
app = App(root)
#
root.mainloop()
root.destroy() # Quit the App when you click "Quit"
Every click on the check button, you will see printed in the console the value of the current state for the checkbutton. You can check the next reference to get an general overview tkinter widgets: http://effbot.org/tkinterbook/checkbutton.htm
I hope this snippet helps you.
Very Best Regards.

tkinter variable for drop down selection empty

I tried to program an app in tkinter that would load random lines from a file you select from a pull down menu and display the selected line in a text window.
It seems like the variable "var" in insert_text does not return the selected "option" but rather an "empty" string resulting in a the following error:
"File not found error" (FileNotFoundError: [Errno2] No such file or
directory: '').
Please help!
#!/usr/bin/env python
# Python 3
import tkinter
from tkinter import ttk
import random
class Application:
def __init__(self, root):
self.root = root
self.root.title('Random Stuff')
ttk.Frame(self.root, width=450, height=185).pack()
self.init_widgets()
var = tkinter.StringVar(root)
script = var.get()
choices = ['option1', 'option2', 'option3']
option = tkinter.OptionMenu(root, var, *choices)
option.pack(side='right', padx=10, pady=10)
def init_widgets(self):
ttk.Button(self.root, command=self.insert_txt, text='Button', width='10').place(x=10, y=10)
self.txt = tkinter.Text(self.root, width='45', height='5')
self.txt.place(x=10, y=50)
def insert_txt(self):
var = tkinter.StringVar(root)
name = var.get()
line = random.choice(open(str(name)).readlines())
self.txt.insert(tkinter.INSERT, line)
if __name__ == '__main__':
root = tkinter.Tk()
Application(root)
root.mainloop()
That's because you're just creating an empty StringVar that isn't modified later, thus returning an empty string.
The OptionMenu takes the command parameter that calls the specified method every time another option is selected. Now, you can call a method like this, replacing you insert_txt:
def __init__(self):
# ...
self.var = tkinter.StringVar()
self.options = tkinter.OptionMenu(root, var, *choices, command=self.option_selected)
# ...
def option_selected(self, event):
name = self.var.get()
# The stuff you already had
Additionally, you have to empty the Text widget, otherwise the previous text would stay. I think the Entry widget is better for that, too.