Any way to make a Python GTK window quit automatically? - python

I'm writing a code that I would like to repeat automatically, and for the most part I have it. If I use a while loop I can get it to repeat, but I have to close the window automatically for it to work. Is there any way I can get it to close by itself?
The program is a slideshow.
Code wise, the loop I'm using looks like this:
while True:
execfile("slideshowtest.py")
If you need the rest of the code it looks like this:
import os
import pygtk
pygtk.require('2.0')
import gtk
import glib
def is_image(filename):
if not os.path.isfile(filename):
return False
for suffix in ['.jpg', '.png', '.bmp']:
if filename.lower().endswith(suffix):
return True
return False
def resizeToFit(image, frame, aspect=True, enlarge=False):
if aspect:
return scaleToFit(image, frame, enlarge)
else:
return stretchToFit(image, frame, enlarge)
def scaleToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
image_aspect = float(image_width) / image_height
frame_aspect = float(frame_width) / frame_height
if not enlarge:
max_width = min(frame_width, image_width)
max_height = min(frame_height, image_height)
else:
max_width = frame_width
max_height = frame_height
if frame_aspect > image_aspect:
height = max_height
width = int(height * image_aspect)
else:
width = max_width
height = int(width / image_aspect)
return (width, height)
def stretchToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
if not enlarge:
width = min(frame_width, image_width)
height = min(frame_height, image_height)
else:
width = frame_width
height = frame_height
return (width, height)
class ResizableImage(gtk.DrawingArea):
def __init__(self, aspect=True, enlarge=False,
interp=gtk.gdk.INTERP_NEAREST, backcolor=None, max=(1600,1200)):
super(ResizableImage, self).__init__()
self.pixbuf = None
self.aspect = aspect
self.enlarge = enlarge
self.interp = interp
self.backcolor = backcolor
self.max = max
self.connect('expose_event', self.expose)
self.connect('realize', self.on_realize)
def on_realize(self, widget):
if self.backcolor is None:
color = gtk.gdk.Color()
else:
color = gtk.gdk.Color(*self.backcolor)
self.window.set_background(color)
def expose(self, widget, event):
self.context = self.window.cairo_create()
self.context.rectangle(
event.area.x, event.area.y,
event.area.width, event.area.height)
self.context.clip()
self.draw(self.context)
return False
def draw(self, context):
rect = self.get_allocation()
x, y = rect.x, rect.y
parent = self.get_parent()
if parent:
offset = parent.get_allocation()
x -= offset.x
y -= offset.y
if self.backcolor:
context.rectangle(x, y, rect.width, rect.height)
context.set_source_rgb(*self.backcolor)
context.fill_preserve()
if not self.pixbuf:
return
width, height = resizeToFit(
(self.pixbuf.get_width(), self.pixbuf.get_height()),
(rect.width, rect.height),
self.aspect,
self.enlarge)
x = x + (rect.width - width) / 2
y = y + (rect.height - height) / 2
context.set_source_pixbuf(
self.pixbuf.scale_simple(width, height, self.interp), x, y)
context.paint()
def set_from_pixbuf(self, pixbuf):
width, height = pixbuf.get_width(), pixbuf.get_height()
if not self.max or (width < self.max[0] and height < self.max[1]):
self.pixbuf = pixbuf
else:
width, height = resizeToFit((width, height), self.max)
self.pixbuf = pixbuf.scale_simple(
width, height,
gtk.gdk.INTERP_BILINEAR)
self.invalidate()
def set_from_file(self, filename):
self.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(filename))
def invalidate(self):
self.queue_draw()
class DemoGtk:
SECONDS_BETWEEN_PICTURES = 2
FULLSCREEN = True
WALK_INSTEAD_LISTDIR = True
def __init__(self):
self.window = gtk.Window()
self.window.connect('destroy', gtk.main_quit)
self.window.set_title('Slideshow')
self.image = ResizableImage( True, True, gtk.gdk.INTERP_BILINEAR)
self.image.show()
self.window.add(self.image)
self.load_file_list()
self.window.show_all()
if self.FULLSCREEN:
self.window.fullscreen()
glib.timeout_add_seconds(self.SECONDS_BETWEEN_PICTURES, self.on_tick)
self.display()
def load_file_list(self):
self.files = []
self.index = 0
if self.WALK_INSTEAD_LISTDIR:
for directory, sub_directories, files in os.walk('.'):
for filename in files:
if is_image(filename):
filepath = os.path.join(directory, filename)
self.files.append(filepath)
else:
for filename in os.listdir('.'):
if is_image(filename):
self.files.append(filename)
print "Images:", self.files
def display(self):
if 0 <= self.index < len(self.files):
self.image.set_from_file(self.files[self.index])
return True
else:
return False
def on_tick(self):
self.index += 1
return self.display()
if __name__ == "__main__":
gui = DemoGtk()
gtk.main()
while True:
execfile("slideshowtest.py")

