Class import in Tkinter Python - python

Hello: I have a few questions:
in the Gallery module widget appears ok to me - 640x480, After import I have a small window, I do not understand why?
How do I move this imported widget to the center of the main window? After import relative to the main window, it appears in the upper right corner. I tried this code fact it can be set but the problem is that here appears a second empty window and this empty one I can operate.
old_code.py
def get_gallery(self):
from GUI.module.gallery import Gallery
Gallery(tk.Toplevel(self.root)).pack(fill=tk.BOTH, expand=1)
x = self.root.winfo_x()
y = self.root.winfo_y()
tk.Toplevel(self.root).geometry("+%d+%d" % (x + 600, y + 800))
How to make this imported window pasted into the main window after import? I.e. so that there are not two windows of the main window and the gallery (after import) but that they constitute one window? Thank you for your help :D
gallery.py
class Gallery(tk.Frame):
code of gallery
if __name__ == '__main__':
root.geometry("%dx%d" % (640, 480))
root.title("Open file")
app = Gallery(tk.Tk())
root.mainloop()
main.py
...
def get_gallery(self):
from GUI.module.gallery import Gallery
Gallery(tk.Toplevel(self.root)).pack()
def get_run_gallery(self):
jpg = tk.Button(self.frame, text="Gallery", command=self.get_gallery)
self.my_canvas.create_window(780, 220, anchor="nw", window=jpg, height=50, width=200)
...
def get_run_first_page():
calling ...
if __name__ == '__main__':
first = MainPage(tk.Tk())
first.get_run_first_page()

Related

Control location on screen where new windows open with graphics.py

I have a python program that deploys a windows via graphics.py. The initial window opened by the GraphWin class opens in the top left corner of the screen. Subsequent calls to GraphWin cascade from the upper left to the lower right.
I'd like to control the placement of each window. (Example: Have all the windows open in a grid-layout so I can create a dashboard.)
I think there is no such method in graphics.py right now.
Ref: The Book and webpage.
If you want to stick to using graphics.py, I suggest creating a dashboard by dividing a single window into different slots.
This option does exist in Tkinter library. Please refer to this answer for more information on that.
graphics.py doesn't provide a way for you to control the location of instances of its GraphWin class. However the fact that it's built on top of Python's Tk GUI toolkit module named tkinter means that sometimes you can work around its limitations by looking at its source code to see how things operate internally.
For example, here's a snippet of code from the module (version 5.0) showing the beginning of GraphWin class' definition from the graphics.py file:
class GraphWin(tk.Canvas):
"""A GraphWin is a toplevel window for displaying graphics."""
def __init__(self, title="Graphics Window",
width=200, height=200, autoflush=True):
assert type(title) == type(""), "Title must be a string"
master = tk.Toplevel(_root)
master.protocol("WM_DELETE_WINDOW", self.close)
tk.Canvas.__init__(self, master, width=width, height=height,
highlightthickness=0, bd=0)
self.master.title(title)
self.pack()
master.resizable(0,0)
self.foreground = "black"
self.items = []
self.mouseX = None
self.mouseY = None
self.bind("<Button-1>", self._onClick)
self.bind_all("<Key>", self._onKey)
self.height = int(height)
self.width = int(width)
self.autoflush = autoflush
self._mouseCallback = None
self.trans = None
self.closed = False
master.lift()
self.lastKey = ""
if autoflush: _root.update()
As you can see it's derived from a tkinter.Canvas widget which has an attribute named master which is a tkinter.Toplevel widget. It then initializes the Canvas base class and specifies the newly created Toplevel window as its parent.
The size and position of a Toplevel window can be controlled by calling its geometry() method as described in the linked documentation. This method expects to be passed a "geometry string" argument in a certain format ('wxh±x±y').
This mean you can take advantage of how this implementation detail in order to put it anywhere you want it and as well as resize if desired.
Here's an example of doing that:
from graphics import *
def main():
win = GraphWin("My Circle", 100, 100)
# Override size and position of the GraphWin.
w, h = 300, 300 # Width and height.
x, y = 500, 500 # Screen position.
win.master.geometry('%dx%d+%d+%d' % (w, h, x, y))
c = Circle(Point(50,50), 10)
c.draw(win)
win.getMouse() # pause for click in window
win.close()
if __name__ == '__main__':
main()
My desktop while script is running:

Tkinter canvas PhotoImage is not appearing

I have a problem with my code. I am creating a small video game called Lumanite. I have created the homepage and have started the graphics generation, but I have run into a bug. I am using Python 3.3 and am on a Win 10 laptop. I run the program through a run file, which accesses the main_game file that uses the classes outlined in a separate file, spritesclasses. I am trying to make a sprite appear. Here is the code for the main_game file and the spritesclasses file. (They import the canvas and root from a MENU file)
#SPRITES
from tkinter import *
from GUI_FILE import canvas, root
from OPENING_FILE import show, hide
class Sprite():
def __init__(self, photoplace):
self.orgin = photoplace
self.photo = PhotoImage(file=photoplace)
self.w = self.photo.width()
self.h = self.photo.height()
def draw(self):
self.sprite = canvas.create_image(self.h, self.w, image=self.photo)
And the MAIN_GAME file:
#Main Game File:
from tkinter import *
from OPENING_FILE import show, hide
from GUI_FILE import root, canvas
from spritesclasses import *
def start_game():
genterrain()
def genterrain():
test = Sprite("logo.gif")
test.draw()
And the sprites are not appearing. No error or anything. Please help me. I will supply you with information at a further notice.
This is a known but tricky issue. You can read about it in Why do my Tkinter images not appear? I've implemented one possible solution below:
from tkinter import *
class Sprite():
def __init__(self, photoplace):
self.photo = PhotoImage(file=photoplace)
self.w = self.photo.width()
self.h = self.photo.height()
self.sprite = None
def draw(self):
canvas = Canvas(root, width=self.w, height=self.h)
canvas.pack()
self.sprite = canvas.create_image(0, 0, anchor=NW, image=self.photo)
def start_game():
genterrain()
def genterrain():
sprite = Sprite("logo.gif")
sprite.draw()
sprites.append(sprite) # keep a reference!
root = Tk()
sprites = []
start_game()
root.mainloop()
The assignment self.photo = PhotoImage(file=photoplace) isn't a sufficient reference as the object test goes out of scope when genterrain() returns and is garbage collected, along with your image. You can test this by commenting out the line sprites.append(sprite) and see your image disappear again.
Also, it wasn't clear why you were positioning the image at it's own width and height -- the first to arguments to create_image() are the X and Y position. I moved canvas creation into draw() so I could size the canvas to the image but that's not a requirement of the visibility fix.

Python:Bad Window Path Name when using deiconify

I've been having this problem with a python program I am making where if I display a TopLevel window, in this case my Help Menu, then withdraw it then try to display it again I get the following error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1533, in __call__
return self.func(*args)
File "C:\Users\****\Documents\GitHub\ProjectName\ProjectName\GUI.py", line 60, in displayHelp
self.helpMenu.display();
File "C:\Users\****\Documents\GitHub\ProjectName\ProjectName\HelpMenu.py", line 35, in display
self.deiconify();
File "C:\Python34\lib\tkinter\__init__.py", line 1646, in wm_deiconify
return self.tk.call('wm', 'deiconify', self._w)
_tkinter.TclError: bad window path name ".60000336"
The error first happened when I was withdrawing from within HelpMenu.py and using deiconify to redisplay it from the GUI.py file.
Since then I have tried multiple ways to fix the problem including calling deiconify from within HelpMenu.py and updating the copy of help menu stored in the GUI when I withdraw it.
I am running Python 3.4.2
I have already done extensive searches online and failed to find a solution to my problem. I have found other mentions of this error but they either didn't relate to my situation or their solutions did not work.
Here is the entire code for the HelpMenu.py followed by an extract from GUI.py that retains the functionality to reproduce the error but has other code removed.
#!/usr/bin/python
try:
from Tkinter import *
except ImportError:
from tkinter import *
class HelpMenu(Toplevel):
def __init__(self, parent, observer):
Toplevel.__init__(self);
self.observer = observer;#Observer is the GUI, this is here just so I can update the GUI when I withdraw this window
self.setup();
self.withdraw();
self.protocol('WM_DELETE_WINDOW', self.quit());#Changes the close button to just hide the window
def setup(self):
self.columnconfigure(0,weight=1);
w = 400;#Sets up the window position on the screen
h = 150;
sw = self.winfo_screenwidth();
sh = self.winfo_screenheight();
x=(sw-w)/2;
y =(sh-h)/2;
self.update();
self.geometry('%dx%d+%d+%d' % (w,h,x,y));
self.resizable(width=0, height=0);
self.grid();
self.title("Help Menu");
def quit(self):#Hides the window
self.withdraw();
self.observer.updateHelp(self);
def display(self):#Re-displays the window
self.deiconify();
Here is code taken from GUI.py and modified to only have the code needed to reproduce the issue.
#!/usr/bin/python
#Allows compatibility with any version of Python by checking for both versions of Tkinter
try:
from Tkinter import *
except ImportError:
from tkinter import *
#Imports the AutoCompleteEntry
from HelpMenu import HelpMenu
class UI(Tk):
def initialize(self):
#Handles setting up most of the GUI
w = 500;#Window width
h = 500;#Window height
sw = self.winfo_screenwidth();#Gets screen width
sh = self.winfo_screenheight();#Gets screen height
x=(sw-w)/2;#Calculates the x position for the left side of the window that allows it to be placed in the center of the screen
y =(sh-h)/2;#Calculates the y position for the top of the window that allows it to be placed in the center of the screen
self.update();#Forces and update on the window
self.geometry('%dx%d+%d+%d' % (w,h,x,y));#Sets the windows width, height and position
self.minsize(int(w),int(h/2));#Sets the minimum size of the window
self.configureMenu();
def updateHelp(self, helpMenu):
self.helpMenu=helpMenu;
def displayHelp(self):
self.helpMenu.display();
def configureMenu(self):
#Handles configuring and setting up the menus
menu = Menu(self);#Setup the menu bar
menu.add_command(label="Help",command=self.displayHelp);
self.config(menu=menu);
def __init__(self, parent):
#Handles the initial call to create a GUI
Tk.__init__(self,parent);#Parent constructor
self.parent = parent;#Store the parent
self.initialize();#Initilize the GUI
self.helpMenu = HelpMenu(self, self);
self.mainloop();#Start the main loop
if __name__ == "__main__":
import sys
main = UI(None);
One last note, I am slightly new to Python, so there might be other errors in my code and while I wont mind if they get pointed out, the main focus I have right now is fixing this path name error.
EDIT:Almost a month now and I have still not found a solution to the problem. Any help would be great but at this point I am probably going to have to abandon my project.
So, after a break I went back to look at this problem again.
Turns out that the issue was self.protocol('WM_DELETE_WINDOW', self.quit()) was not actually calling self.quit() and was destroying the window completely.
A quick change to self.protocol('WM_DELETE_WINDOW', self.quit) seems to have fixed it.
I think maybe the comma causes the problem. Try write it like this:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
try:
from Tkinter import *
except ImportError:
from tkinter import *
class HelpMenu(Toplevel):
def __init__(self, parent, observer):
Toplevel.__init__(self)
self.observer = observer # Observer is the GUI, this is here just so I can update the GUI when I withdraw this window
self.setup()
self.withdraw()
self.protocol('WM_DELETE_WINDOW', self.quit()) # Changes the close button to just hide the window
def setup(self):
self.columnconfigure(0, weight=1)
w = 400 # Sets up the window position on the screen
h = 150
sw = self.winfo_screenwidth()
sh = self.winfo_screenheight()
x = (sw - w) / 2
y = (sh - h) / 2
self.update()
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.resizable(width=0, height=0)
self.grid()
self.title("Help Menu")
def quit(self): # Hides the window
self.withdraw()
self.observer.updateHelp(self)
def display(self): # Re-displays the window
self.deiconify()
class UI(Tk):
def initialize(self):
# Handles setting up most of the GUI
w = 500 # Window width
h = 500 # Window height
sw = self.winfo_screenwidth() # Gets screen width
sh = self.winfo_screenheight() # Gets screen height
x = (sw - w) / 2 # Calculates the x position for the left side of the window that allows it to be placed in the center of the screen
y = (sh - h) / 2 # Calculates the y position for the top of the window that allows it to be placed in the center of the screen
self.update() # Forces and update on the window
self.geometry('%dx%d+%d+%d' % (w, h, x, y)) # Sets the windows width, height and position
self.minsize(int(w), int(h / 2)) # Sets the minimum size of the window
self.configureMenu()
def updateHelp(self, helpMenu):
self.helpMenu = helpMenu
def displayHelp(self):
self.helpMenu.display()
def configureMenu(self):
# Handles configuring and setting up the menus
menu = Menu(self) # Setup the menu bar
menu.add_command(label="Help", command=self.displayHelp)
self.config(menu=menu)
def __init__(self, parent):
# Handles the initial call to create a GUI
Tk.__init__(self, parent) # Parent constructor
self.parent = parent # Store the parent
self.initialize() # Initilize the GUI
self.helpMenu = HelpMenu(self, self)
self.mainloop() # Start the main loop
if __name__ == "__main__":
main = UI(None)
It works perfectly from myside.

