Related
I feel like this is a little bit complicated or at least I'm confused on it, so I'll try to explain it by rendering the issue. Let me know if the issue isn't clear.
I get the output from my viewing_box through the __init__ method and it shows:
(0, 0, 378, 265)
Which is equivalent to a width of 378 and a height of 265.
When failing, I track the output:
1 false
1 false
here ([0.0, -60.0], [100.0, 40.0]) (0, 60, 378, 325)
The tracking is done in _scan_view with the code:
if not viewable:
current = self.itemcget(item,'tags')
if isinstance(current, tuple):
new = current-('viewable',)
else:
print('here',points, (x1,y1,x2,y2))
new = ''
self.inview_items.discard(item)
So the rectangle stays with width and height of 100, the coords however failing to be the expected ones. While view width and height stays the same and moves correctly in my current understanding. Expected:
if x1 <= point[0] <= x2 and y1 <= point[1] <= y2: and it feels like I've created two coordinate systems but I don't get it. Is someone looking on it and see it immediately?
Full Code:
import tkinter as tk
class InfiniteCanvas(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.inview_items = set() #in view
self.niview_items = set() #not in view
self._xshifted = 0 #view moved in x direction
self._yshifted = 0 #view moved in y direction
self._multi = 0
self.configure(confine=False,highlightthickness=0,bd=0)
self.bind('<MouseWheel>', self._vscroll)
self.bind('<Shift-MouseWheel>', self._hscroll)
root.bind('<Control-KeyPress>',lambda e:setattr(self,'_multi', 10))
root.bind('<Control-KeyRelease>',lambda e:setattr(self,'_multi', 0))
print(self.viewing_box())
return None
def viewing_box(self):
'returns x1,y1,x2,y2 of the currently visible area'
x1 = 0 - self._xshifted
y1 = 0 - self._yshifted
x2 = self.winfo_reqwidth()-self._xshifted
y2 = self.winfo_reqheight()-self._yshifted
return x1,y1,x2,y2
def _scan_view(self):
x1,y1,x2,y2 = self.viewing_box()
for item in self.find_withtag('viewable'):
#check if one felt over the edge
coords = self.coords(item)
#https://www.geeksforgeeks.org/python-split-tuple-into-groups-of-n/
points = tuple(
coords[x:x + 2] for x in range(0, len(coords), 2))
viewable = False
for point in points:
if x1 <= point[0] <= x2 and y1 <= point[1] <= y2:
#if any point is in viewing box
viewable = True
print(item, 'true')
else:
print(item, 'false' )
if not viewable:
current = self.itemcget(item,'tags')
if isinstance(current, tuple):
new = current-('viewable',)
else:
print('here',points, (x1,y1,x2,y2))
new = ''
self.inview_items.discard(item)
self.itemconfigure(item,tags=new)
for item in self.find_overlapping(x1,y1,x2,y2):
#check if item inside of viewing_box not in inview_items
if item not in self.inview_items:
self.inview_items.add(item)
current = self.itemcget(item,'tags')
if isinstance(current, tuple):
new = current+('viewable',)
elif isinstance(current, str):
if str:
new = (current, 'viewable')
else:
new = 'viewable'
self.itemconfigure(item,tags=new)
print(self.inview_items)
def _create(self, *args):
if (current:=args[-1].get('tags', False)):
args[-1]['tags'] = current+('viewable',)
else:
args[-1]['tags'] = ('viewable',)
ident = super()._create(*args)
self._scan_view()
return ident
def _hscroll(self,event):
offset = int(event.delta/120)
if self._multi:
offset = int(offset*self._multi)
canvas.move('all', offset,0)
self._xshifted += offset
self._scan_view()
def _vscroll(self,event):
offset = int(event.delta/120)
if self._multi:
offset = int(offset*self._multi)
canvas.move('all', 0,offset)
self._yshifted += offset
self._scan_view()
root = tk.Tk()
canvas = InfiniteCanvas(root)
canvas.pack(fill=tk.BOTH, expand=True)
size, offset, start = 100, 10, 0
canvas.create_rectangle(start,start, size,size, fill='green')
canvas.create_rectangle(
start+offset,start+offset, size+offset,size+offset, fill='darkgreen')
root.mainloop()
PS: Before thinking this is over-complicated and using just find_overlapping isn't working, since it seems the item needs to be at least 51% in the view to get tracked with tkinters algorithm.
You can find an improved version now on CodeReview!
I still don't know what I have done wrong but it works with scan_dragto.
import tkinter as tk
class InfiniteCanvas(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.inview_items = set() #in view
self.niview_items = set() #not in view
self._xshifted = 0 #view moved in x direction
self._yshifted = 0 #view moved in y direction
self._multi = 0
self.configure(confine=False,highlightthickness=0,bd=0)
self.bind('<MouseWheel>', self._vscroll)
self.bind('<Shift-MouseWheel>', self._hscroll)
root.bind('<Control-KeyPress>',lambda e:setattr(self,'_multi', 10))
root.bind('<Control-KeyRelease>',lambda e:setattr(self,'_multi', 0))
return None
def viewing_box(self):
'returns x1,y1,x2,y2 of the currently visible area'
x1 = 0 - self._xshifted
y1 = 0 - self._yshifted
x2 = self.winfo_reqwidth()-self._xshifted
y2 = self.winfo_reqheight()-self._yshifted
return x1,y1,x2,y2
def _scan_view(self):
x1,y1,x2,y2 = self.viewing_box()
for item in self.find_withtag('viewable'):
#check if one felt over the edge
coords = self.coords(item)
#https://www.geeksforgeeks.org/python-split-tuple-into-groups-of-n/
points = tuple(
coords[x:x + 2] for x in range(0, len(coords), 2))
viewable = False
for point in points:
if x1 <= point[0] <= x2 and y1 <= point[1] <= y2:
#if any point is in viewing box
viewable = True
if not viewable:
current = self.itemcget(item,'tags')
if isinstance(current, tuple):
new = current-('viewable',)
else:
print('here',points, (x1,y1,x2,y2))
new = ''
self.inview_items.discard(item)
self.itemconfigure(item,tags=new)
for item in self.find_overlapping(x1,y1,x2,y2):
#check if item inside of viewing_box not in inview_items
if item not in self.inview_items:
self.inview_items.add(item)
current = self.itemcget(item,'tags')
if isinstance(current, tuple):
new = current+('viewable',)
elif isinstance(current, str):
if str:
new = (current, 'viewable')
else:
new = 'viewable'
self.itemconfigure(item,tags=new)
print(self.inview_items)
def _create(self, *args):
if (current:=args[-1].get('tags', False)):
args[-1]['tags'] = current+('viewable',)
else:
args[-1]['tags'] = ('viewable',)
ident = super()._create(*args)
self._scan_view()
return ident
def _hscroll(self,event):
offset = int(event.delta/120)
if self._multi:
offset = int(offset*self._multi)
cx,cy = self.winfo_rootx(), self.winfo_rooty()
self.scan_mark(cx, cy)
self.scan_dragto(cx+offset, cy, gain=1)
self._xshifted += offset
self._scan_view()
def _vscroll(self,event):
offset = int(event.delta/120)
if self._multi:
offset = int(offset*self._multi)
cx,cy = self.winfo_rootx(), self.winfo_rooty()
self.scan_mark(cx, cy)
self.scan_dragto(cx, cy+offset, gain=1)
self._yshifted += offset
self._scan_view()
root = tk.Tk()
canvas = InfiniteCanvas(root)
canvas.pack(fill=tk.BOTH, expand=True)
size, offset, start = 100, 10, 0
canvas.create_rectangle(start,start, size,size, fill='green')
canvas.create_rectangle(
start+offset,start+offset, size+offset,size+offset, fill='darkgreen')
root.mainloop()
I am making a marching cubes project in python using PyQt5 and PyOpenGL. I am trying to hide the wireframe cube which marches across the screen, referenced as mainWindow.marchingCube to disappear after cycling through. I managed to get the disappearing cycle to occur, but the cube does not actually disappear. I called the QOpenGLWidget's update function, but the cube still did not disappear.
import sys
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QSlider,
QOpenGLWidget, QLabel, QPushButton
)
from PyQt5.QtCore import Qt
from OpenGL.GL import (
glLoadIdentity, glTranslatef, glRotatef,
glClear, glBegin, glEnd,
glColor3fv, glVertex3fv,
GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_QUADS, GL_LINES
)
from OpenGL.GLU import gluPerspective
from numerics import sin, cos, tan, avg, rnd #Numerics is my custom math library.
import random, time
class mainWindow(QMainWindow): #Main class.
shapes = [] #this will hold instances of the following classes: cube
dataPoints = []
zoomLevel = -10
rotateDegreeV = -90
rotateDegreeH = 0
marchActive = False
limit = -1
meshPoints = []
class cube():
render = True
solid = False
color = (1, 1, 1)
def config(self, x, y, z, size = 0.1, solid = False, color = (1, 1, 1)):
self.solid = solid
self.color = color
self.size = size / 2
s = self.size
self.vertices = [
(-s + x, s + y, -s + z),
(s + x, s + y, -s + z),
(s + x, -s + y, -s + z),
(-s + x, -s + y, -s + z),
(-s + x, s + y, s + z),
(s + x, s + y, s + z),
(s + x, -s + y, s + z),
(-s + x, -s + y, s + z)
]
self.edges = [
(0,1), (0,3), (0,4), (2,1),
(2,3), (2,6), (7,3), (7,4),
(7,6), (5,1), (5,4), (5,6)
]
self.facets = [
(0, 1, 2, 3), (0, 1, 6, 5),
(0, 3, 7, 4), (6, 5, 1, 2),
(6, 7, 4, 5), (6, 7, 3, 2)
]
def show(self):
self.render = True
def hide(self):
self.render = False
class dataPoint():
location = (0, 0, 0)
value = 0
shape = None
def place(self, x, y, z):
self.location = (x, y, z)
def set(self, val):
self.value = val
def setShape(self, shape):
self.shape = shape
class meshPoint():
location = (0, 0, 0)
shape = None
def place(self, x, y, z):
self.location = (x, y, z)
def setShape(self, shape):
self.shape = shape
def keyPressEvent(self, event): #This is the keypress detector. I use this to determine input to edit grids.
try:
key = event.key()
#print(key)
if key == 87:
self.rotateV(5)
elif key == 65:
self.rotateH(5)
elif key == 83:
self.rotateV(-5)
elif key == 68:
self.rotateH(-5)
elif key == 67:
self.zoom(1)
elif key == 88:
self.zoom(-1)
elif key == 77:
self.marchStep()
except:
pass
def __init__(self):
super(mainWindow, self).__init__()
self.currentStep = 0
self.width = 700 #Variables used for the setting of the size of everything
self.height = 600
self.setGeometry(0, 0, self.width + 50, self.height) #Set the window size
self.initData(3, 3, 3)
def setupUI(self):
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.width, self.height)
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(self.width, self.height) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
self.filterSlider = QSlider(Qt.Vertical, self)
self.filterSlider.setGeometry(self.width + 10, int(self.height / 2) - 100, 30, 200)
self.filterSlider.valueChanged[int].connect(self.filter)
self.limitDisplay = QLabel(self)
self.limitDisplay.setGeometry(self.width, int(self.height / 2) - 130, 50, 30)
self.limitDisplay.setAlignment(Qt.AlignCenter)
self.limitDisplay.setText('-1')
self.marchButton = QPushButton(self)
self.marchButton.setGeometry(self.width, int(self.height / 2) - 160, 50, 30)
self.marchButton.setText('March!')
self.marchButton.clicked.connect(self.marchStep)
def marchStep(self):
if not self.marchActive:
marchAddr = len(self.shapes)
self.shapes.append(self.cube())
self.marchingCube = self.shapes[marchAddr]
self.marchActive = True
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
self.currentStep = 0
#print('meshPoints: {}'.format(self.meshPoints))
for mp in self.meshPoints:
#print(mp.shape)
self.shapes.remove(mp.shape)
self.meshPoints.clear()
self.marchingCube.hide()
return
if self.currentStep == 0:
self.marchingCube.show()
p = self.marchPoints[self.currentStep]
x, y, z = p
self.marchingCube.config(x, y, z, size = 1)
points = []
for i in range(8):
point = self.getDataPointByLocation(self.marchingCube.vertices[i])
points.append(point)
self.openGLWidget.update()
#print('step: {} x: {} y: {} z: {}'.format(self.currentStep, x, y, z))
#for point in points:
# print(point.location, end = ' ')
#print()
for pair in self.marchingCube.edges:
pointA = points[pair[0]]
pointB = points[pair[1]]
#print('pointA.value: {} pointB.value: {} limit: {}'.formatpointA.value, pointB.value, self.limit)
if (pointA.value < self.limit and pointB.value > self.limit) or (pointA.value > self.limit and pointB.value < self.limit):
xA, yA, zA = pointA.location
xB, yB, zB = pointB.location
valA = (pointA.value + 1) / 2
valB = (pointB.value + 1) / 2
xC = float(avg([xA, xB]))
yC = float(avg([yA, yB]))
zC = float(avg([zA, zB]))
mp = self.meshPoint()
mp.place(xC, yC, zC)
mp.setShape(self.cube())
mp.shape.config(xC, yC, zC, size = 0.05, solid = True, color = (1, 0, 0))
self.shapes.append(mp.shape)
self.meshPoints.append(mp)
self.currentStep += 1
self.openGLWidget.update()
def zoom(self, value):
self.zoomLevel += value
self.openGLWidget.update()
def rotateV(self, value):
self.rotateDegreeV += value
self.openGLWidget.update()
def rotateH(self, value):
self.rotateDegreeH += value
self.openGLWidget.update()
def filter(self, value):
self.limit = rnd((value / 49.5) -1, -2)
for d in self.dataPoints:
if d.value < self.limit:
d.shape.hide()
else:
d.shape.show()
self.limitDisplay.setText(str(self.limit))
self.openGLWidget.update()
def getDataPointByLocation(self, coord):
x, y, z = coord
for dp in self.dataPoints:
if dp.location == (x, y, z):
return dp
return False
def paintGL(self):
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 110.0) #set perspective?
glTranslatef(0, 0, self.zoomLevel) #I used -10 instead of -2 in the PyGame version.
glRotatef(self.rotateDegreeV, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glRotatef(self.rotateDegreeH, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
if len(self.shapes) != 0:
glBegin(GL_LINES)
for s in self.shapes:
glColor3fv(s.color)
if s.render and not s.solid:
for e in s.edges:
for v in e:
glVertex3fv(s.vertices[v])
glEnd()
glBegin(GL_QUADS)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.solid:
for f in s.facets:
for v in f:
glVertex3fv(s.vertices[v])
glEnd()
def initData(self, sizeX, sizeY, sizeZ):
marchSizeX = sizeX - 1
marchSizeY = sizeY - 1
marchSizeZ = sizeZ - 1
xOff = -(sizeX / 2) + 0.5
yOff = -(sizeY / 2) + 0.5
zOff = -(sizeZ / 2) + 0.5
xMarchOff = -(marchSizeX / 2) + 0.5
yMarchOff = -(marchSizeY / 2) + 0.5
zMarchOff = -(marchSizeZ / 2) + 0.5
vals = []
self.marchPoints = []
for z in range(marchSizeZ):
for y in range(marchSizeY):
for x in range(marchSizeX):
self.marchPoints.append((x + xMarchOff, y + yMarchOff ,z + zMarchOff))
for z in range(sizeZ):
for y in range(sizeY):
for x in range(sizeX):
loc = len(self.dataPoints)
val = self.generate(x + xOff, y + yOff, z + zOff)
self.dataPoints.append(self.dataPoint())
self.dataPoints[loc].place(x + xOff, y + yOff, z + zOff)
self.dataPoints[loc].set(val)
loc2 = len(self.shapes)
self.shapes.append(self.cube())
self.shapes[loc2].config(x + xOff, y + yOff, z + zOff, solid = True, color = (0, (val + 1) / 2, (val + 1) / -2 + 1))
self.dataPoints[loc].setShape(self.shapes[loc2])
vals.append(val)
print(avg(vals))
def generate(self, xIn, yIn, zIn): #Function which produces semi-random values based on the supplied coordinates.
i = -(xIn * yIn * (10 + zIn))
j = xIn * yIn * (10 + zIn)
if i < j:
mixer = random.randint(i, j)
else:
mixer = random.randint(j, i + 1)
a = avg([sin(cos(xIn)), tan(tan(yIn)), cos(tan(zIn))])
out = mixer * a
while out > 10:
out -= 5
while out < -10:
out += 5
return float(out / 10)
app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())
Why doesn't the cube disappear? I have caught wind during my web searches on the subject that update does not always work as expected. Directly calling self.openGLWidget.paintGL() does not work either. What must I do to make the cube disappear?
EDIT:
If I make a call to rotate, rotate, or zoom, the screen refreshes and the meshpoints as well as the marching cube all disappear. I think I may end up making a workaround by calling one of those with a zero value.
To test, save the following code in a file named numerics.py in the same directory as the rest of the code.
from decimal import Decimal as dec
degrad = 'deg'
pi = 3.14159265358979323846
terms = dec(9) #number of terms used for the trig calculations
def version():
print('numerics.py version 1.0.0')
print('Packaged with the cubes project')
def mode(modeinput = ''): #switch between degrees and radians or check the current mode
global degrad
if modeinput == 'deg':
degrad = 'deg'
return 'deg'
if modeinput == 'rad':
degrad = 'rad'
return 'rad'
if modeinput == '':
return degrad
else:
return False
def accuracy(accinput = ''):
global terms
global pi
if accinput == '':
return terms
terms = dec(accinput)
PI = calculatePi(accinput)
print('Pi is: {}'.format(PI))
return terms
def calculatePi(placeIn = terms):
if placeIn > 15:
if input("Warning: You have chosen to calculate more than 20 digits of pi. This may take a LONG TIME and may be inacurate. Enter 'yes' if you wish to proceed. If you enter anything else, this function will revert to 10 digits.") == 'yes':
place = placeIn
else:
place = 10
else:
place = placeIn
print('Calculating Pi...\nPlease wait, as this may take a while.')
PI = dec(3)
addSub = True
for i in range(2, 2 * (int(place) ** 6) + 1, 2):
if addSub:
PI += dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
elif not addSub:
PI -= dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
addSub = not addSub
return rnd(PI, -(place), mode = 'cutoff')
def radToDeg(radin):
return (dec(radin) * dec(180 / pi))
def degToRad(degin):
return (dec(degin) * dec(pi / 180))
def avg(numsIn): #return the average of two numbers, specified as an integer or float
num1 = dec(0)
for i in numsIn:
num1 += dec(i)
return rnd(dec(num1 / dec(len(numsIn))))
def sin(anglein, dr = degrad): #return sine of the supplied angle using the predetermined mode or the supplied mode
if dr == 'deg':
while anglein > 180:
anglein -= 360
while anglein < -180:
anglein += 360
angle = degToRad(anglein)
if dr == 'rad':
while anglein > pi:
anglein -= (2 * pi)
while anglein < -pi:
anglein += (2 * pi)
angle = anglein
return rnd(rawsin(dec(angle)), -terms)
def arcsin(ratioin, dr = degrad): #return arcsine of the supplied ratio using the predetermined mode or the supplied mode
if ratioin > 1 or ratioin < -1: #if the input is illegal
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(sin(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def cos(anglein, dr = degrad): #return cosine of the supplied angle
if dr == 'deg':
return rawsin(degToRad(90 - anglein))
else:
angle = anglein
return rnd(rawsin(90 - angle), -terms)
def arccos(ratioin, dr = degrad): #return arccosine of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(cos(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def tan(anglein, dr = degrad): #return tangent of the supplied angle
a = sin(anglein, dr)
b = cos(anglein, dr)
if (not a == 0) and (not b == 0):
return rnd((a / b), -terms)
else:
return False
def arctan(ratioin, dr = degrad): #return arctangent of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(tan(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def rawsin(anglein): #return the result of sine of the supplied angle, using radians
#This is the taylor series used.
#final = x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) + (x^9 / 9!) - (x^11 / 11!)...
angle = dec(anglein)
final = angle
add = False
for i in range(3, int(terms) * 3, 2):
if add:
final += dec(angle ** i) / fact(i)
elif not add:
final -= dec(angle ** i) / fact(i)
add = not add
return final
def fact(intin): #return the factorial of the given integer, return False if not given an int
if intin == int(intin):
intout = 1
for i in range(1, intin + 1):
intout *= i
return intout
else:
return False
def rnd(numIn, decPlcIn = -terms, mode = 'fiveHigher'): #return the given number, rounded to the given decimal place.
#use 1 to indicate 10s, 0 to indicate 1s, -2 to indicate 100ths, etc.
num1 = dec(numIn)
decPlc = dec(decPlcIn)
if mode == 'fiveHigher':
return dec(str(dec(round(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
elif mode == 'cutoff':
return dec(str(dec(int(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
def root(numIn, rootVal):
num = dec(numIn)
rt = dec(dec(1) / rootVal)
num1 = num ** rt
return rnd(num1, -terms)
def quad(aIn, bIn, cIn): #Plugin for the quadratic formula. Provide a, b, and c.
a = dec(aIn)
b = dec(bIn)
c = dec(cIn)
try:
posResult = (-b + root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
posResult = False
try:
negResult = (-b - root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
negResult = False
return (posResult, negResult)
You are missing 1 call to self.openGLWidget.update(). There is a return statement in the instruction block of the if. The function is terminated at this point and the self.openGLWidget.update() instruction at the end of the code is never executed.
Add self.openGLWidget.update() right before return, to solve the issue:
class mainWindow(QMainWindow):
# [...]
def marchStep(self):
if not self.marchActive:
# [...]
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
# [...]
self.meshPoints.clear()
self.marchingCube.hide()
self.openGLWidget.update() # <--------- ADD
return
if self.currentStep == 0:
self.marchingCube.show()
# [...]
the code below is to detect the objects in images. The code can detect the objects, count the number of detected objects, drawing rectangles to label the objects. Is there any way to extract and save each detected objects? For example; if there are 20 objects detected in an image, I would like to extract and save these 20 objects seperately. I would be appreciated for any help..
from PIL import Image
class TheOutliner(object):
''' takes a dict of xy points and
draws a rectangle around them '''
def __init__(self):
self.outlineColor = 255, 0, 0
self.pic = None
self.picn = None
self.minX = 0
self.minY = 0
self.maxX = 0
self.maxY = 0
def doEverything(self, imgPath, dictPoints, theoutfile):
self.loadImage(imgPath)
self.loadBrightPoints(dictPoints)
self.drawBox()
self.saveImg(theoutfile)
def loadImage(self, imgPath):
self.pic = Image.open(imgPath)
self.picn = self.pic.load()
def loadBrightPoints(self, dictPoints):
'''iterate through all points and
gather max/min x/y '''
# an x from the pool (the max/min
# must be from dictPoints)
self.minX = dictPoints.keys()[0][0]
self.maxX = self.minX
self.minY = dictPoints.keys()[0][1]
self.maxY = self.minY
for point in dictPoints.keys():
if point[0] < self.minX:
self.minX = point[0]
elif point[0] > self.maxX:
self.maxX = point[0]
if point[1]< self.minY:
self.minY = point[1]
elif point[1] > self.maxY:
self.maxY = point[1]
def drawBox(self):
# drop box around bright points
for x in xrange(self.minX, self.maxX):
# top bar
self.picn[x, self.minY] = self.outlineColor
# bottom bar
self.picn[x, self.maxY] = self.outlineColor
for y in xrange(self.minY, self.maxY):
# left bar
self.picn[self.minX, y] = self.outlineColor
# right bar
self.picn[self.maxX, y] = self.outlineColor
def saveImg(self, theoutfile):
self.pic.save(theoutfile, "JPEG")
class ObjectDetector(object):
''' returns a list of dicts representing
all the objects in the image '''
def __init__(self):
self.detail = 4
self.objects = []
self.size = 1000
self.no = 255
self.close = 100
self.pic = None
self.picn = None
self.brightDict = {}
def loadImage(self, imgPath):
self.pic = Image.open(imgPath)
self.picn = self.pic.load()
self.picSize = self.pic.size
self.detail = (self.picSize[0] + self.picSize[1])/2000
self.size = (self.picSize[0] + self.picSize[1])/8
# each must be at least 1 -- and the larger
# the self.detail is the faster the analyzation will be
self.detail += 1
self.size += 1
def getSurroundingPoints(self, xy):
''' returns list of adjoining point '''
x = xy[0]
y = xy[1]
plist = (
(x-self.detail, y-self.detail), (x, y-self.detail),
(x+self.detail, y-self.detail),
(x-self.detail, y),(x+self.detail, y),
(x-self.detail, y+self.detail),(x, y+self.detail),
(x+self.detail,y+self.detail)
)
return (plist)
def getRGBFor(self, x, y):
try:
return self.picn[x,y]
except IndexError as e:
return 255,255,255
def readyToBeEvaluated(self, xy):
try:
r,g,b = self.picn[xy[0],xy[1]]
if r==255 and g==255 and b==255:
return False
except:
return False
return True
def markEvaluated(self, xy):
try:
self.picn[xy[0],xy[1]] = self.no, self.no, self.no
except:
pass
def collectAllObjectPoints(self):
for x in xrange(self.pic.size[0]):
if x % self.detail == 0:
for y in xrange(self.pic.size[1]):
if y % self.detail == 0:
r,g,b = self.picn[x,y]
if r == self.no and \
g == self.no and \
b == self.no:
# then no more
pass
else:
ol = {}
ol[x,y] = "go"
pp = []
pp.append((x,y))
stillLooking = True
while stillLooking:
if len(pp) > 0:
xe, ye = pp.pop()
# look for adjoining points
for p in
self.getSurroundingPoints((xe,ye)
if self.readyToBeEvaluated((p[0],
p[1])):
r2,g2,b2 = self.getRGBFor(p[0],
p[1])
if abs(r-r2) < self.close and \
abs(g-g2) < self.close and \
abs(b-b2) < self.close:
# then its close enough
ol[p[0],p[1]] = "go"
pp.append((p[0],p[1]))
self.markEvaluated((p[0],p[1]))
self.markEvaluated((xe,ye))
else:
# done expanding that point
stillLooking = False
if len(ol) > self.size:
self.objects.append(ol)
if __name__ == "__main__":
print "Start Process";
# assumes that the .jpg files are in
# working directory
theFile = "new2"
theOutFile = "new2.output"
import os
os.listdir('.')
for f in os.listdir('.'):
if f.find(".jpg") > 0:
theFile = f
print "working on " + theFile + "..."
theOutFile = theFile + ".out.jpg"
bbb = ObjectDetector()
bbb.loadImage(theFile)
print " analyzing.."
print " file dimensions: " + str(bbb.picSize)
print " this files object weight: " + str(bbb.size)
print " this files analyzation detail: " +
str(bbb.detail)
bbb.collectAllObjectPoints()
print " objects detected: " +str(len(bbb.objects))
drawer = TheOutliner()
print " loading and drawing rectangles.."
drawer.loadImage(theFile)
for o in bbb.objects:
drawer.loadBrightPoints(o)
drawer.drawBox()
print "saving image..."
drawer.saveImg(theOutFile)
print "Process complete"
You can use crop() method :
add new method after drawBox():
def saveBox(self,filename):
# Create Box
box = (self.minX, self.minY, self.maxX, self.maxY)
# Crop Image
self.pic.crop(box).save(filename)
change the main code :
drawer.loadImage(theFile)
idBox=0
for o in bbb.objects:
drawer.loadBrightPoints(o)
drawer.drawBox()
idBox=idBox+1
drawer.saveBox("box_"+str(idBox)+"_"+theOutFile)
print "saving image..."
drawer.saveImg(theOutFile)
So im working on project that takes input from a text document and converts it into a image. But for some reason the debugger keeps giving this error message
IndexError: image index out of range
Here is the code
f1 = open(argv[1], "r")
f2 = str(f1.read())
farr = list(f2)
size1 = len(farr)
size2 = input("Size:")
size = width, height = int(size2), int(size2);
size1 + 1
#MAKING THE IMAGE
img = Image.new('RGB', size, "white" )
map = img.load()
print(map[0,0])
Rw,Gw,Bw = [255,255,255]
Rb,Gb,Bb = [0,0,0]
def Converter(num,x,y):
while int(size1) != int(num):
if farr[num - 1: + int(num)] == ["0"]:
img.putpixel((x,y), (Rw,Gw,Bw))
x = x + 1
elif farr[num -1: + int(num)] == ["1"]:
img.putpixel((x,y), (Rb,Gb,Bb))
x = x + 1
elif farr[num -1: + int(num)] == [" "]:
y = y + 1
else:
print("nothing")
num = num + 1
Converter(1,1,1)
img.show()
Put gloabl x and global y in the converter function and it fixes everything.
The following script comes from http://projectproto.blogspot.co.uk/2014/07/opencv-python-2048-game-solver.html
import cv2
import numpy as np
import win32api, win32gui, win32ui, win32con, win32com.client
from PIL import Image, ImageFont, ImageDraw, ImageOps
# create training model based on the given TTF font file
# http://projectproto.blogspot.com/2014/07/opencv-python-digit-recognition.html
def createDigitsModel(fontfile, digitheight):
font = ImageFont.truetype(fontfile, digitheight)
samples = np.empty((0,digitheight*(digitheight/2)))
responses = []
for n in range(10):
pil_im = Image.new("RGB", (digitheight, digitheight*2))
ImageDraw.Draw(pil_im).text((0, 0), str(n), font=font)
pil_im = pil_im.crop(pil_im.getbbox())
pil_im = ImageOps.invert(pil_im)
#pil_im.save(str(n) + ".png")
# convert to cv image
cv_image = cv2.cvtColor(np.array( pil_im ), cv2.COLOR_RGBA2BGRA)
gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
roi = cv2.resize(thresh,(digitheight,digitheight/2))
responses.append( n )
sample = roi.reshape((1,digitheight*(digitheight/2)))
samples = np.append(samples,sample,0)
samples = np.array(samples,np.float32)
responses = np.array(responses,np.float32)
model = cv2.KNearest()
model.train(samples,responses)
return model
class Board(object):
UP, DOWN, LEFT, RIGHT = 1, 2, 3, 4
FONT = "font/ClearSans-Bold.ttf"
def __init__(self, clientwindowtitle):
self.hwnd = self.getClientWindow(clientwindowtitle)
if not self.hwnd:
return
self.hwndDC = win32gui.GetWindowDC(self.hwnd)
self.mfcDC = win32ui.CreateDCFromHandle(self.hwndDC)
self.saveDC = self.mfcDC.CreateCompatibleDC()
self.cl, self.ct, right, bot = win32gui.GetClientRect(self.hwnd)
self.cw, self.ch = right-self.cl, bot-self.ct
self.cl += win32api.GetSystemMetrics(win32con.SM_CXSIZEFRAME)
self.ct += win32api.GetSystemMetrics(win32con.SM_CYSIZEFRAME)
self.ct += win32api.GetSystemMetrics(win32con.SM_CYCAPTION)
self.ch += win32api.GetSystemMetrics(win32con.SM_CYSIZEFRAME)*2
self.saveBitMap = win32ui.CreateBitmap()
self.saveBitMap.CreateCompatibleBitmap(self.mfcDC, self.cw, self.ch)
self.saveDC.SelectObject(self.saveBitMap)
self.tiles, self.tileheight, self.contour = self.findTiles(self.getClientFrame())
if not len(self.tiles):
return
self.digitheight = self.tileheight / 2
self.digitsmodel = createDigitsModel(self.FONT, self.digitheight)
self.update()
def getClientWindow(self, windowtitle):
toplist, winlist = [], []
def enum_cb(hwnd, results):
winlist.append((hwnd, win32gui.GetWindowText(hwnd)))
win32gui.EnumWindows(enum_cb, toplist)
window = [(hwnd, title) for hwnd, title in winlist if windowtitle.lower() in title.lower()]
if not len(window):
return 0
return window[0][0]
def getClientFrame(self):
self.saveDC.BitBlt((0, 0), (self.cw, self.ch),
self.mfcDC, (self.cl, self.ct), win32con.SRCCOPY)
bmpinfo = self.saveBitMap.GetInfo()
bmpstr = self.saveBitMap.GetBitmapBits(True)
pil_img = Image.frombuffer( 'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
array = np.array( pil_img )
cvimage = cv2.cvtColor(array, cv2.COLOR_RGBA2BGRA)
return cvimage
def findTiles(self, cvframe):
tiles, avgh = [], 0
gray = cv2.cvtColor(cvframe,cv2.COLOR_BGRA2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
def findBoard(contours): # get largest square
ww, sqcnt = 10, None
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
if w>ww and abs(w-h)<w/10:
ww = w
sqcnt = cnt
return sqcnt
board = findBoard(contours)
if board==None:
print 'board not found!'
return tiles, avgh, board
bx,by,bw,bh = cv2.boundingRect(board)
#cv2.rectangle(cvframe,(bx,by),(bx+bw,by+bh),(0,255,0),2)
#cv2.imshow('board',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'board' )
maxh = bh/4
minh = (maxh*4)/5
count = 0
for contour in contours:
x,y,w,h = cv2.boundingRect(contour)
if y>by and w>minh and w<maxh and h>minh and h<maxh:
avgh += h
count += 1
if not count:
print 'no tile found!'
return tiles, avgh, board
avgh = avgh / count
margin = (bh-avgh*4)/5
for row in range(4):
for col in range(4):
x0 = bx + avgh*col + margin*(col+1)
x1 = x0 + avgh
y0 = by + avgh*row + margin*(row+1)
y1 = y0 + avgh
tiles.append([x0, y0, x1, y1])
#cv2.rectangle(cvframe,(x0,y0),(x1,y1),(0,255,0),2)
#cv2.imshow('tiles',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'tiles' )
return tiles, avgh, board
def getTileThreshold(self, tileimage):
gray = cv2.cvtColor(tileimage,cv2.COLOR_BGR2GRAY)
row, col = gray.shape
tmp = gray.copy().reshape(1, row*col)
counts = np.bincount(tmp[0])
sort = np.sort(counts)
modes, freqs = [], []
for i in range(len(sort)):
freq = sort[-1-i]
if freq < 4:
break
mode = np.where(counts==freq)[0][0]
modes.append(mode)
freqs.append(freq)
bg, fg = modes[0], modes[0]
for i in range(len(modes)):
fg = modes[i]
#if abs(bg-fg)>=48:
if abs(bg-fg)>32 and abs(fg-150)>4: # 150?!
break
#print bg, fg
if bg>fg: # needs dark background ?
tmp = 255 - tmp
bg, fg = 255-bg, 255-fg
tmp = tmp.reshape(row, col)
ret, thresh = cv2.threshold(tmp,(bg+fg)/2,255,cv2.THRESH_BINARY)
return thresh
def getTileNumbers(self, cvframe):
numbers = []
outframe = np.zeros(cvframe.shape,np.uint8)
def guessNumber(digits):
for i in range(1,16):
nn = 2**i
ss = str(nn)
dd = [int(c) for c in ss]
if set(digits) == set(dd):
return nn
return 0
for tile in self.tiles:
x0,y0,x1,y1 = tile
tileimage = cvframe[y0:y1,x0:x1]
cv2.rectangle(cvframe,(x0,y0),(x1,y1),(0,255,0),2)
cv2.rectangle(outframe,(x0,y0),(x1,y1),(0,255,0),1)
thresh = self.getTileThreshold(tileimage)
contours,hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
dh = self.digitheight
digits = []
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
if h>w and h>(dh*1)/5 and h<(dh*6)/5:
cv2.rectangle(cvframe,(x0+x,y0+y),(x0+x+w,y0+y+h),(0,0,255),1)
roi = thresh[y:y+h,x:x+w]
roi = cv2.resize(roi,(dh,dh/2))
roi = roi.reshape((1,dh*(dh/2)))
roi = np.float32(roi)
retval, results, neigh_resp, dists = self.digitsmodel.find_nearest(roi, k=1)
digit = int((results[0][0]))
string = str(digit)
digits.append(digit)
cv2.putText(outframe,string,(x0+x,y0+y+h),0,float(h)/24,(0,255,0))
numbers.append(guessNumber(digits))
return numbers, outframe
def getWindowHandle(self):
return self.hwnd
def getBoardContour(self):
return self.contour
def update(self):
frame = self.getClientFrame()
self.tilenumbers, outframe = self.getTileNumbers(frame)
return self.tilenumbers, frame, outframe
def copyTileNumbers(self):
return self.tilenumbers[:]
def getCell(self, tiles, x, y):
return tiles[(y*4)+x]
def setCell(self, tiles, x, y, v):
tiles[(y*4)+x] = v
return tiles
def getCol(self, tiles, x):
return [self.getCell(tiles, x, i) for i in range(4)]
def setCol(self, tiles, x, col):
for i in range(4):
self.setCell(tiles, x, i, col[i])
return tiles
def getLine(self, tiles, y):
return [self.getCell(tiles, i, y) for i in range(4)]
def setLine(self, tiles, y, line):
for i in range(4):
self.setCell(tiles, i, y, line[i])
return tiles
def validMove(self, tilenumbers, direction):
if direction == self.UP or direction == self.DOWN:
for x in range(4):
col = self.getCol(tilenumbers, x)
for y in range(4):
if(y < 4-1 and col[y] == col[y+1] and col[y]!=0):
return True
if(direction == self.DOWN and y > 0 and col[y] == 0 and col[y-1]!=0):
return True
if(direction == self.UP and y < 4-1 and col[y] == 0 and col[y+1]!=0):
return True
if direction == self.LEFT or direction == self.RIGHT:
for y in range(4):
line = self.getLine(tilenumbers, y)
for x in range(4):
if(x < 4-1 and line[x] == line[x+1] and line[x]!=0):
return True
if(direction == self.RIGHT and x > 0 and line[x] == 0 and line[x-1]!=0):
return True
if(direction == self.LEFT and x < 4-1 and line[x] == 0 and line[x+1]!=0):
return True
return False
def moveTileNumbers(self, tilenumbers, direction):
def collapseline(line, direction):
if (direction==self.LEFT or direction==self.UP):
inc = 1
rg = xrange(0, 4-1, inc)
else:
inc = -1
rg = xrange(4-1, 0, inc)
pts = 0
for i in rg:
if line[i] == 0:
continue
if line[i] == line[i+inc]:
v = line[i]*2
line[i] = v
line[i+inc] = 0
pts += v
return line, pts
def moveline(line, directsion):
nl = [c for c in line if c != 0]
if directsion==self.UP or directsion==self.LEFT:
return nl + [0] * (4 - len(nl))
return [0] * (4 - len(nl)) + nl
score = 0
if direction==self.LEFT or direction==self.RIGHT:
for i in range(4):
origin = self.getLine(tilenumbers, i)
line = moveline(origin, direction)
collapsed, pts = collapseline(line, direction)
new = moveline(collapsed, direction)
tilenumbers = self.setLine(tilenumbers, i, new)
score += pts
elif direction==self.UP or direction==self.DOWN:
for i in range(4):
origin = self.getCol(tilenumbers, i)
line = moveline(origin, direction)
collapsed, pts = collapseline(line, direction)
new = moveline(collapsed, direction)
tilenumbers = self.setCol(tilenumbers, i, new)
score += pts
return score, tilenumbers
# AI based on "term2048-AI"
# https://github.com/Nicola17/term2048-AI
class AI(object):
def __init__(self, board):
self.board = board
def nextMove(self):
tilenumbers = self.board.copyTileNumbers()
m, s = self.nextMoveRecur(tilenumbers[:],3,3)
return m
def nextMoveRecur(self, tilenumbers, depth, maxDepth, base=0.9):
bestMove, bestScore = 0, -1
for m in range(1,5):
if(self.board.validMove(tilenumbers, m)):
score, newtiles = self.board.moveTileNumbers(tilenumbers[:], m)
score, critical = self.evaluate(newtiles)
newtiles = self.board.setCell(newtiles,critical[0],critical[1],2)
if depth != 0:
my_m,my_s = self.nextMoveRecur(newtiles[:],depth-1,maxDepth)
score += my_s*pow(base,maxDepth-depth+1)
if(score > bestScore):
bestMove = m
bestScore = score
return bestMove, bestScore
def evaluate(self, tilenumbers, commonRatio=0.25):
maxVal = 0.
criticalTile = (-1, -1)
for i in range(8):
linearWeightedVal = 0
invert = False if i<4 else True
weight = 1.
ctile = (-1,-1)
cond = i%4
for y in range(4):
for x in range(4):
if cond==0:
b_x = 4-1-x if invert else x
b_y = y
elif cond==1:
b_x = x
b_y = 4-1-y if invert else y
elif cond==2:
b_x = 4-1-x if invert else x
b_y = 4-1-y
elif cond==3:
b_x = 4-1-x
b_y = 4-1-y if invert else y
currVal=self.board.getCell(tilenumbers,b_x,b_y)
if(currVal == 0 and ctile == (-1,-1)):
ctile = (b_x,b_y)
linearWeightedVal += currVal*weight
weight *= commonRatio
invert = not invert
if linearWeightedVal > maxVal:
maxVal = linearWeightedVal
criticalTile = ctile
return maxVal, criticalTile
def solveBoard(self, moveinterval=500):
boardHWND = self.board.getWindowHandle()
if not boardHWND:
return False
bx, by, bw, bh = cv2.boundingRect(self.board.getBoardContour())
x0, x1, y0, y1 = bx, bx+bw, by, by+bh
win32gui.SetForegroundWindow(boardHWND)
shell = win32com.client.Dispatch('WScript.Shell')
print 'Set the focus to the Game Window, and the press this arrow key:'
keymove = ['UP', 'DOWN', 'LEFT', 'RIGHT']
delay = moveinterval / 3 # milliseconds delay to cancel board animation effect
prev_numbers = []
while True:
numbers, inframe, outframe = self.board.update()
if numbers != prev_numbers:
cv2.waitKey(delay)
numbers, inframe, outframe = self.board.update()
if numbers == prev_numbers: # recheck if has changed
continue
prev_numbers = numbers
move = ai.nextMove()
if move:
key = keymove[move-1]
shell.SendKeys('{%s}'%key)
print key
cv2.waitKey(delay)
cv2.imshow('CV copy',inframe[y0:y1,x0:x1])
cv2.imshow('CV out', outframe[y0:y1,x0:x1])
cv2.waitKey(delay)
cv2.destroyWindow( 'CV copy' )
cv2.destroyWindow( 'CV out' )
# http://gabrielecirulli.github.io/2048/
# http://ov3y.github.io/2048-AI/
board = Board("2048 - Google Chrome")
#board = Board("2048 - Mozilla Firefox")
ai = AI(board)
ai.solveBoard(360)
print 'stopped.'
I have Google Chrome open with the example URL http://ov3y.github.io/2048-AI/ open, running the script has the following error:
20.py:109: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future.
if board==None:
no tile found!
Set the focus to the Game Window, and the press this arrow key:
Then nothing, it just sits there. So the part I'm concerned with most is no tile found!. Un commenting the lines:
#cv2.rectangle(cvframe,(bx,by),(bx+bw,by+bh),(0,255,0),2)
#cv2.imshow('board',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'board' )
Shows the following window on screen:
Can anyone explain why OpenCV is failing to detect the grid, or how to go about debugging this?
Most likely it is not a problem with detecting grid, but with capturing browser window - you are trying to find grid on an empty image which of course fails. First make sure that you have grabbed firefox/chrome/opera screen window correctly - in function getClientFrame(self) put this code:
cv2.imshow('browser window', cvimage)
cv2.waitKey(10000)
just before the final return cvimage. It should show you the browser window for 10 seconds. If it doesn't than it will 100% sure that problem is with capturing browser window, not with detecting grid. To check what's wrong with capturing browser window use win32api.GetLastError() function (you can check error codes here).
Of course there is a chance that i'm wrong and it's a problem with detecting grid - if so, please provide a sample image (just save the image displayed by the code i provided) so we can test it.
\\edit:
I've just noticed the second part of your post - so most likely i'm wrong, but you can test it anyway. It seems that you are capturing one chrome window and part of some other window - try to make your browser window fullscreen.
\\edit2:
After closer look at you image i realised strange thing - capture image has vertical lines and has width (without repeated part onthe right side) smaller than the original window(but height seems to be fine). Width seems to be 75% of original width so i guess that PIL treats every 4 bytes as one pixel, but it should use only 3 bytes per pixel. It's hard to test it, because on my system (win 8.1 64bit) it's working fine. Possible solutions (i can't test them, so you need to check which one will work.. sory :) ):
Try to change this line:
pil_img = Image.frombuffer( 'RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
to something like this:
pil_img = Image.frombuffer( 'RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGR', 0, 1)
generally you need to change value of fifth parameter from BGRX to something else - most likely to 'BGR', full list of options is here. If it won't work try to play with different values of first and fifth parameters.
On the screenshot it looks like you have some quite old version of Windows or at least you are using old gui (which is great btw!). If - except for setting gui style to "old style" - you've set (or windows've done it for you) your color quality to something else than "Highest (32bit)" it may cause your problem as well. Try to set it to "Highest (32 bit)". To be clear - i'm talking about settings from this window:
(on the right side, near bottom and color palette).
If you have 2 (or more) screens, test you program while using only one. Also if you are using some alternative window manager (or some other weird extension like something for multiple desktops) turn it off and try again.