gtk Functions:
The gtk.main() function runs the main loop until the gtk.main_quit() function is called.
So you need to run your while loop in a separate thread and call main_quit() when you're finished.

Extending Ivan's answer, I needed window to close automatically after a couple of seconds. I ended up with something similar.
import threading
import time
def thread_function(name):
time.sleep(DISPLAY_TIME)
Gtk.main_quit()
x = threading.Thread(target=thread_function, args=(1,))
x.start() # Start thread and let thread sleep for N seconds
Gtk.main() # Start main Gtk
x.join()

Related

How Can I Run This Thread in Python?

Im trying to turn my screen capture into a thread. But I dont understand the error, new to threads. Also any idea why I get a random still image only in return or blank screen when I initialize WindowCapture() with a window name.
main.py
wincap = WindowCapture()
wincap.start()
windowcapture.py
import numpy as np
import win32gui, win32ui, win32con
from threading import Thread, Lock
class WindowCapture:
# threading properties
stopped = True
lock = None
screenshot = None
# properties
w = 0
h = 0
hwnd = None
cropped_x = 0
cropped_y = 0
offset_x = 0
offset_y = 0
# constructor
def __init__(self, window_name=None):
# create a thread lock object
self.lock = Lock()
# find the handle for the window we want to capture.
# if no window name is given, capture the entire screen
if window_name is None:
self.hwnd = win32gui.GetDesktopWindow()
else:
self.hwnd = win32gui.FindWindow(None, window_name)
if not self.hwnd:
raise Exception('Window not found: {}'.format(window_name))
# get the window size
window_rect = win32gui.GetWindowRect(self.hwnd)
self.w = window_rect[2] - window_rect[0]
self.h = window_rect[3] - window_rect[1]
# account for the window border and titlebar and cut them off
border_pixels = 8
titlebar_pixels = 30
self.w = self.w - (border_pixels * 2)
self.h = self.h - titlebar_pixels - border_pixels
self.cropped_x = border_pixels
self.cropped_y = titlebar_pixels
# set the cropped coordinates offset so we can translate screenshot
# images into actual screen positions
self.offset_x = window_rect[0] + self.cropped_x
self.offset_y = window_rect[1] + self.cropped_y
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
#dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
# drop the alpha channel, or cv.matchTemplate() will throw an error like:
# error: (-215:Assertion failed) (depth == CV_8U || depth == CV_32F) && type == _templ.type()
# && _img.dims() <= 2 in function 'cv::matchTemplate'
img = img[...,:3]
# make image C_CONTIGUOUS to avoid errors that look like:
# File ... in draw_rectangles
# TypeError: an integer is required (got type tuple)
# see the discussion here:
# https://github.com/opencv/opencv/issues/14866#issuecomment-580207109
img = np.ascontiguousarray(img)
return img
# find the name of the window you're interested in.
# once you have it, update window_capture()
# https://stackoverflow.com/questions/55547940/how-to-get-a-list-of-the-name-of-every-open-window
#staticmethod
def list_window_names():
def winEnumHandler(hwnd, ctx):
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd))
win32gui.EnumWindows(winEnumHandler, None)
# translate a pixel position on a screenshot image to a pixel position on the screen.
# pos = (x, y)
# WARNING: if you move the window being captured after execution is started, this will
# return incorrect coordinates, because the window position is only calculated in
# the __init__ constructor.
def get_screen_position(self, pos):
return (pos[0] + self.offset_x, pos[1] + self.offset_y)
# threading methods
def start(self):
self.stopped = False
t = Thread(target=self.run)
t.start()
def stop(self):
self.stopped = True
def run(self):
# TODO: you can write your own time/iterations calculation to determine how fast this is
while not self.stopped:
# get an updated image of the game
screenshot = self.get_screenshot()
# lock the thread while updating the results
self.lock.acquire()
self.screenshot = screenshot
self.lock.release()

Tkinter: changing canvas pixel size

