Creating a button with a Bitmap image wxPython - python

I am using wxPython on Python 2.7.
I would like some help with creating a button with bitmap images.
I am using this video https://www.youtube.com/watch?v=Y7f0a7xbWHI,
and I followed the codes and typed
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Frame aka window',size=(300,200))
panel=wx.Panel(self)
pic=wx.Image("back.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
self.button=wx.BitmapButton(panel, -1, pic, pos=(10,10))
self.Bind(wx.EVT_BUTTON, self.doMe, self.button)
self.button.SetDefault()
def doMe(self, event):
self.Destroy
to create a button with an image. I got an error stating Invalid Image.
I saved the bitmap image in a folder that has .py file I am working with.
I feel like I am saving the image in the wrong place?
Thank you in advance.
The error I received
Traceback (most recent call last):
File "C:\Python27\FrameWindow.py", line 81, in <module>
frame=bucky(parent=None,id=-1)
File "C:\Python27\FrameWindow.py", line 17, in __init__
pic=wx.Image("back.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
File "C:\Python27\lib\site-packages\wx\core.py", line 708, in _Image_ConvertToBitmap
bmp = wx.Bitmap(self, depth)
wxAssertionError: C++ assertion "image.IsOk()" failed at ..\..\src \msw\bitmap.cpp(922) in wxBitmap::CreateFromImage(): invalid image

I got it to work by adding
"locale = wx.Locale(wx.LANGUAGE_ENGLISH)"
under
class MainFrame(wx.Frame):
def __init__(self):
now I do not get the error message, and it runs as it should.
The error code I received for this problem was:
Traceback (most recent call last):
File "C:\Python27\panel test.py", line 21, in <module>
frame = MainFrame()
File "C:\Python27\panel test.py", line 11, in __init__
pic = wx.Bitmap("back.bmp", wx.BITMAP_TYPE_ANY)
wxAssertionError: C++ assertion "strcmp(setlocale(LC_ALL, NULL), "C") == 0" failed at ..\..\src\common\intl.cpp(1579) in wxLocale::GetInfo(): You probably called setlocale() directly instead of using wxLocale and now there is a mismatch between C/C++ and Windows locale.
Things are going to break, please only change locale by creating wxLocale objects to avoid this!
The error given by my primary question was solved by the codes given by Rolf by Saxony.

Use wx.BITMAP_TYPE_ANY rather than wx.BITMAP_TYPE_BMP it takes the guessing out of whether it really is a BMP or not
or use wx.Bitmap() directly.
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,'Frame aka window',size=(300,200))
panel=wx.Panel(self)
#
# Use wx.BITMAP_TYPE_ANY rather than wx.BITMAP_TYPE_BMP
#
#pic=wx.Image("Discord.bmp", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
#
# or use wx.Bitmap()
pic = wx.Bitmap("Discord.bmp", wx.BITMAP_TYPE_ANY)
#
self.button=wx.BitmapButton(panel, -1, pic, pos=(10,10))
self.Bind(wx.EVT_BUTTON, self.doMe, self.button)
self.Show()
def doMe(self, event):
self.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = MainFrame()
app.MainLoop()
In principle you should name your image/s with a full pathname. If you have many images, then have an image directory and join that directory name to your image names as you use them, which again gives you a full path (/home/images/back_button.bmp)

Related

Python tkinter errors setting icon, TclError linux

I am fairly new to python and very new to GUI's so please let me know if I have made an obvious mistake.
This is also my first post so please let me know if I need to edit my question in some way.
I don't understand why I am getting errors when trying to set an icon to a tkinter GUI.
I have already tried the following posts:
tkinter TclError: error reading bitmap file
https://www.delftstack.com/howto/python-tkinter/how-to-set-window-icon-in-tkinter/
There are other stack exhange posts on this topic but they are for windows and/or their OP's normally can get the icon to apear once then it doesn't work. This isn't my situation the programs fail to run. Though I have tried some of their suggestions as well.
I have tried three different ways of coding this:
Method 1:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
root.iconphoto(True, PhotoImage(os.path.join(os.getcwd(),'favicon-3.png')))
my_gui = MainWindow(root)
root.mainloop()
Result:
runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 23, in <module>
root.iconphoto(True, PhotoImage(os.path.join(os.getcwd(),'favicon-3.png')))
File "/usr/lib/python3.8/tkinter/__init__.py", line 2116, in wm_iconphoto
self.tk.call('wm', 'iconphoto', self._w, "-default", *args)
TclError: can't use "~./envs/thermTD_project/thermTD/favicon-3.png" as iconphoto: not a photo image
File existence check:
In [42]: os.path.exists(os.path.join(os.getcwd(),'favicon-3.png'))
Out[42]: True
Method 2:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
root.iconbitmap(os.path.join(os.getcwd(),'favicon-3.ico'))
my_gui = MainWindow(root)
root.mainloop()
Result:
In [43]: runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 22, in <module>
root.iconbitmap(os.path.join(os.getcwd(),'favicon-3.ico'))
File "/usr/lib/python3.8/tkinter/__init__.py", line 2071, in wm_iconbitmap
return self.tk.call('wm', 'iconbitmap', self._w, bitmap)
TclError: bitmap "~./envs/thermTD_project/thermTD/favicon-3.ico" not defined
File existence check:
In [44]: os.path.exists(os.path.join(os.getcwd(),'favicon-3.ico'))
Out[44]: True
Method 3:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
img = PhotoImage(os.path.join(os.getcwd(),'favicon-3.ico'))
root.tk.call('wm','iconphoto', root._w, img)
my_gui = MainWindow(root)
root.mainloop()
Result:
In [45]: runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 21, in <module>
root.tk.call('wm','iconphoto', root._w, img)
TclError: can't use "~./envs/thermTD_project/thermTD/favicon-3.ico" as iconphoto: not a photo image
File existence check:
See method 1
The code without any of the icon setting lines works as expected.
I have tried to gif, png and ico on each method. I am trying other file types now.
NB: While the filename is favicon actually they are ico, gif, png files of 64x64 pixles. Even the favicon ico only has 1 type of icon in it (if thats the right phrase). Its just I got them from a favicon which I converted to different formats using mogrify.
OS: Ubuntu 20.04.1 LTS
python 3.8.2
IDE: spyder 4.1.5
P.S. I would be gratefull to know what the syntax for including this in my init function. I am trying to do object orientated GUI's I am very much still learning.

How can I exit out of a wxPython application cleanly?

The package wxPython is very good for developing GUI-interface applications in Python, but so far, the only methods that I have found to exit from an application developed in wxPython launched from the Python command line always generate a runtime error when the application is closed programmatically. For example, the method Frame.Destroy() generates the error:
Runtime error
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\PythonSamples\ArcGIS Examples\TIGER Data Loader.py", line 522, in
<module>
frame.Destroy()
RuntimeError: wrapped C/C++ object of type Frame has been deleted
A similar error message is generated if Frame.Close() is called. The only way that I have found to close an application window generated by wxPython WITHOUT generating a run-time error is by deleting the wx.App object:
app=wx.App()
frame = wx.Frame(etc....)
.
.
.
and somewhere in the program where you want to exit the Frame window, you issue
del app
This seems like a bad way to terminate an application. Is there a better way that does NOT generate a run-time error message?
calling frame.Destroy() in an event deletes the frame, but the program then returns to the wx mainloop.
When the mainloop finds that the frame has been destroyed, the error occurs.
Instead (like when using threads), use wx.CallAfter so wx it is executed in the main thread and somewhere where wx expects such changes. For you:
wx.CallAfter(frame.Destroy)
note as suggested in comments that it's cleaner to do:
wx.CallAfter(frame.Close)
because it gives your app a chance to call some cleanup code, unlike Destroy
How about frame.Close() ? Docs are here
For reference, the following code doesn't spit out any error on my machine:
import wx
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="Close Me")
panel = wx.Panel(self)
closeBtn = wx.Button(panel, label="Close")
closeBtn.Bind(wx.EVT_BUTTON, self.onClose)
def onClose(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()

IpAddrCtrl is broken on GTK

I have an IpAddrCtrl element from wx.lib.masked in my simple test:
import wx
import wx.lib.masked as masked
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.ipaddr1 = masked.IpAddrCtrl(self, -1, style=wx.TE_PROCESS_TAB)
self.grid_ip = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
self.grid_ip.Add(self.ipaddr1, 0, wx.ALIGN_LEFT)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.grid_ip, 0, wx.ALIGN_LEFT | wx.ALL, border=5)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_TEXT, self.OnIpAddrChange, id=self.ipaddr1.GetId())
self.Show(True)
def OnIpAddrChange(self, event):
print(self.ipaddr1.GetAddress())
print(self.ipaddr1.IsValid())
if __name__ == '__main__':
app = wx.App(False)
frame = MainWindow(None, "IpAddrCtrl sample")
app.MainLoop()
After application starts, I try to insert correct numeric values in my IpAddrCtrl element, but it falls with following log:
<BaseMaskedTextCtrl: . . . >
False
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3016, in _OnChar
keep_processing = self._keyhandlers[key](event)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3937, in _OnChangeField
self._AdjustField( pos )
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 4291, in _AdjustField
newfield = field._AdjustField(slice)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 1649, in _AdjustField
intStr = str(long(intStr))
ValueError: invalid literal for long() with base 10: '.'
Here a screencast how does it actually works.
Similar demo from wxPython project causes same problems for me.
My environment: Debian Stretch with Python 2.7.13, wxPython 3.0.2 and GTK2 2.24.31-2 from official stable repositories.
Any advices?
Thanks for the help!
Update:
I try to use same sample on Win32 platform and there are no any problems with IpAddrCtrl. After some debugging I found out that the difference in GTK/Win versions in additional EVT_TEXT event call that occurs in IpAddrCtrl when I try to edit selected text region. That causes unnecessary _OnTextChange event handler call, that brokes input.
That strange behavior described on wxPython wiki and commented in a sources of base class for IpAddrCtrl, but currently a have no idea how to fix it.
It's a horrible control, which as far as I can tell, simply doesn't function intuitively at all.
You have to press back space,to clear the field, as you enter each segment of the ip address or it gets itself in a complete tizzy.
Replace your lines
ipaddr = self.FindWindowById(event.GetId())
print ipaddr
print ipaddr.IsValid()
with
print (self.ipaddr1.GetAddress())
print (self.ipaddr1.IsValid())
Clearly, it's a function that needs work, as it is broken in wxpython 4 as well.

