PIL problem, but only when called from within another object( Image doesn't exist) - image

I am trying to develop a python app for a RaspberryPi on a PC. In order to test one aspect, I have written an object class to emulate an OLED display device (hardware) that the PC cannot use (no I2C). The Wdisp class is simply loaded in place of the hardware driver when using the PC.
I have another class that wraps up the few OLED functions I need ( write text , draw progressbar etc. )
# OLED display object
import os
Win = False
if os.name == 'nt':
Win=True
from WindowsOLED import Wdisp
else:
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
class OLEDDisplay(object):
def __init__(self):
# Raspberry Pi pin configuration:
RST = 24
if Win:
self.disp = Wdisp()
else:
self.disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
# Initialize library.
self.disp.begin()
# Create blank image for drawing.
self.width = 128 #self.disp.width
self.height = 64 #self.disp.height
self._image = Image.new('1', (self.width, self.height))
self.draw = ImageDraw.Draw(self._image)
# Load default font.
self.font = ImageFont.load_default()
self.clear()
def __del__(self):
self.clear()
def __repr__(self):
return 'OLED display object for SSD1306 I2C'
def clear(self):
# Clear display
self.disp.clear()
self.disp.display()
def write(self, text):
self.draw.text((0, 2), text, font=self.font, fill=255)
self._show()
def progressbar(self, percent):
#draw a progress bar on top half of screen
shape_width = self.width * (percent/100)
padding =2
top = padding
bottom = (self.height/2) - padding
x = padding
self.draw.rectangle((x, top, self.width-padding, bottom), outline=255, fill=0)
self.draw.rectangle((x, top, x+shape_width, bottom), outline=255, fill=255)
self._show()
def loadimage(self, imagefile):
#open, convert and display an image
self._image = Image.open(imagefile).resize((self.disp.width,
self.disp.height),
Image.ANTIALIAS).convert('1')
self._show()
def _show(self):
# Display image.
self.disp.image(self._image)
self.disp.display()
If I test this class at the command line
Example:
>>> from OLEDDisplay import*
>>> OD = OLEDDisplay()
>>> OD.progressbar(50)
Its works fine - the OLEDDisplay object draws on a PIL image object, passes the image to the Wdisp object and the Wdisp object converts the image to a BitMapImage and displays in on a Tkinter Label ( self.L ).
#Windows subsitute OLED display class
#displays a label image for testing
from tkinter import *
from PIL import Image, ImageTk
from tkinter import filedialog, messagebox
from tkinter.constants import *
class Wdisp(object):
i=None
width = 128
height =64
_image = None
def __init__(self):
self.master = Tk()
self._image = Image.new('1', (self.width, self.height))
self.i = ImageTk.PhotoImage(self._image)
self.L= Label(self.master, text="label place holder", image=self.i)
self.L.pack(expand=YES, fill=BOTH)
def begin(self):
pass
def image(self, newimage):
self._image = newimage
def display(self):
#display the image
self.i = ImageTk.BitmapImage(self._image)
self.L.config(image=self.i)
However, if I use this in my app or test it in a small app:
from tkinter import *
from PIL import Image, ImageTk
from tkinter import filedialog, messagebox
from tkinter.constants import *
from OLEDDisplay import*
def updateOLED():
text = E.get
print(text)
OD.write(text)
master = Tk()
OD = OLEDDisplay()
E = Entry(master)
B = Button(master, text="update OLED", command = updateOLED)
E.pack()
B.pack()
It fails with a tkinter.TclError: image "pyimage4" doesn't exist:
Traceback (most recent call last):
File "D:\My Documents\PI Projects\Pyprojects\PTPIC\OLEDDisplay test.py", line 19, in <module>
OD = OLEDDisplay()
File "D:\My Documents\PI Projects\Pyprojects\PTPIC\OLEDDisplay.py", line 42, in __init__
self.clear()
File "D:\My Documents\PI Projects\Pyprojects\PTPIC\OLEDDisplay.py", line 52, in clear
self.disp.clear()
File "D:\My Documents\PI Projects\Pyprojects\PTPIC\WindowsOLED.py", line 45, in clear
self.display()
File "D:\My Documents\PI Projects\Pyprojects\PTPIC\WindowsOLED.py", line 36, in display
self.L.config(image=self.i)
File "C:\Python34\lib\tkinter\__init__.py", line 1324, in configure
return self._configure('configure', cnf, kw)
File "C:\Python34\lib\tkinter\__init__.py", line 1315, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: image "pyimage4" doesn't exist
I have no idea why : perhaps variable scope? perhaps something weird with PIL , so any suggestions welcome.
Cheers Bill

Related

pyqtgrapgh: Image Item not in the center of GraphicsView

I am using pyqtgraph to visualate 2D arrays. Due to the fact that I have to refresh the visualated Image because of a real time application which I want to evaluate I am using GraphicsView(). Then I create a ImageItem and I want to add the Item to the created window. Unfortunately the image is only visible in the left upper corner of the window. I know that I can show the full image without GraphicsView but as I said I will need GraphicsView later to visualate the updated values in real time for an application.
Here is my current code:
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import struct
import sounddevice as sd
from scipy.fftpack import fft
import sys
import time
class Fenster(pg.GraphicsLayoutWidget): #objektorientiert arbeiten!!
def __init__(self): #constructor
super().__init__() #ruft den constructor von QWidget auf
self.initMe()
class Fenster(pg.GraphicsLayoutWidget):
def __init__(self):
super().__init__()
self.initMe()
def initMe(self):
self.win = pg.GraphicsView()
self.win.show()
self.plot_data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100))
self.img = pg.ImageItem(image=self.plot_data)
self.cm = pg.colormap.get('CET-L9')
self.bar = pg.ColorBarItem( values= (0, 20_000), colorMap=self.cm )
self.bar.setImageItem(self.img)
self.win.setCentralItem(self.img)
w = Fenster()
That is the output image:
How can I set that the image fullfill the whole window?
How can I do that the created ColorBarItem will be showed in the window? (optionally)
Thanks in advance.
I found the solution. You have to create a PlotItem. Then add the ImageItem to the PlotItem and set the PlotItem in the center of GraphicsView
self.win = pg.GraphicsView()
self.win.show()
self.p = pg.PlotItem()
self.win.setCentralItem(self.p)
self.plot_data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100))
self.img = pg.ImageItem(image=self.plot_data)
self.cm = pg.colormap.get('CET-L9')
self.bar = pg.ColorBarItem( values= (0, 20_000), colorMap=self.cm )
self.bar.setImageItem(self.img)
self.p.addItem(self.img)
I also figured out to update the plot (live plot). Leave a comment if I should share.