Here is what I want to do:
I am designing an application that allows, among other things, to view DICOM images. What I will call "image" is actually a set of files that contain 2D arrays corresponding to slices. I am using Tkinter to provide an UI. My application only requires to display binary images.
In order to display the slices, I use tk.Canvas which allows very fast display of images. Indeed, I need the most optimised displaying device since I want to the user to be able to travel across slices using the mouse wheel.
The problem is: when displaying a slice, the canvas is always allocating the same dimensions to pixels and therefore, images with lower resolution appear very small. What I want to do is to prevent the user from killing his/her eyes by resizing the canvas.
I thought of course of using PIL.Image().resize() on the image that is then converted to PIL.ImageTk() but this causes two problems:
The greater the resizing, the more time is needed to perform the process and therefore the less the viewer is optimised
This resizing action actually modifies the number of pixels, which loses the original resolution. I do not want this to happen since I require to retrieve mouse position as it hovers over the canvas, in terms of pixels in the original resolution
The solution in my opinion would therefore be to modify the pixel size of the canvas. If it is possible to define it from the start, then resizing would not be necessary and there would be no optimisation problem.
But I have not been able to find a way to modify this. Would anyone have an idea?
I am providing only the frame and the imager of my project if it can help:
Frame:
import PIL.Image
import PIL.ImageTk
import numpy as np
from gui.statusbar import *
from tkinter.messagebox import showinfo
class DicomViewerFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.image_ax = None
self.image_sag = None
self.photo_ax = None
self.photo_sag = None
self.canvas_axial = None
self.canvas_sagittal = None
self.imager = None
self.arr_axial = None
self.arr_sagittal = None
self.index_axial = None
self.index_sagittal = None
self.status_axial = None
self.status_sagittal = None
self.roi_definition = True
self.upper = 0
self.lower = 0
self.selection_axial = None
self.selection_sagittal = None
self.start_x = self.start_y = self.start_z = 0
self.end_x = self.end_y = self.end_z = 0
self.roi = None
self.offset = 700
self.init_viewer_frame()
def init_viewer_frame(self):
# Image canvas
self.canvas_axial = Canvas(self, bd=0, highlightthickness=0)
self.canvas_axial.grid(row=0, column=0, sticky="nw")
self.canvas_axial.bind("<MouseWheel>", self.scroll_axial_images)
self.canvas_axial.config(width=self.offset)
if self.roi_definition:
self.canvas_axial.bind("<B1-Motion>", self.on_move_press_axial)
self.canvas_axial.bind("<ButtonPress-1>", self.on_button_press_axial)
self.canvas_axial.bind("<ButtonRelease-1>", self.on_button_release)
self.canvas_sagittal = Canvas(self, bd=0, highlightthickness=0)
self.canvas_sagittal.grid(row=0, column=1, sticky="nw")
self.canvas_sagittal.bind("<MouseWheel>", self.scroll_sagittal_images)
if self.roi_definition:
self.canvas_sagittal.bind("<B1-Motion>", self.on_move_press_sagittal)
self.canvas_sagittal.bind("<ButtonPress-1>", self.on_button_press_sagittal)
self.canvas_sagittal.bind("<ButtonRelease-1>", self.on_button_release)
# Status bar
self.status_axial = StatusBar(self)
self.status_axial.grid(row=3, column=0, sticky="w")
self.status_sagittal = StatusBar(self)
self.status_sagittal.grid(row=3, column=1, sticky="w")
self.canvas_axial.bind('<Motion>', self.motion_axial)
self.canvas_sagittal.bind('<Motion>', self.motion_sagittal)
def on_button_press_axial(self, event):
self.canvas_axial.delete(self.selection_axial)
self.canvas_sagittal.delete(self.selection_sagittal)
# save mouse drag start position
self.start_x = event.x
self.start_y = event.y
self.selection_axial = self.canvas_axial.create_rectangle(self.end_x, self.end_y, 0, 0, outline="green")
self.selection_sagittal = self.canvas_sagittal.create_rectangle(0, self.start_x, self.arr_sagittal.shape[1],
self.end_x, outline="green")
def on_button_press_sagittal(self, event):
self.canvas_sagittal.delete(self.selection_sagittal)
# save mouse drag start position
self.start_z = event.x
self.selection_sagittal = self.canvas_sagittal.create_rectangle(self.start_z, self.start_x, 0,
self.end_x, outline="green")
def on_move_press_axial(self, event):
curX, curY = (event.x, event.y)
self.end_x = curX
self.end_y = curY
self.motion_axial(event)
# expand rectangle as you drag the mouse
self.canvas_axial.coords(self.selection_axial, self.start_x, self.start_y, curX, curY)
self.canvas_sagittal.coords(self.selection_sagittal, 0, self.start_x, self.arr_sagittal.shape[1], curX)
def on_move_press_sagittal(self, event):
curZ = event.x
self.end_z = curZ
self.motion_sagittal(event)
# expand rectangle as you drag the mouse
self.canvas_sagittal.coords(self.selection_sagittal, self.start_z, self.start_x, curZ, self.end_x)
def on_button_release(self, event):
roi_axial = self.canvas_axial.bbox(self.selection_axial)
roi_sagittal = self.canvas_sagittal.bbox(self.selection_sagittal)
self.roi = ((roi_axial[0], roi_axial[1], roi_sagittal[0]), (roi_axial[2], roi_axial[3], roi_sagittal[2]))
def show_image(self, array_axial, index_axial, array_sagittal, index_sagittal):
self.upper = int(self.parent.pcd_preparer.get_current_upper().get())
self.lower = int(self.parent.pcd_preparer.get_current_lower().get())
if array_axial is None:
return
if array_sagittal is None:
return
# Convert numpy array into a PhotoImage and add it to canvas
self.image_ax = PIL.Image.fromarray(array_axial)
self.photo_ax = PIL.ImageTk.PhotoImage(self.image_ax)
self.image_sag = PIL.Image.fromarray(array_sagittal)
self.photo_sag = PIL.ImageTk.PhotoImage(self.image_sag)
self.canvas_axial.delete("IMG")
self.canvas_axial.create_image(0, 0, image=self.photo_ax, anchor=NW, tags="IMG")
self.canvas_axial.create_text(40, 10, fill="green", text="Slice " + str(index_axial), font=10)
self.canvas_axial.create_text(40, 40, fill="green", text="Axial", font=10)
self.canvas_sagittal.delete("IMG")
self.canvas_sagittal.create_image(0, 0, image=self.photo_sag, anchor=NW, tags="IMG")
self.canvas_sagittal.create_text(40, 10, fill="green", text="x = " + str(index_sagittal), font=10)
self.canvas_sagittal.create_text(40, 40, fill="green", text="Sagittal", font=10)
width_ax = self.image_ax.width
height_ax = self.image_ax.height
width_sag = self.image_sag.width
height_sag = self.image_sag.height
self.canvas_axial.configure(width=width_ax, height=height_ax)
self.canvas_sagittal.configure(width=width_sag, height=height_sag)
# We need to at least fit the entire image, but don't shrink if we don't have to
width_ax = max(self.parent.winfo_width(), width_ax)
height_ax = max(self.parent.winfo_height(), height_ax + StatusBar.height)
width_sag = max(self.parent.winfo_width(), width_sag)
height_sag = max(self.parent.winfo_height(), height_sag + StatusBar.height)
# Resize root window and prevent resizing smaller than the image
newsize = "{}x{}".format(width_ax + width_sag, height_ax + StatusBar.height)
self.parent.geometry(newsize)
# self.parent.minsize(width_ax + width_sag, height_ax + height_sag)
if self.selection_axial is not None:
self.selection_axial = self.canvas_axial.create_rectangle(self.start_x, self.start_y, self.end_x,
self.end_y, outline="green")
if self.selection_sagittal is not None:
self.selection_sagittal = self.canvas_sagittal.create_rectangle(self.start_z, self.start_x, self.end_z,
self.end_x, outline="green")
def scroll_sagittal_images(self, e):
self.imager.index_sagittal += int(e.delta / 120)
self.arr_sagittal, self.index_sagittal = self.imager.get_current_sagittal_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def scroll_axial_images(self, e):
self.imager.index_axial += int(e.delta / 120)
self.arr_axial, self.index_axial = self.imager.get_current_axial_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def change_range(self):
self.arr_axial, self.index_axial = self.imager.get_current_axial_image(self.upper, self.lower)
self.arr_sagittal, self.index_sagittal = self.imager.get_current_sagittal_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def set_imager(self, im):
self.imager = im
def motion_axial(self, event):
x, y = event.x, event.y
self.status_axial.set('x = {}, y = {}'.format(x, y))
def motion_sagittal(self, event):
z, y = event.x, event.y
self.status_sagittal.set('y = {}, z = {}'.format(y, z))
Imager:
import numpy as np
class DicomImager:
def __init__(self, datasets):
self.values = None
self.datasets = datasets
self._index_axial = 0
self._index_sagittal = 0
self._window_width = 1
self._window_center = 0
self.size = (int(datasets[0].Rows), int(datasets[0].Columns), len(datasets))
self.spacings = (float(datasets[0].PixelSpacing[0]),
float(datasets[0].PixelSpacing[1]),
float(datasets[0].SliceThickness))
self.axes = (np.arange(0.0, (self.size[0] + 1) * self.spacings[0], self.spacings[0]),
np.arange(0.0, (self.size[2] + 1) * self.spacings[2], self.spacings[2]),
np.arange(0.0, (self.size[1] + 1) * self.spacings[1], self.spacings[1]))
# Load pixel data
self.values = np.zeros(self.size, dtype='int32')
for i, d in enumerate(datasets):
# Also performs rescaling. 'unsafe' since it converts from float64 to int32
np.copyto(self.values[:, :, i], d.pixel_array, 'unsafe')
self.max_value = np.amax(self.values)
self.min_value = np.amin(self.values)
#property
def index_sagittal(self):
return self._index_sagittal
#index_sagittal.setter
def index_sagittal(self, value):
while value < 0:
value += self.size[0]
self._index_sagittal = value % self.size[0]
#property
def index_axial(self):
return self._index_axial
#index_axial.setter
def index_axial(self, value):
while value < 0:
value += self.size[2]
self._index_axial = value % self.size[2]
#property
def window_width(self):
return self._window_width
#window_width.setter
def window_width(self, value):
self._window_width = max(value, 1)
#property
def window_center(self):
return self._window_center
#window_center.setter
def window_center(self, value):
self._window_center = value
def get_sagittal_image(self, index, upper, lower):
# int32 true values (HU or brightness units)
img = self.values[index, :, :]
res1 = np.zeros(img.shape)
res1[img < upper] = 1
res1[img < lower] = 0
# Cast to RGB image so that Tkinter can handle it
res = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
res[:, :, 0] = res[:, :, 1] = res[:, :, 2] = res1 * 255
return res
def get_axial_image(self, index, upper, lower):
# int32 true values (HU or brightness units)
img = self.values[:, :, index]
res1 = np.zeros(img.shape)
res1[img < upper] = 1
res1[img < lower] = 0
# Cast to RGB image so that Tkinter can handle it
res = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
res[:, :, 0] = res[:, :, 1] = res[:, :, 2] = res1 * 255
return res
def get_current_sagittal_image(self, upper, lower):
return self.get_sagittal_image(self._index_sagittal, upper, lower), self._index_sagittal
def get_current_axial_image(self, upper, lower):
return self.get_axial_image(self._index_axial, upper, lower), self._index_axial
The solution in my opinion would therefore be to modify the pixel size of the canvas... But I have not been able to find a way to modify this. Would anyone have an idea?
There is no way to modify the size of a pixel in the canvas. Your only option is to resize the image.

