SRL Archive - Development

SPyRL Python Resource Library


Nava2

2010-09-21 17:49:19

Well, as the title suggests:

SPyRL Python Resource Library

Firstly: Please note, this is a work in progress and I am working on it in my spare time, which is limited. I want your help and input!

Currently, the ideals I am aiming for are as follows:
  • Flexible
  • Open
  • Fast
  • Mutable (read: customizable)
  • Heavily Documented
    • This will be done using ReStructured Text and Sphinx. The same as the Simba Docs and the official python documentation.
  • OOP-enabled, but not forced.
  • Threading factories (no guarentees for this one..)
  • Platform Independant

I am posting this because I have made some progress from Wizzup's implementation of pyMML. The point of SPyRL is to be a higher level implementation than pyMML. It will be fully OOP, but with the intent for script writers to have a choice on whether to write their scripts in OOP or in linear style. I don't personally thing RuneScape scripts need OOP nature ;) but the library looks nice as OOP.

If you would like to contribute but do not know Python; I sincerely suggest checking out the Official Python Tutorial.

Here are some example files I have written thus far:

colour.py
"""
Colour Class
------------

Set of classes and methods for finding colour objects. These are essentially
OOP wrappers for the linear style of pyMML.
"""

class Colour(object):
"""
Colour object is created to hold information and methods pertaining to
colour finding. Information stored is completely mutable.
"""

#The colour stored in the current instance.
value = None

#The :class:`CTS.CTS' to attempt to use before using the defaulted global
_cts = None

#The colour tolerance to search with.
_tol = 0

def __init__(self, col, **kwargs):
"""
Creates the colour object. Col is to be an integer or another
Colour object which can then be copied.

:param col: the colour to search for
:type col: int, None, or :class:`colour.Colour`

Valid Keyword Arguements:
:keyword cts: Specify a specific CTS object to be used, will use
global if left un specified.
:keyword tol: Specify a tolerance to search with. Can be set later.
"""

if col.__class__ == int:
self._value = col
elif col.__class__ == Colour:
self.__copy_Colour(col)
elif col != None:
raise TypeError('Invalid Type passed as \'col\' in Colour.__init__')

#check for keyword args passed.
if "cts" in kwargs:
if kwargs["cts"].__class__ == CTS.CTS:
self._cts = kwargs["cts"]
if "tol" in kwargs:
if kwargs["tol"].__class__ == int:
self._tol = kwargs["tol"]

def __copy_colour(self, colour):
"""
Copies *colour* into the current :class:`Colour.colour` into the
current object. The copy includes **all** methods and attributes.

:param :class:`Colour.colour` colour: The colour object to copy.
"""

if not colour.__class__ == Colour:
raise TypeError("Will not copy attributes from other class than \
colour.Colour.")

#clear all values initially
self.__dict__.clear()

#copy values from *colour* to *self*
for key,value in colour.__dict__.items():
self.__dict__[key] = value


def find(self, box, **kwargs):
#TODO add custom CTS support.
"""
Searches *box* for the first occurance of a colour supplied by the
stored information or parameters.

:param tuple box: Area to search in.
:keyword int tol: The immediate tolerance of the colour, if
unspecified will be the stored value.
:keyword bool spiral: Defaults to *false*, if true, will search in
a spiral pattern from another keyword arg *start_point*
:keyword tuple start_point: If keyword *spiral* is *true* then will
default to center of *box* if un-specified. Otherwise, will search
starting from location specified.

:returns tuple: First found occurance of the colour.
"""

tol = kwargs.get("tol", self._tol)
self._tol = tol

#TODO Add spiral support.
#use_spiral = kwargs.get('spiral', None)
#if use_spiral

#use the MMLColour Library to find the colours.
mmlcolor.Color.find(box, self.value, tol)

#TODO add CTS/spiral support
def find_all(self, box, **kwargs):
"""
Overridden method from :class:`interfaces.Finable`.

:param tuple box: Coordinate area to search in.
:keyword int tol: The immediate tolerance of the colour to search
for. If unspecified, will be a stored value.
"""

tol = kwargs.get("tol", self._tol)
self._tol = tol

#use MMLColour to find colours
mmlcolor.Color.findall(box, self.value, self._tol)

def to_RGB(self):
"""
Converts the stored colour into RGB values.

:return tuple: Returns a tuple containing (R,G,B)
"""
r = self.value & 0xff
g = self.value >> 8 & 0xff
b = self.value >> 16 & 0xff

