Related
I am trying to dynamically load a wxGrid with a pandas Dataframe depending on what table is selected in the combobox. I can get the grid to load on intialization, but can't figure out how to get refresh the GridTableClass and grid. Right now I am trying to test with a random Dataframe.
class PageOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageOne object", (20,20))
class PageTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageTwo object", (40, 40))
class PageThree(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageThree object", (60, 60))
class DataTable(gridlib.GridTableBase):
def __init__(self, data):
gridlib.GridTableBase.__init__(self)
self.data = data
#self.colnames = colnames
#Store the row and col length to see if table has changed in size
self._rows = self.GetNumberRows()
self._cols = self.GetNumberCols()
self.odd=gridlib.GridCellAttr()
self.odd.SetBackgroundColour((217,217,217))
self.even=gridlib.GridCellAttr()
self.even.SetBackgroundColour((255,255,255))
def GetAttr(self, row, col, kind):
attr = [self.even, self.odd][row % 2]
attr.IncRef()
return attr
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.data.columns) + 1
def IsEmptyCell(self, row, col):
return False
def GetValue(self, row, col):
#if col == 0:
# return None #self.data.index[row]
return self.data.iloc[row, col-1]
def SetValue(self, row, col, value):
self.data.iloc[row, col - 1] = value
def GetColLabelValue(self, col):
if col == 0:
return None
#pass
#return 'Index' if self.data.index.name is None else self.data.index.name
return self.data.columns[col - 1] #[col-1]
#return None
#---------------------------------------------------------------------------
class DataGrid(gridlib.Grid):
def __init__(self, parent, data): # data
gridlib.Grid.__init__(self, parent, - 1) #,colnames,-1 # data
#data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
table = DataTable(data)
print ("passed")
# The second parameter means that the grid is to take ownership of the
# table and will destroy it when done. Otherwise you would need to keep
# a reference to it and call it's Destroy method later.
self.SetTable(table, True)
self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK, self.OnCellRightClick)
def OnCellRightClick(self, event):
print ("OnCellRightClick: (%d,%d)\n" % (event.GetRow(), event.GetCol()))
#-------------------------------------------------------------------------------
class MainFrame(wx.Frame):
def __init__(self, parent, data): # (self, parent, data):
wx.Frame.__init__(self, parent, -1, "Varkey Foundation") #, size=(640,480))
#Create a panel
self.p = wx.Panel(self)
self.Maximize(True)
#Create blank dataframe
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
#data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
self.data = DataTable(data)
self.nb = wx.Notebook(self.p)
self.p.SetBackgroundColour( wx.Colour( 0, 0, 0 ) ) # 38,38,38
self.nb.SetBackgroundColour(wx.Colour(58, 56, 56) )
#self.SetBackgroundColour( wx.Colour( 255, 255, 56 ) )
#create the page windows as children of the notebook
self.page1 = PageOne(self.nb)
self.page2 = PageTwo(self.nb)
self.page3 = PageThree(self.nb)
# add the pages to the notebook with the label to show on the tab
self.nb.AddPage(self.page1, "Data")
self.nb.AddPage(self.page2, "Analyze")
self.nb.AddPage(self.page3, "Change Log")
#Create the grid and continue layout
self.grid = DataGrid(self.page1, data)
#grid.SetReadOnly(5,5, True)
#CreateFonts
self.b_font = wx.Font(14,wx.ROMAN,wx.NORMAL,wx.BOLD, True)
self.lbl_font = wx.Font(14,wx.ROMAN,wx.NORMAL,wx.NORMAL, True)
self.cb_font = wx.Font(11,wx.SCRIPT,wx.ITALIC,wx.NORMAL, True)
self.h_font = wx.Font(18,wx.DECORATIVE,wx.ITALIC,wx.BOLD, True)
#Create Title bmp
ico = wx.Icon('varkey_bmp.bmp', wx.BITMAP_TYPE_ICO) #'varkey_frame.bmp'
self.SetIcon(ico)
#Page 1 sizers and widgets
self.title = wx.StaticText(self.page1,label="TITLE",style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE)
self.title.SetForegroundColour((255,255,255))
self.title.SetFont(self.h_font)
self.p1_sizer = wx.BoxSizer(wx.VERTICAL)
self.p1_sizer.Add(self.title,0,wx.EXPAND,5)
self.p1_sizer.Add(self.grid,3,wx.EXPAND | wx.ALL ,25)
#self.p1_sizer.Add(self.btn_new,-0,wx.ALIGN_CENTER,5)
self.page1.SetSizer(self.p1_sizer)
#Page 2 sizers and widgets
self.analyze_grid = gridlib.Grid(self.page2)
self.analyze_grid.CreateGrid(0, 10)
self.p2_sizer = wx.BoxSizer(wx.VERTICAL)
self.p2_sizer.Add(self.analyze_grid,1,wx.EXPAND)
self.page2.SetSizer(self.p2_sizer)
#Page 3 sizers and widgets
self.log_grid = gridlib.Grid(self.page3)
self.log_grid.CreateGrid(0, 9)
self.log_grid.EnableEditing(False)
self.p3_sizer = wx.BoxSizer(wx.VERTICAL)
self.p3_sizer.Add(self.log_grid,1,wx.EXPAND)
self.page3.SetSizer(self.p3_sizer)
#Create widgets for top sizer
#Insert Image
self.staticbitmap = wx.StaticBitmap(self.p)
self.staticbitmap.SetBitmap(wx.Bitmap('varkey_logo2.jpg'))
self
self.lbl_user = wx.StaticText(self.p,label="Username:")
self.lbl_password = wx.StaticText(self.p,label="Password:")
self.lbl_interaction = wx.StaticText(self.p,label="Interaction:")
self.lbl_table = wx.StaticText(self.p,label="Table:")
#SetForground colors
self.lbl_user.SetForegroundColour((255,255,255))
self.lbl_password.SetForegroundColour((255,255,255))
self.lbl_interaction.SetForegroundColour((255,255,255))
self.lbl_table.SetForegroundColour((255,255,255))
#Set Fonts
self.lbl_user.SetFont(self.lbl_font)
self.lbl_password.SetFont(self.lbl_font)
self.lbl_interaction.SetFont(self.lbl_font)
self.lbl_table.SetFont(self.lbl_font)
self.tc_user =wx.TextCtrl(self.p,value='cmccall95',size = (130,25))
self.tc_password =wx.TextCtrl(self.p,value='Achilles95', style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER,size = (130,25))
#self.tc_password.Bind(wx.EVT_TEXT_ENTER,self.onLogin)
self.tc_user.SetFont(self.cb_font)
self.tc_password.SetFont(self.cb_font)
self.btn_login = wx.Button(self.p,label="Login", size=(105,30))
self.btn_login.SetBackgroundColour(wx.Colour(198, 89, 17))
self.btn_login.SetFont(self.b_font)
self.btn_login.Bind(wx.EVT_BUTTON, self.onLogin) #connect_mysql
self.btn_logout = wx.Button(self.p,label="Logout",size=(105,30))
self.btn_logout.SetBackgroundColour(wx.Colour(192,0,0))
self.btn_logout.SetFont(self.b_font)
#self.btn_logout.Bind(wx.EVT_BUTTON, self.onLogout)
self.combo_interaction = wx.ComboBox(self.p, size = (160,25),style = wx.CB_READONLY | wx.CB_SORT | wx.CB_SORT)
#self.combo_interaction.Bind(wx.EVT_COMBOBOX, self.onComboInteraction)
self.combo_table = wx.ComboBox(self.p, size = (160,25),style = wx.CB_READONLY | wx.CB_SORT | wx.CB_SORT)
#self.combo_table.Bind(wx.EVT_COMBOBOX, self.onHideCommands)
self.combo_interaction.SetFont(self.cb_font)
self.combo_table.SetFont(self.cb_font)
#self.combo_table.Bind(wx.EVT_COMBOBOX ,self.OnComboTable)
self.btn_load = wx.Button(self.p,label="Load Table", size=(105,30))
self.btn_load.SetBackgroundColour(wx.Colour(31, 216, 6))
self.btn_load.SetFont(self.b_font)
#self.btn_load.Bind(wx.EVT_BUTTON, self.onLoadData)
self.btn_load.Bind(wx.EVT_BUTTON, self.test_return)
self.lc_change = wx.ListCtrl(self.p,-1,style = wx.TE_MULTILINE | wx.LC_REPORT | wx.LC_VRULES)
self.lc_change.InsertColumn(0,"User ID")
self.lc_change.InsertColumn(1,"Status")
self.lc_change.InsertColumn(2,"Description")
self.lc_change.InsertColumn(3,"Date/Time")
#Set column widths
self.lc_change.SetColumnWidth(0, 75)
self.lc_change.SetColumnWidth(1, 75)
self.lc_change.SetColumnWidth(2, 450)
self.lc_change.SetColumnWidth(3, 125)
#Create Filler text
self.lbl_filler = wx.StaticText(self.p,label="",size = (125,20))
#Create FlexGridSizers(For top half)
self.left_fgs = wx.FlexGridSizer(3,4,25,15)
self.left_fgs.AddMany([(self.lbl_user,1,wx.ALIGN_LEFT | wx.LEFT,15),(self.tc_user,1,wx.EXPAND),(self.lbl_interaction,1,wx.ALIGN_RIGHT|wx.RIGHT, 10),(self.combo_interaction,1,wx.EXPAND),
(self.lbl_password,1,wx.ALIGN_LEFT| wx.LEFT,15),(self.tc_password,1,wx.EXPAND),(self.lbl_table,1,wx.ALIGN_RIGHT|wx.RIGHT, 10),(self.combo_table),
(self.btn_login,2,wx.EXPAND),(self.btn_logout,1,wx.EXPAND),(self.lbl_filler,1,wx.EXPAND),(self.btn_load,1)])
#Create Top Sizer
self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.top_sizer.Add(self.left_fgs,proportion = 1, flag = wx.ALL|wx.EXPAND,border = 30)
self.top_sizer.Add(self.staticbitmap,2,wx.TOP | wx.RIGHT, border = 40) #30
self.top_sizer.Add(self.lc_change,2,wx.RIGHT|wx.EXPAND ,30)
#create Bottom Sizer
self.bottom_sizer = wx.BoxSizer(wx.VERTICAL)
self.bottom_sizer.Add(self.nb,proportion = 5, flag = wx.LEFT |wx.RIGHT | wx.EXPAND,border = 30)
self.mainsizer = wx.BoxSizer(wx.VERTICAL)
self.mainsizer.Add(self.top_sizer,proportion = 0, flag = wx.ALL|wx.EXPAND,border = 5)
self.mainsizer.Add(self.bottom_sizer,proportion = 1,flag = wx.ALL|wx.EXPAND,border = 5)
#self.mainsizer.Add(self.status_sizer,proportion =0,flag = wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, border = 15)
self.p.SetSizerAndFit(self.mainsizer)
def test_reload(self, event):
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
#self.data = DataTable(data)
self.table.data = self.data
self.grid.Refresh()
#Some more functions.......
if __name__ == '__main__':
import sys
app = wx.App()
frame = MainFrame(None, sys.stdout) # (None, sys.stdout)
frame.Show(True)
app.MainLoop()
I've tried many different variations of the function and can't understand how this should work. From my understanding, I should the table and then call the grid to refresh.
def test_reload(self, event):
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
self.table.data = self.data
self.grid.Refresh()
I managed to find a solution. Hopefully this helps someone else out.
To reload the grid, I first create new data. Then I destroy the grid, create a new grid, and insert it in place of the old grid. You must then call Layout(). The Freeze() and Thaw() methods are to prevent screen flickering.
def test_reload(self, event):
self.Freeze()
#Create new data
data = pd.DataFrame(np.random.randint(0,100,size=(40000, 5)),columns=list('EFGHD'))
#Destroy and create grid with new data assigned
self.grid.Destroy()
self.grid = DataGrid(self.page1, data)
#Insert grid into existing sizer
self.p1_sizer.Insert(1,self.grid,1,wx.RIGHT| wx.LEFT|wx.EXPAND, 20)
self.p1_sizer.Layout()
self.Thaw()
I'm sure there is a better method out there, but this works and is instant.
Problem: I want to be able to force the scrollbar to the bottom when I call createNewRow(). I can see that the self.Scroll(0, self.scrollRange) is occurring in OnKeyTyped() and the scrollbar moves to the bottom of the window but then the scrollbar moves to the top of the window again. I tried to stop this by binding wx.EVT_SCROLLWIN to OnScroll which calls event.Skip() but it appears that this has not worked. I am out of ideas on how to proceed as I don't know enough about eventHandlers and scroll events in wxPython. Any help on how to proceed would be much appreciated. Full code below.
import os
import wx
import datetime as dt
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
width = 1000
height = 600
today = dt.date.today()
today_date = f"{today:%A - %d %B %Y}"
filename = f"Worklog {today_date}"
wxTHICK_LINE_BORDER = 3
def __init__(self, parent=None, title=filename, size=(width,height - 1)):
wx.Frame.__init__(self, parent=parent, title=title, size=size)
self.parent = parent
self.title = title
self.size = size
self.BuildMenuBar()
def BuildMenuBar(self):
# Menu bar
self.menuBar = wx.MenuBar()
self.fileMenu = wx.Menu()
self.NewOpt = wx.MenuItem(self.fileMenu, wx.ID_NEW, '&New\tCtrl+N')
self.OpenOpt = wx.MenuItem(self.fileMenu, wx.ID_OPEN, '&Open\tCtrl+O')
self.SaveOpt = wx.MenuItem(self.fileMenu, wx.ID_SAVE, '&Save\tCtrl+S')
self.QuitOpt = wx.MenuItem(self.fileMenu, wx.ID_EXIT, '&Quit\tCtrl+Q')
self.fileMenu.Append(self.NewOpt)
self.fileMenu.Append(self.OpenOpt)
self.fileMenu.Append(self.SaveOpt)
self.fileMenu.Append(self.QuitOpt)
self.Bind(wx.EVT_MENU, self.OnQuit, self.QuitOpt)
self.menuBar.Append(self.fileMenu, '&File')
self.SetMenuBar(self.menuBar)
def OnQuit(self, e):
self.Close()
class MyPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self, parent=parent)
self.parent = parent
self.size = parent.size
panel_colour = wx.Colour(240, 240, 240, 255)
self.SetBackgroundColour(panel_colour)
self.Refresh()
class MyScrolledPanel(scrolled.ScrolledPanel):
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent=parent, style = wx.TAB_TRAVERSAL | wx.TB_BOTTOM)
self.parent = parent
# self.size = parent.size
self.width = parent.size[0]
self.height = parent.size[1]
scrollpanel_colour = wx.Colour(255, 255, 255, 255)
self.SetBackgroundColour(scrollpanel_colour)
# Call a refresh to update the UI
self.Refresh()
self.SetAutoLayout(True)
self.SetupScrolling()
self.InitUI()
self.Bind(wx.EVT_SCROLLWIN, self.OnScroll, self)
self.Bind(wx.EVT_SIZE, self.OnSize, self)
def OnScroll(self, e):
e.Skip()
def InitUI(self):
vgap = 0
hgap = 0
self.rowList = []
self.n = 0
self.scrollSizer = wx.GridBagSizer(vgap + 10, hgap + 10)
self.row = self.CreateNewRow(self.n)
self.rowList.append(self.row)
print(f"Row List: {self.rowList[-1]}")
self.scrollSizer.Add(self.row[0], pos = (self.i, 0), flag = wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, border = 10)
self.scrollSizer.Add(self.row[1], pos = (self.i, 1), flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL , border = 10)
self.scrollSizer.AddGrowableCol(1)
self.SetSizer(self.scrollSizer)
self.panelSizer = wx.GridBagSizer(vgap, hgap)
self.panelSizer.AddGrowableRow(0)
self.panelSizer.AddGrowableCol(0)
self.panelSizer.Add(self, pos = (0, 0), flag = wx.EXPAND, border = 0) # Add wx.Window not wx.Sizer
self.parent.SetSizer(self.panelSizer)
def CreateNewRow(self, number):
self.i = number
self.txtStr = "%02d" % (self.i+1) + ". "
self.staticText = wx.StaticText(self, wx.ID_ANY, self.txtStr)
#pos = (x, y)
#self.staticText.SetForegroundColour(wx.Colour(0,0,0))
self.control = wx.TextCtrl(self, self.i)
self.control.SetMaxLength(256)
self.text_history_length = 0
self.control.Bind(wx.EVT_TEXT, self.OnKeyTyped, id = self.i)
#self.control = wx.TextCtrl(self, -1, pos = (x + w + 5,y) )
#style = wx.TE_MULTILINE
elems = [self.staticText, self.control]
return elems
def OnSize(self, e):
self.width, self.height = e.GetSize()
self.SetSize((self.width, self.height))
self.OnSizeChange()
self.Refresh()
def OnSizeChange(self):
# Fit child elements
self.scrollSizer.FitInside(self)
# Resize layout
self.Layout()
# Resize scrolling
self.SetupScrolling()
def OnKeyTyped(self, e):
self.text_length = len(e.GetString())
if (self.text_history_length == 1 and self.text_length == 0):
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.text_history_length = self.text_length
pass
elif (self.text_history_length == 0 and self.text_length == 1):
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.n += 1
self.row = self.CreateNewRow(self.n)
print(f"Action: {self.row}")
print(f"Row List: {self.rowList[-1]}")
self.rowList.append(self.row)
self.scrollSizer.Add(self.row[0], pos = (self.n, 0), flag = wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, border = 10)
self.scrollSizer.Add(self.row[1], pos = (self.n, 1), flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL , border = 10)
self.SetupScrolling()
self.text_history_length = self.text_length
self.rowList[self.n-1][1].Bind(wx.EVT_TEXT, None, id = self.n-1)
self.text_history_length = 0
else:
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.text_history_length = self.text_length
self.rowList[-1][1].SetFocus()
self.scrolledPanelChild = self.GetChildren()[-1] # [ scrollPanel ]
self.ScrollChildIntoView(self.scrolledPanelChild)
self.OnSizeChange()
self.scrollRange = self.GetScrollRange(wx.VERTICAL)
print(f"ScrollRange: {self.scrollRange}")
#self.scrollUnits = self.GetScrollPixelsPerUnit()
#print(f"ScrollUnit: {self.scrollUnits}")
#self.scrollThumb = self.GetScrollThumb(wx.VERTICAL)
#print(f"ScrollThumb: {self.scrollThumb}")
self.Scroll(0, self.scrollRange)
def main():
app = wx.App(False)
app.locale = wx.Locale(wx.Locale.GetSystemLanguage())
frame = MyFrame()
panel = MyPanel(frame)
scrolledPanel = MyScrolledPanel(panel)
frame.Show(True)
app.MainLoop()
if __name__ == "__main__":
main()
In :
def OnSizeChange(self):
# Fit child elements
self.scrollSizer.FitInside(self)
# Resize layout
self.Layout()
# Resize scrolling
self.SetupScrolling()
the SetupScrolling() event causes the scrollbar to reset to the top of the page. So if you comment this the Scroll(0, self.scrollRange) will scroll the scrollbar to the bottom of the page. Improvement would be to get this call to happen after SetupScrolling so that it happens anyway. Maybe the call to SetupScrolling is not necessary in anycase and Layout() is enough.
Old question but still. This can be solved using:
wx.CallAfter(self._scrolled_panel.ScrollChildIntoView, new_text) Or calling any of the other scroll methods from the CallAfter.
or
SetupScrolling(scrollIntoView=True, scrollToTop=False)
I am making a physics simulator using tkinter and I want to have an image on top of a button in dnd, tkinter so that when I move the button, the image moves. How do I do this? The image and code are in the same folder.
I have tried adding an image by using it like a normal button but it gives an error. The code is the code from help(dnd) in python. I am using python 3.6. I need to identify the type of component the button is when I move it
Also, if anyone can tell me how to use collision detect to detect when 2 images are touching.
import tkinter
__all__ = ["dnd_start", "DndHandler"]
def dnd_start(source, event):
h = DndHandler(source, event)
if h.root:
return h
else:
return None
# The class that does the work
class DndHandler:
root = None
def __init__(self, source, event):
if event.num > 5:
return
root = event.widget._root()
try:
root.__dnd
return # Don't start recursive dnd
except AttributeError:
root.__dnd = self
self.root = root
self.source = source
self.target = None
self.initial_button = button = event.num
self.initial_widget = widget = event.widget
self.release_pattern = "<B%d-ButtonRelease-%d>" % (button, button)
self.save_cursor = widget['cursor'] or ""
widget.bind(self.release_pattern, self.on_release)
widget.bind("<Motion>", self.on_motion)
widget['cursor'] = "hand2"
def __del__(self):
root = self.root
self.root = None
if root:
try:
del root.__dnd
except AttributeError:
pass
def on_motion(self, event):
x, y = event.x_root, event.y_root
target_widget = self.initial_widget.winfo_containing(x, y)
source = self.source
new_target = None
while target_widget:
try:
attr = target_widget.dnd_accept
except AttributeError:
pass
else:
new_target = attr(source, event)
if new_target:
break
target_widget = target_widget.master
old_target = self.target
if old_target is new_target:
if old_target:
old_target.dnd_motion(source, event)
else:
if old_target:
self.target = None
old_target.dnd_leave(source, event)
if new_target:
new_target.dnd_enter(source, event)
self.target = new_target
def on_release(self, event):
self.finish(event, 1)
def cancel(self, event=None):
self.finish(event, 0)
def finish(self, event, commit=0):
target = self.target
source = self.source
widget = self.initial_widget
root = self.root
try:
del root.__dnd
self.initial_widget.unbind(self.release_pattern)
self.initial_widget.unbind("<Motion>")
widget['cursor'] = self.save_cursor
self.target = self.source = self.initial_widget = self.root = None
if target:
if commit:
target.dnd_commit(source, event)
else:
target.dnd_leave(source, event)
finally:
source.dnd_end(target, event)
class Icon:
def __init__(self, name):
self.name = name
self.canvas = self.label = self.id = None
def attach(self, canvas, x=10, y=10):
if canvas is self.canvas:
self.canvas.coords(self.id, x, y)
return
if self.canvas:
self.detach()
if not canvas:
return
label = tkinter.Label(canvas, text=self.name,
borderwidth=2, relief="raised")
id = canvas.create_window(x, y, window=label, anchor="nw")
self.canvas = canvas
self.label = label
self.id = id
label.bind("<ButtonPress>", self.press)
def detach(self):
canvas = self.canvas
if not canvas:
return
id = self.id
label = self.label
self.canvas = self.label = self.id = None
canvas.delete(id)
label.destroy()
def press(self, event):
if dnd_start(self, event):
# where the pointer is relative to the label widget:
self.x_off = event.x
self.y_off = event.y
# where the widget is relative to the canvas:
self.x_orig, self.y_orig = self.canvas.coords(self.id)
def move(self, event):
x, y = self.where(self.canvas, event)
self.canvas.coords(self.id, x, y)
def putback(self):
self.canvas.coords(self.id, self.x_orig, self.y_orig)
def where(self, canvas, event):
# where the corner of the canvas is relative to the screen:
x_org = canvas.winfo_rootx()
y_org = canvas.winfo_rooty()
# where the pointer is relative to the canvas widget:
x = event.x_root - x_org
y = event.y_root - y_org
# compensate for initial pointer offset
return x - self.x_off, y - self.y_off
def dnd_end(self, target, event):
pass
class Tester:
def __init__(self, root):
self.top = tkinter.Toplevel(root)
self.canvas = tkinter.Canvas(self.top, width=100, height=100)
self.canvas.pack(fill="both", expand=1)
self.canvas.dnd_accept = self.dnd_accept
def dnd_accept(self, source, event):
return self
def dnd_enter(self, source, event):
self.canvas.focus_set() # Show highlight border
x, y = source.where(self.canvas, event)
x1, y1, x2, y2 = source.canvas.bbox(source.id)
dx, dy = x2-x1, y2-y1
self.dndid = self.canvas.create_rectangle(x, y, x+dx, y+dy)
self.dnd_motion(source, event)
def dnd_motion(self, source, event):
x, y = source.where(self.canvas, event)
x1, y1, x2, y2 = self.canvas.bbox(self.dndid)
self.canvas.move(self.dndid, x-x1, y-y1)
def dnd_leave(self, source, event):
self.top.focus_set() # Hide highlight border
self.canvas.delete(self.dndid)
self.dndid = None
def dnd_commit(self, source, event):
self.dnd_leave(source, event)
x, y = source.where(self.canvas, event)
source.attach(self.canvas, x, y)
def test():
root = tkinter.Tk()
root.geometry("+1+1")
tkinter.Button(command=root.quit, text="Quit").pack()
t1 = Tester(root)
t1.top.geometry("+1+60")
t2 = Tester(root)
t2.top.geometry("+120+60")
t3 = Tester(root)
t3.top.geometry("+240+60")
i1 = Icon("ICON1")
i2 = Icon("ICON2")
i3 = Icon("ICON3")
i1.attach(t1.canvas)
i2.attach(t2.canvas)
i3.attach(t3.canvas)
root.mainloop()
if __name__ == '__main__':
test()
I have this code
import wx, time
def GetRoundBitmap( w, h, r ):
maskColor = wx.Color(0,0,0)
shownColor = wx.Color(5,5,5)
b = wx.EmptyBitmap(w,h)
dc = wx.MemoryDC(b)
dc.SetBrush(wx.Brush(maskColor))
dc.DrawRectangle(0,0,w,h)
dc.SetBrush(wx.Brush(shownColor))
dc.SetPen(wx.Pen(shownColor))
dc.DrawRoundedRectangle(0,0,w,h,r)
dc.SelectObject(wx.NullBitmap)
b.SetMaskColour(maskColor)
return b
def GetRoundShape( w, h, r ):
return wx.RegionFromBitmap( GetRoundBitmap(w,h,r) )
class FancyFrame(wx.Frame):
def __init__(self):
style = ( wx.CLIP_CHILDREN | wx.STAY_ON_TOP | wx.FRAME_NO_TASKBAR |
wx.NO_BORDER | wx.FRAME_SHAPED )
wx.Frame.__init__(self, None, title='Fancy', style = style)
self.SetBackgroundColour(wx.BLACK)
self.SetTransparent( 150 )
self.panel = wx.Panel(self)
self.lblname = wx.StaticText(self.panel, label="Your name aaaa:")
#self.lblname.Wrap(210)
self.lblname.SetForegroundColour((91,91,91)) # set text color
self.sizer = wx.GridBagSizer(500, 500)
self.sizer.Add(self.lblname, (1, 0))
if wx.Platform == '__WXGTK__':
self.Bind(wx.EVT_WINDOW_CREATE, self.SetRoundShape)
else:
self.SetRoundShape()
self.Show(True)
def SetRoundShape(self, event=None):
w, h = self.GetSizeTuple()
w=250
h=50
self.SetShape(GetRoundShape( w,h, 10 ) )
dw, dh = wx.DisplaySize()
#w, h = self.GetSize()
x = dw - w
x = (dw/2) - (w/2)
y = dh - h - 50
self.SetPosition((x, y))
app = wx.App()
f = FancyFrame()
app.MainLoop()
I need to have "Your name aaaa:" aligned to center but I dont know why it is cut. How to solve it?
Your gridbagsizer is a bit out and you aren't setting the sizer.
Try:
self.sizer = wx.GridBagSizer(1,1)
self.sizer.Add(self.lblname,pos=(1,2),flag=wx.ALIGN_CENTER)
and:
self.panel.SetSizer(self.sizer)
self.Show(True)
I'm trying to use wx Python's AGW LabelBook (using wxPython 2.8.11.0, Python 2.7.1+, Ubuntu 11.04), such that the tabs (list) are left-aligned; here I have some short texts, and I expected the tablist area would have its width shortened accordingly; but instead I get this:
At that mouse position, I get a sizer pointer - and I can drag it to the right to increase the width of the tablist area as much as I want; but I cannot drag it any further to the left, to make the width shorter. I also tried to use INB_FIT_LABELTEXT, but it doesn't seem to change anything...
Is it possible to somehow instruct LabelBook to set the minimal width of the left tablist area to the approx width of text (say, indicated at the drawn red line)?
This is the code I used to generate the screenshot:
import wx
import wx.lib.agw
import wx.lib.agw.labelbook as LB
from wx.lib.agw.fmresources import *
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Labelbook test")
self.tlbook = LB.LabelBook(self, -1, size=(400, 200), style = wx.NB_LEFT, agwStyle = INB_LEFT | INB_FIT_LABELTEXT | INB_FIT_BUTTON | INB_SHOW_ONLY_TEXT | INB_USE_PIN_BUTTON)
sizer_1 = wx.BoxSizer(wx.VERTICAL)
self.tlbook_panel_1 = wx.Panel(self.tlbook)
self.tlbook_panel_2 = wx.Panel(self.tlbook)
self.tlbook.AddPage(self.tlbook_panel_1, "Test 1")
self.tlbook.AddPage(self.tlbook_panel_2, "Test 2")
sizer_1.Add(self.tlbook, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.SetSize((450, 250))
self.Layout()
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Ok, I think I got it:
... and the thing is, that the width of the tab area is hardcoded in the source for LabelBook as 100 pixels, but not all in the same class - so some monkeypatching is required, if one wants to leave the source in tact. Here is the code:
import wx
import wx.lib.agw
import wx.lib.agw.labelbook as LB
#~ from wx.lib.agw.labelbook import * # for INB_BOLD_TAB_SELECTION = 16384? nope
INB_BOLD_TAB_SELECTION = 16384
from wx.lib.agw.fmresources import *
WIDTHLIMITPIX=20
class OLabelContainer(LB.LabelContainer): # overloaded version
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, agwStyle=0, name="LabelContainer"):
super(OLabelContainer, self).__init__(parent, id, pos, size, style, agwStyle, name)
def Resize(self, event): # copy from wx/lib/agw/labelbook.py
# Resize our size
self._tabAreaSize = self.GetSize()
newWidth = self._tabAreaSize.x
x = event.GetX()
if self.HasAGWFlag(INB_BOTTOM) or self.HasAGWFlag(INB_RIGHT):
newWidth -= event.GetX()
else:
newWidth = x
# hack: was 100 here
if newWidth < WIDTHLIMITPIX: #100: # Dont allow width to be lower than that
newWidth = WIDTHLIMITPIX #100
self.SetSizeHints(newWidth, self._tabAreaSize.y)
# Update the tab new area width
self._nTabAreaWidth = newWidth
self.GetParent().Freeze()
self.GetParent().GetSizer().Layout()
self.GetParent().Thaw()
LB.LabelContainer = OLabelContainer # do monkeypatch old class
class MyLabelBook(LB.LabelBook):
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, agwStyle=0, name="LabelBook"):
super(MyLabelBook, self).__init__(parent, id, pos, size, style, agwStyle, name)
self._fontSizeMultiple = 1.0
self._fontBold = False
print(self._pages) # is OLabelContainer, OK
def GetFontBold(self): # copy from wx/lib/agw/labelbook.py
return self._fontBold
def ResizeTabArea(self): # copy from wx/lib/agw/labelbook.py
agwStyle = self.GetAGWWindowStyleFlag()
if agwStyle & INB_FIT_LABELTEXT == 0:
return
if agwStyle & INB_LEFT or agwStyle & INB_RIGHT:
dc = wx.MemoryDC()
dc.SelectObject(wx.EmptyBitmap(1, 1))
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
font.SetPointSize(font.GetPointSize()*self._fontSizeMultiple)
if self.GetFontBold() or agwStyle & INB_BOLD_TAB_SELECTION:
font.SetWeight(wx.FONTWEIGHT_BOLD)
dc.SetFont(font)
maxW = 0
for page in xrange(self.GetPageCount()):
caption = self._pages.GetPageText(page)
w, h = dc.GetTextExtent(caption)
maxW = max(maxW, w)
maxW += 24 #TODO this is 6*4 6 is nPadding from drawlabel
if not agwStyle & INB_SHOW_ONLY_TEXT:
maxW += self._pages._nImgSize * 2
maxW = max(maxW, WIDTHLIMITPIX) # hack: was 100 here
self._pages.SetSizeHints(maxW, -1)
self._pages._nTabAreaWidth = maxW
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Labelbook test")
self.tlbook = MyLabelBook(self, -1, size=(400, 200), style = wx.NB_LEFT, agwStyle = INB_LEFT | INB_FIT_LABELTEXT | INB_FIT_BUTTON | INB_SHOW_ONLY_TEXT | INB_USE_PIN_BUTTON)
sizer_1 = wx.BoxSizer(wx.VERTICAL)
self.tlbook_panel_1 = wx.Panel(self.tlbook)
self.tlbook_panel_2 = wx.Panel(self.tlbook)
self.tlbook.AddPage(self.tlbook_panel_1, "Test 1")
self.tlbook.AddPage(self.tlbook_panel_2, "Test 2")
sizer_1.Add(self.tlbook, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.SetSize((450, 250))
self.Layout()
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()