How to make a window fullscreen in a secondary display with tkinter?

I know how to make a window fullscreen in the "main" display, but even when moving my app's window to a secondary display connected to my PC, when I call:
self.master.attributes('-fullscreen', True)
to fullscreen that window, it does so in the "main" display and not in the secondary one (the app's window disappears from the secondary display and instantly appears in the "main" one, in fullscreen).
How can I make it fullscreen in the secondary display?
This works on Windows 7: If the second screen width and height are the same as the first one, you can use win1 or win2 geometry of the following code depending its relative position(leftof or rightof) to have a fullscreen in a secondary display:
from Tkinter import *
def create_win():
def close(): win1.destroy();win2.destroy()
win1 = Toplevel()
win1.geometry('%dx%d%+d+%d'%(sw,sh,-sw,0))
Button(win1,text="Exit1",command=close).pack()
win2 = Toplevel()
win2.geometry('%dx%d%+d+%d'%(sw,sh,sw,0))
Button(win2,text="Exit2",command=close).pack()
root=Tk()
sw,sh = root.winfo_screenwidth(),root.winfo_screenheight()
print "screen1:",sw,sh
w,h = 800,600
a,b = (sw-w)/2,(sh-h)/2
Button(root,text="Exit",command=lambda r=root:r.destroy()).pack()
Button(root,text="Create win2",command=create_win).pack()
root.geometry('%sx%s+%s+%s'%(w,h,a,b))
root.mainloop()
Try:
from Tkinter import *
rot = Tk()
wth,hgh = rot.winfo_screenwidth(),rot.winfo_screenheight()
#take desktop width and hight (pixel)
_w,_h = 800,600 #root width and hight
a,b = (wth-_w)/2,(hgh-_h)/2 #Put root to center of display(Margin_left,Margin_top)
def spann():
def _exit():
da.destroy()
da = Toplevel()
da.geometry('%dx%d+%d+%d' % (wth, hgh,0, 0))
Button(da,text="Exit",command=_exit).pack()
da.overrideredirect(1)
da.focus_set()#Restricted access main menu
Button(rot,text="Exit",command=lambda rot=rot : rot.destroy()).pack()
but = Button(rot,text="Show SUB",command=spann)
but.pack()
rot.geometry('%sx%s+%s+%s'%(_w,_h,a,b))
rot.mainloop()
""" Geometry pattern 'WxH+a+b'
W = Width
H = Height
a = Margin_left+Margin_Top"""
Super simple method working in 2021
This works even if both displays are different resolutions. Use geometry to offset the second display by the width of the first display. The format of the geometry string is <width>x<height>+xoffset+yoffset:
root = tkinter.Tk()
# specify resolutions of both windows
w0, h0 = 3840, 2160
w1, h1 = 1920, 1080
# set up a window for first display, if wanted
win0 = tkinter.Toplevel()
win0.geometry(f"{w0}x{h0}+0+0")
# set up window for second display with fullscreen
win1 = tkinter.Toplevel()
win1.geometry(f"{w1}x{h1}+{w0}+0") # <- this is the key, offset to the right by w0
win1.attributes("-fullscreen", True)
As long as you know the width of the first display, this will work fine. The X system TK runs on puts the second monitor to the right of the first one by default.
Windows, Python 3.8
In this solution, pressing F11 will make the window fullscreen on the current screen.
Note that self.root.state("zoomed") is Windows specific according to doc.
self.root.overrideredirect(True) is weird in Windows and may have unwanted side effects. For instance I've had issues related to changing screen configuration with this option active.
#!/usr/bin/env python3
import tkinter as tk
class Gui:
fullScreen = False
def __init__(self):
self.root = tk.Tk()
self.root.bind("<F11>", self.toggleFullScreen)
self.root.bind("<Alt-Return>", self.toggleFullScreen)
self.root.bind("<Control-w>", self.quit)
self.root.mainloop()
def toggleFullScreen(self, event):
if self.fullScreen:
self.deactivateFullscreen()
else:
self.activateFullscreen()
def activateFullscreen(self):
self.fullScreen = True
# Store geometry for reset
self.geometry = self.root.geometry()
# Hides borders and make truly fullscreen
self.root.overrideredirect(True)
# Maximize window (Windows only). Optionally set screen geometry if you have it
self.root.state("zoomed")
def deactivateFullscreen(self):
self.fullScreen = False
self.root.state("normal")
self.root.geometry(self.geometry)
self.root.overrideredirect(False)
def quit(self, event=None):
print("quiting...", event)
self.root.quit()
if __name__ == '__main__':
Gui()

Any quick Python GUI to display live images from Camera

I am trying to display live images from my 1394 camera.
Currently my code is able to obtain images in a loop from the camera and I was looking for any quick GUI that will update dynamically (as a separate thread). I can do this in PyQt maybe using QThreads but is there any recommendation or faster way of doing this??
Here's my code
#Loop capturing frames from camera
for frame in range(1,500):
print 'frame:',frame
TIME.sleep(1) #capture frame every second
image_binary = pycam.cam.RetrieveBuffer()
#convert to PIL Image
pilimg = PIL.Image.frombuffer("L",(cimg.GetCols(),cimg.GetRows()),image_binary,'raw', "RGBA", 0, 1)
# At this point I want to send my image data to a GUI window and display it
Thank you.
Here's wxPython code that will do it...
import wx
from PIL import Image
SIZE = (640, 480)
def get_image():
# Put your code here to return a PIL image from the camera.
return Image.new('L', SIZE)
def pil_to_wx(image):
width, height = image.size
buffer = image.convert('RGB').tostring()
bitmap = wx.BitmapFromBuffer(width, height, buffer)
return bitmap
class Panel(wx.Panel):
def __init__(self, parent):
super(Panel, self).__init__(parent, -1)
self.SetSize(SIZE)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.update()
def update(self):
self.Refresh()
self.Update()
wx.CallLater(15, self.update)
def create_bitmap(self):
image = get_image()
bitmap = pil_to_wx(image)
return bitmap
def on_paint(self, event):
bitmap = self.create_bitmap()
dc = wx.AutoBufferedPaintDC(self)
dc.DrawBitmap(bitmap, 0, 0)
class Frame(wx.Frame):
def __init__(self):
style = wx.DEFAULT_FRAME_STYLE & ~wx.RESIZE_BORDER & ~wx.MAXIMIZE_BOX
super(Frame, self).__init__(None, -1, 'Camera Viewer', style=style)
panel = Panel(self)
self.Fit()
def main():
app = wx.PySimpleApp()
frame = Frame()
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
I thought I'd try PyQt4 imageviewer.py example and it worked for me.
Thanks for all your help guys.
Here's my modified code:
from PyQt4 import QtCore, QtGui
class CameraViewer(QtGui.QMainWindow):
def __init__(self):
super(CameraViewer, self).__init__()
self.imageLabel = QtGui.QLabel()
self.imageLabel.setBackgroundRole(QtGui.QPalette.Base)
self.imageLabel.setScaledContents(True)
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidget(self.imageLabel)
self.setCentralWidget(self.scrollArea)
self.setWindowTitle("Image Viewer")
self.resize(640, 480)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.open)
timer.start(33) #30 Hz
def open(self):
#get data and display
pilimg = getMyPILImageDatFromCamera()
image = PILQT.ImageQt.ImageQt(pilimg)
if image.isNull():
QtGui.QMessageBox.information(self, "Image Viewer","Cannot load %s." % fileName)
return
self.imageLabel.setPixmap(QtGui.QPixmap.fromImage(image))
self.imageLabel.adjustSize()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
CameraViewer = CameraViewer()
CameraViewer.show()
sys.exit(app.exec_())
I recommend using Tkinter since it's already part of python. I've never used PIL but a quick google shows it's easy to use PIL images in Tk widgets (via the pil.ImageTk.PhotoImage() method).
If you already have a Tkinter widget set up to display images (a Label widget works fine) all you need to do is arrange for the image to be updated every second or so. You can do this by using the after command of tkinter.
Here's an example; I don't have PIL so it uses a static image but it illustrates how to use the event loop to fetch images every second:
import Tkinter
class App(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.label = Tkinter.Label(text="your image here", compound="top")
self.label.pack(side="top", padx=8, pady=8)
self.iteration=0
self.UpdateImage(1000)
def UpdateImage(self, delay, event=None):
# this is merely so the display changes even though the image doesn't
self.iteration += 1
self.image = self.get_image()
self.label.configure(image=self.image, text="Iteration %s" % self.iteration)
# reschedule to run again in 1 second
self.after(delay, self.UpdateImage, 1000)
def get_image(self):
# this is where you get your image and convert it to
# a Tk PhotoImage. For demonstration purposes I'll
# just return a static image
data = '''
R0lGODlhIAAgALMAAAAAAAAAgHCAkC6LV76+vvXeswD/ANzc3DLNMubm+v/6zS9PT6Ai8P8A////
/////yH5BAEAAAkALAAAAAAgACAAAAS00MlJq7046803AF3ofAYYfh8GIEvpoUZcmtOKAO5rLMva
0rYVKqX5IEq3XDAZo1GGiOhw5rtJc09cVGo7orYwYtYo3d4+DBxJWuSCAQ30+vNTGcxnOIARj3eT
YhJDQ3woDGl7foNiKBV7aYeEkHEignKFkk4ciYaImJqbkZ+PjZUjaJOElKanqJyRrJyZgSKkokOs
NYa2q7mcirC5I5FofsK6hcHHgsSgx4a9yzXK0rrV19gRADs=
'''
image = Tkinter.PhotoImage(data=data)
return image
if __name__ == "__main__":
app=App()
app.mainloop()
Since the good answers are pretty large, I feel like I should post a library I built specifically for this:
from cvpubsubs.webcam_pub import VideoHandlerThread
import numpy as np
image_np = numpy.array(pilImage)
def update_function(frame, cam_id):
frame[...] = image_np[...]
VideoHandlerThread(video_source=image_np, callbacks=update_function).display()
Actually, that's if image_binary is a new numpy array every time. If it's assigned to the same location, then just this should work:
from cvpubsubs.webcam_pub import VideoHandlerThread
VideoHandlerThread(video_source=image_np).display()
I know OpenCV barely counts as a GUI, but this is quick code wise.
Try to take a look at gstreamer. This is the first result google gave me searching for "gstreamer 1394" and this one is the first for "gstreamer pyqt".

Categories