TkInter: draw one pixel - python

In TkInter, we can draw several shapes. But what if we just want to draw a pixel instead ?

After creating a Canvas object, you can draw a line that spans a single pixel.
your_canvas_widget.create_line(x, y, x + 1, y)

To create a single pixel with co-ordinates x, y on a canvas, you can use a rectangleobject:
canvas.create_rectangle( (x, y)*2 )
By default, the rectangle object has a one-pixel wide black border, which when one-pixel wide will just be black, regardless of the colour. To give the pixel your desired colour, you can use outline="", so you can then specify your fill colour.
I prefer this method as you only need to provide the x, y co-ordinates, no x+1 is needed.

This topic is already answered and quite old but I'd like to add a comment, maybe it is of interest:
your_canvas_widget.create_line(x, y, x + 1, y)
Is what I have also tried and it works for me. I used it for creating an image of a Mandelbrot-set. It works quite fine for small resoltions, e.g. 200 x 200 pixel. But for larger resolutions, the TK engine seems to have problems to render those many "micro lines". the calculation of the mandelbrot set is finished, but the Tk window seems to hang without rendering anything.
A better solution for drawing pixels, I have found in python-mandelbrot-fractal-with-tkinter. There the pixels are put one by one into a tkinter PhotoImage which is afterwards rendered all in one, very fast.

Related

Pyglet: blit_into texture and alpha

I've been using pyglet for a while now and I really like it. I've got one thing I'd like to do but have been unable to do so far, however.
I'm working on a 2D roleplaying game and I'd like the characters to be able to look different - that is to say, I wouldn't like use completely prebuilt sprites, but instead I'd like there to be a range of, say, hairstyles and equipment, visible on characters in the game.
So to get this thing working, I thought the most sensible way to go on about it would be to create a texture with pyglet.image.Texture.create() and blit the correct sprite source images on that texture using Texture.blit_into. For example, I could blit a naked human image on the texture, then blit a hair texture on that, etc.
human_base = pyglet.image.load('x/human_base.png').get_image_data()
hair_style = pyglet.image.load('x/human_hair1.png').get_image_data()
texture = pyglet.image.Texture.create(width=human_base.width,height=human_base.height)
texture.blit_into(human_base, x=0, y=0, z=0)
texture.blit_into(hair_style, x=0, y=0, z=1)
sprite = pyglet.sprite.Sprite(img=texture, x=0, y=0, batch=my_sprite_batch)
The problem is that blitting the second image into the texture "overwrites" the texture already blitted in. Even though both of the images have an alpha channel, the image below (human_base) is not visible after hair_style is blit on top of it.
One reading this may be wondering why do it this way instead of, say, creating two different pyglet.sprite.Sprite objects, one for human_base and one for hair_style and just move them together. One thing is the draw ordering: the game is tile-based and isometric, so sorting a visible object consisting of multiple sprites with differing layers (or ordered groups, as pyglet calls them) would be a major pain.
So my question is, is there a way to retain alpha when using blit_into with pyglet. If there is no way to do it, please, any suggestions for alternative ways to go on about this would be very much appreciated!
setting the blend function correctly should fix this:
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA,pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
I ran into the very same problem and couldn't find a proper solution. Apparently blitting two RGBA images/textures overlapping together will remove the image beneath. Another approache I came up with was using every 'clothing image' on every character as an independent sprite attached to batches and groups, but that was far from the optimal and reduced the FPS dramatically.
I got my own solution by using PIL
import pyglet
from PIL import Image
class main(pyglet.window.Window):
def __init__ (self):
TILESIZE = 32
super(main, self).__init__(800, 600, fullscreen = False)
img1 = Image.open('under.png')
img2 = Image.open('over.png')
img1.paste(img2,(0,0),img2.convert('RGBA'))
img = img1.transpose(Image.FLIP_TOP_BOTTOM)
raw_image=img.tostring()
self.image=pyglet.image.ImageData(TILESIZE,TILESIZE,'RGBA',raw_image)
def run(self):
while not self.has_exit:
self.dispatch_events()
self.clear()
self.image.blit(0,0)
self.flip()
x = main()
x.run()
This may well not be the optimal solution, but if you do the loading in scene loading, then it won't matter, and with the result you can do almost almost anything you want to (as long as you don't blit it on another texture, heh). If you want to get just 1 tile (or a column or a row or a rectangular box) out of a tileset with PIL, you can use the crop function.

OpenGL Lines coming out gray, with lighting enabled

