MMGP Answerd but wont let me credit him right ;-) So I will at least mention him here.
(And I did finally get to credit him... 8-)
His linked discussion on Double Buffering provided a base code that worked with the following modifications:
Insert this beginning at line 106 (overwritting existing code until you get to the last line shown here):
# Here's the actual drawing code.
cliWidth, cliHeight = self.GetClientSize()
bmp=wx.Bitmap("Logo16x9.png")
bmpWide = bmp.GetWidth()
bmpHeight = bmp.GetHeight()
img = bmp.ConvertToImage()
scaleFactor = cliWidth/bmpWide
bmp = wx.BitmapFromImage(img.Scale(int(bmpWide * scaleFactor), int(bmpHeight * scaleFactor)))
bmpWide = bmp.GetWidth()
bmpHeight = bmp.GetHeight()
xPos = (cliWidth - (bmpWide))/2
yPos = (cliHeight - (bmpHeight))/2
# altered by me
dc.DrawBitmap(bmp, xPos, yPos)
class TestFrame(wx.Frame):
I've been beating my head against this all day.
I'm new to drawing graphics with the wxPython modules, and when I needed to draw a background image on a frame I found this code which works well if the image is the full size of the window.
However, I need to place a company logo as the background, and have it remain centered through resizes. In it's current form the resize causes a graphic artifact the size of a small nation to appear on the screen with any resize event.
The logo image file (used on line 43 of the code) is a 400x300 (WxH) image.
I am looking for a way to either: resize my image on the fly to match wx.GetClientSize(),
or a way to avoid/remove the artifact. Preferably without involving PIL or ImageMagick. App has to function on a local level only, and be system agnostic (Win, Lin and Mac), none of this involves network activities or mapped drives.
Python 2.7 and wxPython 2.8
The code I am using (with my modification annotated) is as follows:
import wx
########################################################################
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.frame = parent
sizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
for num in range(4):
label = "Button %s" % num
btn = wx.Button(self, label=label)
sizer.Add(btn, 0, wx.ALL, 5)
hSizer.Add((1,1), 1, wx.EXPAND)
hSizer.Add(sizer, 0, wx.TOP, 100)
hSizer.Add((1,1), 0, wx.ALL, 75)
self.SetSizer(hSizer)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
#----------------------------------------------------------------------
def OnEraseBackground(self, evt):
"""
Add a picture to the background
"""
# yanked from ColourDB.py
dc = evt.GetDC()
# Added by me
cliWidth, cliHeight = self.GetClientSize()
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
# added by me
xPos = (cliWidth - 400)/2
yPos = (cliHeight - 300)/2
# altered by me
dc.DrawBitmap(bmp, xPos, yPos)
#dc.DrawBitmap(bmp, 0, 0)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, size=(600,450))
panel = MainPanel(self)
self.Center()
########################################################################
class Main(wx.App):
""""""
#----------------------------------------------------------------------
def __init__(self, redirect=False, filename=None):
"""Constructor"""
wx.App.__init__(self, redirect, filename)
dlg = MainFrame()
dlg.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = Main()
app.MainLoop()
Update: Latest Failure - Modified lines 37 to 52
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)
Another attempt and another fail. No difference in the output to screen. Additionally, the referenced document on double buffering does not address this issue, but does suffer from the same result. This code modifies lines 36 through 57 of the original.
brsh = wx.Brush('#000000')
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.SetBackground(brsh)
dc.SetDeviceOrigin(0,0)
dc.DestroyClippingRegion()
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)
From the comments I suggested using double buffered drawing, but I didn't see that in the edited post. Also, I had several drawing issues when self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) was used. But this line might be helpful in other systems beyond mine, so I wanted to keep it. So, in order to handle the situation, here is a updated code that uses double buffered drawing and works fine here:
import wx
class MainPanel(wx.Panel):
def __init__(self, parent, bg_img='Logo4x3.png'):
wx.Panel.__init__(self, parent=parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.bg = wx.Bitmap(bg_img)
self._width, self._height = self.bg.GetSize()
sizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
for num in range(4):
btn = wx.Button(self, label="Button %s" % num)
sizer.Add(btn, 0, wx.ALL, 5)
hSizer.Add((1,1), 1, wx.EXPAND)
hSizer.Add(sizer, 0, wx.TOP, 100)
hSizer.Add((1,1), 0, wx.ALL, 75)
self.SetSizer(hSizer)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
def OnSize(self, size):
self.Layout()
self.Refresh()
def OnEraseBackground(self, evt):
pass
def OnPaint(self, evt):
dc = wx.BufferedPaintDC(self)
self.Draw(dc)
def Draw(self, dc):
cliWidth, cliHeight = self.GetClientSize()
if not cliWidth or not cliHeight:
return
dc.Clear()
xPos = (cliWidth - self._width)/2
yPos = (cliHeight - self._height)/2
dc.DrawBitmap(self.bg, xPos, yPos)
app = wx.App()
frame = wx.Frame(None, size=(400,300))
panel = MainPanel(frame)
frame.Show()
app.MainLoop()
The method OnEraseBackground is intentionally empty.
Related
I want to change the color for my whole pythonwx application. I found out that the currently used colors are noted down in wx.Frame.DefaultAttributes.colBg respectively.colFg. I checked with paint that these are really the used colors.
Now there is a wx.Frame.GetDefaultAttributes() but not wx.Frame.SetDefaultAttributes() method. But I still need to change the color and I do not think that setting every control manually is a desired solution.
I tried:
frame.DefaultProperties = customProperties
and
frame.DefaultProperties.colBg = customColor
but both throw a AttributeError ("can't set attribute"). Any help is appreciated.
The default properties are probably defined within whatever theme you have set for the desktop. I do not believe that there is a way to redefine these from within wxpython.
The easiest way that I have found to set a default colour scheme, is to set the colours for each of the children in an object, such as a panel.
In the following code, keep pressing the Encrypt button to see the results.
import wx
from random import randrange
class CipherTexter(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(1000, 600))
self.panel = wx.Panel(self)
cipherText = wx.StaticText(self.panel, label="Cipher Texter ", pos=(20, 30))
encryptorText = wx.StaticText(self.panel, label="Encryptor ", pos=(20, 70))
decryptorText = wx.StaticText(self.panel, label="Decryptor ", pos=(20, 100))
self.cipher = wx.TextCtrl(self.panel, -1, style=wx.TE_MULTILINE, size=(400,400), pos=(400, 30))
self.encryptor = wx.TextCtrl(self.panel, -1, size=(100,30), pos=(200, 70))
self.decryptor = wx.TextCtrl(self.panel, -1, size=(100,30), pos=(200, 100))
self.encrypt = wx.Button(self.panel, -1, "Encrypt", pos=(20, 140))
self.decrypt = wx.Button(self.panel, -1, "Decrypt", pos=(20, 180))
self.panel.SetBackgroundColour('white')
self.encrypt.Bind(wx.EVT_BUTTON, self.encryptNow)
self.decrypt.Bind(wx.EVT_BUTTON, self.decryptNow)
self.Show()
def AColour(self):
red = randrange(0,255)
green = randrange(0,255)
blue = randrange(0,255)
x = wx.Colour(red,green,blue)
return x
def encryptNow(self, event):
cfg_colour = self.AColour()
txt_colour = self.AColour()
children = self.panel.GetChildren()
for child in children:
child.SetBackgroundColour(cfg_colour)
child.SetForegroundColour(txt_colour)
print(cfg_colour)
def decryptNow(self, event):
pass
app = wx.App(False)
frame = CipherTexter(None, "The SS Cipher")
app.MainLoop()
I want to have an image below a slider in a wxpython GUI. The text asks "What is the value?" and I want to have a picture of a value (such as 35) below the slider, and have different images that change as you navigate through.
I have researched this issue, but only found ways to make an image the entire background, rather then appear in the panel.
import wx
class MyPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id)
self.SetBackgroundColour("white")
text1 = wx.StaticText(self, -1, "What is the value", (10,10))
self.slider1 = wx.Slider(self, -1, 50, 0, 100, (10, 40), (200, 50),
wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_LABELS)
self.Bind(wx.EVT_SLIDER, self.sliderUpdate)
def sliderUpdate(self, event):
self.pos1 = self.slider1.GetValue()
if self.pos1 == 35:
box = wx.MessageDialog(None, "BINGO!", "Title", wx.OK)
box.ShowModal()
app = wx.App()
frame = wx.Frame(None, -1, "Title", size = (400, 310))
MyPanel(frame,-1)
frame.Show(True)
app.MainLoop()
Additionally, I am unsure how to create multiple pages to switch the pictures at the bottom and have a submit button that checks if it is correct before moving on.
Any literature or videos that you can point me to to better learn wxpython would be appreciated as well.
Just add the image to the panel in the required position or start using sizers.
I've added some quick PIL code to generate an image of a number or text, so that you can generate your image on the fly if you need to, although I have left it as a single global instance. You may want to make it a function.
import wx
from PIL import Image, ImageDraw
temp_img=[]
for i in range(101):
img = Image.new('RGB', (100,30))
d = ImageDraw.Draw(img)
d.text((10,10), str(i))
width, height = img.size
temp_img.append(wx.Bitmap.FromBuffer(width, height, img.tobytes()))
class MyPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id)
self.SetBackgroundColour("white")
text1 = wx.StaticText(self, -1, "What is the value", (10,10))
self.slider1 = wx.Slider(self, -1, 50, 0, 100, (10, 40), (200, 50),
wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_LABELS)
self.bmp = wx.StaticBitmap(self, -1, bitmap=temp_img[50], pos=(10,100))
self.Bind(wx.EVT_SLIDER, self.sliderUpdate)
def sliderUpdate(self, event):
self.pos1 = self.slider1.GetValue()
self.bmp.SetBitmap(temp_img[self.pos1])
if self.pos1 == 35:
box = wx.MessageDialog(None, "BINGO 35!", "Title", wx.OK)
box.ShowModal()
app = wx.App()
frame = wx.Frame(None, -1, "Title", size = (400, 310))
MyPanel(frame,-1)
frame.Show(True)
app.MainLoop()
Edited to show multiple images based on the slider value.
I am using wxPython to create a splash screen. It actually works great, but now I want to put some loading feedback on the splashscreen. Does anyone know how I can put a dynamic label on top of the splashscreen? Here is the code I am using:
class myFrame(wxFrame):
wx.Frame.__init__(self, parent, -1, _("Radar"),
size=(800, 600), style=wx.DEFAULT_FRAME_STYLE)
class MySplash(wx.SplashScreen):
def __init__(self, parent=None):
aBitmap = wx.Image(name=VarFiles["Radar_Splash"]).ConvertToBitmap()
splashStyle = wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT
splashDuration = 12000 # ms
wx.SplashScreen.__init__(self, aBitmap, splashStyle, splashDuration, parent)
self.Bind(wx.EVT_CLOSE, self.CloseSplash)
wx.Yield()
def CloseSplash(self, evt):
self.Hide()
global frame
frame = myFrame(parent=None)
app.SetTopWindow(frame)
frame.Show(True)
evt.Skip()
class MyAwesomeApp(wx.App):
def OnInit(self):
MySplash = MySplash()
MySplash.Show()
return True
As the SplashScreen is essentially a window displaying a bitmap, you will need to modify the provided bitmap, doing the layouting yourself.
def _draw_bmp(bmp, txt_lst):
dc = wx.MemoryDC()
dc.SelectObject(bmp)
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
gc.SetFont(font)
for i, line in enumerate(txt_lst):
dc.DrawText(line, 10, i * 20 + 15)
dc.SelectObject(wx.NullBitmap)
# ...
aBitmap = #...
_draw_bmp(aBitmap, ['splash', 'screen', 'text'])
The wx.GraphicsContext will be helpful to have antialiased text looking the same as in the underlying OS.
I'm facing a problem with python.
i want to draw on my monitor a circle, that can move around.
let's say i have my browser open, i want to be able to make the circle go around on his own AND be able to use the mouse to press any button i want.
the idea is that the circle is connected to my hands movement thanks to Leap Motion, and i want to display the gestures i make while being able to use the mouse.
my worries are that i have to make a trasparent window which doesn't let me to use the mouse because i would clik on the trasparent window.
Thanks!
Found the solution, i used wxPython
Down below the code:
import wx, inspect, os, sys, win32gui
IMAGE_PATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '\cerchio.png'
class Cursor(wx.Frame):
def __init__(self, parent, log):
self.log = log
self.delta = wx.Point(0,0)
wx.Frame.__init__(self, parent, -1, "Shaped Window",
style =
wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
| wx.STAY_ON_TOP
)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x=0
self.y=0
self.hasShape = False
self.SetClientSize( (50, 50) )
image = wx.Image(IMAGE_PATH, wx.BITMAP_TYPE_PNG)
image.SetMaskColour(255,255,255)
image.SetMask(True)
self.bmp = wx.BitmapFromImage(image)
self.SetWindowShape()
self.timer.Start(1)
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def SetWindowShape(self, *evt):
# Use the bitmap's mask to determine the region
r = wx.RegionFromBitmap(self.bmp)
self.hasShape = self.SetShape(r)
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def update(self, event):
#self.x, self.y = win32gui.GetCursorPos()
self.SetPosition((self.x,self.y))
if __name__ == '__main__':
app = wx.App( False )
frm = Cursor(None, -1)
frm.Show()
app.MainLoop()
This maps 50x50 png image with white background (modify IMAGE_PATH) to the cursor position (you won't be able to click though)
I'm pretty new to Python, so I'll hope you forgive me for such amateurish code. I've tried pouring over examples that do similar things but I'm having trouble figuring out what they're doing that is different. In examples I've seen each button generated with the loop had a different action, for mine only the last button in the loop is affected by the click, no matter which button I press. Here's the code:
import wx
import mmap
class pt:
Note = open('note.txt', "r+")
buf = mmap.mmap(Note.fileno(), 0)
TL = 0
readline = buf.readline
while readline():
TL += 1
class MainWindow(wx.Frame):
def __init__(self, parent, title):
w, h = wx.GetDisplaySize()
x = w * 0
y = h - bdepth
wx.Frame.__init__(self, parent, title = title, pos = (x, y), size = (200,bdepth), style = wx.STAY_ON_TOP)
self.__DoLayout()
self.Bind(wx.EVT_BUTTON, self.OnClick)
self.Show(True)
def __DoLayout(self):
self.__DoButtons(wx.Panel(self, size=(200,bdepth), pos=(0,0), name='panel'), 'Cheese')
def __DoButtons(self, panel, label):
for i, line in enumerate(pt.Note):
solid = wx.EmptyBitmap(200,50,-1)
dc = wx.MemoryDC()
dc.SelectObject(solid)
solidbrush = wx.Brush(wx.Colour(75,75,75),wx.SOLID)
solidpen = wx.Pen(wx.Colour(75,75,75),wx.SOLID)
dc.SetBrush(solidbrush)
dc.SetPen(solidpen)
dc.DrawRectangle(0, 0, 200, 50)
dc.SetTextForeground(wx.Colour(255, 255, 255))
dc.DrawText(line.rstrip(), 30, 17)
dc.SelectObject(wx.NullBitmap)
self.checked = wx.Image('buttonchecked.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
dc = wx.MemoryDC()
dc.SelectObject(self.checked)
dc.SetTextForeground(wx.Colour(200, 255, 0))
dc.DrawText(line.rstrip(), 30, 17)
dc.SelectObject(wx.NullBitmap)
self.b = wx.BitmapButton(panel, i + 800, solid, (0, i * 50), (solid.GetWidth(), solid.GetHeight()), style = wx.NO_BORDER, name=line.rstrip())
def OnClick(self, event):
self.b.SetBitmapDisabled(self.checked)
self.b.Enable(False)
print('cheese')
bdepth = pt.TL * 50
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()enter code here
Only the last button is working because each time you go through the __DoButtons loop you reassign self.b to a different button. So after the loop has finished self.b is only assigned to the last button. You can get the button pressed using the event.GetEventObject() method.
Change your OnClick method to:
def OnClick(self, event):
button = event.GetEventObject()
button.SetBitmapDisabled(self.checked)
button.Enable(False)
print('cheese')