How to put a button image borderless/tansparent

I'm trying to put my button borderless so we can we see the progressbar on the background
I tried all the forum but coundt put the border of the button round or make something like the second picture
this is the part of the code for the button :
bg3 = Image.open("blanc.png")
resized_bg03 = bg3.resize((20, 20),Image.ANTIALIAS)
new_bg03 = ImageTk.PhotoImage(resized_bg03)
s = Button(Fenetre,image=new_bg03,borderwidth=0,highlightthickness=0)
s.place(x=600, y=10,height = 20 , width = 20)
I would like to have something more like this with a circle and no border :
can someone help me :)
Try this:
import tkinter as tk
# Kindly plagiarised from: https://stackoverflow.com/a/17985217/11106801
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
# Please suggest a better name for the class
class ProgressBar(tk.Canvas):
def __init__(self, height=20, radius=6, width=400, circle_colour="black",
colour="red", bar_width=6, bd=0, highlightthickness=0,
anti_bar_colour="white", **kwargs):
super().__init__(height=height, width=width, bd=bd,
highlightthickness=highlightthickness, **kwargs)
self.radius = radius
self.height = height
self.width = width
self.rectangle = None
self.circle_colour = circle_colour
self.colour = colour
self.bar_width = bar_width
self.circle = None
self.button_1_down = False
self.create_anti_bar(anti_bar_colour)
self.progress = 0
super().bind("<Enter>", self.show_circle)
super().bind("<Leave>", self.hide_circle)
super().bind("<Button-1>", self.mouse_click)
super().bind("<ButtonRelease-1>", self.mouse_release)
super().bind("<B1-Motion>", self.mouse_motion)
def create_anti_bar(self, colour):
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = self.width - self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
super().create_rectangle(start_x, start_y, end_x, end_y,fill=colour,
outline=colour)
def mouse_click(self, event):
self.button_1_down = True
self.progress = (event.x - self.radius)/(self.width - 2*self.radius)
def mouse_release(self, event=None):
self.button_1_down = False
def mouse_motion(self, event):
if self.button_1_down:
self.mouse_click(event)
def hide_circle(self, event=None):
if self.circle is not None:
super().delete(self.circle)
self.circle = None
def show_circle(self, event=None):
# Try removing the circle if we can
self.hide_circle()
x = (self.width - 2*self.radius)*self._progress + self.radius
self.circle = super().create_circle(x, self.height//2, self.radius,
fill=self.circle_colour,
outline=self.circle_colour)
def update_bar(self):
# Try removing the progress bar
if self.rectangle is not None:
super().delete(self.rectangle)
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = (self.width - 2*self.radius)*self._progress + self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
self.rectangle = super().create_rectangle(start_x, start_y, end_x,
end_y, fill=self.colour,
outline=self.colour)
#property
def progress(self):
return self._progress
#progress.setter
def progress(self, new_value):
# Check if the new_value is in the correct range
if new_value < 0:
new_value = 0
elif new_value > 1:
new_value = 1
# Update self._progress
self._progress = new_value
# Update the progress bar
self.update_bar()
# If the circle was shown update it
if self.circle is not None:
self.show_circle()
if __name__ == "__main__":
root = tk.Tk()
pbar = ProgressBar()
pbar.pack()
pbar.progress = 0
# keep incrementing the progress until the end then stop
def increment_progress():
pbar.progress += 0.001
if pbar.progress >= 1:
return None
pbar.after(10, increment_progress)
increment_progress()
root.mainloop()
Tell me if you don't get what any of the methods do. It is too much code to properly annotate