return r,g,b

def to_hsl(self, fixed=false):
"""
Converts the stored colour into its HSL values.

:return tuple: Returns a tuple in the form of H,S,L.
"""

r,g,b = self.to_RGB()

r /= 255.0
g /= 255.0
b /= 255.0

cmin = min(r,g,b)
cmax = max(r,g,b)

L = 0.5 * (cmax+cmin)
if cmax == cmin:
H,S = 0.0,0.0
else:
D = cmax-cmin
if L < 0.5:
S = D/(cmax+cmin)
else:
S = D / (2 - cmax - cmin)

if r == cmax:
H = (g-b)/D
elif g == cmax:
H = 2 + (b-r)/D
else:
H = 4 + (r-g)/D

H /= 6
if H < 0:
H += 1

if fixed:
return H*100, S*100, L*100
else:
return H,S,L

def to_XYZ(self):
"""
Converts the stored colour to XYZ.

:returns tuple: Tuple in the form of X,Y,Z
"""

r,g,b = self.to_RGB()

r /= 255.0
g /= 255.0
b /= 255.0

if r > 0.04045:
r = ((r + 0.055) / 1.05 ) ** 2.4 * 100
else:
r /= 7.73994

if g > 0.04045:
g = ((g + 0.055) / 1.05 ) ** 2.4 * 100
else:
g /= 7.73994

if b > 0.04045:
b = ((b + 0.055) / 1.05 ) ** 2.4 * 100
else:
b /= 7.73994

X = r*0.4124 + g*0.3576 + b*0.1805
Y = r*0.2126 + g*0.7152 + b*0.0722
Z = r*0.0193 + g*0.1192 + b*0.9505

return X,Y,Z

def __add__(self, other):
"""
Adds two colours together, will return an :class:`int` if *int* is
passed, otherwise will return :class:`colour.Colour`. Will raise an
error if the type is not supported.

:type other: int or :class:`colour.Colour`
:returns: int or :class:`colour.Colour` dependant on
*other.__class__*
"""

if other.hasattr('__int__'):
return self.value + other
elif other.hasattr('value'):
return Colour(self.value + other.value)
else:
raise NotImplemented('Type {0} is not implemented.'.format( \
other.__class__))

def __sub__(self, other):
"""
Subtracts two colours together, will return an :class:`int` if
*int* is passed, otherwise will return :class:`colour.Colour`. Will
raise an error if the type is not supported.

:type other: int or :class:`colour.Colour`
:returns: int or :class:`colour.Colour` dependant on
*other.__class__*
"""

if other.hasattr('__int__'):
return self.value - other
elif other.hasattr('value'):
return Colour(self.value - other.value)
else:
raise NotImplemented('Type {0} is not implemented.'.format( \
other.__class__))

CTS.py:

class UnsupportedSpeed(Exception):
pass


#Default modifiers for each Colour Tolerance Speed.
_def_modifiers = [(),(),(0.2,0.2)]

class CTS(object):
"""CTS objects are used to store information for colour searches when using
the different colour tolerance speeds. One global CTS is maintained and
localized CTS objects can be created.
"""

## Current Speed Used
__speed = 0

## Current modifiers for the CTS object.
__mods = ()

def __init__(self, speed, modifiers=None, **kwargs):
"""Creates CTS objects. These maintain their information and are not to
be modified outside the objects internal methods.

:param int speed: The Colour Tolerance Speed to be stored and set.
:param tuple modifiers: The modifiers used in the varios CTS formulas.
"""
if not isinstance(speed, int):
raise TypeError('`speed\' must be of type int.')

if speed in (0,1,2):
__speed = speed
else:
raise UnsupportedSpeedError('Unsupported CTS passed as CTS.')

#check to make sure the modifiers correspond to the CTS.
if isinstance(modifiers, tuple) and __speed_mod_check(speed, modifiers):
__mods = modifiers
else:
__mods = _def_modifiers[__speed]

#TODO insert warning for kwargs not supported, yet passed.

def _speed_mod_check(self, speed, modlen):
if speed in (0,1):
return modlen == 0
elif speed == 2:
return modlen == 2

#TODO is this right..? :/
def set_modifiers(self, *mods):
"""Set the modifier list to contain the mods passed. Speeds is assumed
to be the current speed if no speed is specified."""

if _speed_mod_check(__speed, len(mods)):
__mods = mods

