Skip to content

Instantly share code, notes, and snippets.

@cryzed
Created January 28, 2017 23:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cryzed/2ac5802e2bc9c6d541787cd81fd63623 to your computer and use it in GitHub Desktop.
Save cryzed/2ac5802e2bc9c6d541787cd81fd63623 to your computer and use it in GitHub Desktop.
import argparse
import hashlib
import os
import shutil
import sys
import xml.etree.ElementTree as ET
from argparse import Namespace
from PySide import QtCore
from PySide.QtCore import Qt
from PySide.QtGui import (QApplication, QIcon, QMainWindow, QWidget, QDialog, QListWidget, QListWidgetItem, QMenuBar,
QPushButton, QVBoxLayout)
argument_parser = argparse.ArgumentParser()
argument_parser.add_argument('pack_name')
class UnsupportedPlatformError(Exception):
pass
def _posix_get_emoji_path():
data_home = os.getenv('XDG_DATA_HOME', os.path.join(os.getenv('HOME'), '.local', 'share'))
return os.path.join(data_home, 'emoticons')
def _nt_get_emoji_path():
return os.path.join(os.getenv('%LOCALAPPDATA%'), 'Emoticons')
def get_emoticon_path():
if os.name == 'posix':
return _posix_get_emoji_path()
elif os.name == 'nt':
return _nt_get_emoji_path()
raise UnsupportedPlatformError(f'Unsupported platform: {sys.platform}')
def get_md5_hexdigest(path):
with open(path, 'rb') as file:
return hashlib.md5(file.read()).hexdigest()
class Emoticon:
def __init__(self, filename, strings):
self.filename = filename
self.strings = strings
@classmethod
def from_etree_element(cls, element):
instance = cls(element.get('file'), [element.text for element in element.iterfind('string')])
return instance
def to_etree_element(self):
emoticon = ET.Element('emoticon', {'file': self.filename})
for text in self.strings:
string = ET.SubElement(emoticon, 'string')
string.text = text
return emoticon
# Sort by first substitution string
def __lt__(self, other):
return (self.strings[0] if self.strings else '') < (other.strings[0] if other.strings else '')
def __repr__(self):
return f'<Emoticon {", ".join(self.strings): {self.filename}}>'
def __str__(self):
return repr(self)
class EmoticonFolder:
_XML_TEMPLATE = "<?xml version='1.0'?><messaging-emoticon-map></messaging-emoticon-map>"
def __init__(self, path):
self.path = path
self._xml_path = os.path.join(path, 'emoticons.xml')
tree = ET.parse(self._xml_path)
self.emoticons = [Emoticon.from_etree_element(element) for element in tree.iter('emoticon')]
def add_emoticon_from_path(self, path, strings, filename=None):
filename = filename or os.path.basename(path)
shutil.copy(path, os.path.join(self.path, filename))
emoticon = Emoticon(filename, strings)
self.emoticons.append(emoticon)
return emoticon
def write(self, path=None):
tree = ET.fromstring(self._XML_TEMPLATE)
tree.extend([emoticon.to_etree_element() for emoticon in self.emoticons])
with open(path or self._xml_path, 'wb') as file:
file.write(ET.tostring(tree))
def __iter__(self):
return iter(self.emoticons)
def __len__(self):
return len(self.emoticons)
def __repr__(self):
return f'<EmoticonFolder {self.path}: {len(self)} emoticons>'
def __str__(self):
return repr(self)
class EditEmoticonDialog(QDialog):
def __init__(self, item, *args, **kwargs):
super().__init__(*args, **kwargs)
self._item = item
self._emoticon = item.data(Qt.UserRole)
self.setWindowTitle(f'Emoticon - {", ".join(self._emoticon.strings)}')
self._ui = self._init_ui(self)
for string in self._emoticon.strings:
item = QListWidgetItem(string)
self._ui.strings.addItem(item)
def _init_ui(self, widget):
layout = QVBoxLayout(widget)
layout.setAlignment(Qt.AlignCenter)
icon = self._item.icon()
emoticon_button = QPushButton(icon, '')
emoticon_button.setIconSize(sorted(icon.availableSizes())[-1])
emoticon_button.setToolTip('Open file...')
emoticon_button.setFlat(True)
strings_list_widget = QListWidget(widget)
strings_list_widget.setAlternatingRowColors(True)
strings_list_widget.setEditTriggers(strings_list_widget.AllEditTriggers)
close_button = QPushButton(widget, text='Close')
layout.addWidget(emoticon_button)
layout.addWidget(strings_list_widget)
layout.addWidget(close_button)
return Namespace(emoticon=emoticon_button, strings=strings_list_widget, close=close_button)
class EmoticonListWidget(QListWidget):
right_clicked = QtCore.Signal(QListWidgetItem)
def contextMenuEvent(self, event):
item = self.itemAt(event.pos())
self.right_clicked.emit(item)
class MainWindow(QMainWindow):
TITLE = 'qTox Emoticons'
SIZE = 300, 300
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle(self.TITLE)
self.setGeometry(0, 0, *self.SIZE)
self.setCentralWidget(QWidget(self))
self.setMenuBar(QMenuBar(self))
self._emoticons = None
self._ui = self._init_ui(self.centralWidget())
self._ui.emoticons.itemDoubleClicked.connect(self._on_emoticon_double_clicked)
self._ui.emoticons.right_clicked.connect(self._on_emoticon_right_clicked)
self._ui.save.pressed.connect(self._on_save_pressed)
def _init_ui(self, widget):
layout = QVBoxLayout(widget)
list_widget = EmoticonListWidget(widget)
list_widget.setViewMode(list_widget.IconMode)
list_widget.setResizeMode(list_widget.Adjust)
list_widget.setDragEnabled(False)
save_button = QPushButton(widget, text='Save')
layout.addWidget(list_widget)
layout.addWidget(save_button)
return Namespace(emoticons=list_widget, save=save_button)
def open(self, path):
self._emoticons = EmoticonFolder(path)
self.setWindowTitle(f'{self.TITLE} - {os.path.basename(path)}')
self._ui.emoticons.clear()
for emoticon in sorted(self._emoticons):
file_path = os.path.join(path, emoticon.filename)
item = QListWidgetItem(QIcon(file_path), '')
item.setToolTip(', '.join(emoticon.strings))
item.setData(Qt.UserRole, emoticon)
self._ui.emoticons.addItem(item)
@QtCore.Slot()
def _on_save_pressed(self):
pass
@QtCore.Slot()
def _on_emoticon_double_clicked(self, item):
EditEmoticonDialog(item).exec_()
@QtCore.Slot()
def _on_emoticon_right_clicked(self, item):
pass
def main():
application = QApplication(sys.argv)
window = MainWindow()
path = os.path.join(get_emoticon_path(), arguments.pack_name)
window.open(path)
window.show()
application.exec_()
if __name__ == '__main__':
arguments = argument_parser.parse_args()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment