I'm trying to create a text editor that will manipulate a string like object. Ideally I would like to subclass Tkinter's Text Widget or some other Gui module to allow me to substitute this custom object instead of using strings.
I understand how to create the object itself (which essentially just tags every word with meta-data), but not how to manipulate the rendered object as text while retaining its other attributes.
Eg.
The text editor imports a file that contains "Hello World". Upon opening this text, both words are tag'd with relevant attributes. (Displayed below as a dictionary, but ideally an object Hello.strContent, Hello.index, etc.)
Hello={strContent: "Hello", index: 0, speaker: "Joe", paragraph: 3}
World={strContent: "World", index: 1, speaker: "Joe", paragraph: 3}
Now I'm completely stumped though how I could make these string like objects manipulable, and subject to cutting, copying, pasting, deleting, and rearranging, inside a Text gui. I would not need to have the program allow me to type any new words or create new objects, just manipulate the objects that have been initialized upon converting the file I opened.
Any help or direction would be greatly appreciated. Thanks.
Note: this attempts to answer the question that was asked, but I suspect this is an xy problem. At the end of this answer I'll give some other suggestions.
The text widget can't have some complex object as its underlying data structure. It can display text, and it can have tags associated with the text. You can also insert images and widgets, but I think that's irrelevant to what you are asking.
When reading the data in, you can construct tags for each piece of metadata, and associate those tags with a range of text. For example, the word "Hello" could have the tags "paragraph:3", "speaker:Joe", and "index:0". "World" would be similar, except it would have the tag "index:1".
This would be easy to do when initially displaying the data. For example:
data = [{"strContent": "Hello", "index": 0, "speaker": "Joe", "paragraph": 3},
{"strContent": "World", "index": 1, "speaker": "Joe", "paragraph": 3}
]
for item in data:
tags = (
"index:%d" % item['index'],
"speaker:%s" % item['speaker'],
"paragraph:%d" % item['paragraph']
)
self.text.insert("end", item['strContent'], tags)
If you then go in and insert "r" in the word "World", it will inherit the tags of the surrounding text.
You can get the data back out of the widget with the dump
method, which returns a stream of data. For example, self.text.dump("1.0", "end-1c", tag=True, text=True, mark=False)
yields this information:
[
('tagon', 'paragraph:3', '1.0'),
('tagon', 'speaker:Joe', '1.0'),
('tagon', 'index:0', '1.0'),
('text', 'Hello', '1.0'),
('tagoff', 'index:0', '1.5'),
('tagon', 'index:1', '1.5'),
('text', 'World', '1.5')
]
Reassembling that data back to your original format is tricky. Here's a rough cut of an attempt, though I don't know how it would stand up in the real world. It might be possible for users to edit the data in ways that totally mess up the structure.
def get_data(self):
result = []
meta = {}
for item in self.text.dump("1.0", "end-1c", tag=True, text=True, mark=False):
if item[0] == "tagon":
(name, value) = item[1].split(":")
meta[name] = value
if item[0] == "tagoff":
(name, value) = item[1].split(":")
del meta[name]
if item[0] == "text":
text = item[1]
# if this text has the same tags as the previous text,
# don't create a new item; instead, append the text to
# the previous item
if result and all(item in result[-1].items() for item in meta.items()):
result[-1]["strContent"] += text
else:
data = {"strContent": text}
data.update(meta)
result.append(data)
return result
Not knowing what you're actually trying to accomplish, a text widget might not be the best solution since it gives the user a bit too much freedom to alter the text. For example, what should happen if they change "Hello" to "HelloWorld", and then delete the original "World"? Do they end up with one "HelloWorld" item, or the original two "Hello" and "World" items?
You might want to consider using either a canvas, where each text item is a distinct object (which can also have tags), or perhaps you might want to use a series of entry widgets so that the data in once can't bleed over into the other.
i think that you could do something like
class myobj:
def __init__(self, text):
self.strContent=text
hello = [[myobj("Hello"), myObj("World")], [myobj("Paragraph"), myobj("2")]]
then whenever you need to display that text, you would have it loop through like so
printableStr = ""
for paragraph in hello:
for word in paragraph:
printableStr += word.strContent + " "
printableStr += "\n"
and then printableStr will be a nice string containing all info from hello