def get_modifiers(self):
"""Returns the modifiers used by this CTS.

:param int speed: The CTS speed to return modifiers to.
:returns tuple: A tuple containing the modifiers stored.
"""

return __mods

#TODO is this right..? :/
def set_speed(self, speed):
"""Sets a new CTS to the object.

:param int speed: The new CTS.
"""

if speed in range(0,2):
__speed = speed
__mods = _def_modifiers[speed]

#global CTS used in all colour functions if another is not specified.
#TODO Rename
current_CTS = CTS(0)

All of these files are attached as a .zip.

This is more or less a `lets get the gears going' kind of approach. I want input, help and anything else you can offer. I do not have the free time to do this, but I am willing to put what I can.

Due to the immature nature of pyMML, this is far from complete and needs some help with getting more methods exported.

Changes:
  • Interfaces.py is no more, much better approach being used.
  • Attachment is useless, don't download it. :<

Cheers,
n2

Nava2

2010-09-21 17:53:09

Also, this wasn't posted in the Simba Development forums because it is far from mature. This was work from today.

Also, I will draw up a diagram for class hierarchies I had in mind, later. I have training. :<

Wizzup?

2010-09-21 17:59:03

Slightly off topic: http://www.canonical.org/~kragen/isinstance/

Nava2

2010-09-21 20:00:47

From: Wizzup?
Slightly off topic: http://www.canonical.org/~kragen/isinstance/


So, checking for all the attributes or checking against the class is probably a better choice?

E: Nice read by the way.

Nava2

2010-09-21 23:09:06

New colour.py:

"""
Colour Class
------------

Set of classes and methods for finding colour objects. These are essentially
OOP wrappers for the linear style of pyMML.
"""

class Colour(object):
"""
Colour object is created to hold information and methods pertaining to
colour finding. Information stored is completely mutable.
"""

#The colour stored in the current instance.
value = None

#The :class:`CTS.CTS' to attempt to use before using the defaulted global
_cts = None

#The colour tolerance to search with.
_tol = 0

def __init__(self, col, **kwargs):
"""
Creates the colour object. Col is to be an integer or another
Colour object which can then be copied.

:param col: the colour to search for
:type col: int, None, or :class:`colour.Colour`

Valid Keyword Arguements:
:keyword cts: Specify a specific CTS object to be used, will use
global if left un specified.
:keyword tol: Specify a tolerance to search with. Can be set later.
"""

if col.__class__ == int:
self._value = col
elif col.__class__ == Colour:
self._copy_Colour(col)
elif col != None:
raise TypeError('Invalid Type passed as \'col\' in Colour.__init__')

#check for keyword args passed.
if "cts" in kwargs:
if kwargs["cts"].__class__ == CTS.CTS:
self._cts = kwargs["cts"]
if "tol" in kwargs:
if kwargs["tol"].__class__ == int:
self._tol = kwargs["tol"]

def __copy_colour(self, colour):
"""
Copies *colour* into the current :class:`Colour.colour` into the
current object. The copy includes **all** methods and attributes.

:param :class:`Colour.colour` colour: The colour object to copy.
"""

if not colour.__class__ == Colour:
raise TypeError("Will not copy attributes from other class than \
colour.Colour.")

#clear all values initially
self.__dict__.clear()

#copy values from *colour* to *self*
for key,value in colour.__dict__.items():
self.__dict__[key] = value


def find(self, box, **kwargs):
#TODO add custom CTS support.
"""
Searches *box* for the first occurance of a colour supplied by the
stored information or parameters.

:param tuple box: Area to search in.
:keyword int tol: The immediate tolerance of the colour, if
unspecified will be the stored value.
:keyword bool spiral: Defaults to *false*, if true, will search in
a spiral pattern from another keyword arg *start_point*
:keyword tuple start_point: If keyword *spiral* is *true* then will
default to center of *box* if un-specified. Otherwise, will search
starting from location specified.

:returns tuple: First found occurance of the colour.
"""

tol = kwargs.get("tol", self._tol)
self._tol = tol

#TODO Add spiral support.
#use_spiral = kwargs.get('spiral', None)
#if use_spiral

#use the MMLColour Library to find the colours.
mmlcolor.Color.find(box, self.value, tol)

