wxPython Drag and Drop files onto image, to add to array - python

I have a simple wxPython application with 1 image "Drop files here!" and 2 buttons.
I want the user to be able to drag and drop files onto the top section/image, at which point the image changes and the files are loaded into an array.
That's all I need but I have hit a major roadblock getting the drag and drop to work. Can someone please take a look at my code and figure out how/where to integrate the Drag and drop event? Any help would be great.
UI image
import wx
class DropTarget(wx.FileDropTarget):
def OnDropFiles(self, x, y, filenames):
print(filenames)
image = Image.open(filenames[0])
image.thumbnail((PhotoMaxSize, PhotoMaxSize))
image.save('thumbnail.png')
pub.sendMessage('dnd', filepath='thumbnail.png')
return True
def __init__(self, parent, ID, title):
wx.FileDropTarget.__init__(self, parent, ID, title, size=(300, 340), style= wx.CLOSE_BOX)
#self.widget = widget
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title, size=(300, 340), style= wx.CLOSE_BOX)
panel1 = wx.Panel(self,-1, style=wx.SUNKEN_BORDER)
panel2 = wx.Panel(self,-1, style=wx.SUNKEN_BORDER)
panel1.SetBackgroundColour("BLUE")
panel2.SetBackgroundColour("RED")
image_file = 'bgimage1.png'
bmp1 = wx.Image(
image_file,
wx.BITMAP_TYPE_ANY).ConvertToBitmap()
# image's upper left corner anchors at panel
# coordinates (0, 0)
self.bitmap1 = wx.StaticBitmap(
self, -1, bmp1, (0, 0))
# show some image details
str1 = "%s %dx%d" % (image_file, bmp1.GetWidth(),
bmp1.GetHeight())
# button
closeButton = wx.Button(self.bitmap1, label='Generate', pos=(30, 280))
closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
clearButton = wx.Button(self.bitmap1, label='Clear', pos=(170, 280))
clearButton.Bind(wx.EVT_BUTTON, self.OnClose)
box = wx.BoxSizer(wx.VERTICAL)
box.Add(panel1, 5, wx.EXPAND)
box.Add(panel2, 1, wx.EXPAND)
self.SetAutoLayout(True)
self.SetSizer(box)
self.Layout()
def OnDropFiles(self, x, y, filenames):
self.window.updateDisplay(filenames)
for name in filenames:
self.window.WriteText(name + "\n")
print(name)
return True
def OnClose(self, e):
self.Close(True)
app = wx.App()
frame = MyFrame(None, -1, "Sizer Test")
frame.Show()
app.MainLoop()

