Guest User

Untitled

a guest
Feb 19th, 2018
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.70 KB | None | 0 0
  1. # U08_Ex17_ImageManipulation.py
  2. #
  3. # Author: Bill Montana
  4. # Course: Coding for OOP
  5. # Section: A3
  6. # Date: 24 Nov 2017
  7. # IDE: PyCharm Community Edition
  8. #
  9. # Assignment Info
  10. # Exercise: 17
  11. # Source: Python Programming
  12. # Chapter: 8
  13. #
  14. # Problem Description
  15. # Write a program that provides a vertical menu on right side of window for image manipulation algorithms.
  16. # The menu should include Load Image, Save Image, and Quit buttons at the top. Image manipulation algorithm
  17. # buttons should be displayed below, separated from the top buttons by a separator line. A vertical separator
  18. # line should divide the menu from image space. The program should accept mouse clicks for the buttons. When
  19. # image manipulation algorithm buttons are clicked, their fill color should toggle. Clicking again will
  20. # untoggle and reverse the effect. The user should also be allowed to press Ctrl/Command O to Load Image,
  21. # Ctrl/Command S to Save Image, and the Esc key to quit. The image should be displayed centered in the image
  22. # space. The image may need to be scaled if it is too big for the image space. The GraphWin should be 800
  23. # pixels in height and 1000 pixels in width (200 pixels are reserved for menu space).
  24. #
  25. # Program Description
  26. # Adds a menu to code for exercises 14 and 15.
  27. # Buttons: Load Image, Save Image, Quit, Grayscale, Negative, user defined buttons
  28. # Esc can be pressed to quit.
  29. #
  30. # Algorithm (pseudocode)
  31. # global variables: button objects, button constant values (e.g. LOADIMAGE = 0), image height and width, win
  32. # create GraphWin
  33. #
  34. # main:
  35. # call drawMenu()
  36. # enter into event loop, looking for mouse clicks and key presses
  37. # key press: call handleKeys()
  38. # mouse clicks: call handleClicks()
  39. #
  40. # drawMenu:
  41. # at top of program, define global variables for button locations (they get set here)
  42. # set standard height, width, and separation space for buttons
  43. # draw three buttons at top, a separator line, and at least five buttons below
  44. # set global button object variables as you go
  45. # set button fill color to light gray
  46. # draw button text for each button (bottom three will be blank)
  47. # draw a vertical separator line between menu and image space
  48. #
  49. # handleKeys:
  50. # O -> loadImage()
  51. # S -> saveImage()
  52. #
  53. # handleClicks:
  54. # call buttonClicked()
  55. # take action based on return value (see global constants)
  56. # 0 -> do nothing
  57. # LOADIMAGE -> loadImage()
  58. # SAVEIMAGE -> saveImage()
  59. # Quit -> quit
  60. # GRAYSCALE -> toggle fill; grayscale()
  61. # NEGATIVE -> toggle fill; negative()
  62. # Other Effects toggle fill and call appropriate user-defined functions
  63. #
  64. # buttonClicked:
  65. # test to see if pt is on a button; return appropriate global constant (or zero)
  66. # if ptX is within x coords AND ptY is within y coords for this button return global var for button
  67. # otherwise, return 0
  68. #
  69. # loadImage:
  70. # get the image file using askopenfilename from tkinter.filedialog
  71. # if no selection, return
  72. # open the file
  73. # get width and height of image and store to global variables
  74. # display image
  75. #
  76. # saveImage:
  77. # save image using asksavefilename from tkinter.filedialog
  78. # if canceled, return
  79. # save the file
  80. #
  81. # grayscale:
  82. # if already grayscale
  83. # revert to original pixels
  84. # otherwise
  85. # save state
  86. # convert pixels to grayscale
  87. # using image width and height, get pixel rgb values and set to grayscale
  88. # display image
  89. #
  90. # negative:
  91. # convert pixels to color negative
  92. # using image width and height, get pixel rgb values and set to color negative
  93. # display image
  94. #
  95. # other effects:
  96. # if effect already applied
  97. # revert to original pixels
  98. # otherwise
  99. # save state
  100. # convert pixels to effect
  101. # using image width and height, get pixel rgb values and set to effect
  102. # display image
  103.  
  104.  
  105. from graphics import *
  106. from tkinter.filedialog import asksaveasfilename, askopenfilename
  107.  
  108. # global variables: button objects, button constant values (e.g. LOADIMAGE = 0), image height and width, win
  109. buttonLoad = Rectangle(Point(0,0), Point(1,1)); LOADIMAGE = 1; buttonLoadActive = 0
  110. buttonSave = Rectangle(Point(0,0), Point(1,1)); SAVEIMAGE = 2; buttonSaveActive = 0
  111. buttonQuit = Rectangle(Point(0,0), Point(1,1)); QUIT = 3; buttonQuitActive = 0
  112. buttonGray = Rectangle(Point(0,0), Point(1,1)); GRAYSCALE = 4; buttonGrayActive = 0; isGrayscale = False
  113. buttonNeg = Rectangle(Point(0,0), Point(1,1)); NEGATIVE = 5; buttonNegActive = 0; isNegative = False
  114. buttonOther1 = Rectangle(Point(0,0), Point(1,1)); OTHER1 = 6; buttonOther1Active = 0; isOther1 = False
  115. buttonOther2 = Rectangle(Point(0,0), Point(1,1)); OTHER2 = 7; buttonOther2Active = 0; isOther2 = False
  116. buttonOther3 = Rectangle(Point(0,0), Point(1,1)); OTHER3 = 8; buttonOther3Active = 0; isOther3 = False
  117. buttonIdleFill = '#cccccc'; buttonActiveFill = '#999999'
  118.  
  119. # create GraphWin
  120. win = GraphWin('Image Manipulation', 1000, 800)
  121. midX = (win.getWidth() - 200) / 2; midY = win.getHeight() / 2
  122. buttonHeight = 30; buttonWidth = 180; buttonSep = 10; menuLeft = win.getWidth() - 200
  123. img = Image(Point(midX, midY), 800, 800); imgHeight = 0; imgWidth = 0
  124. imgOrig = img
  125.  
  126. # main:
  127. def main():
  128. # call drawMenu()
  129. drawMenu()
  130.  
  131. # enter into event loop, looking for mouse clicks and key presses
  132. while True:
  133. # key press: call handleKeys()
  134. key = win.checkKey()
  135. if key == "Escape": # loop exit
  136. break
  137.  
  138. if key:
  139. handleKeys(key)
  140.  
  141. # mouse clicks: call handleClicks()
  142. pt = win.checkMouse()
  143. if pt:
  144. if not handleClicks(pt):
  145. break
  146.  
  147. win.close()
  148.  
  149. # drawMenu:
  150. def drawMenu():
  151. # at top of program, define global variables for button locations (they get set here)
  152. global buttonLoad, buttonSave, buttonQuit, buttonGray, buttonNeg, buttonOther1, buttonOther2, buttonOther3
  153.  
  154. # set standard height, width, and separation space for buttons
  155. # these are global variables
  156.  
  157. # draw three buttons at top, a separator line, and at least five buttons below
  158. # set global button object variables as you go
  159. # set button fill color to light gray
  160. # draw button text for each button (bottom three will be blank)
  161. # draw a vertical separator line between menu and image space
  162. Line(Point(menuLeft, 0), Point(menuLeft, win.getHeight())).draw(win)
  163.  
  164. # Load Image button
  165. topLeft = Point(menuLeft + buttonSep, buttonSep)
  166. botRight = Point(menuLeft + buttonSep + buttonWidth, buttonSep + buttonHeight)
  167. buttonLoad = Rectangle(topLeft, botRight)
  168. buttonLoad.setFill(color=buttonIdleFill)
  169. buttonLoad.draw(win)
  170. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  171. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Load Image').draw(win)
  172.  
  173. # Save Image button
  174. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  175. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  176. buttonSave = Rectangle(topLeft, botRight)
  177. buttonSave.setFill(color=buttonIdleFill)
  178. buttonSave.draw(win)
  179. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  180. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Save Image').draw(win)
  181.  
  182. # Quit button
  183. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  184. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  185. buttonQuit = Rectangle(topLeft, botRight)
  186. buttonQuit.setFill(color=buttonIdleFill)
  187. buttonQuit.draw(win)
  188. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  189. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Quit').draw(win)
  190.  
  191. # Horizontal separator line
  192. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  193. botRight = Point(botRight.getX(), botRight.getY() + buttonSep)
  194. Line(Point(topLeft.getX() - buttonSep, topLeft.getY()),
  195. Point(botRight.getX() + buttonSep ,botRight.getY())).draw(win)
  196.  
  197. # Grayscale button
  198. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonSep)
  199. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  200. buttonGray = Rectangle(topLeft, botRight)
  201. buttonGray.setFill(color=buttonIdleFill)
  202. buttonGray.draw(win)
  203. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  204. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Grayscale').draw(win)
  205.  
  206. # Negative button
  207. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  208. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  209. buttonNeg = Rectangle(topLeft, botRight)
  210. buttonNeg.setFill(color=buttonIdleFill)
  211. buttonNeg.draw(win)
  212. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  213. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Negative').draw(win)
  214.  
  215. # Other Effect 1 button
  216. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  217. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  218. buttonOther1 = Rectangle(topLeft, botRight)
  219. buttonOther1.setFill(color=buttonIdleFill)
  220. buttonOther1.draw(win)
  221. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  222. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Lower Brightness of Picture').draw(win)
  223.  
  224. # Other Effect 2 button
  225. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  226. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  227. buttonOther2 = Rectangle(topLeft, botRight)
  228. buttonOther2.setFill(color=buttonIdleFill)
  229. buttonOther2.draw(win)
  230. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  231. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'Raise Brightness of Picture').draw(win)
  232.  
  233. # Other Effect 3 button
  234. topLeft = Point(topLeft.getX(), topLeft.getY() + buttonHeight + buttonSep)
  235. botRight = Point(botRight.getX(), botRight.getY() + buttonHeight + buttonSep)
  236. buttonOther3 = Rectangle(topLeft, botRight)
  237. buttonOther3.setFill(color=buttonIdleFill)
  238. buttonOther3.draw(win)
  239. Text(Point(topLeft.getX() + (botRight.getX() - topLeft.getX()) / 2,
  240. topLeft.getY() + (botRight.getY() - topLeft.getY()) / 2), 'No Blue').draw(win)
  241.  
  242. # handleKeys:
  243. def handleKeys(key):
  244. """
  245. Handles keys pressed to execute menu options (except ESC)
  246. :param key: str -> key that was pressed
  247. :return: None
  248. """
  249. # O -> loadImage()
  250. if key == "o" or key == "O":
  251. loadImage()
  252.  
  253. # S -> saveImage()
  254. if key == "s" or key == "S":
  255. saveImage()
  256.  
  257. # handleClicks:
  258. def handleClicks(pt):
  259. """
  260. Handles valid mouse clicks, executing button actions
  261. :param pt: Point -> location of mouse click as a Point object
  262. :return: int -> 0 if Quit button clicked; otherwise 1
  263. """
  264. global buttonLoadActive, buttonSaveActive, buttonQuitActive, buttonGrayActive, \
  265. buttonNegActive, buttonOther1Active, buttonOther2Active, buttonOther3Active
  266. # call buttonClicked()
  267. clickResult = buttonClicked(pt)
  268. # take action based on return value (see global constants)
  269. # 0 -> do nothing
  270. # LOADIMAGE -> loadImage()
  271. # SAVEIMAGE -> saveImage()
  272. # Quit -> quit
  273. # GRAYSCALE -> toggle fill; grayscale()
  274. # NEGATIVE -> toggle fill; negative()
  275. # Other Effects toggle fill and call appropriate user-defined functions
  276. if clickResult == LOADIMAGE:
  277. buttonLoadActive = buttonFillToggle(buttonLoad, buttonLoadActive)
  278. loadImage()
  279. buttonLoadActive = buttonFillToggle(buttonLoad, buttonLoadActive)
  280. if clickResult == SAVEIMAGE:
  281. buttonSaveActive = buttonFillToggle(buttonSave, buttonSaveActive)
  282. saveImage()
  283. buttonSaveActive = buttonFillToggle(buttonSave, buttonSaveActive)
  284. if clickResult == QUIT:
  285. buttonQuitActive = buttonFillToggle(buttonQuit, buttonQuitActive)
  286. return 0
  287. if clickResult == GRAYSCALE:
  288. buttonGrayActive = buttonFillToggle(buttonGray, buttonGrayActive)
  289. grayscale()
  290. if clickResult == NEGATIVE:
  291. buttonNegActive = buttonFillToggle(buttonNeg, buttonNegActive)
  292. negative()
  293. if clickResult == OTHER1:
  294. buttonOther1Active = buttonFillToggle(buttonOther1, buttonOther1Active)
  295. other1()
  296. if clickResult == OTHER2:
  297. buttonOther2Active = buttonFillToggle(buttonOther2, buttonOther2Active)
  298. other2()
  299. if clickResult == OTHER3:
  300. buttonOther3Active = buttonFillToggle(buttonOther3, buttonOther3Active)
  301. other3()
  302. return 1
  303.  
  304. # buttonClicked:
  305. def buttonClicked(pt):
  306. """
  307. Checks to see if a button was clicked
  308. :param pt: Point -> location of mouse click as a Point object
  309. :return: int -> global constant matching button clicked; zero if none
  310. """
  311. # test to see if pt is on a button; return appropriate global constant (or zero)
  312. # if ptX is within x coords AND ptY is within y coords for this button return global var for button
  313. # otherwise, return 0
  314. buttonLoadCenter = buttonLoad.getCenter()
  315. buttonSaveCenter = buttonSave.getCenter()
  316. buttonQuitCenter = buttonQuit.getCenter()
  317. buttonGrayCenter = buttonGray.getCenter()
  318. buttonNegCenter = buttonNeg.getCenter()
  319. buttonOther1Center = buttonOther1.getCenter()
  320. buttonOther2Center = buttonOther2.getCenter()
  321. buttonOther3Center = buttonOther3.getCenter()
  322.  
  323. retVal = 0
  324.  
  325. if buttonLoadCenter.getX() - buttonWidth / 2 < pt.getX() < buttonLoadCenter.getX() + buttonWidth / 2 and \
  326. buttonLoadCenter.getY() - buttonHeight / 2 < pt.getY() < buttonLoadCenter.getY() + buttonHeight / 2:
  327. retVal = LOADIMAGE
  328.  
  329. elif buttonSaveCenter.getX() - buttonWidth / 2 < pt.getX() < buttonSaveCenter.getX() + buttonWidth / 2 and \
  330. buttonSaveCenter.getY() - buttonHeight / 2 < pt.getY() < buttonSaveCenter.getY() + buttonHeight / 2:
  331. retVal = SAVEIMAGE
  332.  
  333. elif buttonQuitCenter.getX() - buttonWidth / 2 < pt.getX() < buttonQuitCenter.getX() + buttonWidth / 2 and \
  334. buttonQuitCenter.getY() - buttonHeight / 2 < pt.getY() < buttonQuitCenter.getY() + buttonHeight / 2:
  335. retVal = QUIT
  336.  
  337. elif buttonGrayCenter.getX() - buttonWidth / 2 < pt.getX() < buttonGrayCenter.getX() + buttonWidth / 2 and \
  338. buttonGrayCenter.getY() - buttonHeight / 2 < pt.getY() < buttonGrayCenter.getY() + buttonHeight / 2:
  339. retVal = GRAYSCALE
  340.  
  341. elif buttonNegCenter.getX() - buttonWidth / 2 < pt.getX() < buttonNegCenter.getX() + buttonWidth / 2 and \
  342. buttonNegCenter.getY() - buttonHeight / 2 < pt.getY() < buttonNegCenter.getY() + buttonHeight / 2:
  343. retVal = NEGATIVE
  344.  
  345. elif buttonOther1Center.getX() - buttonWidth / 2 < pt.getX() < buttonOther1Center.getX() + buttonWidth / 2 and \
  346. buttonOther1Center.getY() - buttonHeight / 2 < pt.getY() < buttonOther1Center.getY() + buttonHeight / 2:
  347. retVal = OTHER1
  348.  
  349. elif buttonOther2Center.getX() - buttonWidth / 2 < pt.getX() < buttonOther2Center.getX() + buttonWidth / 2 and \
  350. buttonOther2Center.getY() - buttonHeight / 2 < pt.getY() < buttonOther2Center.getY() + buttonHeight / 2:
  351. retVal = OTHER2
  352.  
  353. elif buttonOther3Center.getX() - buttonWidth / 2 < pt.getX() < buttonOther3Center.getX() + buttonWidth / 2 and \
  354. buttonOther3Center.getY() - buttonHeight / 2 < pt.getY() < buttonOther3Center.getY() + buttonHeight / 2:
  355. retVal = OTHER3
  356.  
  357. win.checkMouse()
  358. return retVal
  359.  
  360. def buttonFillToggle(button, buttonFillActive):
  361. """
  362. Toggles button fill color when clicked
  363. :param button: Rectangle -> button as a Rectangle object
  364. :param buttonFillActive: boolean -> current state of button
  365. :return: boolean -> toggled state for button
  366. """
  367. if buttonFillActive:
  368. button.setFill(color=buttonIdleFill)
  369. return False
  370. button.setFill(color=buttonActiveFill)
  371. return True
  372.  
  373. # loadImage:
  374. def loadImage():
  375. """
  376. Loads a user-specified image file from disk
  377. :return: None
  378. """
  379. global img, imgHeight, imgWidth, imgOrig
  380. img.undraw()
  381.  
  382. # get the image file using askopenfilename from tkinter.filedialog
  383. imgFile = askopenfilename(filetypes=(("GIF files", "*.gif"), ("All files", "*.*")))
  384.  
  385. # if no selection, return
  386. if not imgFile:
  387. return
  388.  
  389. # open the file
  390. img = Image(Point(midX, midY), imgFile)
  391. imgOrig = img.clone()
  392.  
  393. # get width and height of image and store to global variables
  394. imgWidth = img.getWidth(); imgHeight = img.getHeight()
  395.  
  396. # display color image
  397. img.draw(win)
  398.  
  399. # saveImage:
  400. def saveImage():
  401. """
  402. Saves the image to disk with user-specified path
  403. :return: None
  404. """
  405. global img
  406. # save image using asksavefilename from tkinter.filedialog
  407. imgNew = asksaveasfilename()
  408.  
  409. # if canceled, return
  410. # save the file
  411. if imgNew:
  412. img.save(imgNew)
  413.  
  414. # grayscale:
  415. def grayscale():
  416. """
  417. Converts image to grayscale (or back to previous state if already grayscale)
  418. :return: None
  419. """
  420. global img, imgOrig, isGrayscale
  421. # if already grayscale
  422. if isGrayscale:
  423. # revert to original pixels
  424. img = imgOrig.clone()
  425. isGrayscale = False
  426.  
  427. # otherwise
  428. else:
  429. # save state
  430. imgOrig = img.clone()
  431.  
  432. # convert pixels to grayscale
  433. # using image width and height, get pixel rgb values and set to grayscale
  434. for i in range(imgWidth):
  435. for j in range(imgHeight):
  436. rgb = img.getPixel(i, j)
  437. grayAvg = int(sum(rgb) / 3)
  438. grayLum = int(round(0.21 * rgb[0] + 0.72 * rgb[1] + 0.07 * rgb[2]))
  439. grayLit = int((max(rgb) + min(rgb)) / 2)
  440. gray = grayLum
  441. img.setPixel(i, j, color_rgb(gray, gray, gray))
  442. isGrayscale = True
  443.  
  444. # display grayscale image
  445. img.undraw()
  446. img.draw(win)
  447.  
  448. # negative:
  449. def negative():
  450. """
  451. Converts image to color-negative
  452. :return: None
  453. """
  454. global img
  455. # convert pixels to color negative
  456. # using image width and height, get pixel rgb values and set to color negative
  457. for i in range(imgWidth):
  458. for j in range(imgHeight):
  459. rgb = img.getPixel(i, j)
  460. negR = 255 - rgb[0]; negG = 255 - rgb[1]; negB = 255 - rgb[2]
  461. img.setPixel(i, j, color_rgb(negR, negG, negB))
  462.  
  463.  
  464. # display color negative image
  465. img.undraw()
  466. img.draw(win)
  467.  
  468.  
  469. # other effects:
  470. # save state
  471. # convert pixels to effect
  472. # using image width and height, get pixel rgb values and set to effect
  473. # display image
  474. def other1():
  475. global img
  476. for x in range(imgWidth):
  477. for y in range(imgHeight):
  478. rgb = img.getPixel(x, y)
  479. r = int(rgb[0] * .5); g = int(rgb[1] * .5); b = int(rgb[2] * .5)
  480. img.setPixel(x, y, color_rgb(r, g, b))
  481. img.undraw()
  482. img.draw(win)
  483.  
  484.  
  485. # other effects:
  486. # save state
  487. # convert pixels to effect
  488. # using image width and height, get pixel rgb values and set to effect
  489. # display image
  490. def other2():
  491. global img
  492. for x in range(imgWidth):
  493. for y in range(imgHeight):
  494. rgb = img.getPixel(x, y)
  495. r = int(rgb[0] * 1.5); g = int(rgb[1] * 1.5); b = int(rgb[2] * 1.5)
  496. img.setPixel(x, y, color_rgb(r, g, b))
  497. img.undraw()
  498. img.draw(win)
  499.  
  500.  
  501. def other3():
  502. global img
  503. for x in range(imgWidth):
  504. for y in range(imgHeight):
  505. rgb = img.getPixel(x, y)
  506. r = rgb[0]; g = rgb[1]; b = 0
  507. img.setPixel(x, y, color_rgb(r, g, b))
  508. img.undraw()
  509. img.draw(win)
  510.  
  511.  
  512.  
  513.  
  514. if __name__ == '__main__':
  515. main()
Add Comment
Please, Sign In to add comment