#TODO add CTS/spiral support
def find_all(self, box, **kwargs):
"""
Overridden method from :class:`interfaces.Finable`.

:param tuple box: Coordinate area to search in.
:keyword int tol: The immediate tolerance of the colour to search
for. If unspecified, will be a stored value.
"""

tol = kwargs.get("tol", self._tol)
self._tol = tol

#use MMLColour to find colours
mmlcolor.Color.findall(box, self.value, self._tol)

def to_RGB(self):
"""
Converts the stored colour into RGB values.

:return tuple: Returns a tuple containing (R,G,B)
"""
r = self.value & 0xff
g = self.value >> 8 & 0xff
b = self.value >> 16 & 0xff

return r,g,b

def to_hsl(self, fixed=false):
"""
Converts the stored colour into its HSL values.

:return tuple: Returns a tuple in the form of H,S,L.
"""

r,g,b = self.to_RGB()

r /= 255.0
g /= 255.0
b /= 255.0

cmin = min(r,g,b)
cmax = max(r,g,b)

L = 0.5 * (cmax+cmin)
if cmax == cmin:
H,S = 0.0,0.0
else:
D = cmax-cmin
if L < 0.5:
S = D/(cmax+cmin)
else:
S = D / (2 - cmax - cmin)

if r == cmax:
H = (g-b)/D
elif g == cmax:
H = 2 + (b-r)/D
else:
H = 4 + (r-g)/D

H /= 6
if H < 0:
H += 1

if fixed:
return H*100, S*100, L*100
else:
return H,S,L

def to_XYZ(self):
"""
Converts the stored colour to XYZ.

:returns tuple: Tuple in the form of X,Y,Z
"""

r,g,b = self.to_RGB()

r /= 255.0
g /= 255.0
b /= 255.0

if r > 0.04045:
r = ((r + 0.055) / 1.05 ) ** 2.4 * 100
else:
r /= 7.73994

if g > 0.04045:
g = ((g + 0.055) / 1.05 ) ** 2.4 * 100
else:
g /= 7.73994

if b > 0.04045:
b = ((b + 0.055) / 1.05 ) ** 2.4 * 100
else:
b /= 7.73994

X = r*0.4124 + g*0.3576 + b*0.1805
Y = r*0.2126 + g*0.7152 + b*0.0722
Z = r*0.0193 + g*0.1192 + b*0.9505

return X,Y,Z

def __add__(self, other):
"""
Adds two colours together, will return an :class:`int` if *int* is
passed, otherwise will return :class:`colour.Colour`. Will raise an
error if the type is not supported.

:type other: int or :class:`colour.Colour`
:returns: int or :class:`colour.Colour` dependant on
*other.__class__*
"""

if other.hasattr('__int__'):
return self.value + other
elif other.hasattr('value'):
return Colour(self.value + other.value)
else:
raise NotImplemented('Type {0} is not implemented.'.format( \
other.__class__))

def __sub__(self, other):
"""
Subtracts two colours together, will return an :class:`int` if
*int* is passed, otherwise will return :class:`colour.Colour`. Will
raise an error if the type is not supported.

:type other: int or :class:`colour.Colour`
:returns: int or :class:`colour.Colour` dependant on
*other.__class__*
"""

if other.hasattr('__int__'):
return self.value - other
elif other.hasattr('value'):
return Colour(self.value - other.value)
else:
raise NotImplemented('Type {0} is not implemented.'.format( \
other.__class__))

E: Made the colour class emulate regular integers a bit more… More can be done with it from: http://docs.python.org/reference/datamodel.html#emulating-numeric-types

E2: -> SSRL

Nava2

2010-10-15 19:06:41

Moved to public, no one seemed to like it in SSRL..

noidea

2010-10-15 20:43:54

Woo!!!
When I learn some more, I will contribute :)
Edit: I definitely think this should be in a repo of some sort.

Boreas

2010-10-15 23:22:40

Same here. Gradually making my way through these videos. From what I can see so far, the way these functions are set up should be very helpful :)
http://code.google.com/edu/languages/google-python-class/
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/

Nava2

2010-10-15 23:45:11

Updated the first post with some information.

Boreas, I would suggest checking out the official tutorial as well. Its very good, and its what I learned from.