Game Of Life - Embedding the pygame window in tkinter to add buttons [duplicate]

This question already has answers here:
Embedding a Pygame window into a Tkinter or WxPython frame
(3 answers)
Closed last year.
So I'm making a game in pygame, and I want to use tkinter as well. I embedded a pygame window into a tkinter window, but I can't seem to do anything with it.
For context, here is the full code:
import Tkinter as tk
import os
import platform
import pygame
class window(object):
def __init__(self):
self.root = tk.Tk() # Main window
self.root.title("SquareScape")
self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
self.root.configure(background='#9b9b9b')
# Large Frame
self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)
# menu (left side)
self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')
# pygame
self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)
# Packing
self.win_frame.pack(expand=True)
self.win_frame.pack_propagate(0)
self.menu.pack(side="left")
self.menu.pack_propagate(0)
self.menu_label.pack(ipadx=60, ipady=2)
self.mute.pack(ipadx=40, ipady=2, pady=5)
self.pygame_frame.pack(side="left")
self.embed.pack()
#This embeds the pygame window
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
if platform.system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib'
#Start pygame
pygame.init()
self.win = pygame.display.set_mode((512, 512))
self.win.fill(pygame.Color(255, 255, 255))
pygame.display.init()
self.root.mainloop()
screen = window()
#Here is sample code that I want to run
pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100))
When I use pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100)), nothing happens. Using pygame inside the class worked, but in my more complicated game, using self.variable for all my variables seems unnecessary.
How can I run my code in the pygame window outside of the window class?
So - besides the obvious missing call to pygame.display.flip - which I suppose is what you intended with the call to pygame.display.init (pygame.init already calls that one) - what I found out is that tkinter needs to initialize its windows and widgets before the packed frame is fully available to be used by Pygame.
I did that by adding a call to self.root.update_idletasks() before calling pygame.init -- that, and explicitly setting the video driver for my platform (which you already does for Windows), made things work.
Anyway, also, in your code you did not show were you wanted to make the calls to Pygamedrawing functions - as it is, it is well possible that everything is correct, but the code after screen.window() is just never run (or rather, just run at program exit) - because you call tkinter.mainloop inside the __init__ method of your application class.
Moving the call to mainloop outside the __init__ is a good practice, so you can initialize other objects and resources as well - and you actualy do have the screen object to operate things on. By making that call inside __init__ is like your whole program was running "inside the initialization".
In short:
call tkinter.update_iddletasks() before initializing pygame
remember to call pygame.display.flip after you draw anything with Pygame
arrange your code so that your drawing calls are actually executed, and not blocked after the call to enter tkinter's loop
You should seriously consider using Python 3.7 or later - (the only "python 2" code there is import Tkinter which becomes import tkinter in Python 3). Python 2 is really at the end of line, and there are no updates for projects like pygame on it.
.
That said, here is your code, modified to run on Linux + Python 3 (should still work on Windows), and to actually perform some actions using the embedded pygame frame.
import tkinter as tk
import os
import platform
import pygame
import time
class window(object):
def __init__(self):
self.root = tk.Tk() # Main window
self.root.title("SquareScape")
# self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
self.root.configure(background='#9b9b9b')
# Large Frame
self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)
# menu (left side)
self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')
tk.Button(self.menu, text="<->", command=lambda: setattr(self, "direction", (-self.direction[0], self.direction[1]))).pack()
tk.Button(self.menu, text="^", command=lambda: setattr(self, "direction", (self.direction[0], -self.direction[1]))).pack()
# pygame
self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)
# Packing
self.win_frame.pack(expand=True)
self.win_frame.pack_propagate(0)
self.menu.pack(side="left")
self.menu.pack_propagate(0)
self.menu_label.pack(ipadx=60, ipady=2)
self.mute.pack(ipadx=40, ipady=2, pady=5)
self.pygame_frame.pack(side="left")
self.embed.pack()
#This embeds the pygame window
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
system = platform.system()
if system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib'
elif system == "Linux":
os.environ['SDL_VIDEODRIVER'] = 'x11'
self.root.update_idletasks()
#Start pygame
pygame.init()
self.win = pygame.display.set_mode((512, 512))
self.bg_color = (255, 255, 255)
self.win.fill(self.bg_color)
self.pos = 0, 0
self.direction = 10, 10
self.size = 40
self.color = (0, 255, 0)
self.root.after(30, self.update)
self.root.mainloop()
def update(self):
first_move = True
pygame.draw.rect(self.win, self.bg_color, self.pos + (self.size, self.size))
self.pos = self.pos[0] + self.direction[0], self.pos[1] + self.direction[1]
if self.pos[0] < 0 or self.pos[0] > 512 - self.size:
self.direction = -self.direction[0], self.direction[1]
self.pos = self.pos[0] + 2 * self.direction[0], self.pos[1] + self.direction[1]
if self.pos[1] < 0 or self.pos[1] > 512 - self.size:
self.direction = self.direction[0], -self.direction[1]
self.pos = self.pos[0] + self.direction[0], self.pos[1] + 2 * self.direction[1]
pygame.draw.rect(self.win, self.color, self.pos + (self.size, self.size))
pygame.display.flip()
self.root.after(30, self.update)
screen = window()
tk.mainloop()