How can i reload a picture with Tkinter after GPIO button press?

I have the following script based on a solution of another Stackoverflow question. I have a tkinter GUI with a picture that should refresh when there is taken a new one.
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = Tk()
self.root.geometry("{0}x{1}+0+0".format(800,800))
files = sorted(os.listdir(os.getcwd()),key=os.path.getmtime)
Label(self.root,image = ImageTk.PhotoImage(Image.open(files[-1]).resize((300, 200)))).grid(row = 1, column = 0)
Label(self.root, text=files[-1]).grid(row=0, column=0)
self.root.mainloop()
def update(self):
files = sorted(os.listdir(os.getcwd()),key=os.path.getmtime)
img1 = self.ImageTk.PhotoImage(self.Image.open(files[-1]).resize((300, 200)))
Label(self.root,image = img1).grid(row = 1, column = 0)
Label(self.root, text=files[-1]).grid(row=0, column=0)
self.root.update()
app = App()
app.update()
I get this error:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 176, in paste
tk.call("PyImagingPhoto", self.__photo, block.id)
_tkinter.TclError: invalid command name "PyImagingPhoto"
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./mainloop.py", line 98, in <module>
app.update()
File "./mainloop.py", line 79, in update
img1 = ImageTk.PhotoImage(Image.open(files[-1]).resize((300, 200)))
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 115, in __init__
self.paste(image)
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 182, in paste
_imagingtk.tkinit(tk.interpaddr(), 1)
OverflowError: Python int too large to convert to C ssize_t
What am i doing wrong? Creating a label with with the file name does work. The filename is a valid file. The script does work when it is not inside the thread.Threading part. I would like to update the screen with the newest picture when a Raspberry GPIO button is pressed (script will take a new photo with a camara). So the screen should load with the lastest taken picture.
tkinter is not thread-safe (by default). All operations dealing with the GUI must be done in the main thread. You can do what you are asking using callbacks on events, but threading can and will break tkinter unless you implement an event queue that feeds into the mainloop.
Additionally, the stack trace seems to point to not your code here- that stack trace doesn't intersect with the presented code at all.
Also, calling root.update() is generally a bad idea- it starts another local event loop, which may be able to call another root.update() for another event loop ad infinium. root.update_idletasks() is much safer than a full root.update()