How to pass pixbuf from a class in a module,after user interaction, in Python

Im working on a python module to handle screenshots. The module itself works fine if alone. however when i import it into my main project i cant figure out how to return a pixbuf after waiting for the user to select a region to capture.
here is the screenshot module:
class CaptureRegion():
def __init__(self):
self.ThePic = None
self.drawArea = Gtk.DrawingArea()
self.drawArea.connect('draw',self.onDraw)
mBox = Gtk.VBox(False,2)
mBox.pack_start(self.drawArea,True,True,2)
self.MaskWin = Gtk.Window()
self.MaskWin.set_position(Gtk.WindowPosition.CENTER)
self.MaskWin.connect("key-press-event", self.KeyPress)
self.MaskWin.connect("button-press-event", self.ButtonPress)
self.MaskWin.connect("button-release-event", self.ButtonRelease)
self.MaskWin.connect("motion-notify-event", self.Motion)
self.MaskWin.add(mBox)
self.button_x = 0
self.button_y = 0
self.button_down = False
self.sel_rect = Gdk.Rectangle(-10, -10, 10, 10)
self.capfull = CaptureScreen()
self.fullpixbuf = self.capfull.Go()
def onDraw(self, widget, event):
print("Draw")
myGdkWindow = self.MaskWin.get_window()
self.cr = Gdk.cairo_create(myGdkWindow)
self.cr.set_operator(cairo.OPERATOR_SOURCE)
self.cr.set_source_rgb(1,1,1)
Gdk.cairo_set_source_pixbuf(self.cr, self.fullpixbuf, 5, 5)
self.cr.paint()
self.cr.set_line_width(1)
self.cr.set_source_rgb(0,0,0)
if self.button_down == True:
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
if w <= 0 or h <= 0:
return True
self.cr.rectangle(x,y,w,h)
self.cr.set_source_rgba(0.2, 0.6, 0.8, 0.35)
self.cr.set_line_width(2)
self.cr.stroke()
return True
def Motion(self,widget,event):
if self.button_down == False:
return False
x1,y1 = self.button_x, self.button_y
x2,y2 = event.x, event.y
x = int(min(x1,x2))
y = int(min(y1,y2))
w = int(abs(x1-x2))
h = int(abs(y1-y2))
old = self.sel_rect
self.sel_rect.x = x
self.sel_rect.y = y
self.sel_rect.width = w
self.sel_rect.height = h
win = self.MaskWin.get_window()
sx, sy, sw, sh = self.MaskWin.get_window().get_geometry()
self.drawArea.queue_draw_area(sx,sy,sw,sh)
def ButtonPress(self,widget,event):
if not event.button == 1:
return False
print("button down")
self.debounce = 0
self.button_x, self.button_y = event.x, event.y
self.button_down = True
return True
def ButtonRelease(self,widget,event):
self.button_down = False
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
self.drawArea.queue_draw_area(x-1, y-1, w-1, h-1)
if self.debounce == 0:
self.debounce += 1
print("Snipping x:%s y:%s w:%s h:%s" %(x,y,w,h))
self.ThePic = Gdk.pixbuf_get_from_window(self.MaskWin.get_window(), x, y, w, h)
self.MaskWin.hide()
#option 2: to return ThePic here after the selection is made thru a callback to a function in the main class
# i.e.
#self.CallbackInMain(self.ThePic) (not sure how to callback to the main class, self is wrong maybe?)
def KeyPress(self,widget,event):
if event.keyval == Gdk.KEY_Escape:
self.ThePic = None
return
def WaitThread(self):
for i in range(1, 50):
time.sleep(0.1)
print(i)
self.done_waiting.set()
def Go(self):
self.MaskWin.fullscreen()
self.MaskWin.show_all()
self.MaskWin.set_decorated(False)
#option 1 find a way to wait for the selection to complete like this:
#(this code doesnt end, the self.done_waiting.set() call doesnt stop it)
# self.done_waiting = threading.Event()
# self.thread = threading.Thread(target=self.WaitThread)
# self.thread.start()
# self.done_waiting.wait(1)
# print("Main thread is done waiting")
return(self.ThePic)
class CaptureScreen():
def __init__(self):
self.ThePic = None
def Go(self):
screen = Gdk.get_default_root_window()
x, y, width, height = screen.get_geometry()
self.fullpixbuf = Gdk.pixbuf_get_from_window(screen, x, y, width, height)
return(self.fullpixbuf)
im calling the class like this:
import Screentshot as Shot
self.captureregion = Shot.CaptureRegion()
pic = self.captureregion.Go()
pic.savev("region.png",'png',(),())
with the CaptureScreen class im just capturing the whole screen so i can return the pixbuf directly from the CaptureScreen.Go() function. however with the CaptureRegion class i have to wait for the user to select an area, and im not sure how to have CaptureRegion.Go() return the pixbuf after the users selection. I have two ideas, one use threading to wait for the selection to complete and then return the pixbuf from Capture Region.Go() (option 1 in code) , or second somehow have the screenshot class callback to function in main with the pixbuf as an arg(option 2 in code)
Im not very familiar with classes nor have i ever used the threading module. ive searched and found lots of info, i just cant quite wrap my head around what i need to do.
Of course now that i finally post a question about this, i figured it out. i just needed to add a second arg to Go(self,mainself) and call back to a function in the main code.
this is what i changed:
def ButtonRelease(self,widget,event):
self.button_down = False
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
self.drawArea.queue_draw_area(x-1, y-1, w-1, h-1)
if self.debounce == 0:
self.debounce += 1
print("Snipping x:%s y:%s w:%s h:%s" %(x,y,w,h))
self.ThePic = Gdk.pixbuf_get_from_window(self.MaskWin.get_window(), x, y, w, h)
self.MaskWin.hide()
self.GotTheShot = True
self.mainself.ScreenShotCallBack(self.ThePic)
def Go(self,main):
self.mainself = main
self.GotTheShot = False
self.MaskWin.fullscreen()
self.MaskWin.show_all()
self.MaskWin.set_decorated(False)
and here is the call and callback:
def ScreenShotRegion(self):
pic = self.captureregion.Go(self)# self,mainself... self passed by default
pic.savev("region.png",'png',(),())
def ScreenShotCallBack(self,capturepixbuf):
self.window.show_all()
capturepixbuf.savev("region.png",'png',(),())