noidea`, I will look into it. I might branch the simba repos. pymml is still incomplete, but as that API is built, this can develop around it.

I wish I had more time to personally work on this.. :(

zippoxer

2010-10-16 04:04:53

While scripting something, python is easier & nicer than pascal.
If it's too hard to implement python there are lots of similars (ruby, boo, etc…)

Wizzup?

2010-10-16 06:19:02

I still think this (SPyRL) is rather pointless because pyMML will already be OOP and offer full abstraction from libmml.

noidea

2010-10-16 13:15:41

From: Wizzup?
I still think this (SPyRL) is rather pointless because pyMML will already be OOP and offer full abstraction from libmml.


Is there anywhere we can look at/download pyMML?

Nava2

2010-10-16 13:19:34

From: noidea
Is there anywhere we can look at/download pyMML?


Its in the Simba git located here: http://git.villavu.com/?p=simba.git;a=tree;f=Projects/libmml;hb=HEAD.

Wizzup, I disagree. I believe that pyMML should be a set of bindings to the library setup to provide an API to other libraries. Personal opinion though, this project is also more OOP-esque than that of pyMML currently.

R0b0t1

2010-10-16 13:29:55

You don't indent docstrings. It looks ugly (besides docstrings not looking spectacular in the first place w/ this forums highlighting…)

Wizzup?

2010-10-16 14:58:06

From: R0b0t1
You don't indent docstrings. It looks ugly (besides docstrings not looking spectacular in the first place w/ this forums highlighting…)


I can't think of a reason not to indent them. It doesn't matter to the generated documentation.

Nava2

2010-10-17 07:08:40

From: Wizzup?
I can't think of a reason not to indent them. It doesn't matter to the generated documentation.


That is more or less what I was thinking. I actually prefer them indented as it makes it easier to read the comments.

bevardis

2010-11-02 14:10:38

This project looks like the light in the end of the tunnel.

Cstrike

2010-11-05 22:47:50

Incoming noob question:

What is this supposed to be? An alternative coding resource for the pascal-like coding we do already? Or an OOP for autoing? If so, what uniqueness does it bring to the table?

Thanks :)

Nava2

2010-11-05 23:18:16

From: cstrike
Incoming noob question:

What is this supposed to be? An alternative coding resource for the pascal-like coding we do already? Or an OOP for autoing? If so, what uniqueness does it bring to the table?

Thanks :)


Its Python, but I do not have time to work on this, thus it is stagnating.

By using python, it allows the system to be platform independent as well as very flexible.

E: http://www.linuxjournal.com/article/3882

ForgotMyName

2010-11-05 23:31:08

Are there plans for bindings between Python and Simba? (Or really, any other language and Simba.) Haven't done anything with MML but seems like it and this are going down that path.

Nava2

2010-11-05 23:34:10

From: ForgotMyName
Are there plans for bindings between Python and Simba? (Or really, any other language and Simba.) Haven't done anything with MML but seems like it and this are going down that path.


It would be my hope that SPyRL would eventually be integrated into Simba.

i luffs yeww

2010-11-06 14:49:56

From: Nava2
Also, I will draw up a diagram for class hierarchies I had in mind, later. I have training. :<


I've been spending my free time working on learning more Python. I haven't done anything crazy in Python, but I have done some mouse functions (very crude). I'd love to help with this, as I think it'd be awesome. It'd help a lot if you were to draw the diagram (even in writing on a piece of paper, if need be). :)

Wizzup?

2010-11-06 15:07:13

From: ForgotMyName
Are there plans for bindings between Python and Simba? (Or really, any other language and Simba.) Haven't done anything with MML but seems like it and this are going down that path.


SPyRL depends (afaik) on my pyMML library; which is an object oriented wrapper for the MML. It's not finished yet, but I'll get to work on it in a bit, I hope.

i luffs yeww

2010-11-06 17:35:01

From: Wizzup?
Slightly off topic: http://www.canonical.org/~kragen/isinstance/


"It appears to be recognized that isinstance is usually a bad idea; rather like Euclid's parallel postulate, the proof of this is in the frequency of its use.

In Python 2.1.1, there are 98586 lines in 447 .py files in the standard library; 68 of those lines mention 'isinstance'.

19 of those 68 are in the test suite, which contains 19364 of the total lines; seven of these (10% of the total standard-library use!) are verifying that isinstance() itself works.

24 of these are in User*.py, which uses isinstance to see if it's safe to bypass public interfaces and use other objects' data members directly. They should use try-except or hasattr instead."

I don't know if it's saying it's good or bad to use, especially after what that says..

**BANNED The Man

2011-01-27 08:55:18

Nava that diagram would be awesome.. If you have time?

-Boom