Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I enable support for emoji in Tkinter applications?

I’ve implemented this Python chat application by Saurabh Chaturvedi, to start learning about how networking works. The application is simple and makes use of the Tkinter module.

I’d like to extend the app's functionality by enabling users to type emoticons in the message box and have them rendered as emoji in the message list, when they send the messages. For example, if the user types ‘:)’ in the message box, it should render as 😊.

So far, I've researched how to enable support for emoji in Tkinter applications. I came across Displaying emojis/symbols in Python using tkinter lib, but I don’t think it directly addresses my issue. I’m not sure if I’m currently going about solving this issue in the right way.

If it’s of any help, I’m running Windows 10 and using Python 3. And you should be able to implement the chat app by running the two scripts (server and client) described in Saurabh’s article. (Also, a potentially related problem is that emoji aren't displayed properly in my Python 3 interpreter. For example,

>>> import emoji
>>> print(emoji.emojize(":thumbs_up:"))

results in two question marks in boxes, rather than 👍.)

Any suggestions on how I could enable emoji to be rendered in the message list?


EDIT:

As per @abarnert's comment below, it's worth mentioning that this problem did not originate from my trying to use the emoji library to translate a user’s ‘:)’ to emoji. Rather, the original question arose because I was unsure of how to go about carrying out the rendering of ‘:)’ as 😊 in a Tkinter-based app, in the first place.

like image 709
Caleb Owusu-Yianoma Avatar asked Sep 07 '18 20:09

Caleb Owusu-Yianoma


1 Answers

You have multiple problems here, and I'm not sure which one you're stuck on, so I'll try to cover everything.


First, how do you "emojify" a string?

You need to define exactly what that means—what set of inputs do you map to what outputs, is if :8 is an input does that mean 80:80 should turn the :8 in the middle into an emoji (and, if not, what exactly is the rule that says why not), etc.

Then it's not that hard to implement—whether you're looping over s.split() or a more complicated re.finditer, whether your rules need to take context outside the current pattern into account, etc. depends on what rules you chose.

At any rate, there are a number of libraries on PyPI that do a variety of variations on this, and you already found at least one, emoji. so I'll assume this problem is solved.


Next, where do you hook that into your code?

Presumably, you have a callback that gets fired whenever the user submits a chat message. I don't know exactly how you set that up, but it doesn't really matter, so let's just make up a simple example:

def callback(widget):
    msg = widget.get()
    conn.sendall(msg)
    msglog.append(f'>>> {msg}')

This function gets the string contents out of a message input widget, and then both sends that string to the server, and displays it in a message log widget. So, all you need to do is:

def callback(widget):
    msg = widget.get()
    msg = emojify(msg)
    conn.sendall(msg)
    msglog.append(f'>>> {msg}')

Third, how do you deal with the fact that tkinter can't handle emoji (and other non-BMP characters) correctly?

This part is a dup of the question you linked. Tkinter has a known bug, which is still there as of 3.7, and the best known workaround is kind of ugly.

But, while it may be ugly, it's not that hard. The answer to that question links to another question, where Martijn Pieters provides a nice with_surrogates function that you can just call on your emojified string.

If you want something simple but hacky—which will make the server basically useless with anything but a buggy tkinter client—you can do that before sending the messages over the wire:

def callback(widget):
    msg = widget.get()
    msg = emojify(msg)
    tkmsg = with_surrogates(msg)
    conn.sendall(tkmsg)
    msglog.append(f'>>> {tkmsg}')

But a cleaner solution is to send them over the wire as proper UTF-8, and only surrogatize it for display, both here:

def callback(widget):
    msg = widget.get()
    msg = emojify(msg)
    conn.sendall(msg)
    tkmsg = with_surrogates(msg)
    msglog.append(f'>>> {tkmsg}')

… and in the code that receives messages from other users:

def on_msg(msg):
    tkmsg = with_surrogates(msg)
    msglog.append(tkmsg)
like image 119
abarnert Avatar answered Sep 18 '22 00:09

abarnert