You have the class DropTarget back to front with the init after the dropfiles. You also need to put the image and buttons on to one of the panels.
See below:
import wx
class DropTarget(wx.FileDropTarget):
def __init__(self, obj):
wx.FileDropTarget.__init__(self)
self.obj = obj
def OnDropFiles(self, x, y, filenames):
print("Drop Event",filenames)
# image = Image.open(filenames[0])
# image.thumbnail((PhotoMaxSize, PhotoMaxSize))
# image.save('new.png')
# pub.sendMessage('dnd', filepath='new.png')
return True
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title, size=(300, 340))
panel1 = wx.Panel(self,-1, style=wx.SUNKEN_BORDER)
panel2 = wx.Panel(self,-1, style=wx.SUNKEN_BORDER)
panel1.SetBackgroundColour("BLUE")
panel2.SetBackgroundColour("RED")
image_file = 'bgimage1.png'
bmp1 = wx.Image(image_file,wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.bitmap1 = wx.StaticBitmap(panel1, -1, bmp1, (0, 0))
# button
closeButton = wx.Button(panel2, -1, label='Generate',pos=(30, 280))
closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
clearButton = wx.Button(panel2, -1, label='Clear',pos=(170, 280))
clearButton.Bind(wx.EVT_BUTTON, self.OnClose)
self.file_drop_target = DropTarget(self)
self.SetDropTarget(self.file_drop_target)
box = wx.BoxSizer(wx.VERTICAL)
box.Add(panel1, 0, wx.EXPAND,0)
box.Add(panel2, 0, wx.EXPAND,0)
self.SetAutoLayout(True)
self.SetSizer(box)
self.Layout()
def OnClose(self, e):
self.Close(True)
app = wx.App()
frame = MyFrame(None, -1, "Sizer Test")
frame.Show()
app.MainLoop()
This may not be what you want to achieve but at least it's a startiing point and the drag and drop works.

Related

Wxpython panel is cropped with only a small box shown at the top left hand corner

I am using Hide() and Show() from wx to do the "next page" effect by hiding a panel and showing the next one but in the same frame (not very sure if I am doing it correctly though). At certain pages, the panel is just a small cropped version at the top left corner while some other panels can work normally (display the full thing). How do I solve this problem?
I saw something on stackoverflow about child/parent of the panel or frame and tried changing my code but it does not work, not very sure about how to do it correctly.
class MyPanel(wx.Panel):
def __init__(self, parent):
#Constructor
wx.Panel.__init__(self, parent=parent)
#self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
#This is for older versions of wx
self.frame = parent
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Layout()
def OnEraseBackground(self, evt):
#Add background pic
#From ColourDB.py
dc = evt.GetDC()
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
bmp = wx.Bitmap("RszHive.jpg")
dc.DrawBitmap(bmp, 0, 0)
class StartPage(wx.Frame):
def __init__(self, current_dt):
#Settings for frame
super().__init__(parent=None, title='Test', size=(850,790))
#setting up main panel (home page)
self.panel = MyPanel(self)
self.current_dt = current_dt
#so that frame will be in the center of the screen
self.Center()
self.vert_sizer = wx.BoxSizer(wx.VERTICAL)
from Database import DataBase, OperatingHours, GetDayTime, GetMenuByDayTime
dDataBase = DataBase("Full_Menu_Database.txt")
dOperatingHours = OperatingHours("Operating Hours.txt")
# Convert to a tuple needed for the functions
tDayTime = GetDayTime(self.get_dt())
# Function to get menu dictionary by date and time
# Will return an empty dictionary if no food/stores are available
self.stores_open = GetMenuByDayTime(dDataBase, dOperatingHours, tDayTime)
if self.stores_open == {}:
self.ophours = wx.StaticText(self.panel, -1, style=wx.ALIGN_CENTER)
self.ophours.SetLabel("Test")
self.ophours_font = wx.Font(19, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
self.ophours.SetFont(self.ophours_font)
self.vert_sizer.Add(self.ophours, 0, wx.ALL | wx.CENTER, 10)
else:
self.store_names, self.stores_ = [], []
for onestorename in self.stores_open.keys():
self.store_names.append(onestorename)
self.stores_.append(self.stores_open[onestorename])
#btn for store1
store_btn1 = wx.Button(self.panel, label= self.store_names[0])
store_btn1.Bind(wx.EVT_BUTTON, self.click_store1)
self.vert_sizer.Add(store_btn1, 0, wx.ALL | wx.CENTER, 5)
#btn for store2 if have
if len(self.store_names) > 1:
store_btn2 = wx.Button(self.panel, label=self.store_names[1])
store_btn2.Bind(wx.EVT_BUTTON, self.click_store2)
self.vert_sizer.Add(store_btn2, 0, wx.ALL | wx.CENTER, 5)
# btn for store3 if have
if len(self.store_names) > 2:
store_btn3 = wx.Button(self.panel, label=self.store_names[2])
store_btn3.Bind(wx.EVT_BUTTON, self.click_store3)
self.vert_sizer.Add(store_btn3, 0, wx.ALL | wx.CENTER, 5)
# btn for store4 if have
if len(self.store_names) > 3:
store_btn4 = wx.Button(self.panel, label=self.store_names[3])
store_btn4.Bind(wx.EVT_BUTTON, self.click_store4)
self.vert_sizer.Add(store_btn4, 0, wx.ALL | wx.CENTER, 5)
# btn for store5 if have
if len(self.store_names) > 4:
store_btn5 = wx.Button(self.panel, label=self.store_names[4])
store_btn5.Bind(wx.EVT_BUTTON, self.click_store5)
self.vert_sizer.Add(store_btn5, 0, wx.ALL | wx.CENTER, 5)
self.SetSizer(self.vert_sizer)
self.Layout()
self.Show()
Picture of what the panel looks like when i run the code
#igor is correct a call to Layout will get the job done.
Here is an example:
Click on the displayed panel to swap to the other one.
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 1", size=(250,75))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
def switch(self, event):
self.parent.Swap()
class MyOtherPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 2", size=(175,250))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
self.panel.Hide()
def switch(self, event):
self.parent.Swap()
class PanelSwitcher(wx.Frame):
def __init__(self):
super().__init__(None)
vbox = wx.BoxSizer(wx.VERTICAL)
self.panel1 = MyPanel(self)
self.panel2 = MyOtherPanel(self)
vbox.Add(self.panel1)
vbox.Add(self.panel2)
self.SetSizer(vbox)
self.Show()
def Swap(self):
if self.panel1.panel.IsShown():
self.panel1.panel.Hide()
self.panel2.panel.Show()
else:
self.panel2.panel.Hide()
self.panel1.panel.Show()
self.Layout()
if __name__ == "__main__":
app = wx.App()
PanelSwitcher()
app.MainLoop()
I also had the problem a very long time and did not know the solution. The sizers did not work (as I expected)
For me, the problem was, that the panel had no (or the incorrect size). The solution was eiter:
panel.Fit()
or
panel.SetSize(x,y)
Another possibility was, to first add the panel into a sizer. And then set them to the frame.
Afterwards put the buttons into the sizer - and add them to the panel.
This also solves the incorrect size of the panel.

wxPython append and pop message box inside panel serving like a notification center

I would like to make a wxpython program that has a notification center just like the one on windows or mac. Whenever I have a message, the message will show inside the the notification panel, and the user could close that message afterwards.
I have a sample code for illustration as follows:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,-1,wx.EXPAND|wx.ALL,border=10)
sizer.Add(self.panel2,-1,wx.EXPAND|wx.ALL,border=10)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
new_text = wx.TextCtrl(self.panel2, value="New Message")
self.sizer2.Add(new_text,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
In the above I code, the right panel (i.e. panel2) serves as a notification center that all the messages should shown inside it. On the left panel (i.e. panel1) I have a button to generate message just to mimic the notification behavior. Ideally the message on the right panel should be a message box that you could close (maybe a frame? Or a MessageDialog?)
Any hint or advice is much appreciated, and an example would be the best!
Thanks!
Finally figured out myself, it was easier than I initially thought.
Here is the code:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
self.number_of_panels = 0
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,0,wx.EXPAND|wx.ALL,border=5)
sizer.Add(self.panel2,1,wx.EXPAND|wx.ALL,border=5)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
self.number_of_panels += 1
panel_label = "Panel %s" % self.number_of_panels
panel_name = "panel%s" % self.number_of_panels
new_panel = wx.Panel(self.panel2, name=panel_name, style=wx.SIMPLE_BORDER)
self.closeButton = wx.Button(new_panel, label='Close %s' % self.number_of_panels)
self.closeButton.panel_number = self.number_of_panels
self.closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
self.sizer2.Add(new_panel,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
def OnClose(self, e):
if self.panel2.GetChildren():
e.GetEventObject().GetParent().Destroy()
self.number_of_panels -= 1
self.panel2.Layout() # Reset layout after destroy the panel
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
Basically I can destroy the newly created panel. I just need to know which panel it is when I click the close button. This should work very similar to the Notification Center.

How to add panel with event button in wxpython?

I want to ask is it possible to add wx.Panel with event button in wxpython? There are plenty examples how to switch panels Hide first one and show second, but they are useless for me. I want to create panel with add button. For example I have panel something like this
import wx
import wx.grid as grid
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent)
class SecondPanel(wx.Panel):
def __init__(self, parent,a,b):
wx.Panel.__init__(self, parent=parent)
MyGrid=grid.Grid(self)
MyGrid.CreateGrid(a, b)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(MyGrid, 0, wx.EXPAND)
self.SetSizer(sizer)
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="test",
size=(800,600))
self.splitter = wx.SplitterWindow(self)
self.panelOne = MainPanel(self.splitter)
self.panelTwo = SecondPanel(self.splitter, 1, 1)
txtOne = wx.StaticText(self.panelOne, -1, label = "piradoba", pos = (20,10))
self.txtTwo = wx.StaticText(self.panelOne, -1, label = "", pos = (40,80))
self.txtPlace = wx.TextCtrl(self.panelOne, pos = (20,30))
button = wx.Button(self.panelOne, label = "search", pos = (40,100))
button.Bind(wx.EVT_BUTTON, self.Onbutton)
self.splitter.SplitHorizontally(self.panelOne, self.panelTwo)
self.splitter.SetMinimumPaneSize(20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.splitter, 1, wx.EXPAND)
self.SetSizer(sizer)
def Onbutton(self, event):
var=self.txtPlace.GetValue()
if len(var) == 9 or len(var) == 11:
???????????????????????????????????????????????
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
for example now I want to add new panel with this event what can I do? and I want to create this panel with event.
I don't know if it is what you need but in this example you have:
panel with button and event
button call function in mainframe
mainframe add next panel (with grid) to boxsizer
Tested on Linux Mint + Python 2.7.4
import wx
import wx.grid as grid
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent)
self.txtOne = wx.StaticText(self, -1, label = "piradoba", pos = (20,10))
self.txtPlace = wx.TextCtrl(self, pos = (20,30))
self.txtTwo = wx.StaticText(self, -1, label = "", pos = (20,40))
button = wx.Button(self, label = "search", pos = (20,70))
button.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self, event):
var=self.txtPlace.GetValue()
if len(var) == 9 or len(var) == 11:
print "???"
# MainPanel->SplitterWindow->MainFrame ( 2x GetParent() )
self.GetParent().GetParent().AddPanel()
class SecondPanel(wx.Panel):
def __init__(self, parent,a,b):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
MyGrid=grid.Grid(self)
MyGrid.CreateGrid(a, b)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(MyGrid, 0, wx.EXPAND)
self.SetSizer(sizer)
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="test", size=(800,600))
self.splitter = wx.SplitterWindow(self)
self.panelOne = MainPanel(self.splitter)
self.panelTwo = SecondPanel(self.splitter, 1, 1)
self.splitter.SplitHorizontally(self.panelOne, self.panelTwo)
self.splitter.SetMinimumPaneSize(20)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.splitter, 2, wx.EXPAND)
self.SetSizer(self.sizer)
def AddPanel(self):
self.newPanel = SecondPanel(self, 1, 1)
self.sizer.Add(self.newPanel, 1, wx.EXPAND)
self.sizer.Layout()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()

