diff --git a/.gitignore b/.gitignore index 20bcf40..b9f3db2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ tutorial/* irctest.log *.pyc pesterchum.js +quirks/* +!quirks/defaults.py diff --git a/CHANGELOG.mkdn b/CHANGELOG.mkdn index b153e1e..17b69c2 100644 --- a/CHANGELOG.mkdn +++ b/CHANGELOG.mkdn @@ -38,6 +38,7 @@ CHANGELOG * Memo OP options: Secret, Invite-only, Mute - Kiooeht [evacipatedBox] * Notify user if channel blocks message - Kiooeht [evacipatedBox] * Bug reporter - Kiooeht [evacipatedBox] +* Python quirks (users can create own quirk functions) - Kiooeht [evacipatedBox] * Bug fixes * Logviewer updates - Kiooeht [evacipatedBox] * Memo scrollbar thing - Kiooeht [evacipatedBox] diff --git a/PYQUIRKS.mkdn b/PYQUIRKS.mkdn new file mode 100644 index 0000000..343539b --- /dev/null +++ b/PYQUIRKS.mkdn @@ -0,0 +1,87 @@ +Python Quirk Functions +=============== + +0. Table of Contents +----------------- +1. Introduction +2. Create A Module +3. Functions In A Module +4. Command Requirements +5. Completed Quirk Function + +1. Introduction +--------------- +Over the course of this short tutorial you will learn: + +* How to create your own Quirk Functions +* VERY basic Python syntax + +You will not learn: + +* How to write Python +* How to bake a cake + +Throughout this tutorial there will be +
+Instructions in special boxes. +If you follow the instructions in these boxes, by the end of this tutorial +you will have recreated the default reverse() Quirk Function. ++ +2. Create A Module +------------------- +All Quirk Function Modules should be created in the 'quirks/' directory. File names must end in '.py'. +You can have multiple Quirk Functions per Module. + +Each Module can also have a 'setup' function which will be called once, the moment the Module is loaded. + +
+Create 'reverse.py' in the 'quirks/' directory. ++ +3. Functions In A Module +-------------------------- +If you've ever done programming before, you should know what a function is. If not, I suggest picking up a good programming book (or e-book). + +In Python, function syntax looks like this: + +def function_name(myvar1, myvar2): + +'def' is used to declare that this is a function, and 'function_name' is obviously the name of your function. +'myvar1' and 'myvar2' are variables coming into your function. For most of your functions, the only variable being passed will be 'text'. + +In Python, curly braces ({}) are not used to declare the beginning and end of a function. Instead, a colon (:) is used to declare the beginning of a function. After that, indentation is used to declare the body and end of a function. + +
+def reverserep(text): + return text[::-1] ++ +4. Command Requirements +------------------------ +For a function to be registered as a Quirk Function, it must conform to three simple rules: + +1. It must have a command name. +2. It must take exactly one arguement. +3. It must return a string. + +What is meant by having a command name, is that a name for the Quirk Function has to be defined. This is done by defining the 'command' variable for a function. + +function_name.command = "name" + +
+reverserep.command = "reverse" ++ +5. Completed Quirk Function +--------------------------- +Below is the completed, fully working, reverse Quirk Function. After it I will break down the pieces of each line. +
+def reverserep(text): + return text[::-1] +reverserep.command = "reverse" ++ +As stated before, to start a function, you need to use the keyword 'def'. All Quirk Functions must take exactly one argument (in this case 'text'). +In this example, the text is reversed and returned all in one line. 'text[::-1]' is the Pythonic way of reversing a list or string. +The last line is the most important part. This tells Pesterchum to call this function whenever 'reverse()' is used in a quirk. diff --git a/TODO.mkdn b/TODO.mkdn index e7203de..60d0fd9 100644 --- a/TODO.mkdn +++ b/TODO.mkdn @@ -11,7 +11,6 @@ Features * Spy mode * Turn @ and # links on/off? * "someone has friended you" notifier -* Python quirks (so normal users can write their own quirk functions in python) Bugs ---- diff --git a/menus.py b/menus.py index 48223e3..f077ffb 100644 --- a/menus.py +++ b/menus.py @@ -326,6 +326,29 @@ class RandomQuirkDialog(MultiTextDialog): self.replacelist.takeItem(self.replacelist.currentRow()) self.replaceinput.setFocus() +class QuirkFuncWindow(QtGui.QDialog): + def __init__(self, parent): + QtGui.QDialog.__init__(self, parent) + self.mainwindow = parent + self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) + self.setWindowTitle("Quirk Functions") + + self.funclist = QtGui.QListWidget(self) + self.funclist.setStyleSheet("background-color: #FFFFFF;") + + from parsetools import quirkloader + funcs = [q+")" for q in quirkloader.quirks.keys()] + funcs.sort() + self.funclist.addItems(funcs) + + layout_0 = QtGui.QVBoxLayout() + layout_0.addWidget(QtGui.QLabel("Avaliable Quirk Functions")) + layout_0.addWidget(self.funclist) + self.setLayout(layout_0) + + def closeEvent(self, event): + self.mainwindow.quirkmenu.funclistwindow = None + class PesterChooseQuirks(QtGui.QDialog): def __init__(self, config, theme, parent): QtGui.QDialog.__init__(self, parent) @@ -338,6 +361,12 @@ class PesterChooseQuirks(QtGui.QDialog): self.quirkList = PesterQuirkList(self.mainwindow, self) + self.viewQuirkFuncButton = QtGui.QPushButton("VIEW FUNCTIONS", self) + self.connect(self.viewQuirkFuncButton, QtCore.SIGNAL('clicked()'), + self, QtCore.SLOT('viewQuirkFuncSlot()')) + self.reloadQuirkFuncButton = QtGui.QPushButton("RELOAD FUNCTIONS", self) + self.connect(self.reloadQuirkFuncButton, QtCore.SIGNAL('clicked()'), + self, QtCore.SLOT('reloadQuirkFuncSlot()')) self.addPrefixButton = QtGui.QPushButton("ADD PREFIX", self) self.connect(self.addPrefixButton, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('addPrefixDialog()')) @@ -379,6 +408,9 @@ class PesterChooseQuirks(QtGui.QDialog): layout_quirklist.addWidget(self.quirkList) layout_quirklist.addLayout(layout_shiftbuttons) + layout_4 = QtGui.QHBoxLayout() + layout_4.addWidget(self.viewQuirkFuncButton) + layout_4.addWidget(self.reloadQuirkFuncButton) layout_1 = QtGui.QHBoxLayout() layout_1.addWidget(self.addPrefixButton) layout_1.addWidget(self.addSuffixButton) @@ -411,6 +443,7 @@ class PesterChooseQuirks(QtGui.QDialog): layout_0 = QtGui.QVBoxLayout() layout_0.addLayout(layout_quirklist) + layout_0.addLayout(layout_4) layout_0.addLayout(layout_1) layout_0.addLayout(layout_2) layout_0.addLayout(layout_3) @@ -425,6 +458,20 @@ class PesterChooseQuirks(QtGui.QDialog): return u #return [self.quirkList.item(i).quirk for i in range(self.quirkList.count())] + @QtCore.pyqtSlot() + def viewQuirkFuncSlot(self): + if not hasattr(self, 'funclistwindow'): + self.funclistwindow = None + if self.funclistwindow: + return + self.funclistwindow = QuirkFuncWindow(self.mainwindow) + self.funclistwindow.show() + + @QtCore.pyqtSlot() + def reloadQuirkFuncSlot(self): + from parsetools import reloadQuirkFunctions + reloadQuirkFunctions() + @QtCore.pyqtSlot() def editSelected(self): q = self.quirkList.currentQuirk() diff --git a/parsetools.py b/parsetools.py index 711508b..58578e1 100644 --- a/parsetools.py +++ b/parsetools.py @@ -5,6 +5,7 @@ from datetime import timedelta from PyQt4 import QtGui from generic import mysteryTime +from pyquirks import PythonQuirks _ctag_begin = re.compile(r'(?i)