@ -1,63 +1,117 @@
from __future__ import division
from PIL import Image , ImageDraw , ImageFont , ImageOps
from trello import TrelloClient
import os
import requests
from requests_oauthlib import OAuth1Session
import re
def textBox ( text , font , position , limits , colour , draw_black ) :
str = " "
limit = [ 0 , 0 ]
limit [ 0 ] = limits [ 0 ] - position [ 0 ]
limit [ 1 ] = limits [ 1 ] - position [ 1 ]
words = text . split ( " " )
i = 0 #index of first word
sizex , sizey = draw_black . multiline_textsize ( str , font ) #size of the textbox
#while more words to be added to string AND string textbox height < height limit
i = 0 # index of first word
sizex , sizey = draw_black . multiline_textsize (
str , font ) # size of the textbox
# while more words to be added to string AND string textbox height < height limit
for word in words :
sizex , sizey = draw_black . multiline_textsize ( str + word , font )
sizex2 , sizey2 = draw_black . multiline_textsize ( str , font )
if sizex < limit [ 0 ] : # if width of gen textbox < width limit
if sizex < limit [ 0 ] : # if width of gen textbox < width limit
str + = word + " "
else :
sizex , sizey = draw_black . multiline_textsize ( str + " \n " + word , font )
sizex , sizey = draw_black . multiline_textsize (
str + " \n " + word , font )
if ( sizey > limit [ 1 ] ) :
break
else :
str = str . rstrip ( " " )
str + = " \n " + word + " "
draw_black . text ( position , str , font = font , fill = 0 )
sizex , sizey = draw_black . multiline_textsize ( str , font )
draw_black . text ( position , str , font = font , fill = 0 )
return sizex , sizey
def putImage ( imagepath , maskpath , position , colour ) :
img = Image . open ( imagepath )
mask = Image . open ( maskpath ) . convert ( mode = ' 1 ' )
if type ( imagepath ) is str :
img = Image . open ( imagepath )
else :
img = imagepath
if type ( imagepath ) is str :
mask = Image . open ( maskpath ) . convert ( mode = ' 1 ' )
else :
mask = maskpath
if colour is " black " :
image_black . paste ( img , position , mask )
if colour is " yellow " :
image_yellow . paste ( img , position , mask )
black_invert = ImageOps . invert ( image_black . convert ( ' L ' ) )
draw_yellow . bitmap ( ( 0 , 0 ) , black_invert , 255 )
draw_yellow . bitmap ( ( 0 , 0 ) , black_invert , 255 )
def roundRect ( draw , topLeft , bottomRight , arcsize ) :
a = 180
arcStart = topLeft
arcEnd = ( arcStart [ 0 ] + arcsize , arcStart [ 1 ] + arcsize )
lastLineEnd = ( arcStart [ 0 ] , arcStart [ 1 ] + arcsize / 2 )
lineStart = ( arcStart [ 0 ] + arcsize / 2 , arcStart [ 1 ] )
draw . arc ( ( arcStart , arcEnd ) , a , a + 90 )
a = ( a + 90 ) % 360
arcStart = ( bottomRight [ 0 ] - arcsize , topLeft [ 1 ] )
arcEnd = ( arcStart [ 0 ] + arcsize , arcStart [ 1 ] + arcsize )
lineEnd = ( arcStart [ 0 ] + arcsize / 2 , arcStart [ 1 ] )
draw . line ( ( lineStart , lineEnd ) )
lineStart = ( arcEnd [ 0 ] , arcEnd [ 1 ] - arcsize / 2 )
draw . arc ( ( arcStart , arcEnd ) , a , a + 90 )
a = ( a + 90 ) % 360
arcStart = ( bottomRight [ 0 ] - arcsize , bottomRight [ 1 ] - arcsize )
arcEnd = ( arcStart [ 0 ] + arcsize , arcStart [ 1 ] + arcsize )
lineEnd = ( arcEnd [ 0 ] , arcEnd [ 1 ] - arcsize / 2 )
draw . line ( ( lineStart , lineEnd ) )
lineStart = ( arcEnd [ 0 ] - arcsize / 2 , arcEnd [ 1 ] )
draw . arc ( ( arcStart , arcEnd ) , a , a + 90 )
a = ( a + 90 ) % 360
arcStart = ( topLeft [ 0 ] , bottomRight [ 1 ] - arcsize )
arcEnd = ( arcStart [ 0 ] + arcsize , arcStart [ 1 ] + arcsize )
lineEnd = ( arcEnd [ 0 ] - arcsize / 2 , arcEnd [ 1 ] )
draw . line ( ( lineStart , lineEnd ) )
lineStart = ( arcStart [ 0 ] , arcStart [ 1 ] + arcsize / 2 )
draw . arc ( ( arcStart , arcEnd ) , a , a + 90 )
draw . line ( ( lineStart , lastLineEnd ) )
class TextWidget ( ) :
#this example widget will have a set size: 3x1 cells (one column)
#an instance of this widget class can be created and pasted to canvas
def __init__ ( self , cwidth , cheight , xy = ( 0 , 0 ) , dim = ( 1 , 1 ) , str = " " ) :
# this example widget will have a set size: 3x1 cells (one column)
# an instance of this widget class can be created and pasted to canvas
def __init__ ( self , cwidth , cheight , xy = ( 0 , 0 ) , dim = ( 1 , 1 ) , str = " " ) :
self . width = dim [ 0 ]
self . height = dim [ 1 ]
self . wt = cwidth * dim [ 0 ]
self . ht = cheight * dim [ 1 ]
self . wt = cwidth * dim [ 0 ]
self . ht = cheight * dim [ 1 ]
self . cellX = xy [ 0 ]
self . cellY = xy [ 1 ]
self . image_yellow = Image . new ( ' 1 ' , ( self . wt , self . ht ) , 255 ) #255: clear the frame
self . image_yellow = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_yellow = ImageDraw . Draw ( self . image_yellow )
self . image_black = Image . new ( ' 1 ' , ( self . wt , self . ht ) , 255 ) #255: clear the frame
self . image_black = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_black = ImageDraw . Draw ( self . image_black )
self . font = ImageFont . truetype ( ' DejaVuSans.ttf ' , 16 )
self . updateWidget ( str )
def drawText ( self , str ) :
#define parameters for text placement
position = ( 0 , 0 )
# define parameters for text placement
position = ( 0 , 0 )
limit = ( self . wt , self . ht )
#write the string in the widget
# write the string in the widget
textBox ( str , self . font , position , limit , " black " , self . draw_black )
def saveImages ( self ) :
@ -71,34 +125,37 @@ class TextWidget():
self . drawText ( str )
self . saveImages ( )
class ImageWidget ( ) :
# this widget displays an image
# needs to recieve an image file as input. Then resize and convert to monochrome.
def __init__ ( self , cwidth , cheight , xy = ( 0 , 0 ) , dim = ( 1 , 1 ) , bwMode = " mono " , scaleMode = " fill " , img = " " ) :
# this widget displays an image
# needs to recieve an image file as input. Then resize and convert to monochrome.
def __init__ ( self , cwidth , cheight , xy = ( 0 , 0 ) , dim = ( 1 , 1 ) , bwMode = " mono " , scaleMode = " fill " , img = " " ) :
self . width = dim [ 0 ]
self . height = dim [ 1 ]
self . wt = cwidth * dim [ 0 ]
self . ht = cheight * dim [ 1 ]
self . wt = cwidth * dim [ 0 ]
self . ht = cheight * dim [ 1 ]
self . cellX = xy [ 0 ]
self . cellY = xy [ 1 ]
self . image_yellow = Image . new ( ' 1 ' , ( self . wt , self . ht ) , 255 ) #255: clear the frame
self . image_yellow = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_yellow = ImageDraw . Draw ( self . image_yellow )
self . image_black = Image . new ( ' 1 ' , ( self . wt , self . ht ) , 255 ) #255: clear the frame
self . image_black = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_black = ImageDraw . Draw ( self . image_black )
self . bwMode = bwMode
self . scaleMode = scaleMode
self . updateWidget ( Image . open ( img ) )
def resizeImg ( self , img ) :
# scale image down until whole image fits inside the cell
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
if cRatio > iRatio : # height is limiting dimension
fixedImg = img . resize ( ( int ( self . ht * iRatio ) , self . ht ) )
# scale image down until whole image fits inside the cell
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
if cRatio > iRatio : # height is limiting dimension
fixedImg = img . resize ( ( int ( self . ht * iRatio ) , self . ht ) )
print ( " limited by height " )
return fixedImg
if cRatio < iRatio : # width is limiting dimension
fixedImg = img . resize ( ( self . wt , int ( self . wt / iRatio ) ) )
if cRatio < iRatio : # width is limiting dimension
fixedImg = img . resize ( ( self . wt , int ( self . wt / iRatio ) ) )
print ( " limited by width " )
return fixedImg
fixedImg = img
@ -106,57 +163,60 @@ class ImageWidget():
return fixedImg
def fillImg ( self , img ) :
# enlarge the image and crop to fill the widget canvas
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
# enlarge the image and crop to fill the widget canvas
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
if cRatio > iRatio :
print ( " new sizes as calcd in resize: " + str ( self . wt ) + " " + str ( self . wt / iRatio ) )
fixedImg = img . resize ( ( self . wt , int ( self . wt / iRatio ) ) ) #width = wt, calc new height w/ ratio
offset = 0.5 * ( fixedImg . size [ 1 ] - self . ht ) #centre by height
cropped = fixedImg . crop ( ( 0 , offset , self . wt , offset + self . ht - 1 ) )
#cropped.load()
print ( " zoomed to fill height " )
return cropped
print ( " new sizes as calcd in resize: " +
str ( self . wt ) + " " + str ( self . wt / iRatio ) )
# width = wt, calc new height w/ ratio
fixedImg = img . resize ( ( self . wt , int ( self . wt / iRatio ) ) )
offset = 0.5 * ( fixedImg . size [ 1 ] - self . ht ) # centre by height
cropped = fixedImg . crop ( ( 0 , offset , self . wt , offset + self . ht - 1 ) )
# cropped.load()
print ( " zoomed to fill height " )
return cropped
if cRatio < iRatio :
fixedImg = img . resize ( ( int ( self . ht * iRatio ) , self . ht ) ) #height = ht, calc new width w/ ratio
offset = 0.5 * ( fixedImg . size [ 0 ] - self . wt ) #centre by width
cropped = fixedImg . crop ( ( offset , 0 , offset + self . wt , self . ht - 1 ) )
#cropped.load()
print ( " zoomed to fill width " )
return cropped
print ( " not zoomed " )
return img . resize ( ( self . wt , self . ht ) )
# height = ht, calc new width w/ ratio
fixedImg = img . resize ( ( int ( self . ht * iRatio ) , self . ht ) )
offset = 0.5 * ( fixedImg . size [ 0 ] - self . wt ) # centre by width
cropped = fixedImg . crop ( ( offset , 0 , offset + self . wt , self . ht - 1 ) )
# cropped.load()
print ( " zoomed to fill width " )
return cropped
print ( " not zoomed " )
return img . resize ( ( self . wt , self . ht ) )
def autoThresholdLimit ( self , img ) :
gscale = img . convert ( mode = " L " )
gscale . save ( " greyscale.bmp " )
extrema = gscale . getextrema ( )
print ( " EXTREMA: " + str ( extrema ) )
limit = extrema [ 0 ] + 0.4 * ( extrema [ 1 ] - extrema [ 0 ] )
print ( limit )
self . thresLim = limit
gscale = img . convert ( mode = " L " )
gscale . save ( " greyscale.bmp " )
extrema = gscale . getextrema ( )
print ( " EXTREMA: " + str ( extrema ) )
limit = extrema [ 0 ] + 0.4 * ( extrema [ 1 ] - extrema [ 0 ] )
print ( limit )
self . thresLim = limit
def threshold ( self , val ) :
if ( val > self . thresLim ) :
if ( val > self . thresLim ) :
return 1
else :
return 0
def bwImg ( self , img ) :
self . autoThresholdLimit ( img )
self . autoThresholdLimit ( img )
if self . bwMode is " mono " :
return img . convert ( ' L ' ) . point ( self . threshold , ' 1 ' )
if self . bwMode is " dither " :
return img . convert ( mode = ' 1 ' )
def pasteImg ( self , img ) :
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
if cRatio > iRatio : # needs to be centred horizontally
offset = int ( 0.5 * ( self . wt - img . size [ 0 ] ) )
iRatio = img . size [ 0 ] / img . size [ 1 ] # image ratio
cRatio = self . wt / self . ht # canvas ratio
if cRatio > iRatio : # needs to be centred horizontally
offset = int ( 0.5 * ( self . wt - img . size [ 0 ] ) )
self . image_black . paste ( img , ( offset , 0 ) )
if cRatio < iRatio : # needs to be centred vertically
offset = int ( 0.5 * ( self . ht - img . size [ 1 ] ) )
if cRatio < iRatio : # needs to be centred vertically
offset = int ( 0.5 * ( self . ht - img . size [ 1 ] ) )
self . image_black . paste ( img , ( 0 , offset ) )
def saveImages ( self ) :
@ -166,15 +226,182 @@ class ImageWidget():
def updateWidget ( self , img ) :
if self . scaleMode is " fill " :
rsImg = self . fillImg ( img )
rsImg = self . fillImg ( img )
if self . scaleMode is " resize " :
rsImg = self . resizeImg ( img )
rsImg = self . resizeImg ( img )
if self . scaleMode is " none " :
rsImg = img
rsImg = img
rsImg . save ( " rsImg.bmp " )
newImg = self . bwImg ( rsImg )
newImg . save ( " bwImg.bmp " )
print ( " Dimensions are now: " + str ( newImg . size [ 0 ] ) + " x " + str ( newImg . size [ 1 ] ) )
print ( " Dimensions of widget canvas: " + str ( self . wt ) + " x " + str ( self . ht ) )
print ( " Dimensions are now: " +
str ( newImg . size [ 0 ] ) + " x " + str ( newImg . size [ 1 ] ) )
print ( " Dimensions of widget canvas: " +
str ( self . wt ) + " x " + str ( self . ht ) )
self . pasteImg ( newImg )
self . saveImages ( )
class TrelloWidget ( ) :
# displays your trello boards in text form
# card function: Name, description in one 'cell' or box with rounded corners
def __init__ ( self , cwidth , cheight , xy = ( 0 , 0 ) , dim = ( 1 , 1 ) , boardName = None , listName = None ) :
self . boardName = boardName
self . listName = listName
self . initTrelloClient ( )
self . width = dim [ 0 ]
self . height = dim [ 1 ]
self . wt = cwidth * dim [ 0 ]
self . ht = cheight * dim [ 1 ]
self . cellX = xy [ 0 ]
self . cellY = xy [ 1 ]
self . image_yellow = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_yellow = ImageDraw . Draw ( self . image_yellow )
self . image_black = Image . new (
' 1 ' , ( self . wt , self . ht ) , 255 ) # 255: clear the frame
self . draw_black = ImageDraw . Draw ( self . image_black )
self . lastY = 0
self . cardLastY = 0
self . font1 = ImageFont . truetype ( ' DejaVuSans.ttf ' , 12 )
self . font2 = ImageFont . truetype ( ' DejaVuSans.ttf ' , 13 )
self . font3 = ImageFont . truetype ( ' DejaVuSans.ttf ' , 10 )
self . updateWidget ( )
def initTrelloClient ( self ) :
self . trello_key = ' a3d8f7c04c266e5c9571f6b7747aa353 '
self . trello_secret = ' d4ac9968d997aa3e5a0ebf129a627b03701520de6ff19834c81d6d9c56fe7e9b '
try :
with open ( ' tokenfile.secret ' , ' r ' ) as file :
token = file . readline ( ) . rstrip ( )
secret = file . readline ( ) . rstrip ( )
self . client = TrelloClient (
api_key = self . trello_key ,
api_secret = self . trello_secret ,
token = token ,
token_secret = secret
)
all_boards = self . client . list_boards ( )
except Exception as e :
print ( e )
print ( " token expired or does not exist \n Creating new token " )
self . access_token , self . resource_owner_key , self . resource_owner_secret = self . create_oauth_token ( )
with open ( ' tokenfile.secret ' , ' w ' ) as file :
file . write ( self . access_token [ " oauth_token " ] +
" \n " + self . access_token [ " oauth_token_secret " ] )
self . client = TrelloClient (
api_key = self . trello_key ,
api_secret = self . trello_secret ,
token = self . access_token [ " oauth_token " ] ,
token_secret = self . access_token [ " oauth_token_secret " ]
)
def create_oauth_token ( self , expiration = None , scope = None , key = None , secret = None , name = None , output = True ) :
request_token_url = ' https://trello.com/1/OAuthGetRequestToken '
authorize_url = ' https://trello.com/1/OAuthAuthorizeToken '
access_token_url = ' https://trello.com/1/OAuthGetAccessToken '
expiration = os . environ . get ( ' TRELLO_EXPIRATION ' , " 30days " )
scope = scope or os . environ . get ( ' TRELLO_SCOPE ' , ' read,write ' )
#trello_key = key or os.environ['TRELLO_API_KEY']
#trello_secret = secret or os.environ['TRELLO_API_SECRET']
name = name or os . environ . get ( ' TRELLO_NAME ' , ' py-trello ' )
session = OAuth1Session ( client_key = self . trello_key ,
client_secret = self . trello_secret )
response = session . fetch_request_token ( request_token_url )
resource_owner_key , resource_owner_secret = response . get (
' oauth_token ' ) , response . get ( ' oauth_token_secret ' )
print ( " Go to the following link in your browser: " )
print ( " {authorize_url} ?oauth_token= {oauth_token} &scope= {scope} &expiration= {expiration} &name= {name} " . format (
authorize_url = authorize_url ,
oauth_token = resource_owner_key ,
expiration = expiration ,
scope = scope ,
name = name
) )
accepted = " n "
while accepted . lower ( ) == " n " :
accepted = raw_input ( ' Have you authorized me? (y/n) ' )
#oauth_verifier = ""
# if input('Have you authorized me? (y/n) ') is "y":
# oauth_verifier = input('What is the PIN? ')
oauth_verifier = raw_input ( ' What is the PIN? ' )
session = OAuth1Session ( client_key = self . trello_key , client_secret = self . trello_secret ,
resource_owner_key = resource_owner_key , resource_owner_secret = resource_owner_secret ,
verifier = oauth_verifier )
access_token = session . fetch_access_token ( access_token_url )
return access_token , resource_owner_key , resource_owner_secret
def updateWidget ( self ) :
card_list = self . cardsFromBoard ( self . boardName , self . listName )
self . printCards ( card_list )
#self.draw_black.rectangle((0, 0, self.wt, self.ht), fill=255) #inclusive/exclusive dimensions?
#self.draw_yellow.rectangle((0, 0, self.wt, self.ht), fill=255)
#textBox(content, self.font, (0,0), (self.wt, self.ht), "black", self.draw_black)
def printCards ( self , card_list ) :
for cardImg in card_list :
self . image_black . paste ( cardImg , ( 0 , self . lastY ) )
print ( self . lastY )
self . lastY + = cardImg . size [ 1 ]
def makeCard ( self , card ) :
# calculate card dimensions for an image to draw the card on
# then draw the card on the widget canvas
# card dimensions: wt/width, ht/height (cards as large as one cell)
arcsize = 25
padding = 3 * arcsize / 8
cardLastY = 0
card_wt = int ( self . wt / self . width )
card_ht = int ( self . ht ) * 5
self . cardLastY = 0
card_image = Image . new ( ' 1 ' , ( card_wt , card_ht ) , 255 )
card_draw = ImageDraw . Draw ( card_image )
self . drawText ( card . name , self . font1 , card_draw , card_image , card_wt , padding )
self . drawText ( card . description , self . font3 , card_draw , card_image , card_wt , padding )
roundRect ( card_draw , ( 2 , 2 ) , ( card_wt - 3 , self . cardLastY + padding + 4 ) , arcsize )
card_image = card_image . crop ( ( 0 , 0 , card_wt , self . cardLastY + padding + 4 + 1 ) )
card_image . save ( " card_image.bmp " )
self . cardLastY = 0
return card_image
def drawText ( self , text , font , draw , ima , card_wt , padding ) :
sizex , sizey = textBox ( text , font , ( 0 + padding , self . cardLastY + 2 * padding / 3 ) ,
( card_wt - padding , ima . size [ 1 ] ) , " black " , draw )
# find predicted height of textbox and set lastY to it
self . cardLastY + = sizey + 4
def cardsFromBoard ( self , boardName , listName ) :
cards = [ ]
all_boards = self . client . list_boards ( )
for board in all_boards :
if board . name == boardName :
slctd_board = board
slctd_board . list_lists ( )
all_lists_in_board = slctd_board . all_lists ( )
for lst in all_lists_in_board :
if lst . name == listName :
card_list = lst . list_cards ( )
for crd in card_list :
cards . append ( self . makeCard ( crd ) )
return cards