wxPython display a login box

I'm trying to build a small application in wxPython (absolute beginner) in which I display a login box before showing the content. I created a frame, inside the frame a panel with a flexigrid to put the login form inside but it doesn't show. If I launch the application the login form is invisible. If I resize the application the login box shows. Any idea why? Here's my code so far:
import wx
class AP_App(wx.App):
def OnInit(self):
frame = AP_MainFrame("Test application", (0, 0), (650, 350))
frame.Show()
self.SetTopWindow(frame)
loginPanel = AP_LoginPanel(frame)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
return True
def OnCloseWindow(self, event):
self.Destroy()
class AP_MainFrame(wx.Frame):
def __init__(self, title, pos, size):
wx.Frame.__init__(self, None, -1, title, pos, size)
self.CreateStatusBar()
class AP_LoginPanel(wx.Panel):
def __init__(self, frame):
self.panel = wx.Panel(frame)
self.frame = frame
self.frame.SetStatusText("Authentification required!")
self.showLoginBox()
def showLoginBox(self): #Create the sizer
sizer = wx.FlexGridSizer(rows = 3, cols = 2, hgap = 5, vgap = 15)
# Username
self.txt_Username = wx.TextCtrl(self.panel, 1, size = (150, -1))
lbl_Username = wx.StaticText(self.panel, -1, "Username:")
sizer.Add(lbl_Username,0, wx.LEFT|wx.TOP| wx.RIGHT, 50)
sizer.Add(self.txt_Username,0, wx.TOP| wx.RIGHT, 50)
# Password
self.txt_Password = wx.TextCtrl(self.panel, 1, size=(150, -1), style=wx.TE_PASSWORD)
lbl_Password = wx.StaticText(self.panel, -1, "Password:")
sizer.Add(lbl_Password,0, wx.LEFT|wx.RIGHT, 50)
sizer.Add(self.txt_Password,0, wx.RIGHT, 50)
# Submit button
btn_Process = wx.Button(self.panel, -1, "&Login")
self.panel.Bind(wx.EVT_BUTTON, self.OnSubmit, btn_Process)
sizer.Add(btn_Process,0, wx.LEFT, 50)
self.panel.SetSizer(sizer)
def OnSubmit(self, event):
UserText = self.txt_Username.GetValue()
PasswordText = self.txt_Password.GetValue()
if __name__ == '__main__':
app = AP_App()
app.MainLoop()
I just discovered I'm calling frame.Show() too soon. :)

