Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Ring-Analyzer/ringAnalyzer.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
477 lines (356 sloc)
16.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""***************************************************************************************\ | |
PROGRAM NAME: ringAnalyzer.py | |
AUTHOR: Hannah Burrall | |
DATE: July 14th, 2017 | |
PROGRAM DESCRPTION: Program to place rings/atoms on an image and returns the center | |
position of each placed object | |
******************************************************************************************""" | |
#IMPORTS | |
from graphics import * | |
from Image import PIL | |
from buttonClass import Button | |
from ringClass import Ring | |
import matplotlib.pylab as plt | |
import numpy as np | |
#Set up main windown | |
win = GraphWin('Ring Analyzer', 1500, 1000) | |
def createBoard(win): | |
"""Creates program boad & instructions""" | |
#Create line diving instructions, toolbar, and image | |
divideLine1 = Line(Point(498, 0), Point(498, 1000)) | |
divideLine1.setWidth(2) | |
divideLine1.draw(win) | |
divideLine2 = Line(Point(0, 600), Point(498, 600)) | |
divideLine2.setWidth(2) | |
divideLine2.draw(win) | |
instructionLable = Text(Point(250, 25), 'Instructions') | |
instructionLable.setSize(20) | |
instructionLable.setStyle('italic') | |
instructionLable.draw(win) | |
toolLabel = Text(Point(250, 625), 'Toolbar') | |
toolLabel.setSize(20) | |
toolLabel.setStyle('italic') | |
toolLabel.draw(win) | |
imageLabel = Text(Point(1000, 500), 'Upload Image Here') | |
imageLabel.setSize(20) | |
imageLabel.setStyle('italic') | |
imageLabel.draw(win) | |
#INSTRUCTIONS | |
instruction1 = Text(Point(250, 75), "1.) Enter the name of the image file, then click ENTER. \ | |
\n Note that the file must be saved as .png \n (ex. filename.png)") | |
instruction1.draw(win) | |
entry1 = Entry(Point(250, 125), 25) | |
entry1.draw(win) | |
enterButton1 = Button(win, Point(425, 125), 80, 20, '#52E643', 'ENTER') | |
#Click enterButton, open the image, deactivate image | |
loop = True | |
while loop: | |
if enterButton1.clicked(win.getMouse()): | |
imageName = entry1.getText() | |
#Check user imput for name of file | |
if imageName != '' and imageName[-4:] == '.png': | |
loop = False | |
else: | |
errorMessage('Please enter a PNG image file. \n (ex. filename.png)') | |
enterButton1.activate() | |
#Upload image into window | |
myImage = Image(Point(1000, 500), imageName) | |
myImage.draw(win) | |
enterButton1.deactivate() | |
instruction2 = Text(Point(250, 190), "2.) Click on the buttons below to place an atom or ring center. \ | |
\n Double click to place the object on the image. \ | |
\n Then click FINISH when all of the objects have been placed.") | |
instruction2.draw(win) | |
#Create TOOLBAR | |
siButton = Button(win, Point(300, 700), 40, 25, '#1DAA43', 'Si') | |
oButton = Button(win, Point(400, 700), 40, 25, '#FF1D0F', 'O') | |
generalRing = Button(win, Point(100, 680), 140, 34, '#F8A61E', 'General Ring') | |
button4MR = Button(win, Point(100, 750), 140, 22, '#9E4BF6', '4 Membered Ring') | |
button5MR = Button(win, Point(100, 785), 140, 22, '#4BB0F6', '5 Membered Ring') | |
button6MR = Button(win, Point(100, 820), 140, 22, '#43C47F', '6 Membered Ring') | |
button7MR = Button(win, Point(100, 855), 140, 22, '#F9DE3E', '7 Membered Ring') | |
button8MR = Button(win, Point(100, 890), 140, 22, '#F94C3E', '8 Membered Ring') | |
button9MR = Button(win, Point(100, 925), 140, 22, '#F726E8', '9 Membered Ring') | |
removeButton = Button(win, Point(350, 845), 100, 25, '#F96A61', 'REMOVE') | |
doneButton = Button(win, Point(350, 800), 100, 25, '#41EFC9', 'DONE') | |
finishButton = Button(win, Point(350, 900), 120, 30, '#D4FF33', 'FINISH') | |
return [siButton, oButton, generalRing, button4MR, button5MR, button6MR, button7MR, button8MR, \ | |
button9MR, removeButton, finishButton, doneButton] | |
def checkDone(mousePress, doneButton): | |
"""Check if DONE button is clicked. If yes, return TRUE""" | |
if doneButton.clicked(mousePress): | |
return True | |
else: | |
return False | |
def resetButtons(buttonLst): | |
"""Activates buttons in buttonLst""" | |
for i in buttonLst: | |
i.activate() | |
def placeCenter(win, clickButton, doneButton, centerPtLst, genRingLst, buttonsLst, color, label): | |
"""Places a center when the corresponding button is pressed""" | |
#Deactivate other buttons (not DONE or the selected button) | |
deactButtons = buttonsLst[:-1] | |
deactButtons.remove(clickButton) | |
for i in deactButtons: | |
i.deactivate() | |
while clickButton.getOutlineColor() == 'yellow': | |
mousePress = win.getMouse() | |
if checkDone(mousePress, doneButton) == False: | |
centerPtLst.append(win.getMouse()) | |
ring = Ring(win, centerPtLst[-1], 10, color, label) | |
genRingLst.append(ring) | |
elif checkDone(mousePress, doneButton) == True: | |
resetButtons(buttonsLst) | |
def convertCenter2nm(pixelCenter, conversionFactor): | |
"""Converts center point in pixels to nanometers given facor in nm/pixels""" | |
x, y = pixelCenter.getX(), pixelCenter.getY() | |
return (x * conversionFactor, y * conversionFactor) | |
def plotDistribution(labelLst, colorLst, centerPointsLst): | |
"""Plots the distribution of atoms and ring sizes | |
Note: centerPointsLst is a nested list of all center points""" | |
datas = [] | |
for i in range(len(labelLst)): | |
datas.append({'label': labelLst[i], 'color': colorLst[i], 'height': len(centerPointsLst[i])}) | |
i = 0 | |
for data in datas: | |
plt.bar(i, data['height'],align='center',color=data['color']) | |
i += 1 | |
labels = [data['label'] for data in datas] | |
pos = [i for i in range(len(datas)) ] | |
plt.xticks(pos, labels) | |
plt.xlabel('Atom / Ring Size') | |
plt.ylabel('Number of Atoms / Rings') | |
plt.title('Distribution of Atoms / Ring Sizes') | |
plt.show() | |
def errorMessage(message): | |
"""Displays an error message in a new window""" | |
win = GraphWin('Error Message', 350, 175) | |
win.setBackground('#F75454') | |
error = Text(Point(win.getWidth()/2, win.getHeight()/2 - 50), 'Error') | |
error.setSize(30) | |
error.setStyle('bold') | |
error.draw(win) | |
text = Text(Point(win.getWidth()/2, win.getHeight()/2), message) | |
text.setFace('times roman') | |
text.draw(win) | |
#Press OK button to close the window | |
okButton = Button(win, Point(win.getWidth()/2, win.getHeight()/2 + 55), 100, 30, 'lightgrey', 'OK') | |
loop = True | |
while loop: | |
mousePress = win.getMouse() | |
if okButton.clicked(mousePress): | |
win.close() | |
loop = False | |
def alertMessage(message): | |
"""Displays an error message in a new window""" | |
win = GraphWin('Alert Message', 300, 100) | |
win.setBackground('#6FF3CC') | |
text = Text(Point(win.getWidth()/2, win.getHeight()/2 - 20), message) | |
text.setSize(11) | |
text.setFace('times roman') | |
text.draw(win) | |
okButton = Button(win, Point(win.getWidth()/2 - 70, win.getHeight()/2 + 30), 80, 25, 'lightgrey', 'OK') | |
okButton.setSizeText(10) | |
cancelButton = Button(win, Point(win.getWidth()/2 + 70, win.getHeight()/2 + 30), 80, 25, 'lightgrey', 'CANCEL') | |
cancelButton.setSizeText(10) | |
#Press OK button to continue with function or cancel to quit alert window | |
loop = True | |
while loop: | |
mousePress = win.getMouse() | |
if okButton.clicked(mousePress): | |
win.close() | |
loop = False | |
return True | |
elif cancelButton.clicked(mousePress): | |
win.close() | |
loop = False | |
return False | |
def checkDecimal(string): | |
"""Checks if the string has a decimal place""" | |
for i in string: | |
if i == '.': | |
return True | |
return False | |
def writeXYZ(filename, atomLst, centerLst): | |
"""Exports center poitision to an xyz file""" | |
#Convert nm -> angstroms | |
for i in range(len(centerLst)): | |
for j in range(len(centerLst[i])): | |
x, y = centerLst[i][j] | |
x = x*10 | |
y = y *10 | |
centerLst[i][j] = x, y | |
n = 0 #Varibale to count number of atoms | |
#Convert points from touple -> list | |
#Set z coordinate to 0 | |
for i in range(len(centerLst)): | |
for j in range(len(centerLst[i])): | |
centerLst[i][j] = list(centerLst[i][j]) | |
centerLst[i][j].append(0.00) | |
n += 1 #Update the count | |
#Round each point to 5 decimal places | |
for k in range(len(centerLst[i][j])): | |
centerLst[i][j][k] = round(centerLst[i][j][k], 5) | |
#Make each point the same length | |
while len(str(centerLst[i][j][k])) != 9: | |
centerLst[i][j][k] = str(centerLst[i][j][k]) + '0' | |
#Format the xyz file | |
with open(filename, 'w') as file: | |
file.write(str(n) + '\n') | |
for i in range(len(centerLst)): | |
for j in range(len(centerLst[i])): | |
file.write('\n' + str(atomLst[i])) | |
file.write(' ' + str(centerLst[i][j][0])) | |
file.write(' ' + str(centerLst[i][j][1])) | |
file.write(' ' + str(centerLst[i][j][2])) | |
def main(win): | |
"""The main code of the program""" | |
#Create the board & buttons | |
buttons = createBoard(win) | |
# Make a nested list for ring centers | |
centerLst = [] | |
for i in range(9): | |
centerLst.append([]) | |
#centerLst = [[Si Centers], [O], [General Ring], [4 Membered], [5 Membered], [6 Membered], | |
# [7 Membered], [8 Membered], [9 Membered]] | |
# Define Lists to be used later | |
colorLst = ['#1DAA43', '#FF1D0F', '#F8A61E', '#9E4BF6', '#4BB0F6', \ | |
'#43C47F', '#F9DE3E', '#F94C3E', '#F726E8'] #Colors for rings | |
labelLst = ['Si', 'O', 'GR', '4', '5', '6', '7', '8', '9'] #Labels for rings | |
ringLst = [] #List to store ring objects | |
#Check if a button is clicked & place rings until user clicks FINISH | |
loop1 = True | |
while loop1: | |
mousePress = win.getMouse() | |
#Place rings when button is clicked | |
for i in range(9): | |
if buttons[i].clicked(mousePress): | |
placeCenter(win, buttons[i], buttons[11], centerLst[i], \ | |
ringLst, buttons, colorLst[i], labelLst[i]) | |
#REMOVE atoms/rings when button is clicked | |
if buttons[9].clicked(mousePress): | |
removeRingsLst = [] | |
#Deactivate all buttons except REMOVE and DONE | |
for i in range(9): | |
buttons[i].deactivate() | |
buttons[10].deactivate() | |
#Select rings to remove | |
while buttons[9].getOutlineColor() == 'yellow': | |
mousePress = win.getMouse() | |
for i in ringLst: | |
if i.clicked(mousePress): | |
i.removeRing() | |
ringLst.remove(i) | |
#Remove center from corresponding list | |
center = i.getCenter() | |
for j in range(len(labelLst)): | |
if i.getLabel() == labelLst[j]: | |
centerLst[j].remove(center) | |
if checkDone(mousePress, buttons[11]) == True: | |
resetButtons(buttons) | |
#Input dimensions of photo when FINISH is clicked | |
elif buttons[10].clicked(mousePress): | |
if alertMessage('By clicking OK you will not be able \n to return to the image editing phase'): | |
#Exist fist while loop | |
loop1 = False | |
finishProgram = True | |
#Deactivate all buttons | |
for i in buttons: | |
i.deactivate() | |
else: | |
buttons[10].activate() | |
if finishProgram: | |
#Find pixel -> nm conversion factor | |
#Enter Pixel dimensions | |
instruction3 = Text(Point(250, 265), "3.) Enter the size of the image file in pixels. \ | |
\n Note that the entry must include a decimal point.\n(ex. 900.0 x 900.0 pixels)") | |
instruction3.draw(win) | |
#Enter nm dimensions | |
instruction4 = Text(Point(250, 360), "4.) Enter the size of the image file in nanometers (nm). \ | |
\n Note that the entry must include a decimal point.\n(ex. 5.0 x 5.0 nm)") | |
instruction4.draw(win) | |
#Set up entry boxes for user | |
nmBoxH = Entry(Point(282, 405), 7) | |
nmBoxH.draw(win) | |
text2 = Text(Point(237.5, 405), "X") | |
text2.draw(win) | |
txtLabel2 = Text(Point(330, 405), "nm") | |
txtLabel2.draw(win) | |
nmBoxW = Entry(Point(194, 405), 7) | |
nmBoxW.draw(win) | |
pixBoxH = Entry(Point(282, 310), 7) | |
pixBoxH.draw(win) | |
text = Text(Point(237.5, 310), "X") | |
text.draw(win) | |
txtLabel = Text(Point(340, 310), "pixels") | |
txtLabel.draw(win) | |
pixBoxW = Entry(Point(194, 310), 7) | |
pixBoxW.draw(win) | |
enterButton2 = Button(win, Point(425, 405), 85, 20, '#52E643', 'ENTER') | |
#Program dimensions enter button | |
loop2 = True | |
while loop2: | |
mousePress = win.getMouse() | |
if enterButton2.clicked(mousePress): | |
#enterButton2.deactivate() | |
#Sore values of dimensions | |
nmHeight = nmBoxH.getText() | |
nmWidth = nmBoxW.getText() | |
pixHeight = pixBoxH.getText() | |
pixWidth = pixBoxW.getText() | |
#Check the user input values are decimals | |
if checkDecimal(nmHeight) and checkDecimal(nmWidth) and checkDecimal(pixHeight) \ | |
and checkDecimal(pixWidth): | |
#Calculate average conversion factor | |
convtFact = (((float(nmWidth) / float(pixWidth)) + \ | |
(float(nmHeight) / float(pixHeight))) / 2) | |
#Finish the program after user has input dimensions | |
instruction5 = Text(Point(250, 450), "5.) Click on the buttons below to finish the program.") | |
instruction5.draw(win) | |
enterButton2.deactivate() | |
#Exit second while loop | |
loop2 = False | |
else: | |
errorMessage('Please enter dimensions as decimals. \n (ex. 900.0 x 900.0 pixels)') | |
enterButton2.activate() | |
#Create final buttons | |
exportButton = Button(win, Point(100, 520), 100, 40, '#FFEE33', 'Export to \n xyz File') | |
exportButton.setBoldText() | |
distButton = Button(win, Point(250, 520), 150, 40, '#33D6FF', 'Show Distribution \n Graph') | |
distButton.setBoldText() | |
quitButton = Button(win, Point(400, 520), 100, 40, '#FF3369', 'QUIT') | |
quitButton.setBoldText() | |
#List of labels | |
textLst = ['Si', 'O ', 'GR', '4M', '5M', '6M', '7M', '8M', '9M'] | |
#Print lists in pixels | |
#for i in range(len(textLst)): | |
# print(textLst[i] + ' Centers (pixels) = ' + str(centerLst[i])) | |
#Convert list of centers in pixels -> nm | |
for i in range(len(centerLst)): | |
for j in range(len(centerLst[i])): | |
centerLst[i][j] = convertCenter2nm(centerLst[i][j], convtFact) | |
#Print lists in nm | |
#for i in range(len(textLst)): | |
# print(textLst[i] + 'Centers (nm) = ' + str(centerLst[i])) | |
#Program last three buttons | |
loop3 = True | |
while loop3: | |
mousePress = win.getMouse() | |
if exportButton.clicked(mousePress): | |
#Export to xyz file (cords in Angstroms) | |
if alertMessage('Export coordinates to xyz file \n saved as ringAnalyzerCoordinates.txt'): | |
writeXYZ('ringAnalyzerCoordinates.txt', textLst, centerLst) | |
exportButton.deactivate() | |
else: | |
exportButton.activate() | |
elif distButton.clicked(mousePress): | |
#PLOT atom / ring distribution | |
plotDistribution(textLst, colorLst, centerLst) | |
distButton.deactivate() | |
elif quitButton.clicked(mousePress): | |
if alertMessage('Click OK to exit the program \n (The image file will not be saved)'): | |
win.close() | |
loop3 = False | |
else: | |
quitButton.activate() | |
main(win) |