python - checkbox in new window update fails and stays false

import matplotlib
from tkinter import *
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import tkinter as tk
from tkinter import ttk
import numpy as np
import matplotlib.animation as animation
from matplotlib import style
from matplotlib import pyplot as plt
from scipy.misc import *
import webbrowser
import subprocess
import csv
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
from os import startfile
LARGE_FONT = ("Times", 11, "bold italic")
NORM_FONT = ("Helvetica", 9)
SMALL_FONT = ("Helvetica",7)
HELP_FONT=("Times", 9 , "bold")
class PAL3_guide(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage,Tut01):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self,parent, controller):
tk.Frame.__init__(self,parent, background="white")
button_PAL = ttk.Button(self, text="Setup Guide", command=lambda:controller.show_frame(Tut01))
button_PAL.pack()
class Tut01(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
var=BooleanVar()
label = tk.Label(self, text="Guide", font=LARGE_FONT)
label.pack(pady=10, padx=10)
label = tk.Label(self, text="Check the methods to accomplish", font=NORM_FONT)
label.pack(pady=10, padx=10)
c1 = Checkbutton(self, state=ACTIVE).pack()
c1Label = tk.Label(self, text="digestion1",font=SMALL_FONT).pack()
c2 = Checkbutton(self, state=ACTIVE).pack()
c2Label = tk.Label(self, text="digestion2",font=SMALL_FONT).pack()
c3 = Checkbutton(self, text="enrichment",state=ACTIVE, variable=var, command=lambda:onClick()).pack()
def onClick():
var.get()
if var == True:
print("hi")
else:
print("hiww")
app = PAL3_guide()
app.geometry("1280x920")
app.mainloop()
Please check last code lines around the c3 checkbox.
I dont get why my boolean variable var is not getting updated by clicking the checkbox on the 2nd page. By clicking the checkbox (c3) the GUI should act differently. However as mentioned above it is not getting updated.. and stays False. I feel it keeps the value from start and does not get changed. Thanks for help ..
EDIT:
Above the if statement missed the var.get() and I could make it work in smaller setup.. however, in the real tool I am calling a plt.figure() in the beginning since it included and anmiate function, If I erase this call, everything works like it should, might be that this figure calling forces the checkbutton to have the first onvalue ?
I would change var, to be self.var and set the off and on values explicitly:
offvalue=
The value corresponding to a non-checked button. The default is 0. (offValue/Value)
onvalue=
The value corresponding to a checked button. The default is 1. (onValue/Value)
c3 = Checkbutton(self, text="enrichment",state=ACTIVE,
variable=self.var, command=onClick,
onvalue=True, offvalue=False)
c3.pack()
command= does not take a function call, just a function.
You would have to write either
command=lambda: print(self.var.get())
OR:
def printVar():
print(self.var.get())
THEN:
..., command=printVar, ...
NOTICE the abscence of the paranthesis!
You are supposed to be settting command to a function.
When you use parenthesis, you CALL the function, you DO NOT want that here.

Running Pyqt UIs in tabs using TabWidget

I want to run my app in different tabs. Rightnow, it is running in main window. I have done some search work on how to create tabs. I found this to be useful, but not sufficient to meet my requirements
Create TAB and create textboxes that take data in the TAB-page
I want to have a feature of adding a new tab (like new tab in chrome)
Below is my code sample. I described what i require in the comments.
from PyQt4 import Qt, QtCore, QtGui
import sys
class Map(QtGui.QMainWindow):
def __init__(self,parentQExampleScrollArea=None,parentQWidget = None):
super(Map,self).__init__()
self.initUI()
#Initialize the UI
def initUI(self):
#Initilizing Menus toolbars
#This must be maintained in all tabbed panes
filename = ""
#Filename is obtained through open file button in file menu
self.filename = filename
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp,self.filename)
qp.end()
def drawPoints(self, qp,FILENAME=""):
#Read contents in file
#Get the necessary coordinates
#A separate class for storing the info of all the coordinates
#Run a for loop for all the coordinates in the list
#Actually, object is created here and image of that object is set
# as a square using the coordinates
qp.setBrush(QtGui.QColor(255, 0, 20, 200))
qp.drawRect(20,20,75,75)
qp.drawRect(100,20,75,75)
self.update()
#There are many functions to handle keyboard and mouse events
def main():
#How do I modify so that I can have multiple tabs
#And show images of the coordinates in files
#Basically I want to have the feature of opening many files
# and displaying them in UI
#Basically a feature to add a new tab
#like that of in eclipse netbeans sublime etc
app = QtGui.QApplication(sys.argv)
myQExampleScrollArea = Map()
myQExampleScrollArea.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Thanks in advance.. :)
It's simply to use method int QTabWidget.addTab (self, QWidget widget, QString) to create widget in tab. In each tab, I suggest use QtGui.QWidget more than QtGui.QMainWindow;
Example;
import sys
from PyQt4 import QtGui
class QCustomWidget (QtGui.QWidget):
# Your widget to implement
# Put your override method here
def paintEvent (self, eventQPaintEvent):
currentQPainter = QtGui.QPainter()
currentQPainter.begin(self)
currentQPainter.setBrush(QtGui.QColor(255, 0, 20, 200))
currentQPainter.drawRect(20, 20, 75, 75)
currentQPainter.drawRect(100, 20, 75, 75)
self.update()
currentQPainter.end()
class QCustomTabWidget (QtGui.QTabWidget):
def __init__ (self, parent = None):
super(QCustomTabWidget, self).__init__(parent)
self.addTab(QtGui.QPushButton('Test'), 'Tab 1')
self.addTab(QCustomWidget(), 'Tab 2')
myQApplication = QtGui.QApplication([])
myQCustomTabWidget = QCustomTabWidget()
myQCustomTabWidget.show()
sys.exit(myQApplication.exec_())
So handle with more tab, It's bad to create many line call int QTabWidget.addTab (self, QWidget widget, QString). Anyway, All widget has add in QTabWidget is can reference in ifself. So, your can control element in it by call QWidget QTabWidget.widget (self, int index).
Example to call widget in tab widget;
import os
import sys
from PyQt4 import QtCore, QtGui
class QCustomLabel (QtGui.QLabel):
def __init__(self, imagePath, parentQWidget = None):
super(QCustomLabel, self).__init__(parentQWidget)
self.setPixmap(QtGui.QPixmap(imagePath))
class QCustomWidget (QtGui.QWidget):
def __init__ (self, parentQWidget = None):
super(QCustomWidget, self).__init__(parentQWidget)
self.addQPustButton = QtGui.QPushButton('Open image')
self.addQPustButton.setMaximumWidth(120)
self.addQPustButton.released.connect(self.openImage)
self.workSpaceQTabWidget = QtGui.QTabWidget()
self.workSpaceQTabWidget.setTabsClosable(True)
self.workSpaceQTabWidget.tabCloseRequested.connect(self.closeImage)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addWidget(self.addQPustButton)
allQVBoxLayout.addWidget(self.workSpaceQTabWidget)
self.setLayout(allQVBoxLayout)
def openImage (self):
path = QtGui.QFileDialog.getOpenFileName(self, 'Open image')
if not path.isEmpty():
self.workSpaceQTabWidget.addTab(QCustomLabel(path), QtCore.QString(os.path.basename(str(path))))
def closeImage (self, currentIndex):
currentQWidget = self.workSpaceQTabWidget.widget(currentIndex)
currentQWidget.deleteLater()
self.workSpaceQTabWidget.removeTab(currentIndex)
myQApplication = QtGui.QApplication([])
myQCustomWidget = QCustomWidget()
myQCustomWidget.show()
sys.exit(myQApplication.exec_())