How to over come this pdfviewer "No module named viewer" error in wxpython?

I am trying to create an application that has a feature of displaying pdf files and decided to use wxpython to do this due to the pdfviewer class.
I made sure that I have pyPDF2 and pyPdf. (can use either but installed both to see if that was the issue.)
However when I run the code at the bottom. (taken from here) (got rid of the ``` on lines 31 and 17. Added wx before .VSCROLL and .SUNKEN_BORDER on line 16) I get the message:
Traceback (most recent call last):
File "E:\Test\pdf.py", line 4, in <module>
from wx.lib.pdfviewer import pdfViewer, pdfButtonPanel
File "C:\Python34\lib\site-packages\wx\lib\pdfviewer\__init__.py", line 124, in <module>
from viewer import pdfViewer
ImportError: No module named 'viewer'
When I then went to that package file to confirm that the module viewer was there I then ran it and line 124 worked. It just doesnt work when running through this example file which I assume is the same as when it will be in my application.
Does anyone know what I need to do to fix this. This module looks perfect for what I plan on doing.
Thanks
import wx
import wx.lib.sized_controls as sc
from wx.lib.pdfviewer import pdfViewer, pdfButtonPanel
class PDFViewer(sc.SizedFrame):
def __init__(self, parent, **kwds):
super(PDFViewer, self).__init__(parent, **kwds)
paneCont = self.GetContentsPane()
self.buttonpanel = pdfButtonPanel(paneCont, wx.NewId(),
wx.DefaultPosition, wx.DefaultSize, 0)
self.buttonpanel.SetSizerProps(expand=True)
self.viewer = pdfViewer(paneCont, wx.NewId(), wx.DefaultPosition,
wx.DefaultSize,
wx.HSCROLL|wx.VSCROLL|wx.SUNKEN_BORDER)
self.viewer.UsePrintDirect = False
self.viewer.SetSizerProps(expand=True, proportion=1)
# introduce buttonpanel and viewer to each other
self.buttonpanel.viewer = self.viewer
self.viewer.buttonpanel = self.buttonpanel
if __name__ == '__main__':
import wx.lib.mixins.inspection as WIT
app = WIT.InspectableApp(redirect=False)
pdfV = PDFViewer(None, size=(800, 600))
pdfV.viewer.UsePrintDirect = False
pdfV.viewer.LoadFile(r'a path to a .pdf file')
pdfV.Show()
app.MainLoop()
This needed a bit of work to get pdfviewer to work on Py3, I did a PR for it. Not the PR switches from pyPDF to PyPDF2 as the former does not support Py3 and it also depends on PR 172 someone has done to get PyPDF2 to work correctly on Py3.

Categories

Resources