├── .gitignore ├── Basics ├── 3-1-conditional.py ├── 3-2-iteration.py ├── 3-3-retyping.py ├── 4-1-functions-formattedOutput.py ├── 4-2-functions-getNameAndSuffix.py ├── 4-3-functions-reverseDict.py └── 4-4-functions-unicode2string.py ├── DrawBot ├── DrawBot-dotted-grid-animated.py ├── DrawBot-dotted-grid-randomized.py ├── DrawBot-dotted-grid.py └── DrawBot-randomized-circles.py ├── Glyphs ├── Change sidebearings (with dialogue).py ├── Change sidebearings.py ├── Chopper.py ├── Guideliner - add.py ├── Guideliner - remove.py ├── Monospacer.py ├── Print glyph info.py ├── TODO.md └── template.py ├── Presentation └── Python workshop.pdf ├── README.md ├── Shell └── studentlist.py └── basics ├── 1-1-first-script.py ├── 1-2-expressions.py ├── 1-3-assignment.py ├── 2-1-integer.py ├── 2-2-float.py ├── 2-3-list.py ├── 2-4-string.py ├── 2-5-tuple.py └── 2-6-dictionary.py /.gitignore: -------------------------------------------------------------------------------- 1 | _presentation -------------------------------------------------------------------------------- /Basics/3-1-conditional.py: -------------------------------------------------------------------------------- 1 | 2 | ### Conditional ### 3 | 4 | # simple conditional 5 | number = 8 6 | if number % 2: 7 | print "The number is odd." 8 | else: 9 | print "The number is even." 10 | 11 | # zero test before division 12 | num = 20 13 | dnom = -3.7 14 | if dnom != 0: 15 | print num/dnom 16 | 17 | # without else, but with elif 18 | s = "00041" 19 | print s, " is " 20 | if len(s) > 4: 21 | print "too long" 22 | elif len(s) < 4: 23 | print "too short." 24 | 25 | # with elif and else 26 | s = "00041" 27 | print s, " is " 28 | if len(s) > 4: 29 | print "too long" 30 | elif len(s) < 4: 31 | print "too short." 32 | else: 33 | print "is accurate!" 34 | -------------------------------------------------------------------------------- /Basics/3-2-iteration.py: -------------------------------------------------------------------------------- 1 | 2 | ### Iteration ### 3 | 4 | # iteration over list items 5 | A = [0,1,2, "xp3"] 6 | for i in A: 7 | print i 8 | 9 | # iteration over string 10 | for char in "Monotype": 11 | print char 12 | 13 | # iteration over range of integers 14 | for i in range(50): 15 | print i**2 # power of 2 16 | 17 | for i in range(2,8): 18 | print i**2 # power of 2 19 | 20 | # iteration using list/string length 21 | MT = "Monotype" 22 | for i, char in enumerate(MT): 23 | print i, char 24 | 25 | # iteration over dictionary 26 | unicodes = {"space":"0020", "A":"0041", "f":"0066", "Agrave":"00C0"} 27 | for glName, glUnicode in unicodes.items(): 28 | print '"%s" has unicode %d (in decimal).' % (glName, int(glUnicode, 16)) 29 | 30 | # with condition included 31 | unicodes = {"space":"0020", "A":"0041", "f":"0066", "Agrave":"00C0"} 32 | for glName, glUnicode in unicodes.items(): 33 | if int(glUnicode, 16) > 65: 34 | print '"%s" has greater unicode than A.' % glName 35 | elif int(glUnicode, 16) < 65: 36 | print '"%s"" has smaller unicode than A.' % glName 37 | else: 38 | print '"%s" is A.' % glName 39 | -------------------------------------------------------------------------------- /Basics/3-3-retyping.py: -------------------------------------------------------------------------------- 1 | 2 | ### Retyping ### 3 | 4 | str(11) 5 | int("20") 6 | tuple([1, 2, 3]) 7 | 8 | hex(41) 9 | int("0041", 16) 10 | -------------------------------------------------------------------------------- /Basics/4-1-functions-formattedOutput.py: -------------------------------------------------------------------------------- 1 | # Define two functions 2 | # one to fill glyph dictionary with data 3 | # and the other to print formatted 4 | # glyph data to console 5 | # if isComposite is true the glyph has a list of components 6 | # that is list of component names 7 | 8 | # header: 9 | # def getGlyph(name, uni, isComposite, components): 10 | # returns glyph record 11 | # def printGlyph(glyphDict): 12 | # return nothing 13 | 14 | # format of glyph record 15 | glyphDict = { 16 | "name":"", 17 | "unicode":0, 18 | "isComposite":False, 19 | "components":[] 20 | } 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | # solution 32 | def getGlyph(name, uni, isComposite, components): 33 | """ 34 | Build glyph dictionary. 35 | """ 36 | 37 | glyph = {} 38 | glyph["name"] = name 39 | glyph["unicode"] = uni 40 | glyph["isComposite"] = isComposite 41 | glyph["components"] = components 42 | 43 | return glyph 44 | 45 | def printGlyph(glyphDict): 46 | """ 47 | Print glyph dictionary formatted. 48 | """ 49 | 50 | print "Glyph ", glyphDict["name"] 51 | print " ", glyph["unicode"] 52 | if isComposite: 53 | print "Has components: ", ", ".join(components) 54 | -------------------------------------------------------------------------------- /Basics/4-2-functions-getNameAndSuffix.py: -------------------------------------------------------------------------------- 1 | # Define function to decompose glyph name to name and suffix 2 | # if name does not have suffix (separated by period) return 3 | # empty suffix 4 | # the function should return tuple (name, suffix) 5 | # examples: "Agrave.sc" -> ("Agrave", "sc") 6 | # "ampersand.alt" -> ("ampersand", "alt") 7 | # "space" -> ("space") 8 | # hint: you can use string method find() 9 | 10 | # header: 11 | # def getNameAndSuffix(name): 12 | # returns tuple 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | # long solution 24 | def getNameAndSuffix(name): 25 | """ 26 | Returns tuple of name and its suffix. 27 | """ 28 | 29 | index = name.find(".") 30 | if index != -1: 31 | # there is a suffix 32 | shortname = name[:index] 33 | suffix = name[index+1:] 34 | else: 35 | # there is no period, no suffix 36 | shortname = name 37 | suffix = "" 38 | return (shortname, suffix) 39 | 40 | 41 | # shorter solution 42 | def getNameAndSuffix(name): 43 | """ 44 | Returns tuple of name and its suffix. 45 | """ 46 | index = name.find(".") 47 | if index != -1: 48 | # there is a suffix 49 | return name[:index], name[index+1:] 50 | else: 51 | # there is no period, no suffix 52 | return name, "" 53 | 54 | 55 | # another solution, using split() and join() 56 | def getNameAndSuffix(name): 57 | """ 58 | Returns tuple of name and its suffix. 59 | """ 60 | 61 | x = name.split(".") 62 | if len(x) == 1: # if there is not any suffix add empty record 63 | x.append("") 64 | 65 | suffixes = ".".join(x[1:]) # there may be more periods inthe name 66 | return (x[0], suffixes) # convert list to tuple 67 | -------------------------------------------------------------------------------- /Basics/4-3-functions-reverseDict.py: -------------------------------------------------------------------------------- 1 | # Define a function that revereses 2 | # a language dictionary 3 | # expect that all values can work as 4 | # keys and that they are unique 5 | 6 | # header: 7 | # def reverseDict(dic): 8 | # returns dictionary 9 | 10 | # dictionary 11 | cs_en = { 12 | "ahoj":"heya", 13 | "nashledanou":"goodbye", 14 | "na zdravi":"cheers", 15 | "jedna":"one", 16 | "dva":"two", 17 | "tri":"three" 18 | } 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | # solution 30 | def reverseDict(dic): 31 | """ 32 | Function reverses dictionary to be indexed by its values, 33 | keys become values. 34 | """ 35 | 36 | dic_rev = {} 37 | for key, item in dic.items(): 38 | dic_rev[item] = key 39 | 40 | return dic_rev 41 | 42 | 43 | # short solution 44 | def reverseDict(dic): 45 | """ 46 | Function reverses dictionary to be indexed by its values, 47 | keys become values. 48 | """ 49 | 50 | return dict(zip(dic.values(), dic.keys())) 51 | -------------------------------------------------------------------------------- /Basics/4-4-functions-unicode2string.py: -------------------------------------------------------------------------------- 1 | # Define function to convert decimal unicode integer 2 | # to hexadecimal number represented as four-character string 3 | # hint: use hex() function 4 | # test on different values 5 | # different numbers give strings of different length 6 | # there are various solutions how to add missing leading zeros 7 | # use .upper() to convert the string to uppercase only 8 | 9 | # header: 10 | # def unicode2string(unicodeValue): 11 | # retruns string 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | # long solution 23 | def unicode2string(unicodeValue): 24 | """ 25 | Convert unicode value to hexadecimal 26 | string of exactly 4 characters. 27 | """ 28 | 29 | hexcode = hex(unicodeValue) 30 | hexcode = hexcode[2:] 31 | hexcode = "0000" + hexcode 32 | hexcode = hexcode[-4:] 33 | hexcode = hexcode.upper() 34 | return hexcode 35 | 36 | 37 | # very short solution 38 | def unicode2string(unicodeValue): 39 | """ 40 | Convert unicode value to hexadecimal 41 | string of exactly 4 characters. 42 | """ 43 | 44 | return ("000"+hex(unicodeValue)[2:])[-4:].upper() 45 | -------------------------------------------------------------------------------- /DrawBot/DrawBot-dotted-grid-animated.py: -------------------------------------------------------------------------------- 1 | ### Drawing with DrawBot ### 2 | 3 | # Parametric grid of blue dots, randomized size and colour 4 | 5 | # parameters 6 | frames = 20 # number of frames 7 | number = 10 # number of dots in one row/column 8 | minSize = 10 # size of a dot 9 | maxSize = 20 10 | step = 30 11 | margin = 50 12 | 13 | # size of the canvas 14 | for i in range(0, frames): 15 | newPage(2 * margin + (number - 1) * step, 2 * margin + (number - 1) * step) 16 | frameDuration(0.4) 17 | 18 | # dots 19 | for x in range(margin, number * step + margin, step): 20 | for y in range(margin, number * step + margin, step): 21 | r = random() 22 | g = random() 23 | b = random() 24 | size = randint(minSize, maxSize) 25 | fill(r, g, b) 26 | oval(x - size/2, y - size/2, size, size) 27 | 28 | saveImage('~/Desktop/Dotted-grid.mov') 29 | -------------------------------------------------------------------------------- /DrawBot/DrawBot-dotted-grid-randomized.py: -------------------------------------------------------------------------------- 1 | ### Drawing with DrawBot ### 2 | 3 | # Parametric grid of blue dots, randomized size and colour 4 | 5 | # parameters 6 | number = 10 # number of dots in one row/column 7 | minSize = 10 # size of a dot 8 | maxSize = 20 9 | step = 30 10 | margin = 50 11 | 12 | # size of the canvas 13 | size(2 * margin + (number - 1) * step, 2 * margin + (number - 1) * step) 14 | 15 | 16 | # dots 17 | for x in range(margin, number * step + margin, step): 18 | for y in range(margin, number * step + margin, step): 19 | r = random() 20 | g = random() 21 | b = random() 22 | size = randint(minSize, maxSize) 23 | fill(r, g, b) 24 | oval(x - size/2, y - size/2, size, size) 25 | -------------------------------------------------------------------------------- /DrawBot/DrawBot-dotted-grid.py: -------------------------------------------------------------------------------- 1 | ### Drawing with DrawBot ### 2 | 3 | # Grid (10 x 10) of blue dots 4 | 5 | distance = 100 6 | fill(0, 0, 0.75) 7 | for x in range(0, 10): 8 | for y in range(0, 10): 9 | oval(x*distance + distance, y*distance + distance, 10, 10) 10 | -------------------------------------------------------------------------------- /DrawBot/DrawBot-randomized-circles.py: -------------------------------------------------------------------------------- 1 | ### Drawing with DrawBot ### 2 | 3 | # Random circles 4 | 5 | HEIGHT = WIDTH = 400 6 | 7 | for i in range(100): 8 | x = randint(0, HEIGHT) 9 | y = randint(0, WIDTH) 10 | r = randint(50, 100) 11 | oval(x, y, r, r) 12 | -------------------------------------------------------------------------------- /Glyphs/Change sidebearings (with dialogue).py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Change sidebearings (with dialogue) 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Increase/decrease sidebearings of selected glyphs by adding specific value. 5 | """ 6 | 7 | # based on Rainer Erich Scheichelbauer’s script Change Metrics by Percentage 8 | 9 | import vanilla 10 | import GlyphsApp 11 | 12 | class ChangeSidebearings(object): 13 | def __init__(self): 14 | self.w = vanilla.FloatingWindow((200, 120), "Change sidebearings of selected layers", autosaveName="com.pythonworkshop.ChangeSidebearings.mainwindow") 15 | 16 | self.w.text_LSB = vanilla.TextBox((15, 12+2, 90, 14), "Change LSB by", sizeStyle='small') 17 | self.w.LSB = vanilla.EditText((105, 12, -15, 15+3), "10.0", sizeStyle = 'small') 18 | 19 | self.w.text_RSB = vanilla.TextBox((15, 42+2, 90, 14), "Change RSB by", sizeStyle='small') 20 | self.w.RSB = vanilla.EditText((105, 42, -15, 15+3), "10.0", sizeStyle = 'small') 21 | 22 | self.w.runButton = vanilla.Button((15, 82-1, -15, 19), "Change", sizeStyle='small', callback=self.ChangeSidebearingsMain) 23 | 24 | self.w.setDefaultButton(self.w.runButton) 25 | 26 | try: 27 | self.LoadPreferences() 28 | except: 29 | pass 30 | 31 | self.w.open() 32 | 33 | def SavePreferences(self, sender): 34 | try: 35 | Glyphs.defaults["com.pythonworkshop.ChangeSidebearings.LSB"] = self.w.LSB.get() 36 | Glyphs.defaults["com.pythonworkshop.ChangeSidebearings.RSB"] = self.w.RSB.get() 37 | except: 38 | return False 39 | 40 | return True 41 | 42 | def LoadPreferences(self): 43 | try: 44 | self.w.LSB.set(Glyphs.defaults["com.pythonworkshop.ChangeSidebearings.LSB"]) 45 | self.w.RSB.set(Glyphs.defaults["com.pythonworkshop.ChangeSidebearings.RSB"]) 46 | except: 47 | return False 48 | 49 | return True 50 | 51 | def ChangeSidebearingsMain(self, sender): 52 | selectedLayers = Glyphs.font.selectedLayers 53 | 54 | changeLSB = int(self.w.LSB.get()) 55 | changeRSB = int(self.w.RSB.get()) 56 | 57 | if changeLSB: 58 | for layer in selectedLayers: 59 | layer.LSB += changeLSB 60 | 61 | if changeRSB: 62 | for layer in selectedLayers: 63 | layer.RSB += changeRSB 64 | 65 | if not self.SavePreferences(self): 66 | print "Note: could not write preferences." 67 | 68 | 69 | ChangeSidebearings() 70 | -------------------------------------------------------------------------------- /Glyphs/Change sidebearings.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Change sidebearings 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Increase/decrease sidebearings of selected glyphs by adding specific value. 5 | """ 6 | 7 | import GlyphsApp 8 | 9 | changeLSB = 200 10 | changeRSB = 200 11 | 12 | font.disableUpdateInterface() 13 | 14 | for layer in Glyphs.font.selectedLayers: 15 | thisGlyph = layer.parent 16 | thisGlyph.beginUndo() 17 | layer.LSB += changeLSB 18 | layer.width += changeRSB 19 | thisGlyph.endUndo() 20 | 21 | font.enableUpdateInterface() 22 | -------------------------------------------------------------------------------- /Glyphs/Chopper.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Chopper 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Chop all contours in all selected layers into individual segments. 5 | """ 6 | 7 | import GlyphsApp 8 | 9 | font = Glyphs.font 10 | 11 | selectedLayers = font.selectedLayers 12 | 13 | font.disableUpdateInterface() 14 | 15 | for layer in selectedLayers: 16 | thisGlyph = layer.parent 17 | thisGlyph.beginUndo() 18 | 19 | tempPaths = [] 20 | for path in layer.paths: 21 | for segment in path.segments: 22 | tempPath = GSPath() 23 | tempNodes = [] 24 | for node in path.nodes: 25 | if node.position in segment: 26 | tempNodes.append(node) 27 | tempPath.nodes = tempNodes 28 | tempPath.segments = [segment] 29 | tempPaths.append(tempPath) 30 | 31 | layer.paths = tempPaths 32 | 33 | thisGlyph.endUndo() 34 | 35 | font.enableUpdateInterface() 36 | -------------------------------------------------------------------------------- /Glyphs/Guideliner - add.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Guideliner - add 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Delete all global guidelines from current master and add new ones stored in a list in this script. 5 | """ 6 | 7 | # list of global guides to add 8 | # (x,y,angle) 9 | 10 | globalGuidelines = [ 11 | (0,100,0), 12 | (0,200,0), 13 | (0,300,0), 14 | (0,400,0), 15 | (0,500,0), 16 | (0,600,0), 17 | (0,700,0), 18 | (0,800,0), 19 | (0,900,0), 20 | (0,1000,0), 21 | (400,400,15), 22 | (400,400,30), 23 | (400,400,45), 24 | (400,400,60), 25 | (400,400,75), 26 | (400,400,90), 27 | (400,400,105), 28 | (400,400,120), 29 | (400,400,135), 30 | (400,400,150), 31 | (400,400,165) 32 | ] 33 | 34 | import GlyphsApp 35 | 36 | font = Glyphs.font 37 | 38 | font.disableUpdateInterface() 39 | 40 | # delete guidelines 41 | font.selectedFontMaster.guideLines = [] 42 | 43 | # add guidelines 44 | for x,y,angle in globalGuidelines: 45 | guide = GSGuideLine() 46 | guide.position = (x,y) 47 | guide.angle = angle 48 | font.selectedFontMaster.guideLines.append(guide) 49 | 50 | font.enableUpdateInterface() 51 | -------------------------------------------------------------------------------- /Glyphs/Guideliner - remove.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Guideliner - remove 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Delete all global guidelines. 5 | """ 6 | 7 | import GlyphsApp 8 | 9 | font = Glyphs.font 10 | 11 | font.disableUpdateInterface() 12 | 13 | # delete all global guidelines 14 | font.selectedFontMaster.guideLines = [] 15 | 16 | font.enableUpdateInterface() 17 | -------------------------------------------------------------------------------- /Glyphs/Monospacer.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Monospacer 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Extend width of all selected glyphs while increasing RSB and LSB equally. 5 | """ 6 | 7 | import GlyphsApp 8 | 9 | font = Glyphs.font 10 | selectedLayers = font.selectedLayers 11 | 12 | font.disableUpdateInterface() 13 | 14 | newWidth = 610 15 | 16 | for layer in selectedLayers: 17 | thisGlyph = layer.parent 18 | thisGlyph.beginUndo() 19 | layer.LSB += float(newWidth - layer.width) / 2 20 | # no need to set RSB if width is set 21 | layer.width = newWidth 22 | thisGlyph.endUndo() 23 | 24 | font.enableUpdateInterface() 25 | -------------------------------------------------------------------------------- /Glyphs/Print glyph info.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Print glyph info 2 | 3 | __doc__ = """ 4 | Prints name, unicode, LSB, RSB, and width for all selected glyphs 5 | """ 6 | 7 | import GlyphsApp 8 | 9 | print "name | unicode | LSB | RSB | width" 10 | 11 | for layer in Glyphs.font.selectedLayers: 12 | print "%s | %s | %d | %d | %d" % (layer.parent.name, layer.parent.unicode, layer.LSB, layer.RSB, layer.width) 13 | -------------------------------------------------------------------------------- /Glyphs/TODO.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | - [ ] print names and sidebearings for selected glyphs 4 | - [ ] increase sidebearings by X units 5 | - [ ] increase sidebearings by X units with a dialogue 6 | - [ ] make selected glyphs monospaced, distribute the change of width equally on both sides (monospacer) 7 | - [ ] chop curve into into individual segments (chopper) 8 | - [ ] add/delete global guidelines -------------------------------------------------------------------------------- /Glyphs/template.py: -------------------------------------------------------------------------------- 1 | #MenuTitle: Script name 2 | # -*- coding: utf-8 -*- 3 | __doc__=""" 4 | Doc string to describe what is the script supposed to do 5 | """ 6 | 7 | # your code 8 | 9 | import GlyphsApp 10 | 11 | font = Glyphs.font 12 | -------------------------------------------------------------------------------- /Presentation/Python workshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrBrezina/python-workshop/1b1fcd6689e4ed3cd05a1bc2045911735b65c288/Presentation/Python workshop.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Python workshop for MATD students 3 | 4 | ## Students to-do list 5 | 6 | - [ ] install [Atom](http://atom.io) 7 | - [ ] install *Script* package via `Atom > Preferences > Install` 8 | - [ ] install [DrawBot](http://drawbot.readthedocs.org) 9 | - [ ] install free trial of [Gitbox](http://gitboxapp.com) or [GitHub app for Mac](https://mac.github.com) or [GitHub app for Windows](https://windows.github.com) 10 | - [ ] set up an account on GitHub 11 | 12 | ## Agenda 13 | 14 | A very brief intro to Python 2.x, its use in font engineering and type design. Optionally, an intro into Git. 15 | 16 | ### Thursday morning (9:30–13:00) 17 | 18 | - **basic concepts** 19 | - expressions 20 | - variables 21 | - types 22 | - mutable vs. immutable types 23 | - methods 24 | - assignment 25 | - commands 26 | - blocks (indentation) 27 | - **environments** (Python interactive mode, Python command, Atom with Script package, font editors, …) 28 | - note on naming variables 29 | - **basic types** (`None`, `int` (integer), `float`, `bool` (boolean), `tuple`) 30 | - **arithmetic operations** (`+`, `-`, `*`, `/`, `%`, `()`), priority 31 | - **comparisons** (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`) 32 | - **boolean operations** (`and`, `or`, `not`, `()`) 33 | - **basic commands** 34 | - assignments (`=`, `+=`, `-=`) 35 | - print 36 | - comment 37 | - ☕️ 38 | - **conditionals (`if`, `elif`, `else`)** 39 | - note on indentation 40 | - nested conditionals 41 | - **strings (`str`)** 42 | - accessing items in strings 43 | - string operations (`+`, `*`) 44 | - escape and special characters (`\"`, `\n`, `\t`, `\\`) 45 | - multi-line strings 46 | - unicode strings 47 | - formatting strings (also with a tuple) 48 | - string methods (`startswith`, `endswith`, `join`, `split`) 49 | - **tuples (`tuple`)** 50 | - accessing items in tuples 51 | - modifying tuples 52 | - packing, unpacking 53 | - error messages 54 | - **task:** try to code and run simple Python scripts in Atom 55 | 56 | ### Thursday afternoon (14:00–17:00) 57 | 58 | - **lists (`list`)** 59 | - creating lists 60 | - assignment does not create new list 61 | - accessing list items 62 | - nested lists 63 | - list operations (`+`, `*`, `in`) 64 | - list and string slicing 65 | - list methods (`append`, `extend`, `insert`, `sort`, `reverse`, `index`) 66 | - list functions (`sorted`, `reversed`, `sum`, `len`, `range`, `min`, `max`) 67 | - removing items from lists 68 | - **iterative loops (`for`)** 69 | - looping over list or range 70 | - nesting loops 71 | - loops with conditionals nested 72 | - using indexes 73 | - mention `while` loops 74 | - **task:** write scripts with loops 75 | - ☕️ 76 | - **dictionaries (`dict`)** 77 | - creating new dictionary 78 | - adding an item to a dict, changing an item 79 | - what can be a key 80 | - dictionary methods (`keys`, `values`, `items`) 81 | - iterating over dictionary (conversion to `list` happens) 82 | - testing if key exists (`in`) 83 | - **built-in functions** 84 | - re-typing (`int`, `str`, `list`, …) 85 | - `enumerate` 86 | - `zip` and creating `dict` from `lists` 87 | - `abs` 88 | - introduction to Drawbot 89 | - **task:** write scripts for DrawBot 90 | - 🍺🍟 91 | 92 | ### Friday morning (9:30–13:00) 93 | 94 | - **functions (`def`)** 95 | - parameters 96 | - returning value 97 | - **modules (`import`)** 98 | - note on name spaces 99 | - ☕️ 100 | - **task:** advanced code with DrawBot 101 | 102 | ### Friday afternoon (14:00–17:00) 103 | 104 | - introduction to Glyphs API 105 | - **task:** write scripts for Glyphs 106 | - ☕️ 107 | - **task:** write scripts for Glyphs 108 | - explain [Git](http://git-scm.org) 109 | - **task:** work with this repo 110 | - clone this repo 111 | - create your own forder within the repo 112 | - push the changes to the GitHub 113 | - pull other students’ commits 114 | - 🚀 115 | 116 | #### Additional topics not covered in this workshop 117 | 118 | - `set` type 119 | - `while` loop 120 | - `break`, `else`, `continue` statements used with loops 121 | - dealing with exceptions (`try`, `except`) 122 | - `object` oriented programming 123 | 124 | ### Resources 125 | 126 | - [Python documentation](https://docs.python.org/2/) 127 | - [Robodocs](http://robodocs.readthedocs.org) 128 | - [Stack Overflow](http://stackoverflow.com) 129 | - [Robofab](http://robofab.com) 130 | - [Git](http://git-scm.com) 131 | - [GitHub](http://github.com) - hosting of Git repos – free public repos, large community 132 | - [BitBucket](http://bitbucket.org) – free private and public repos 133 | -------------------------------------------------------------------------------- /Shell/studentlist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | Print list of students. 5 | """ 6 | 7 | students = ["María", "Noel", "Norbert", "Fermín", "Becca", "Juan", "Thalia", "Najla", "Borna", "Ueli", "Vincenzo", "Anna", "Zeynep", "Sergio", "Sebastian", "John"] 8 | guests = ["Anna", "Zeynep"] 9 | 10 | print 11 | print "*" * 66 12 | print 13 | print "There should be %d students in the room." % len(students) 14 | print 15 | print "Namely (in alphabetical order):" 16 | 17 | for student in sorted(students): 18 | if student in guests: 19 | print " ", student, "(guest)" 20 | else: 21 | print " ", student 22 | 23 | print 24 | print "*" * 66 25 | print 26 | -------------------------------------------------------------------------------- /basics/1-1-first-script.py: -------------------------------------------------------------------------------- 1 | 2 | ### First script ### 3 | 4 | # simple example to demonstrate 5 | # basic Python syntax elements 6 | 7 | a = 25 # assignment, initializing variable a 8 | b = 3 # assignment, initializing variable b 9 | 10 | if a > 20: # conditional statement if 11 | # starting indented block 12 | a = 15 # assignment 13 | b = 4 14 | print "a is bigger than 20" # print statement 15 | # end of indented block 16 | 17 | else: 18 | # another indented block 19 | a = a + b + 1 # assigning result of an expression 20 | b = a - 5 # assigning result of an expression 21 | print "a is smaller than 20" 22 | # end of indented block 23 | 24 | print a # print statement 25 | print b 26 | -------------------------------------------------------------------------------- /basics/1-2-expressions.py: -------------------------------------------------------------------------------- 1 | 2 | # Expressions 3 | 4 | # calculate how much is: 12 times 13 5 | 6 | print 12 * 13 7 | 8 | # calculate: 11 times 123 minus 45 9 | 10 | print 11 * 123 - 45 11 | 12 | # calculate: 11 minus 45 times 123 13 | 14 | print (11 - 45) * 123 15 | 16 | # calculate: 27 divided by 3 17 | 18 | print 27 / 3 19 | 20 | # calculate: 5 divided by 2 21 | 22 | print 5 / 2 23 | 24 | # calculate: 5 divided by 2 and 5 modulo 2 25 | 26 | print 5 / 2 27 | print 5 % 2 28 | 29 | # compare: is 5 greater than 7? 30 | 31 | print 5 > 7 32 | 33 | # compare: does 6 equal 8? 34 | 35 | print 6 == 8 36 | 37 | # compare: is 37 less or equal than 47? 38 | 39 | print 37 <= 47 40 | -------------------------------------------------------------------------------- /basics/1-3-assignment.py: -------------------------------------------------------------------------------- 1 | 2 | ### Assignment ### 3 | 4 | # integer 5 | 6 | a = 12 7 | print a 8 | 9 | # changing the value 10 | a = 5 11 | print a 12 | 13 | # string 14 | 15 | A = "Random text" 16 | print A 17 | 18 | # float 19 | 20 | b = -2.5 21 | print b 22 | 23 | # reference assignment 24 | 25 | c = A 26 | print c 27 | a = c 28 | print a 29 | 30 | # assignments can chain 31 | 32 | a = b = c = 7422 33 | print a,b,c 34 | 35 | x = 7 36 | y = 11 37 | z = x**2 + y**2 38 | print z 39 | -------------------------------------------------------------------------------- /basics/2-1-integer.py: -------------------------------------------------------------------------------- 1 | 2 | ### Integer ### 3 | 4 | # calculate how much is: 12 times 13 5 | 6 | print 12 * 13 7 | 8 | # calculate: 11 times 123 minus 45 9 | 10 | print 11 * 123 - 45 11 | 12 | # calculate: 11 minus 45 times 123 13 | 14 | print (11 - 45) * 123 15 | 16 | # calculate: 27 divided by 3 17 | 18 | print 27 / 3 19 | 20 | # calculate: 5 divided by 2 21 | 22 | print 5/2 23 | 24 | # calculate: 5 divided by 2 and 5 modulo 2 25 | 26 | print 5/2, 5%2 27 | 28 | # compare: is 5 greater than 7? 29 | 30 | print 5 > 7 31 | 32 | # compare: does 6 equal 8? 33 | 34 | print 6 == 8 35 | 36 | # compare: is 37 less or equal than 47? 37 | 38 | print 37 <= 47 39 | 40 | # compare: is 4 equal or less than 1? 41 | 42 | print 4 =< 1 # invalid syntax ! 43 | 44 | # compare: is 2^4 greater than 15? 45 | 46 | print 2**4 > 15 47 | -------------------------------------------------------------------------------- /basics/2-2-float.py: -------------------------------------------------------------------------------- 1 | 2 | ### Float ### 3 | 4 | print 3.1 / 2 5 | 6 | print 5.1 % 3 7 | 8 | print 3.0 / 2 9 | 10 | print 5.0 % 3 11 | 12 | print 5 + 6.0 13 | 14 | print 5 / 0 15 | 16 | 17 | # Comparing values and identity 18 | 19 | print 6.0 == 6.1 20 | 21 | print 6 == 6.0 # compares value 22 | 23 | print 6 is 6.0 # compares value AND type 24 | -------------------------------------------------------------------------------- /basics/2-3-list.py: -------------------------------------------------------------------------------- 1 | 2 | ### List ### 3 | 4 | # list of numbers 5 | 6 | x = [1,2,3,5,4] 7 | print x 8 | 9 | x.append(0) 10 | print x 11 | 12 | print x[1] 13 | print x[0] 14 | print x[2:] 15 | print x[:-1] 16 | print x[2:4] 17 | 18 | # get length of a list 19 | print len(x) 20 | print len(x[2:4]) 21 | 22 | # sort list 23 | print x 24 | x.sort() 25 | print x 26 | 27 | # list of strings 28 | 29 | y = ["John", "Jane", "Charlene", "Paul"] 30 | print y 31 | y.reverse() 32 | print y 33 | print sorted(y) 34 | print y 35 | 36 | # list with mixed content 37 | 38 | a = [] # empty list 39 | a.append(0) # append item to a list 40 | a.append(-2.7) 41 | a.append("text") 42 | b = a # just a reference! 43 | b.append([1,2,3]) # appending list 44 | b.append(True) 45 | print a 46 | print a[3][1] # referring to the sublist 47 | print a[3] # referring to the sublist 48 | print len(a) # length 49 | print "here", a 50 | print len(a[3]) 51 | print 3 in a # test for presence of an item in the list 52 | print "text" in a 53 | print 3 in a[3] # presence in the sub-list 54 | print [1,2,3] in a 55 | print sorted([3,2,1]) in a 56 | del a[0] # delete first item 57 | print a 58 | del a[1:] # delete all, but first item 59 | print a 60 | 61 | # create a duplicate copy of a list, not just link 62 | 63 | c = list(a) 64 | 65 | # extending vs. appending 66 | 67 | print a 68 | a.append([1,2]) # append a list 69 | print a 70 | print c 71 | c.extend([1,2]) # append contents of a list 72 | print c 73 | 74 | print a 75 | a.insert(1, 76) # first goes the index, second is the item 76 | print a 77 | -------------------------------------------------------------------------------- /basics/2-4-string.py: -------------------------------------------------------------------------------- 1 | 2 | ### String ### 3 | 4 | myString = "Test string" 5 | myString2 = 'Test string' 6 | longString = """ 7 | this is often 8 | used for documentation 9 | or just anywhere where more lines 10 | are needed 11 | """ 12 | longString2 = ''' 13 | another way 14 | to write the same 15 | ''' 16 | 17 | myNewString = "Hi in Czech is 'Ahoj'" 18 | myNewString = myNewString + "!" # concatenation 19 | myNewString += "!!" 20 | print myNewString 21 | repetition = "ten" * 10 # repeat 10x 22 | print repetition 23 | myNewString[0] # first character 24 | print repetition[10] # eleventh character 25 | print repetition[0:3] # three characters, from index 0 to index 3 26 | print repetition[2:4] # two characters,from index 0 to index 4 27 | print repetition[:6] # first six characters 28 | print myNewString[-1] # last character 29 | print myNewString[5:] # only characters from sixth on 30 | print len(myNewString) # string length 31 | 32 | # comparisons 33 | print "abcd" > "abc" # is longer 34 | print "abcd" < "abce" # is same, but comparing alphabetically 35 | a = "password" # assignment 36 | print a == "password" # equality test 37 | 38 | # case conversions 39 | name = "dan" 40 | surname = "RhaTigan" 41 | print name.capitalize() 42 | print surname.upper() 43 | print surname.lower() 44 | print name.capitalize() + " " + surname.lower().capitalize() 45 | 46 | # find & replace 47 | text = "When you are finished changing, you are finished. (Ben Franklin)" 48 | print text 49 | print text.find("you") # index of the first occurrence 50 | text = text.replace("you", "we") # substitutes second argument for first 51 | print text 52 | 53 | # content tests 54 | print "textual56".isdigit() 55 | print "textual56".isalpha() 56 | print "56".isdigit() 57 | -------------------------------------------------------------------------------- /basics/2-5-tuple.py: -------------------------------------------------------------------------------- 1 | 2 | ### Tuple ### 3 | 4 | record = () # empty tuple 5 | record = ("Gerry") # error 6 | record = ("Gerry", ) # one-item tuple (the comma is necessary) 7 | record = ("Gerry", "Leonidas", "senior lecturer") 8 | print record[1].upper() 9 | record += ("Ian",) # extending tuple 10 | print record 11 | record[1] = "David" # error, tuples are immutable (items cannot be changed directly) 12 | print record[0:3]*3 13 | print record[-1] + record[-3] # result is string 14 | print record[0:2] + record[-1] # error, second operand is a string 15 | first, second = record[0:2] # sub-tuple is unpacked to two variables 16 | print first 17 | print second 18 | print len(record) # the length of a tuple 19 | 20 | record = ("Gerasimos", )+record[1:] 21 | print record 22 | -------------------------------------------------------------------------------- /basics/2-6-dictionary.py: -------------------------------------------------------------------------------- 1 | 2 | ### Dictionary ### 3 | 4 | person = {"name":"David", "height": 182, "weight": 72 } 5 | print person["name"] 6 | person["name"] = "John" 7 | key = "weight" 8 | print person 9 | person[key] += 10 10 | person["age"] = 28 11 | print person 12 | print person["height"], person["age"] 13 | equipment = {"name":"book", "pages":200} 14 | person["stuff"] = equipment # nesting 15 | print person 16 | print person.keys() # list of keys 17 | print person.values() # list of values 18 | print person.items() # list of tuples (key, value) 19 | print person["stuff"]["name"] 20 | 21 | person.has_key("name") 22 | "name" in person # not clear, perhaps 23 | "name" in person.keys() # more readable 24 | --------------------------------------------------------------------------------