PyGTK Change Window Dimensions from Two Entry's

It's pretty simple what I'm trying to accomplish here.
Say you put down 320 in the left textbox. That means the width of the window will be 320px. Same applies for height, except for the right textbox.
However when I debug I get this error.
Traceback (most recent call last):
File "./app.py", line 37, in change_size
self.win.set_size_request(width,height)
TypeError: an integer is required
Here's the code.
#!/usr/bin/env python
import gtk
class app:
def __init__(self):
self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.set_title("Change Dimensions")
self.win.set_default_size(235, 60)
self.win.connect("destroy", gtk.main_quit)
vbox = gtk.VBox(spacing=4)
hbox = gtk.HBox(spacing=4)
self.entry = gtk.Entry()
self.entry2 = gtk.Entry()
hbox.pack_start(self.entry)
hbox.pack_start(self.entry2)
hbox2 = gtk.HBox(spacing=4)
ok = gtk.Button("OK")
ok.connect("clicked", self.change_size)
hbox2.pack_start(ok)
exit = gtk.Button("Exit")
exit.connect("clicked", gtk.main_quit)
hbox2.pack_start(exit)
vbox.pack_start(hbox)
vbox.pack_start(hbox2)
self.win.add(vbox)
self.win.show_all()
def change_size(self, w):
width = self.entry.get_text()
height = self.entry2.get_text()
self.win.set_size_request(width,height)
app()
gtk.main()
That's because get_text returns a string, but set_size_request requires integers. Simple fix:
width = int(self.entry.get_text())
height = int(self.entry2.get_text())

Resources