Module OLED1306
[hide private]
[frames] | no frames]

Source Code for Module OLED1306

  1  # OLED1306.py 
  2  # Support class for the OLED1306 controller based OLED display 
  3  # 128x32 or 128x64 resolution. 
  4  # Sits on top of the Adafruit OLED1306 driver class (with thanks to the author) 
  5   
  6  import SSD1306 
  7  import RPi.GPIO as GPIO 
  8  import smbus 
  9   
 10   
 11  import os, time 
 12  from threading import Thread 
 13  from PIL import Image 
 14  from PIL import ImageFont 
 15  from PIL import ImageDraw 
16 -class OLED1306():
17 ''' 18 Creates a display instances with given OLED display type (128x32 or 128x64, black & white). 19 with standard font from font file /usr/share/fonts/truetype/freefont/FreeSans.ttf. 20 @param imagePath: the path to the PPM image file used as background (must have size 128x32 or 128x64 resp.) 21 @param type: 32 or 64 defining 128x32 or 128x64 bit resolution (default: 64) 22 @param inverse: if True, the background is white and the text is black; 23 otherwise the background is black and the text is white (default) 24 '''
25 - def __init__(self, bkImagePath = None, type = 64, inverse = False):
26 bus = smbus.SMBus(1) 27 i2c_address = 0x3C 28 try: 29 bus.read_word_data(i2c_address, 0) 30 except: 31 self._isAvailable = False 32 return 33 self._isAvailable = True 34 if type == 32: 35 # 128x32 display with hardware I2C: 36 self.disp = SSD1306.SSD1306_128_32(rst = None, gpio = GPIO) 37 elif type == 64: 38 # 128x64 display with hardware I2C: 39 self.disp = SSD1306.SSD1306_128_64(rst = None, gpio = GPIO) 40 else: 41 print "Device type", type, "not supported" 42 self.inverse = inverse 43 self.bkImagePath = bkImagePath 44 self.type = type 45 self.blinkerThread = None 46 47 # Initialize library 48 self.disp.begin() 49 50 # Get display width and height 51 self.width = self.disp.width 52 self.height = self.disp.height 53 54 # Clear display 55 self.disp.clear() 56 self.disp.display() 57 58 # Create 1-bit image buffer 59 self.image = Image.new('1', (self.width, self.height)) 60 61 # Load font 62 # self.font = ImageFont.load_default() 63 self.fontSize = 10 64 # self.ttfFileStandard = "/home/pi/Fonts/OpenSans-Light.ttf" 65 self.ttfFileStandard = "/home/pi/Fonts/OpenSans-Semibold.ttf" 66 self.ttfFile = self.ttfFileStandard 67 self.font = ImageFont.truetype(self.ttfFile, 10) 68 69 # Create drawing object 70 self.draw = ImageDraw.Draw(self.image) 71 72 # Set full contrast 73 self.disp.set_contrast(255) 74 75 # Initialize text buffer 76 self.textBuf = {} 77 self.cursor = [0, 0] 78 self.scroll = False
79
80 - def dim(self, enable):
81 ''' 82 Enables/disables dimming the display. 83 @param enable: if True, the display is slightly dimmed; 84 otherwise it is set to full contrast 85 ''' 86 if enable: 87 self.disp.set_contrast(0) 88 else: 89 self.disp.set_contrast(255)
90
91 - def setBkImage(self, bkImagePath):
92 ''' 93 Defines a background image to be displayed with next setText() or show(). 94 ''' 95 self.bkImagePath = bkImagePath
96
97 - def setFont(self, ttfFile, fontSize = 10):
98 ''' 99 Sets a new font defined by the given TTF font file. 100 @ttfFile: the path to the font file (only TTF fonts supported) 101 @fontSize: the font size (default: 10) 102 ''' 103 self.ttfFile = ttfFile 104 self.ttfFileStandard = ttfFile 105 self.fontSize = fontSize 106 self.font = ImageFont.truetype(ttfFile, fontSize)
107
108 - def setFontSize(self, fontSize):
109 ''' 110 Sets a new font size of current font. 111 @fontSize: the new font size 112 ''' 113 self.font = ImageFont.truetype(self.ttfFile, fontSize) 114 self.fontSize = fontSize
115
116 - def clear(self):
117 ''' 118 Erases the display and clears the text buffer. 119 ''' 120 if self.inverse: 121 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 255) 122 else: 123 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 0) 124 self.disp.image(self.image) 125 self.disp.display() 126 self.textBuf = {} 127 self.cursor = [0, 0] 128 self.scroll = False
129
130 - def erase(self):
131 ''' 132 Erases the display without clearing the text buffer. 133 ''' 134 if self.inverse: 135 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 255) 136 else: 137 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 0) 138 self.disp.image(self.image) 139 self.disp.display()
140
141 - def setText(self, text, lineNum = 0, fontSize = None, indent = 0):
142 ''' 143 Displays text at given line left adjusted. 144 The old text of this line is erased, other text is not modified 145 The line distance is defined by the font size (text height + 1). 146 If no text is attributed to a line, the line is considered to consist of a single space 147 character with the font size of the preceeding line. 148 The position of the text cursor is not modified. 149 Text separated by \n is considered as a multiline text. In this case lineNum is the line number of the 150 first line. 151 @param text: the text to display. If emtpy, text with a single space character is assumed. 152 @param lineNum: the line number where to display the text (default: 0) 153 @param fontSize: the size of the font (default: None, set to current font size) 154 @indent: the line indent in pixels (default: 0) 155 ''' 156 if "\n" not in text: 157 self._setLine(text, lineNum, fontSize, indent) 158 else: 159 lines = text.split("\n") 160 nb = lineNum 161 for line in lines: 162 self._setLine(line, nb, fontSize, indent) 163 nb += 1
164
165 - def _setLine(self, text, lineNum, fontSize, indent):
166 if text == "": 167 text = " " 168 if fontSize == None: 169 fontSize = self.fontSize 170 self.textBuf[lineNum] = (text, fontSize, indent) 171 self.repaint()
172
173 - def getFontSize(self):
174 ''' 175 Returns the current font size. 176 @return: the font size 177 ''' 178 return self.fontSize
179
180 - def getLineHeight(self):
181 ''' 182 Returns the height of one line. 183 @return: line spacing in pixels 184 ''' 185 charWidthDummy, charHeight = self.draw.textsize('I', font = self.font) 186 return charHeight
187
188 - def repaint(self):
189 ''' 190 Repaints the screen (background image and text buffer). 191 ''' 192 if len(self.textBuf) == 0: 193 maxNum = 0 194 else: 195 maxNum = max(self.textBuf.keys()) 196 # Clear display 197 if self.inverse: 198 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 255) # White 199 else: 200 self.draw.rectangle((0, 0, self.width, self.height), outline = 0, fill = 0) # Black 201 # Insert background image 202 if self.bkImagePath != None: 203 picture = Image.open(self.bkImagePath).convert('1') 204 self.image.paste(picture) 205 y = 0 206 lastFontSize = 0 207 for lineNb in range(maxNum + 1): 208 try: 209 text = self.textBuf[lineNb][0] 210 fontSize = self.textBuf[lineNb][1] 211 x = self.textBuf[lineNb][2] # tab 212 except: 213 text = " " 214 fontSize = 10 215 x = 0 216 self.font = ImageFont.truetype(self.ttfFile, fontSize) 217 charWidthDummy, charHeight = self.draw.textsize('A', font = self.font) 218 for c in text: 219 charWidth, charHeightDummy = self.draw.textsize(c, font = self.font) 220 if self.inverse: 221 self.draw.text((x, y), c, font = self.font, fill = 0) # Black 222 else: 223 self.draw.text((x, y), c, font = self.font, fill = 255) # White 224 x += charWidth 225 y += charHeight 226 227 # Renders the current image buffer. 228 self.disp.image(self.image) 229 self.disp.display()
230
231 - def showImage(self, imagePath):
232 ''' 233 Shows the image (1 pixel monochrome) with given filename (must have size 128x32 for display type 32 234 or 128x64 for display type 64). 235 @param imagePath: the path to the PPM image file 236 ''' 237 picture = Image.open(imagePath).convert('1') 238 self.disp.image(picture) 239 self.disp.display()
240
241 - def println(self, text):
242 ''' 243 Appends text at current cursor position and scrolls, if necessary. 244 Sets the cursor at the beginning of next line. 245 @param text: the text to display 246 ''' 247 charWidthDummy, charHeight = self.draw.textsize('I', font = self.font) 248 nbLines = int(self.type / (charHeight)) 249 250 if not self.scroll: 251 self.setText(text, self.cursor[1]) 252 self.cursor[1] += 1 253 if self.cursor[1] == nbLines: 254 self.scroll = True 255 else: 256 for i in range(nbLines - 1): 257 self.textBuf[i] = self.textBuf[i + 1] 258 self.setText(text, nbLines - 1)
259
260 - def setNumberOfLines(self, nbLines):
261 ''' 262 Sets the current font size to a maximum to show the given number of lines. 263 @param: the number of lines to display 264 ''' 265 fontSize = int(self.type / nbLines) + 1 266 self.setFontSize(fontSize)
267
268 - def setInverse(self, inverse):
269 ''' 270 @param inverse: if True, the background is white and the text is black; 271 otherwise the background is black and the text is white (default) 272 ''' 273 self.inverse = inverse 274 self.repaint()
275
276 - def startBlinker(self, count = 3, offTime = 1000, onTime = 1000, blocking = False):
277 ''' 278 Blicks the entire screen for given number of times (off-on periods). 279 @param count: the number of blinking (default: 3) 280 @param offTime: the time the display is erased (in ms, default: 1000) 281 @param onTime: the time the display is shown (in ms, default: 1000) 282 @param blocking: if True, the function blocks until the blinking is finished; otherwise 283 it returns immediately 284 ''' 285 if self.blinkerThread != None: 286 self.stopBlinker() 287 self.blinkerThread = BlinkerThread(self, count, offTime, onTime) 288 if blocking: 289 while self.isBlinkerAlive(): 290 continue
291
292 - def stopBlinker(self):
293 ''' 294 Stops a running blinker. 295 The method blocks until the blinker thread is finished and isBlinkerAlive() returns False. 296 ''' 297 if self.blinkerThread != None: 298 self.blinkerThread.stop() 299 self.blinkerThread = None
300
301 - def isBlinking(self):
302 ''' 303 @return: True, if the blinker is displaying; otherwise False 304 ''' 305 time.sleep(0.001) 306 if self.blinkerThread == None: 307 return False 308 return self.blinkerThread.isAlive
309
310 - def isDeviceAvailable(self):
311 ''' 312 Returns True, if the device is detected on the I2C bus; 313 otherwise returns False 314 ''' 315 return self._isAvailable
316 317 # ------------------- class BlinkerThread ----------------------
318 -class BlinkerThread(Thread):
319 - def __init__(self, display, count, offTime, onTime):
320 Thread.__init__(self) 321 self.display = display 322 self.offTime = offTime 323 self.onTime = onTime 324 self.count = count 325 self.isRunning = False 326 self.isAlive = True 327 self.start() 328 while not self.isRunning: 329 continue
330
331 - def run(self):
332 nb = 0 333 time.sleep(1) 334 self.isRunning = True 335 while self.isRunning: 336 self.display.erase() 337 startTime = time.time() 338 while time.time() - startTime < self.offTime / 1000 and self.isRunning: 339 time.sleep(0.001) 340 if not self.isRunning: 341 break 342 nb += 1 343 self.display.repaint() 344 startTime = time.time() 345 while time.time() - startTime < self.onTime / 1000 and self.isRunning: 346 time.sleep(0.001) 347 if not self.isRunning: 348 break 349 if nb == self.count: 350 self.isRunning = False 351 self.isAlive = False
352
353 - def stop(self):
354 self.isRunning = False 355 while self.isAlive: # Wait until thread is finished 356 continue
357