I'm using Python OpenGL, version '3.3.0 - Build 8.15.10.2725' on Win7 x64.
I'm using GL_LINES to draw 2D grids and XYZ axes in 3D space.
When I disable lighting the grid colors come out fine.
When I enable lighting and the light Z position is > 0, the grid colors come out fine.
However when I put the light Z position < 0, the grid colors are almost gray, even though the 3D portion of the model is rendering properly. Since lines are one dimensional, I don't think they have a surface; I'm not sure how OpenGL regards lines in the presence of lighting.
I can get colored lines with lighting Z position < 0 if I use a dumb fragment shader as follows:
# From http://bazaar.launchpad.net/~mcfletch/openglcontext/trunk/view/head:/tests/shader_2.py
self.color_fragmentshader = shaders.compileShader("""
varying vec4 vertex_color;
void main() {
gl_FragColor = vertex_color;
}
""", GL.GL_FRAGMENT_SHADER)
But if I leave the default shader (by not calling glUseProgram), or use some other shader I found, then the 2D lines come out gray.
My grid and axes functions are as follows:
def drawGrid(self, size, squares):
GL.glLineWidth(0.1)
GL.glColor((trolltechGreen.light().red()/255,
trolltechGreen.light().green()/255,
trolltechGreen.light().blue()/255))
GL.glBegin(GL.GL_LINES)
for x in np.linspace(-size, size, squares+1, True):
GL.glVertex2f(x, -size)
GL.glVertex2f(x, size)
for y in np.linspace(-size, size, squares+1, True):
GL.glVertex2f(-size, y)
GL.glVertex2f(size, y)
GL.glVertex2f(-size,-size)
GL.glVertex2f(size,-size)
GL.glVertex2f(size,size)
GL.glVertex2f(-size,size)
GL.glEnd()
def drawAxes(self, size):
GL.glLineWidth(5)
GL.glBegin(GL.GL_LINES)
GL.glColor(1,0,0,1)
GL.glVertex2f(0,0)
GL.glVertex2f(size,0)
GL.glColor(0,1,0,1)
GL.glVertex2f(0,0)
GL.glVertex2f(0,size)
GL.glColor(0,0,1,1)
GL.glVertex2f(0,0)
GL.glVertex3f(0,0,size)
GL.glEnd()
I can also get the line color to come out fine if I change the shader each time I render the lines, then change again to render the 3D objects, but I don't think this is the right solution.
Here is what the problem looks like. The lighting is "behind" the teapot in -Z. How can I force the lines to ignore the lighting, without constantly changing the shader? Thanks!
I'm not sure how OpenGL regards lines in the presence of lighting.
When you enable lighting in the fixed function pipeline, it will affec all primitives you draw - points, lights, triangles.
Lines might not define a mathematical surface, but you still have normal vectors defined for them. Remember that OpenGL is a state machine, so if you don't specify them with your line geometry, you still have some "current" normal vector which will be used.
When you use shaders, you yourself control the color which is generated. Shaders apply to all primitive types as well.
If you want the lines to be unaffected by the lighting, disable lighting when you draw the grid in the fixed-function pipeline, or use a color-only shader if you use the programmable pipeline.
You can of course set some material properties so that your lines will appear just in the color you whish while lighting is enabled. In fixed-function GL, this can be achieved by setting the "emissive" material coeffients to the desired color, and all others to 0, for example. If you use shaders, it will depend on how the shader work, of course.

Semi-transparent 2d VTK text background

Simple question, but I've tried a few things and nothing seems to work.
I want to overlay some statistics onto a 3d VTK scene, using 2D vtkTextActors. This works fine, but the text is at times difficult to see, depending on what appears behind it in the 3D scene.
For this reason, I'd like to add a 2d, semi-transparent "box" behind my text actors to provide a darker background.
Which VTK object is appropriate for this? I've tried so far:
vtkLegendBoxActor: Not what I want, but I can use this with no text to display a semi-transparent box on screen. I cannot size it directly and I get warnings about not initialising some of the content.
vtkImageData: Tried manually creating image data and adding it to the scene; I believe it was placed within the 3d scene and not used as an overlay. If that's not the case then I couldn't get it to show at all.
vtkCornerAnnotation: Scales with window size, is fixed to a corner and the background opacity cannot be set AFAIK.
vtkTextActor: Cannot set a background color or opacity
Can anyone tell me how they might achieve what I'm after in VTK?
I've found a way to do this with vtkPolyMapper2D which seems to work okay. It seems to be a very stupid way to do this. If there is something more elegant, I'm all ears.
import vtk
extents = [[0,0],[620,0],[620,220],[0,220]]
polyPoints = vtk.vtkPoints()
for x, y in extents:
polyPoints.InsertNextPoint(x, y, 0)
num_corners = len(extents)
polyCells = vtk.vtkCellArray()
polyCells.InsertNextCell(num_corners + 1)
for i in range(0, num_corners):
polyCells.InsertCellPoint(i)
polyCells.InsertCellPoint(0) ## Rejoin at the end
poly_profile = vtk.vtkPolyData()
poly_profile.SetPoints(polyPoints)
poly_profile.SetPolys(polyCells) ## Goes solid
cut_triangles = vtk.vtkTriangleFilter()
cut_triangles.SetInput(poly_profile)
_poly_mapper = vtk.vtkPolyDataMapper2D()
_poly_mapper.SetInput(poly_profile)
_poly_mapper.SetInputConnection(cut_triangles.GetOutputPort())
_actor = vtk.vtkActor2D()
_actor.SetMapper(_poly_mapper)
_actor.GetProperty().SetColor([0.1,0.1,0.1])
_actor.GetProperty().SetOpacity(0.5)
#Add to renderer as normal
just use vtktexture, vtkimagedata & add your own image as texture background to the vtkrenderer by reducing the opacity like a watermark. thats it

