You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
16 KiB

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
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
str += word + " "
else:
sizex, sizey = draw_black.multiline_textsize(
str + "\n" + word, font)
if(sizey > limit[1]):
break
else:
str = str.rstrip(" ")
str += "\n" + word + " "
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):
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)
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=""):
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.font = ImageFont.truetype('DejaVuSans.ttf', 16)
self.updateWidget(str)
def drawText(self, str):
# define parameters for text placement
position = (0, 0)
limit = (self.wt, self.ht)
# write the string in the widget
textBox(str, self.font, position, limit, "black", self.draw_black)
def saveImages(self):
print("saving widget images")
self.image_black.save("imgBlackWidget.bmp")
self.image_yellow.save("imgYellowWidget.bmp")
def updateWidget(self, str):
self.draw_black.rectangle((0, 0, self.wt, self.ht), fill=255)
self.draw_yellow.rectangle((0, 0, self.wt, self.ht), fill=255)
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=""):
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.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))
print(" limited by height")
return fixedImg
if cRatio < iRatio: # width is limiting dimension
fixedImg = img.resize((self.wt, int(self.wt / iRatio)))
print(" limited by width")
return fixedImg
fixedImg = img
print(" ratios are equal, not limited")
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
if cRatio > iRatio:
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:
# 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
def threshold(self, val):
if(val > self.thresLim):
return 1
else:
return 0
def bwImg(self, 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]))
self.image_black.paste(img, (offset, 0))
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):
print("saving widget images")
self.image_black.save("imgBlackWidget.bmp")
self.image_yellow.save("imgYellowWidget.bmp")
def updateWidget(self, img):
if self.scaleMode is "fill":
rsImg = self.fillImg(img)
if self.scaleMode is "resize":
rsImg = self.resizeImg(img)
if self.scaleMode is "none":
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))
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\nCreating 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