Grid Generation for minesweeper

Hi so I am making a minesweeper game and I am a bit stuck with the grid generation part. This is my code so far:
from random import randint
import pygame
def MineGen():
mineamount = 100
grid_across = 40
grid_up = 25
mine_list = []
my2dthatlist = []
numacoss = 0
for i in range (mineamount):
numacoss = randint(1,40)
my2dthatlist.append(numacoss)
numup = randint(1,25)
my2dthatlist.append(numup)
mine_list.append(my2dthatlist)
my2dthatlist = []
return mine_list
def GridGen():
grid_across = 40
grid_up = 25
GRIDD = [[0]* grid_across for i in range(grid_up)]
return GRIDD
def MineGrid(GridOutMine, mine_list):
mineplace = 0
placeX = 0
placeY = 0
for i in range(100):
mineplace = mine_list[i]
placeX = mineplace[0]
placeY = mineplace[1]
GridOutMine[placeX][placeY] = 1
print(GridOutMine)
mine_list = MineGen()
GridOutMine = GridGen()
MineGrid(GridOutMine, mine_list)
My issue is that i am getting a list index out of range for the
GridOutMine[placeX][placeY] = 1
part. I don't really know why this is. If you could give me some assistance in what to do, or just some general comments on my code, I would really appreciate it thanks.
That's because, unlike range, random.randint outputs numbers within the specified bounds inclusively. That is, randint(1, 25) could output 25, which is not a valid index for a list that's only 25 elements long (since the last index is 24).
In MineGen, you need to change randint(1, 25) to randint(1, 25-1) or randint(1, 24), and likewise for randint(1, 40) which needs to be randint(1, 39). I'd actually suggest randint(0, 24) and (0, 39), but I don't know if that's intentional.
There are many other things that could/should be improved about this code, but I'd suggest you ask for that kind of input over on CodeReview instead of here once your code is working (they don't fix broken code).
EDIT:
Also, you're indexing your grid in the wrong order. It's a list (25-long) of rows (40-long), so you need to index it in the Y dimension first, then X: GridOutMine[placeY][placeX] = 1
If you are trying to build a grid for the game and want to display buttons in a GUI your users can interact with, you may want to start with the following code as a basic framework for the rest of the code you will be writing.
import tkinter
import functools
class MineSweep(tkinter.Frame):
#classmethod
def main(cls, width, height):
root = tkinter.Tk()
window = cls(root, width, height)
root.mainloop()
def __init__(self, master, width, height):
super().__init__(master)
self.__width = width
self.__height = height
self.__build_buttons()
self.grid()
def __build_buttons(self):
self.__buttons = []
for y in range(self.__height):
row = []
for x in range(self.__width):
button = tkinter.Button(self)
button.grid(column=x, row=y)
button['text'] = '?'
command = functools.partial(self.__push, x, y)
button['command'] = command
row.append(button)
self.__buttons.append(row)
def __push(self, x, y):
print('Column = {}\nRow = {}'.format(x, y))
if __name__ == '__main__':
MineSweep.main(10, 10)
If you want a more complete example of a minesweeper game to either borrow ideas from or adapt for your own needs, the following program implements much of the functionality you might want from a finished game.
import tkinter
import functools
import random
from tkinter.simpledialog import askstring, Dialog
from tkinter.messagebox import showinfo
import os.path
################################################################################
class MineSweep(tkinter.Frame):
#classmethod
def main(cls, width, height, mines, scores):
root = tkinter.Tk()
root.resizable(False, False)
root.title('MineSweep')
window = cls(root, width, height, mines, scores)
root.protocol('WM_DELETE_WINDOW', window.close)
root.mainloop()
################################################################################
def __init__(self, master, width, height, mines, scores):
super().__init__(master)
self.__width = width
self.__height = height
self.__mines = mines
self.__wondering = width * height
self.__started = False
self.__playing = True
self.__scores = ScoreTable()
self.__record_file = scores
if os.path.isfile(scores):
self.__scores.load(scores)
self.__build_timer()
self.__build_buttons()
self.grid()
def close(self):
self.__scores.save(self.__record_file)
self.quit()
def __build_timer(self):
self.__secs = tkinter.IntVar()
self.__timer = tkinter.Label(textvariable=self.__secs)
self.__timer.grid(columnspan=self.__width, sticky=tkinter.EW)
self.__after_handle = None
def __build_buttons(self):
self.__reset_button = tkinter.Button(self)
self.__reset_button['text'] = 'Reset'
self.__reset_button['command'] = self.__reset
self.__reset_button.grid(column=0, row=1,
columnspan=self.__width, sticky=tkinter.EW)
self.__reset_button.blink_handle = None
self.__buttons = []
for y in range(self.__height):
row = []
for x in range(self.__width):
button = tkinter.Button(self, width=2, height=1,
text='?', fg='red')
button.grid(column=x, row=y+2)
command = functools.partial(self.__push, x, y)
button['command'] = command
row.append(button)
self.__buttons.append(row)
def __reset(self):
for row in self.__buttons:
for button in row:
button.config(text='?', fg='red')
self.__started = False
self.__playing = True
self.__wondering = self.__width * self.__height
if self.__after_handle is not None:
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__secs.set(0)
def __push(self, x, y, real=True):
button = self.__buttons[y][x]
if self.__playing:
if not self.__started:
self.__build_mines()
while self.__buttons[y][x].mine:
self.__build_mines()
self.__started = True
self.__after_handle = self.after(1000, self.__tick)
if not button.pushed:
self.__push_button(button, x, y)
elif real:
self.__blink(button, button['bg'], 'red')
elif real:
self.__blink(button, button['bg'], 'red')
def __blink(self, button, from_bg, to_bg, times=8):
if button.blink_handle is not None and times == 8:
return
button['bg'] = (to_bg, from_bg)[times & 1]
times -= 1
if times:
blinker = functools.partial(self.__blink, button,
from_bg, to_bg, times)
button.blink_handle = self.after(250, blinker)
else:
button.blink_handle = None
def __tick(self):
self.__after_handle = self.after(1000, self.__tick)
self.__secs.set(self.__secs.get() + 1)
def __push_button(self, button, x, y):
button.pushed = True
if button.mine:
button['text'] = 'X'
self.__playing = False
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__blink(self.__reset_button, button['bg'], 'red')
else:
button['fg'] = 'SystemButtonText'
count = self.__total(x, y)
button['text'] = count and str(count) or ' '
self.__wondering -= 1
if self.__wondering == self.__mines:
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__finish_game()
def __finish_game(self):
self.__playing = False
score = self.__secs.get()
for row in self.__buttons:
for button in row:
if button.mine:
button['text'] = 'X'
if self.__scores.eligible(score):
name = askstring('New Record', 'What is your name?')
if name is None:
name = 'Anonymous'
self.__scores.add(name, score)
else:
showinfo('You did not get on the high score table.')
HighScoreView(self, 'High Scores', self.__scores.listing())
def __total(self, x, y):
count = 0
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__width and 0 <= y_index < self.__height:
count += self.__buttons[y_index][x_index].mine
if not count:
self.__propagate(x, y)
return count
def __propagate(self, x, y):
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__width and 0 <= y_index < self.__height:
self.__push(x_index, y_index, False)
def __build_mines(self):
mines = [True] * self.__mines
empty = [False] * (self.__width * self.__height - self.__mines)
total = mines + empty
random.shuffle(total)
iterator = iter(total)
for row in self.__buttons:
for button in row:
button.mine = next(iterator)
button.pushed = False
button.blink_handle = None
################################################################################
class ScoreTable:
def __init__(self, size=10):
self.__data = {999: [''] * size}
def add(self, name, score):
assert self.eligible(score)
if score in self.__data:
self.__data[score].insert(0, name)
else:
self.__data[score] = [name]
if len(self.__data[max(self.__data)]) == 1:
del self.__data[max(self.__data)]
else:
del self.__data[max(self.__data)][-1]
def eligible(self, score):
return score <= max(self.__data)
def listing(self):
for key in sorted(self.__data.keys()):
for name in self.__data[key]:
yield name, key
def load(self, filename):
self.__data = eval(open(filename, 'r').read())
def save(self, filename):
open(filename, 'w').write(repr(self.__data))
################################################################################
class HighScoreView(Dialog):
def __init__(self, parent, title, generator):
self.__scores = generator
super().__init__(parent, title)
def body(self, master):
self.__labels = []
for row, (name, score) in enumerate(self.__scores):
label = tkinter.Label(master, text=name)
self.__labels.append(label)
label.grid(row=row, column=0)
label = tkinter.Label(master, text=str(score))
self.__labels.append(label)
label.grid(row=row, column=1)
self.__okay = tkinter.Button(master, command=self.ok, text='Okay')
self.__okay.grid(ipadx=100, columnspan=2, column=0, row=row+1)
return self.__okay
def buttonbox(self):
pass
################################################################################
if __name__ == '__main__':
MineSweep.main(10, 10, 10, 'scores.txt')
Reference: ActiveState Code » Recipes » MineSweep

Categories