Pygame Surface Mechanics

I'm currently writing up some GUI code for a small project I'm working on and I've come to the point where I need to implement scroll bars and their associated containers. For ease of execution, I would love to be able to draw all elements within the "scroll box" (the window that the scroll bar will affect) to a separate surface from my main display surface. The separate surface would then be cropped as need be and then drawn to the display surface in the render loop. I'm having trouble getting this to work, however.
In the draw() method of my ScrollBox class, I have the following code.
def draw(self):
self.subSurface.blit(self.image, (x, y))
#subSurface is, naturally, a Surface, and image is a pygame.Image; x and y are whatever
self.displaySurface.blit(self.subSurface, (x,y))
As with all drawable GUI elements in my code, draw() is called every pass through the main render loop. What the above code gives me is the default filled-in black Rect and self.image is not displayed in any capacity. I tried replacing the first line with
pygame.draw.rect(self.subSurface, color, rect)
but it yielded the same results. From my reading up on other Pygame GUI libraries, it seems what I want to do is possible but I don't think I'm executing it properly. How do I attach other sources/surfaces to subSurface and then have subSurface be drawn (with the sources attached) by displaySurface?
Any help would be much appreciated. Thanks.
For people visiting this question in the future:
Remember that the dest argument for Surface.blit() is relative to the upper-left corner of the destination surface. So if you're assembling an image on a subsurface, remember to use coordinates relative to the top-left corner of the object you're assembling, rather than absolute display coordinates.
So to assemble a scrollbar and draw it somewhere:
class ScrollBar:
# ... code ...
def render(self, display, x, y):
self.subSurface.blit(self.handle_image, (0, self.handle_pos))
self.subSurface.blit(self.upbtn_image, (0, 0))
self.subSurface.blit(self.dnbtn_image, (0, self.height - self.btn_height))
# ... other rendering operations
display.blit(self.subSurface, (x, y))
Adjust all numbers and variable names to taste, but you get the idea. Notice that all the scrollbar elements are positioned in "scrollbar-local" coordinates, with only the final blit to the display surface positioned in screen/application coordinates.

how to create a transparent rectangle responding to click event in Tkinter

I need to draw a rectangle in a tkinter.canvas to respond click event:
click_area = self.canvas.create_rectangle(0,0,pa_width,pa_height,fill='LightBlue',outline='lightBlue',tags=['A','CLICK_AREA'])
self.canvas.tag_bind('CLICK_AREA','<Button>',self.onClickArea)
it works.
at this moment, I have to draw a series of grid on the canvas, and I want them to be covered with the click_area, so that I need to make the click_area transparent.
but, when I wrote like this:
click_area = self.canvas.create_rectangle(0,0,pa_width,pa_height,fill='',outline='lightBlue',tags=['A','CLICK_AREA'])
it did not respond to click any longer.
So, my question is how to make it transparent and keep it responding to click. Or, is there any other way to implement what I want.
Thank you very much.
I came across this same issue trying to use the find_closest Canvas method to modify existing rectangles, but simply binding to the canvas didn't work. The issue is that a Tkinter rectangle without a fill will only respond to clicks on its border.
Then I read about the stipple argument to create_rectangle from here:
stipple: A bitmap indicating how the interior of the rectangle will
be stippled.
Default is stipple="", which means a solid color. A
typical value would be stipple='gray25'. Has no effect unless the fill
has been set to some color. See Section 5.7, “Bitmaps”.
And the section on bitmaps states that only a few stipple options are available by default, but none of them are completely transparent. However, you can specify your own custom bitmap as an X bitmap image (a .xbm file).
XBM files are really just text files with a C-like syntax, so I made my own 2x2 bitmap with all transparent pixels and saved it as transparent.xbm in the same directory as my Tkinter script. Here's the code for the XBM file:
#define trans_width 2
#define trans_height 2
static unsigned char trans_bits[] = {
0x00, 0x00
};
Then, you can specify the custom stipple by prefixing the xbm file name with a # when creating your rectangle:
self.canvas.create_rectangle(
x1,
y1,
x2,
y2,
outline='green',
fill='gray', # still needed or stipple won't work
stipple='#transparent.xbm',
width=2
)
Note, you still need to provided some fill value or the stipple will not be applied. The actual fill value doesn't matter as the stipple will "override" it in the canvas.
I think I got it: Bind the canvas, not the rectangle.
replace
self.canvas.tag_bind('CLICK_AREA','<Button>',self.onClickArea)
with
self.canvas.bind('<Button>',self.onClickArea)
problem solved.

Categories

Resources