Giter Club home page Giter Club logo

pingmote's Introduction

pingmote

A cross-platform Python global emote picker to quickly insert custom images/gifs

Motivation: Poor Man's Discord Nitro

Demo

pingmote demo

How It Works

  • Since Discord autoembeds images, we can paste in links to custom emotes
  • The emote picker is written in Python with PySimpleGUI, with global hotkeys for activation (using keyboard)

Getting Started

  • Clone this repo: git clone https://github.com/dchen327/pingmote.git or download as a zip and extract
  • Change into the pingmote directory (make sure you can see pingmote.py)
  • Run pip install -r requirements.txt to install dependencies (pip3 if needed)

Usage

  • Running python3 pingmote.py (Mac and Linux: sudo python3 pingmote.py) starts the script, and when you hit the hotkey at the top of config.py (default ctrl+q), the emote picker will show up, allowing you to click an emote to insert
  • Hit the hotkey again to toggle the GUI, and drag the GUI somewhere convenient

Configs

  • Check config.py for configs

Adding Your Own Emotes

  • Sorry for this being a bit complicated, I'm working on simplifying the workflow
  • Drop files in assets/original, then run image_resizer.py which will resize all the images (ignoring gifs) and drop them in assets/resized
  • Gif resizing (disabled by default) requires gifsicle, but a website like ezgif also works
  • Resize gifs to 64x64 and drop them in assets/original (they'll be ignored when the resizer is run)
  • Upload files from assets/resized to an image hoster (I like postimages). Copy the direct image links (ending in file extension) and paste in links.txt
  • A simpler alternative using GitHub: fork this repo and push your images, then drop your forked repo's URL in config.py (GITHUB_URL). This method doesn't require alternate hosting or links.txt
  • Note: Imgur doesn't work currently, since Imgur links don't contain the original filename
  • Some emote sources (right click > save image): discordmojis.com, emoji.gg, discord.st

Notes

  • Since this program relies on autoembedding, we can't use inline emotes or reacts
  • Pretty much only Discord works (Facebook Messenger and Slack make embeds ugly)
  • On Windows, renaming the file extension to pingmote.pyw allows for running the script in the background, and then it can be dropped into shell:startup
  • Windows should work out of the box, Mac and Linux may require jumping through some hoops
  • The Apple M1 chip is currently unsupported (bus error)
  • On Linux, if you get the error KeyError: 'XDG_SESSION_TYPE', set the environment variable by running

    sudo XDG_SESSION_TYPE=x11 python3 pingmote.py

TODOs

  • Better ordering of emotes (categorization, etc.)
  • Simplify install process
  • Simplify the process for adding new emotes
  • Emote deletion in GUI
  • Ensure gif thumbnail isn't blank (not fully sure how to do this)
  • Search emotes by keyword (would require files to be named, since most of my files now are just a bunch of numbers)

Reasons you should still buy Discord Nitro

  • Support Discord!
  • Inline emotes/gifs, keyboard shortcuts by name (ex: :emote_name:)
  • React with emotes
  • Other nitro benefits!

Acknowledgements

  • Thanks to Luke Tong for cross-platform GUI and clipboard testing
  • Thanks to Stephane Morel for Windows testing
  • Thanks to Brazil-0034 for adding support for non-destructive pasting
  • Thanks to PySimpleGUI for major feature additions (incl. GitHub repo storage and a system tray) and debugging

Progress Timeline

  • Initial method (50 lines): xclip for copying local images, xdotool for pasting and keyboard commands
  • Switched to PyAutoGUI for keyboard simulation, xdotool no longer needed
  • Wrote image_resizer.py for locally resizing images
  • Uploaded images to postimages; simplified copy pasting of links only and not image data (removed all subprocess calls)
  • Added frequents section for favorite emotes
  • Added feature to open the GUI near the mouse cursor
  • Cleaned up links for better file to link mapping
  • Switched to pynput for cross-platform global hotkey mapping, fully removed PyAutoGUI dependencies
  • Added section labels and ability to separate images and gifs
  • Switched to keyboard from pynput to fix hotkey blocking behavior (after 3 weeks of zero progress)
  • Cleaned up image_resizer.py
  • Shifted configs to separate file
  • Added tray icon (from PySimpleGUI's psgtray)

License

MIT License

pingmote's People

Contributors

dchen327 avatar nowaythisworks avatar thelastprime avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

pingmote's Issues

Why is it called pingmote?

This is such a stupid question for me to ask. But, I see it on my launcher and it bugs me that I have a hard time coorelating the name with the activity.

pingmote is located on the far right side of my launcher.

image

I haven't come up with my own name yet either. Why does this drive me crazy?? one my ask......
frust_112

It's because like to name things. I have names for important stuff at home and same with my programs.

Sorry for my ignorance David....... some days..... I'm a moron.... this month seems to be one of those....
palm_112

Suggestion - User settings using PySimpleGUI or json

I used to have a config.py in my first Python projects. They seemed to always end being a pain and screw me up.

Not long ago the User Settings APIs were added to PySimpleGUI. I use them extensively in the demos and personal programs.

The Template for rainmeter style widgets is one place to see them, the other is the demo browser. The demo browser uses a "settings" window that you can copy easily.

image

The Desktop Widgets doesn't use a settings window and instead uses the right click menu.
image

You've got enough settings in your config file to warrant a settings window if you change them very often. If not, you can put them in a PySimpleGUI User Settings file (json). All I do is make the interface look easier and does autosaving, autoloading, etc.

Wow! Amazing work! Reporting a tkinter crash, a 3.6 issue, and a menu suggestion.....

How about starting with the recognition that you've done a really amazing thing here. Congrats. I'm SURE I'll learn a few things from you.

:::'###::::'##::::'##::::'###::::'########:'####:'##::: ##::'######:::
::'## ##::: ###::'###:::'## ##:::..... ##::. ##:: ###:: ##:'##... ##::
:'##:. ##:: ####'####::'##:. ##:::::: ##:::: ##:: ####: ##: ##:::..:::
'##:::. ##: ## ### ##:'##:::. ##:::: ##::::: ##:: ## ## ##: ##::'####:
 #########: ##. #: ##: #########::: ##:::::: ##:: ##. ####: ##::: ##::
 ##.... ##: ##:.:: ##: ##.... ##:: ##::::::: ##:: ##:. ###: ##::: ##::
 ##:::: ##: ##:::: ##: ##:::: ##: ########:'####: ##::. ##:. ######:::
..:::::..::..:::::..::..:::::..::........::....::..::::..:::......::::
'##:::::'##::'#######::'########::'##:::'##:'####:
 ##:'##: ##:'##.... ##: ##.... ##: ##::'##:: ####:
 ##: ##: ##: ##:::: ##: ##:::: ##: ##:'##::: ####:
 ##: ##: ##: ##:::: ##: ########:: #####::::: ##::
 ##: ##: ##: ##:::: ##: ##.. ##::: ##. ##::::..:::
 ##: ##: ##: ##:::: ##: ##::. ##:: ##:. ##::'####:
. ###. ###::. #######:: ##:::. ##: ##::. ##: ####:
:...::...::::.......:::..:::::..::..::::..::....::

Threading

I am unsure how you're handling threads, but it appears one of them is attempting to perform calls into PySimpleGUI.

This MAY sometimes work.... for a while.... until you get something like this that shows up:

Traceback (most recent call last):
  File "X:\Python\Python37\lib\site-packages\keyboard\_generic.py", line 22, in invoke_handlers
    if handler(event):
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 249, in custom_hotkey
    func()
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 265, in on_activate
    self.show_gui()
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 258, in show_gui
    self.window.un_hide()
  File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 9181, in un_hide
    self.TKroot.deiconify()
  File "X:\Python\Python37\lib\tkinter\__init__.py", line 1818, in wm_deiconify
    return self.tk.call('wm', 'deiconify', self._w)
RuntimeError: main thread is not in main loop
Traceback (most recent call last):
  File "X:\Python\Python37\lib\site-packages\keyboard\_generic.py", line 22, in invoke_handlers
    if handler(event):
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 249, in custom_hotkey
    func()
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 265, in on_activate
    self.show_gui()
  File "C:/Python/PycharmProjects/GitHub/pingmote/pingmote.py", line 258, in show_gui
    self.window.un_hide()
  File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 9181, in un_hide
    self.TKroot.deiconify()
  File "X:\Python\Python37\lib\tkinter\__init__.py", line 1818, in wm_deiconify
    return self.tk.call('wm', 'deiconify', self._w)
RuntimeError: main thread is not in main loop

The mechanism that PySimpleGUI provides is a call that enables you to communicate from a thread to any window. The call is:

window.write_event_value('-TheEvent-', some_values)

It causes your read call to return with the event and a value added to your values dictionary. If you need to pass back multiple values, no problem, the value can be a tuple, dictionary, etc.

The latest PySimpleGUI on GitHub has an important fix to this method. It will be released to PyPI this weekend.

Python 3.6 problem

This was only discovered very recently... this past week... by the PySimpleGUI team. I initially ran into it working with the new PIL book, and then Jason tracked it down. Some PNG files have something in them that causes tkinter to not be happy. Upgrading to 3.7 fixes it.


Right Click Menu

I love the hotkey idea! Nice work. Exiting the program was problematic until I dug further into the instructions (i.e. I read instead of continuing to randomly trying stuff).

I ended up adding a right-click menu to your window. This enabled a few things

  • Easily hide the window, just like the hide button
  • Exit the program
  • Edit the program - a feature I'm adding to nearly everything I write now because it's super efficient

Here is what your program looks like with this right click menu

image


Storing images

I put my images on GitHub and was able to add them to your links.txt file. It wasn't clear to me if that would work or not.
It worked out great!

https://raw.githubusercontent.com/PySimpleGUI/PySimpleGUI/master/images/emojis/happy_112.png


Thank you again for the inspiration! Keep building stuff!!

focus_force

I saw one place that directly called tkinter:

    def show_gui(self):
        self.window.un_hide()
        self.window.TKroot.focus_force()  # force window to be focused
        self.hidden = False

There's a function in PySimpleGUI to do this for you so you don't have to invoke any tk calls directly.

self.window.force_focus()

System Tray Icon or other "running" indicator

I'm still working on a TRUE System Tray Icon for the tkinter port. For now, it looks like an icon on the desktop.

I will be using your program for SURE (I'm already using it). I would like some indicator that it's running. I guess I could add something to my launcher that I'll use to start it up to indicate that it's running, but the system tray would be the best place. Then it can be used to manage the program too.,

Suggestion - SystemTray Icon

I changed your event loop to be like this:

    def create_window_gui(self):
        """ Run the event loop for the GUI, listening for clicks """
        # Event loop
        while True:
            event, _ = self.window.read(timeout=100)
            tevent = self.system_tray.read(timeout=10)
            # Process events common in Window and Tray
            if 'Exit' in (event, tevent) or event == sg.WINDOW_CLOSED:
                break
            elif 'Hide' in (event, tevent):
                self.hide_gui()
            elif 'Edit Me' in (event, tevent):
                sg.execute_editor(__file__)

            # Process events found only in Window or Tray
            if tevent == 'Show':
                self.on_activate()
            if event in self.filename_to_link:
                print(f'selection event = {event}')
                self.on_select(event)
            # A tray double-click toggles visibility
            if tevent == sg.EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED:
                self.on_activate() if self.hidden else self.hide_gui()

        self.system_tray.close()
        self.window.close()

In the layout method where you create the window, I created a system tray

        self.system_tray = sg.SystemTray(menu=['_', ['Show', 'Hide', 'Edit Me', 'Settings', 'Exit']], data_base64=sg.DEFAULT_BASE64_ICON)

At the moment, the system tray for tkinter displays like an icon on the desktop instead of the system tray. I'm working on using another library with PySimpleGUI so they will be in the actual system tray like the feature is in PySimpleGUIQt and PySimpleGUIWx.

This screencapture shows the kinds of things is does with the icon. You can double click it to show/hide your window. The operation you see at the end of the video is where I'm double-clicking the system tray icon. The ControlQ key is quite problematic since it's a heavily used key in PyCharm, so I'm adding the ability to set it using the settings (not quite done but it's all I had time for this morning). It eats up a fair amount of CPU time because of the polling, so I'll look into doing something about that down the road maybe.

PingMote with  System Tray

Thank you for providing a fun distraction and something new for me to learn from. It's nice to run across programs like this that I can use every day, all day, that are PySimpleGUI based, and are fun to tinker with for a few minutes here and there.


Right click enhancement added

I made a change, BTW, based on your program. Buttons don't normally have the opportunity to show a right click menu. But for programs like yours that are almost all buttons, it leaves very few places to right click. So, I added right click menu to buttons. :-)

Now I can see the menu when right clicking a button. This change will be released to PyPI today

image

It's programs like this that push the envelope of PySimpleGUI's capabilities. Or in the very least push it in ways that haven't been done before. This is how PySimpleGUI has grown over the years. I utilize real-world problems to grow features. Your program had a real-world problem of it mostly having buttons and also a right click menu would benefit it. That's a trigger for me that here's an area where PySimpleGUI can grow a little. There is an immediate need and it will benefit a lot of people in the future perhaps.

I'm working on the Button lesson on the Udemy course, so I'm really looking hard at Buttons. I fixed the Realtime Button for example because it kinda needs to work in order to teach it. And I wanted to get this change in before I finish recording the Button lessons.

Two "bad files" when running on 3.6

There are 2 images in the assets that cause problems for tkinter when running Python 3.6.

This is the error message in the PySimpleGUI on GitHub.... the error checking isn't in the released code. It simply crashes in an ungraceful manner.

image

(BTW, unable to see the buttons in this error window because your code set all buttons, globally, to have the background and text be the same of the background

I've recently learned about 3.6 having this problem with some images. I've found it to be extremely arere as I've used 3.6 since the start of the project.

When I removed these 2 images, the code had no problems running on 3.6.

crydeath
757356443878948944


Button colors.... you really only need to set the background color in order for your buttons with images to look OK. If you leave the text alone, then you'll at least be able to see the buttons.

I removed your set_options call and instead used the themes calls to set up the conditions you're wanting. At least I think I did.

        button_color = sg.theme_button_color()
        sg.theme_background_color(GUI_BG_COLOR)
        sg.theme_text_element_background_color(GUI_BG_COLOR)
        sg.theme_text_color('white')
        sg.theme_button_color((button_color[0], GUI_BG_COLOR))
        sg.theme_border_width(0)

This way you'll see the "Close" and "Take me to error" buttons, even though they don't have a shape around them, you know that you can click them.

image

Using the new psgtray package

Hi there.... this is the last message I posed in another issue here. I thought it would be best if I opened an issue to discuss this specific topic:


OK! As promised, back with more details.

I've changed my version of pingmore to use the newly released psgtray feature.

This system tray icon is for PingMote. The keyboard shortcuts still work of course. It's another way to bring up the interface. A single click of the icon will both bring it up or hide it again.

image

The code is in my repo:

https://github.com/PySimpleGUI/pingmote

Information about the new package is in the latest announcement and there will be a new issue opened a bit later today to tell everyone more about this feature. It's not yet in the documentation.


Since posting that message, I have taken the psgtray code and made a package from it that can be installed via pip:

pip install psgtray

The code in my repo (link above) has the icon I made for pingmote as well as code that integrates your code with the new psgtray feature.

Clearly this is a program I use on a frequent basis. Thank you for writing it.

Thanks for the inspiration and code!

Hiya David. I wanted to take a moment to say "thank you" for being an inspiration and also for providing code that I used to make a new PySimpleHotKey program.

It's far from perfect, but it's turning out to be making a big impact already on my efficiency and abilities.

It's one thing to have a hotkey program that enables you to launch programs, etc, but it's entirely different when you've got complete control of that program and the actions. Being able to call PySimpleGUI code directly from the HotKey manager is AWESOME. Same with being able to control how it operates in the system tray.

There's a public version of it posted, and there's a private version I use. This is the private table settings that I'm using:

# Defintions of the hotkeys
DOCSTRING_SHORTCUT = 'ctrl+alt+shift+d'
DOCSTRING_TYPES_SHORTCUT = 'ctrl+alt+shift+f5'
KOLYA_SHORTCUT = 'alt+shift+k'
MILKA_SHORTCUT = 'ctrl+alt+shift+m'
POPUP_SHORTCUT = 'ctrl+alt+shift+f6'

# Special keys for PySimpleGUI to take action
POPUP_EMOJIS = '-POPUP EMOJIS-'

# dictionary that maps a hotkey to an action
hotkey_dict = {
    DOCSTRING_SHORTCUT :        (lambda :  sg.execute_py_file(r'DocstringTools\AlignDocstrings.py', '--clipboard', cwd='.')),
    DOCSTRING_TYPES_SHORTCUT :  (lambda :  sg.execute_py_file(r'DocstringTools\AddTypesToDocstring.py', cwd='.')),
    KOLYA_SHORTCUT:             (lambda: keyboard.write('Коля')),
    MILKA_SHORTCUT:             (lambda: keyboard.write('Милка')),
    POPUP_SHORTCUT:             (lambda: show_popup_at_cursor(POPUP_EMOJIS))
                }

You did a fantastic job of showing me a nice design pattern as well as providing a way to specify and parse the keystrokes.

I'm using this program to:

  • Insert text
  • Launch some utilities that work on the clipboard
  • Show a simple emoji chooser (sort of like pingmote, but much more direct)

The point I want to make is that anyone, regardless of skill level, can be a contributor to the greater good of the programming world. You don't have to be someone with decades of experience to have a substantial and positive impact. You did this with your pingmote program. Thank you for creating it and making it available to everyone.

I'll close with an example of your hotkey inspired emoji chooser that was used to add the little guy at the bottom:

46rLq4V29z

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.