How to draw text in a bitmap using wxpython?

I want to draw a number centered inside a wx.EmptyBitmap.
How can I do it using wxpython?
Thanks in advance :)
import wx
app = None
class Size(wx.Frame):
def __init__(self, parent, id, title):
frame = wx.Frame.__init__(self, parent, id, title, size=(250, 200))
bmp = wx.EmptyBitmap(100, 100)
dc = wx.MemoryDC()
dc.SelectObject(bmp)
dc.DrawText("whatever", 50, 50)
dc.SelectObject(wx.NullBitmap)
wx.StaticBitmap(self, -1, bmp)
self.Show(True)
app = wx.App()
Size(None, -1, 'Size')
app.MainLoop()
This code only gives me a black image, what am I doing wrong?
What's missing here..
Select the bmp in a wx.MemoryDC, draw anything on that dc and then select that bitmap out e.g.
import wx
app = None
class Size(wx.Frame):
def __init__(self, parent, id, title):
frame = wx.Frame.__init__(self, parent, id, title, size=(250, 200))
w, h = 100, 100
bmp = wx.EmptyBitmap(w, h)
dc = wx.MemoryDC()
dc.SelectObject(bmp)
dc.Clear()
text = "whatever"
tw, th = dc.GetTextExtent(text)
dc.DrawText(text, (w-tw)/2, (h-th)/2)
dc.SelectObject(wx.NullBitmap)
wx.StaticBitmap(self, -1, bmp)
self.Show(True)
app = wx.App()
app.MainLoop()
You use a wx.MemoryDC

Categories

Resources