Recently I moved from Debian to Mac OS X (Yosemite), and after starting my application getting error in terminal while running:
relrin at MacBook-Relrin in ~/code/Helenae/Helenae/helenae/gui on git:dev+? workon code
=> run_wx.sh CloudStorage.py
Traceback (most recent call last):
File "/Users/savicvalera/code/Helenae/Helenae/helenae/gui/widgets/Filemanager.py", line 286, in OnSize
self.sb.SetStatusText(os.getcwd())
File "/Users/savicvalera/code/Helenae/lib/python2.7/site-packages/wx-3.0-osx_cocoa/wx/_core.py", line 16711, in __getattr__
raise PyDeadObjectError(self.attrStr % self._name)
wx._core.PyDeadObjectError: The C++ part of the StatusBar object has been deleted, attribute access no longer allowed.
However in Debian this application didn't say about any errors. How can I fix this issue?
Code:
# -*- coding: utf-8 -*-
import os
import wx
import shutil
import ntpath
from FileListCtrl import FileListCtrl
from OptionsCtrl import OptionsCtrl
from InputDialogCtrl import InputDialog
from validators.FolderValidator import FolderValidator
from validators.FileValidator import FileValidator
from validators.ValidatorMsgDlg import ValidatorMsgDialog as MsgDlg
ID_BUTTON = 100
ID_BUTTON_WRITE = 101
ID_BUTTON_TRANSFER = 102
ID_BUTTON_CREATE_FOLDER = 103
ID_BUTTON_REMOVE_FILE = 104
ID_BUTTON_RENAME = 105
ID_NEW = 150
ID_FOLDER = 151
ID_RENAME = 152
ID_REPLACE = 153
ID_REMOVE = 154
ID_SHOW_STATUSBAR = 155
ID_OPTIONS = 156
ID_WRITE = 157
ID_COPY_FILE = 158
ID_COPY_FOLDER = 159
ID_EXIT = 200
ID_SPLITTER = 300
ID_TOOLBAR_UPDIR = 201
ID_TOOLBAR_HOME = 202
ID_TOOLBAR_REFRESH = 203
ID_TOOLBAR_HELP = 204
class FileManager(wx.Frame):
def __init__(self, parent, id, title, ico_folder):
wx.Frame.__init__(self, parent, -1, title)
self.parent = parent
self.ico_folder = ico_folder
wx.Log.SetLogLevel(0)
# file frame
self.files_folder = FileListCtrl(self, -1, ico_folder)
# options frame
self.options_frame = OptionsCtrl(self, -1, "Опции", ico_folder)
# menu
filemenu= wx.Menu()
newItem = wx.MenuItem(filemenu, ID_NEW, "&Новый файл", help='Создать новый файл в каталоге')
newItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/new.png'))
createItem = wx.MenuItem(filemenu, ID_FOLDER, "&Создать каталог", help='Создать новый каталог')
createItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/folder.png'))
copyFileItem = wx.MenuItem(filemenu, ID_COPY_FILE, "&Скопировать файл", help='Скопировать файл извне в текущий каталог')
copyFileItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/copy_file.png'))
copyFolderItem = wx.MenuItem(filemenu, ID_COPY_FOLDER, "&Скопировать каталог", help='Скопировать каталог извне в текущий каталог')
copyFolderItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/copy_folder.png'))
renameItem = wx.MenuItem(filemenu, ID_RENAME, "&Перемеиновать", help='Перемеиновать выделенный файл')
renameItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/rename.png'))
removeItem = wx.MenuItem(filemenu, ID_REPLACE, "&Перенос", help='Перенести выделенные файлы в другой каталог')
removeItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/remove.png'))
writeItem = wx.MenuItem(filemenu, ID_WRITE, "&Запись", help='Записать выделенные файлы')
writeItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/write.png'))
deleteItem = wx.MenuItem(filemenu, ID_REMOVE, "&Удалить", help='Удалить выделенные файлы')
deleteItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/delete.png'))
filemenu.AppendItem(newItem)
filemenu.AppendItem(createItem)
filemenu.AppendSeparator()
filemenu.AppendItem(copyFileItem)
filemenu.AppendItem(copyFolderItem)
filemenu.AppendSeparator()
filemenu.AppendItem(renameItem)
filemenu.AppendItem(removeItem)
filemenu.AppendItem(writeItem)
filemenu.AppendItem(deleteItem)
filemenu.AppendSeparator()
exitItem = wx.MenuItem(filemenu, ID_EXIT, "&Выход", help='Выход из приложения')
exitItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/file/exit.png'))
filemenu.AppendItem(exitItem)
configmenu = wx.Menu()
self.show_statusbar = configmenu.Append(ID_SHOW_STATUSBAR, 'Отображать строку статуса', 'Отображать строку статуса', kind=wx.ITEM_CHECK)
configmenu.Check(self.show_statusbar.GetId(), True)
self.preferencesItem = wx.MenuItem(filemenu, ID_OPTIONS, "&Параметры", help='Основные параметры приложения')
self.preferencesItem.SetBitmap(wx.Bitmap(ico_folder + '/icons/ui/menu/preferences/preferences.png'))
configmenu.AppendItem(self.preferencesItem)
about = wx.Menu()
helpmenu = wx.Menu()
menuBar = wx.MenuBar()
menuBar.Append(filemenu, "&Файл")
menuBar.Append(configmenu, "&Настройки")
menuBar.Append(helpmenu, "&Помощь")
menuBar.Append(about, "&О программе")
self.SetMenuBar(menuBar)
# toolbar
tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT | wx.TB_TEXT)
tb.AddSimpleTool(ID_TOOLBAR_UPDIR, wx.Bitmap(ico_folder + '/icons/ui/toolbar/up.png'), 'Up one directory')
tb.AddSimpleTool(ID_TOOLBAR_HOME, wx.Bitmap(ico_folder + '/icons/ui/toolbar/home.png'), 'Home')
tb.AddSimpleTool(ID_TOOLBAR_REFRESH, wx.Bitmap(ico_folder + '/icons/ui/toolbar/refresh.png'), 'Refresh')
tb.AddSeparator()
tb.AddSimpleTool(ID_TOOLBAR_HELP, wx.Bitmap(ico_folder + '/icons/ui/toolbar/help.png'), 'Help')
tb.Realize()
# button panel
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
button1 = wx.Button(self, ID_BUTTON_WRITE, "F5 Запись")
button1.SetBackgroundColour('#BFD8DF')
button1.SetForegroundColour("#2F4D57")
button2 = wx.Button(self, ID_BUTTON_TRANSFER, "F6 Перенос")
button2.SetBackgroundColour('#BFD8DF')
button2.SetForegroundColour("#2F4D57")
button3 = wx.Button(self, ID_BUTTON_CREATE_FOLDER, "F7 Созд. каталог")
button3.SetBackgroundColour('#BFD8DF')
button3.SetForegroundColour("#2F4D57")
button4 = wx.Button(self, ID_BUTTON_REMOVE_FILE, "F8 Удалить")
button4.SetBackgroundColour('#BFD8DF')
button4.SetForegroundColour("#2F4D57")
button5 = wx.Button(self, ID_BUTTON_RENAME, "F9 Перемеиновать")
button5.SetBackgroundColour('#BFD8DF')
button5.SetForegroundColour("#2F4D57")
button6 = wx.Button(self, ID_EXIT, "F10 Выход")
button6.SetBackgroundColour('#BFD8DF')
button6.SetForegroundColour("#2F4D57")
self.sizer2.Add(button1, 1, wx.EXPAND)
self.sizer2.Add(button2, 1, wx.EXPAND)
self.sizer2.Add(button3, 1, wx.EXPAND)
self.sizer2.Add(button4, 1, wx.EXPAND)
self.sizer2.Add(button5, 1, wx.EXPAND)
self.sizer2.Add(button6, 1, wx.EXPAND)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.files_folder, 1,wx.EXPAND)
self.sizer.Add(self.sizer2, 0,wx.EXPAND)
self.SetSizer(self.sizer)
# events bindings
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.show_statusbar)
self.Bind(wx.EVT_MENU, self.ShowMenu, self.preferencesItem)
self.Bind(wx.EVT_CLOSE, self.OnExitProgramm)
# create folder
self.Bind(wx.EVT_BUTTON, self.OnCreateFolder, id=ID_BUTTON_CREATE_FOLDER)
self.Bind(wx.EVT_MENU, self.OnCreateFolder, id=ID_FOLDER)
# create file
self.Bind(wx.EVT_MENU, self.OnCreateFile, id=ID_NEW)
# toolbar events
self.Bind(wx.EVT_TOOL, self.UpDir, id=ID_TOOLBAR_UPDIR)
self.Bind(wx.EVT_TOOL, self.Home, id=ID_TOOLBAR_HOME)
self.Bind(wx.EVT_TOOL, self.RefreshFileCtrl, id=ID_TOOLBAR_REFRESH)
# copy file
self.Bind(wx.EVT_MENU, self.CopyFile, id=ID_COPY_FILE)
# copy folder
self.Bind(wx.EVT_MENU, self.CopyFolder, id=ID_COPY_FOLDER)
# define size and icon for app
size = (800, 600)
self.SetSize(size)
self.icon = wx.Icon(ico_folder + '/icons/app.ico', wx.BITMAP_TYPE_ICO)
self.SetIcon(self.icon)
self.SetMinSize(size)
self.Center()
def UpDir(self, event):
if self.files_folder.currentDir != self.files_folder.defaultDir:
self.files_folder.currentDir = self.files_folder.getParentDir(self.files_folder.currentDir)
filepath = self.files_folder.currentDir
self.files_folder.showFilesInDirectory(filepath)
def Home(self, event):
self.files_folder.currentDir = self.files_folder.defaultDir
self.files_folder.showFilesInDirectory(self.files_folder.defaultDir)
def RefreshFileCtrl(self, event):
self.files_folder.showFilesInDirectory(self.files_folder.currentDir)
def OnCreateFolder(self, event):
dlg = InputDialog(self, -1, 'Введите каталог', self.ico_folder, FolderValidator())
dlg.ShowModal()
if dlg.result is not None:
try:
new_folder = self.files_folder.currentDir + dlg.result
os.makedirs(new_folder)
except OSError:
wx.MessageBox("Вероятнее всего, такой файл или каталог уже существует!\nПожалуйста, дайте файлу другое имя.", 'Ошибка')
except Exception, exc:
wx.MessageBox("%s" % exc.message, "Ошибка")
self.files_folder.showFilesInDirectory(self.files_folder.currentDir)
dlg.Destroy()
def OnCreateFile(self, event):
dlg = InputDialog(self, -1, 'Введите имя файла и расширение', self.ico_folder, FileValidator())
dlg.ShowModal()
if dlg.result is not None:
try:
new_file = self.files_folder.currentDir + dlg.result
new_folders, filename = ntpath.split(dlg.result)
if len(new_folders) > 2:
os.makedirs(self.files_folder.currentDir + new_folders)
open(new_file, 'a').close()
except OSError:
wx.MessageBox("Вероятнее всего, такой файл или каталог уже существует!\nПожалуйста, дайте файлу другое имя.", 'Ошибка')
except Exception, exc:
wx.MessageBox("%s" % exc.message, "Ошибка")
self.files_folder.showFilesInDirectory(self.files_folder.currentDir)
dlg.Destroy()
def CopyFile(self, event):
dlg = wx.FileDialog(self, "Укажите путь к файлу", "", "", "All files (*.*)|*.*", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if dlg.ShowModal() == wx.ID_OK:
filepath = dlg.GetPath()
dir, filename = ntpath.split(filepath)
shutil.copyfile(filepath, self.files_folder.currentDir+filename)
self.files_folder.showFilesInDirectory(self.files_folder.currentDir)
msg_dlg = MsgDlg(None, "Копирование завершено!")
msg_dlg.ShowModal()
dlg.Destroy()
def CopyMoveOperation(self, src, target, operation='copy'):
for src_dir, dirs, files in os.walk(src):
dst_dir = src_dir.replace(src, target)
if not os.path.exists(dst_dir):
os.mkdir(dst_dir)
for file_ in files:
src_file = os.path.join(src_dir, file_)
dst_file = os.path.join(dst_dir, file_)
if os.path.exists(dst_file):
os.remove(dst_file)
if operation is 'copy':
shutil.copy(src_file, dst_dir)
elif operation is 'move':
shutil.move(src_file, dst_dir)
def CopyFolder(self, event):
dlg = wx.DirDialog(self, "Укажите директорию", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
folderpath = dlg.GetPath()
try:
new_folder = folderpath.split('/')[-1]
os.mkdir(new_folder)
# if this folder already exists - then catch exception, ignore him, and copy files right now
except OSError:
pass
self.CopyMoveOperation(folderpath, self.files_folder.currentDir+new_folder)
self.files_folder.showFilesInDirectory(self.files_folder.currentDir)
msg_dlg = MsgDlg(None, "Копирование завершено!")
msg_dlg.ShowModal()
dlg.Destroy()
def ShowMenu(self, event):
self.options_frame.Show()
def ToggleStatusBar(self, event):
if self.show_statusbar.IsChecked():
self.sb.Show()
else:
self.sb.Hide()
def OnExit(self, event):
self.Close()
def OnExitProgramm(self, event):
self.parent.Close()
def OnSize(self, event):
size = self.GetSize()
self.files_folder.ResizeColumns(size.x)
if not hasattr(self, 'sb'):
self.sb = self.CreateStatusBar()
self.sb.SetStatusText(os.getcwd())
event.Skip()
if __name__ == '__main__':
app = wx.App(0)
ico_folder = '..'
frame = FileManager(None, -1, 'CloudStorage', ico_folder)
frame.Show(True)
app.MainLoop()
You can capture the error in a try exception block with a wx.PyDeadObjectError exception.
You can also remove the hasattr by using a AttributeError exception.
def OnSize(self, event):
size = self.GetSize()
self.files_folder.ResizeColumns(size.x)
cwd = os.getcwd()
try:
self.sb.SetStatusText(cwd)
except AttributeError:
self.sb = self.CreateStatusBar()
self.sb.SetStatusText(cwd)
except wx.PyDeadObjectError:
pass
event.Skip()
Related
I am having trouble with the layout of the frame. The data is stored in a dataframe, and the intent is to be able to browse for a file and select that file from a ComboBox. I am trying to display the contents in the frame. The first time it works correctly. The second time it just adds it to the vertical sizer under the previous data. I need to delete or replace the data. The data displayed should be the the updated dataframe. I have tried the .Destroy() on the GridSizer, the Vertical BoxSizer, I've tried adding more sizers combined with the Freeze() and Thaw() methods, and adding a new Panel Class. Nothing seems to work.
I am also having a difficult time getting the StaticText boxes in the GridSizer to adjust to fit the screen. What am I doing wrong?
def onOpenFile(self, event):
"""
Create and show the Open FileDialog
"""
global column_names
global df_lookup_options
global options_ID
#self.Freeze
#Dialog 1
dlg1 = wx.FileDialog(
self, message="Browse Files",
defaultFile="",
wildcard=wildcard,
style=wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_CHANGE_DIR)
if dlg1.ShowModal() == wx.ID_OK:
paths = dlg1.GetPaths()
for path in paths:
filepath = path.replace("\\","/")
xls = xlrd.open_workbook(filepath, on_demand=True)
sheet_options = xls.sheet_names()
dlg1.Destroy()
#Dialog 2
dlg2 = wx.SingleChoiceDialog(None,"Select
Worksheet","Worksheet",sheet_options,wx.CHOICEDLG_STYLE)
if dlg2.ShowModal() == wx.ID_OK:
sel_sheet = dlg2.GetStringSelection()
sel_index = dlg2.GetSelection()
#Get Count of Worksheet Rows and Colums
wb = xlrd.open_workbook(filepath)
sheet = wb.sheet_by_index(sel_index)
row_count = sheet.nrows
column_count = sheet.ncols
df_rows = len(df_lookup_options.index)
dlg2.Destroy()
#Dialog 3
dlg3 = wx.TextEntryDialog(None,"Enter Unique ID","Unique ID")
if dlg3.ShowModal() == wx.ID_OK:
unique_id = dlg3.GetValue()
dlg3.Destroy()
#Dialog 4
dlg4 = wx.TextEntryDialog(None,"Enter Header Row","Header Row")
if dlg4.ShowModal() == wx.ID_OK:
header_row = dlg4.GetValue()
dlg4.Destroy()
#Dialog 5
dlg5 = wx.TextEntryDialog(None,"Enter First Data Column #","Start Column")
if dlg5.ShowModal() == wx.ID_OK:
column_start = dlg5.GetValue()
dlg5.Destroy()
#Add Variables to Dataframe Row
df_lookup_options = df_lookup_options.append({'Unique
ID':unique_id,'Worksheet':sel_sheet,'Index':sel_index,'Filepath':filepath,
'Rows':row_count,'Columns':column_count,'Header
Row':header_row,
'Start Column':column_start},ignore_index=True)
options_ID = df_lookup_options['Unique ID'].tolist()
self.combo1.Clear()
self.combo1.Append(options_ID)
#Get Dataframe Size
df_row = len(df_lookup_options)
df_col = len(df_lookup_options.columns)
#Display Source info
#self.fgs.Destroy()
#self.vbox = wx.BoxSizer(wx.VERTICAL)
self.fgs = wx.FlexGridSizer(df_row + 1,df_col,2,25)
#Create Headers
for i in column_names:
self.fgs.Add(wx.StaticText(self, label = i), proportion=1, flag=wx.ALIGN_CENTER_VERTICAL
| wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, border=5)
self.Layout
##
for r in range(df_row):
for y in range(df_col):
for c in range(1,df_col + 1):
i = df_lookup_options.iloc[r, y]
txt = str(i)
self.fgs.Add(wx.StaticText(self, label = txt), proportion=1, flag=wx.ALL | wx.EXPAND)
self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL|wx.EXPAND, border=10)
self.SetSizer(self.vbox)
self.Fit()
self.Layout()
#self.Thaw
Complete Code:
import wx
import xlrd
import pandas as pd
wildcard = "Excel Files(*.xlsm; *.xlsx)|*.xlsm;*.xlsx|" \
"All files (*.*)|*.*"
#"Python source (*.py; *.pyc)|*.py;*.pyc|" \
#"All files (*.*)|*.*"
column_names = ['Unique ID','Worksheet','Index','Filepath','Rows','Columns','Header Row','Start
Column']
df_lookup_options = pd.DataFrame(columns=column_names)
df_lookup = pd.DataFrame()
options_ID = []
source_selection = ""
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Set Source Files")
self.vbox = wx.BoxSizer(wx.VERTICAL)
#self.vbox2 = wx.BoxSizer(wx.VERTICAL)
#self.hbox = wx.BoxSizer(wx.HORIZONTAL)
self.btn = wx.Button(self, label="Add Source File")
self.btn.Bind(wx.EVT_BUTTON, self.onOpenFile)
self.combo1 = wx.ComboBox(self, choices=options_ID)
self.combo1.Bind(wx.EVT_COMBOBOX, self.onCombo)
self.combo1.SetHint('-Choose Source-')
self.vbox.Add(self.btn, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=4)
self.vbox.Add(self.combo1, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=4)
#self.hbox.Add(self.fgs1, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=4)
#self.vbox.Add(self.hbox, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=4)
#self.vbox.Add(self.fgs, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=4)
self.SetSizer(self.vbox)
#----------------------------------------------------------------------
def onOpenFile(self, event):
"""
Create and show the Open FileDialog
"""
global column_names
global df_lookup_options
global options_ID
#self.Freeze
#Dialog 1
dlg1 = wx.FileDialog(
self, message="Browse Files",
defaultFile="",
wildcard=wildcard,
style=wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_CHANGE_DIR)
if dlg1.ShowModal() == wx.ID_OK:
paths = dlg1.GetPaths()
for path in paths:
filepath = path.replace("\\","/")
xls = xlrd.open_workbook(filepath, on_demand=True)
sheet_options = xls.sheet_names()
dlg1.Destroy()
#Dialog 2
dlg2 = wx.SingleChoiceDialog(None,"Select
Worksheet","Worksheet",sheet_options,wx.CHOICEDLG_STYLE)
if dlg2.ShowModal() == wx.ID_OK:
sel_sheet = dlg2.GetStringSelection()
sel_index = dlg2.GetSelection()
#Get Count of Worksheet Rows and Colums
wb = xlrd.open_workbook(filepath)
sheet = wb.sheet_by_index(sel_index)
row_count = sheet.nrows
column_count = sheet.ncols
df_rows = len(df_lookup_options.index)
dlg2.Destroy()
#Dialog 3
dlg3 = wx.TextEntryDialog(None,"Enter Unique ID","Unique ID")
if dlg3.ShowModal() == wx.ID_OK:
unique_id = dlg3.GetValue()
dlg3.Destroy()
#Dialog 4
dlg4 = wx.TextEntryDialog(None,"Enter Header Row","Header Row")
if dlg4.ShowModal() == wx.ID_OK:
header_row = dlg4.GetValue()
dlg4.Destroy()
#Dialog 5
dlg5 = wx.TextEntryDialog(None,"Enter First Data Column #","Start Column")
if dlg5.ShowModal() == wx.ID_OK:
column_start = dlg5.GetValue()
dlg5.Destroy()
#Add Variables to Dataframe Row
df_lookup_options = df_lookup_options.append({'Unique
ID':unique_id,'Worksheet':sel_sheet,'Index':sel_index,'Filepath':filepath,
'Rows':row_count,'Columns':column_count,'Header
Row':header_row,
'Start Column':column_start},ignore_index=True)
options_ID = df_lookup_options['Unique ID'].tolist()
self.combo1.Clear()
self.combo1.Append(options_ID)
#Get Dataframe Size
df_row = len(df_lookup_options)
df_col = len(df_lookup_options.columns)
#Display Source info
#self.fgs.Destroy()
#self.vbox = wx.BoxSizer(wx.VERTICAL)
self.fgs = wx.FlexGridSizer(df_row + 1,df_col,2,25)
#Create Headers
for i in column_names:
self.fgs.Add(wx.StaticText(self, label = i), proportion=1, flag=wx.ALIGN_CENTER_VERTICAL
| wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, border=5)
self.Layout
##
for r in range(df_row):
for y in range(df_col):
for c in range(1,df_col + 1):
i = df_lookup_options.iloc[r, y]
txt = str(i)
self.fgs.Add(wx.StaticText(self, label = txt), proportion=1, flag=wx.ALL | wx.EXPAND)
self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL|wx.EXPAND, border=10)
self.SetSizer(self.vbox)
self.Fit()
self.Layout()
#self.Thaw
def onCombo(self, event):
global df_lookup
self.source_selection = self.combo1.GetSelection()
file_name = df_lookup_options.iloc[self.source_selection, 3]
sheet_name = df_lookup_options.iloc[self.source_selection, 1]
start_header_row = int(df_lookup_options.iloc[self.source_selection, 6]) - 1
start_column = int(df_lookup_options.iloc[self.source_selection, 7]) - 1
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
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)
Firstly, this is what I'm trying to make with wxPython. I would like the region labeled "Picture 1" to be able to accept a dragged image, and then when an image is dragged there, be replaced with a thumbnail of that image. I have researched "Drag and Drop" in wxPython, but I can't seem to find the tools needed for me to do this.
Any help getting me started on the right track would be greatly appreciated.
You will want the Pillow package for creating a thumbnail. The other piece you will want is most likely the wx.FileDropTarget class. I ended up doing the following:
import os
import wx
from PIL import Image
from wx.lib.pubsub import pub
PhotoMaxSize = 240
class DropTarget(wx.FileDropTarget):
def __init__(self, widget):
wx.FileDropTarget.__init__(self)
self.widget = widget
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
class PhotoCtrl(wx.App):
def __init__(self, redirect=False, filename=None):
wx.App.__init__(self, redirect, filename)
self.frame = wx.Frame(None, title='Photo Control')
self.panel = wx.Panel(self.frame)
pub.subscribe(self.update_image_on_dnd, 'dnd')
self.PhotoMaxSize = 240
self.createWidgets()
self.frame.Show()
def createWidgets(self):
instructions = 'Browse for an image'
img = wx.Image(240,240)
self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY,
wx.Bitmap(img))
filedroptarget = DropTarget(self)
self.imageCtrl.SetDropTarget(filedroptarget)
instructLbl = wx.StaticText(self.panel, label=instructions)
self.photoTxt = wx.TextCtrl(self.panel, size=(200,-1))
browseBtn = wx.Button(self.panel, label='Browse')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(instructLbl, 0, wx.ALL, 5)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
self.sizer.Add(self.photoTxt, 0, wx.ALL, 5)
self.sizer.Add(browseBtn, 0, wx.ALL, 5)
self.mainSizer.Add(self.sizer, 0, wx.ALL, 5)
self.panel.SetSizer(self.mainSizer)
self.mainSizer.Fit(self.frame)
self.panel.Layout()
def onBrowse(self, event):
"""
Browse for file
"""
wildcard = "JPEG files (*.jpg)|*.jpg"
dialog = wx.FileDialog(None, "Choose a file",
wildcard=wildcard,
style=wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
self.photoTxt.SetValue(dialog.GetPath())
dialog.Destroy()
self.onView()
def update_image_on_dnd(self, filepath):
self.onView(filepath=filepath)
def onView(self, filepath=None):
if not filepath:
filepath = self.photoTxt.GetValue()
img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
# scale the image, preserving the aspect ratio
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.PhotoMaxSize
NewH = self.PhotoMaxSize * H / W
else:
NewH = self.PhotoMaxSize
NewW = self.PhotoMaxSize * W / H
img = img.Scale(NewW,NewH)
self.imageCtrl.SetBitmap(wx.Bitmap(img))
self.panel.Refresh()
if __name__ == '__main__':
app = PhotoCtrl()
app.MainLoop()
This worked for me on Windows 7 with wxPython 4. Note that I am dragging a file from Windows Explorer onto my wxPython application's image widget.
I am using a Panel within a Frame to display images (the GUI need to switch between multiple panels and hence the hierarchy). As images should be displayed in native size I used ScrolledWindow as the panel parent. The scrolls do appear and work, but it causes the Panel to collapse to minimum size and it needs to be resized using drag&drop every time.
Is there a way around this?
Below is a reduced version of the code, which shows the problem:
import os
import wx
from wx.lib.pubsub import pub
class Edit_Panel(wx.PyScrolledWindow):
def __init__(self, parent):
super(Edit_Panel, self).__init__(parent)
# Display size
width, height = wx.DisplaySize()
self.photoMaxSize = height - 500
# Loaded image
self.loaded_image = None
# Icons
self.open_icon_id = 500
# Generate panel
self.layout()
def layout(self):
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
divider = wx.StaticLine(self, -1, style = wx.LI_HORIZONTAL)
self.main_sizer.Add(divider, 0, wx.ALL | wx.EXPAND)
self.toolbar = self.init_toolbar()
self.main_sizer.Add(self.toolbar, 0, wx.ALL)
img = wx.EmptyImage(self.photoMaxSize, self.photoMaxSize)
self.image_control = wx.StaticBitmap(self, wx.ID_ANY,
wx.BitmapFromImage(img))
self.main_sizer.Add(self.image_control, 0, wx.ALL | wx.CENTER, 5)
self.image_label = wx.StaticText(self, -1, style = wx.ALIGN_CENTRE)
self.main_sizer.Add(self.image_label, 0, wx.ALL | wx.ALIGN_CENTRE, 5)
self.SetSizer(self.main_sizer)
fontsz = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).GetPixelSize()
self.SetScrollRate(fontsz.x, fontsz.y)
self.EnableScrolling(True, True)
def init_toolbar(self):
toolbar = wx.ToolBar(self)
toolbar.SetToolBitmapSize((16, 16))
open_ico = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, (16, 16))
open_tool = toolbar.AddSimpleTool(self.open_icon_id, open_ico, "Open", "Open an Image Directory")
handler = self.on_open_reference
self.Bind(event = wx.EVT_MENU, handler = handler, source = open_tool)
toolbar.Realize()
return toolbar
def on_open_reference(self, event, wildcard = None):
if wildcard is None:
wildcard = self.get_wildcard()
defaultDir = '~/'
dbox = wx.FileDialog(self, "Choose an image to display", defaultDir = defaultDir, wildcard = wildcard, style = wx.OPEN)
if dbox.ShowModal() == wx.ID_OK:
file_name = dbox.GetPath()
# load image
self.load_image(image = file_name)
dbox.Destroy()
def get_wildcard(self):
wildcard = 'Image files (*.jpg;*.png;*.bmp)|*.png;*.bmp;*.jpg;*.jpeg'
return wildcard
def load_image(self, image):
self.loaded_image = image
# Load image
img = wx.Image(image, wx.BITMAP_TYPE_ANY)
# Label image name
image_name = os.path.basename(image)
self.image_label.SetLabel(image_name)
# scale the image, preserving the aspect ratio
scale_image = True
if scale_image:
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.photoMaxSize
NewH = self.photoMaxSize * H / W
else:
NewH = self.photoMaxSize
NewW = self.photoMaxSize * W / H
img = img.Scale(NewW, NewH)
self.image_control.SetBitmap(wx.BitmapFromImage(img))
# Render
self.main_sizer.Layout()
self.main_sizer.Fit(self)
self.Refresh()
pub.sendMessage("resize", msg = "")
class Viewer_Frame(wx.Frame):
def __init__(self, parent, id, title):
super(Viewer_Frame, self).__init__(parent = parent, id = id, title = title)
# Edit panel
self.edit_panel = Edit_Panel(self)
# Default panel
self.main_panel = self.edit_panel
# Render frame
self.render_frame()
# Subscription to re-render
pub.subscribe(self.resize_frame, ("resize"))
def render_frame(self):
# Main Sizer
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
# Add default sizer
self.main_sizer.Add(self.main_panel, 1, wx.EXPAND)
# Render
self.SetSizer(self.main_sizer)
self.Show()
self.main_sizer.Fit(self)
self.Center()
def resize_frame(self, msg):
self.main_sizer.Fit(self)
if __name__ == "__main__":
app = wx.App(False)
frame = Viewer_Frame(parent = None, id = -1, title = 'Toolkit')
app.MainLoop()
You're calling Fit(), so you're explicitly asking the panel to fit its contents, but you don't specify the min/best size of this contents anywhere (AFAICS, there is a lot of code here, so I could be missing something).
If you want to use some minimal size for the panel, just set it using SetMinSize().
I am almost done my wxPython soundboard and want to implement one quick feature.
How does one add another panel to a wxPython window, and how do you implement the text, [ NOW PLAYING ] within that panel.
Here is my code so far:
import wx
import os
import pygame
pygame.init()
##SOUNDS##
##SOUNDS##
class windowClass(wx.Frame):
__goliathwav = pygame.mixer.Sound("goliath.wav")
__channelopen = pygame.mixer.Sound("channelopen.wav")
def __init__(self, *args, **kwargs):
super(windowClass,self).__init__(*args,**kwargs)
self.__basicGUI()
def __basicGUI(self):
panel = wx.Panel(self)
menuBar = wx.MenuBar()
fileButton = wx.Menu()
aboutButton = wx.Menu()
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit','status msg...')
aboutItem = aboutButton.Append(wx.ID_ABOUT, "About")
menuBar.Append(fileButton, 'File')
menuBar.Append(aboutButton, 'About this program')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.__quit, exitItem)
self.Bind(wx.EVT_MENU, self.__onmenuhelpabout, aboutItem)
self.__sound_dict = { "Goliath" : self.__goliathwav,
"Goliath2" : self.__channelopen
}
self.__sound_list = sorted(self.__sound_dict.keys())
self.__list = wx.ListBox(panel,pos=(20,20), size=(250,150))
for i in self.__sound_list:
self.__list.Append(i)
self.__list.Bind(wx.EVT_LISTBOX,self.__on_click)
textarea = wx.TextCtrl(self, -1,
style=wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_READONLY|
wx.TE_RICH2, size=(250,150))
self.usertext = textarea
#self.__list2 = wx.ListBox(panel,pos=(19.5,180), size=(251,21)) #second panel
#for j in self.__sound_list:
# self.__list2.Append(i)
#self.__list2.Bind(wx.EVT_LISTBOX,self.__on_click)
#wx.TextCtrl(panel,pos=(10,10), size=(250,150))
self.SetTitle("Soundboard")
self.Show(True)
def __onmenuhelpabout(self,event):
dialog = wx.Dialog(self, -1, "[Soundboard]") # ,
#style=wx.DIALOG_MODAL | wx.STAY_ON_TOP)
dialog.SetBackgroundColour(wx.WHITE)
panel = wx.Panel(dialog, -1)
panel.SetBackgroundColour(wx.WHITE)
panelSizer = wx.BoxSizer(wx.VERTICAL)
boldFont = wx.Font(panel.GetFont().GetPointSize(),
panel.GetFont().GetFamily(),
wx.NORMAL,wx.BOLD)
lab1 = wx.StaticText(panel, -1, " SOUNDBOARD ")
lab1.SetFont(wx.Font(36,boldFont.GetFamily(), wx.ITALIC, wx.BOLD))
lab1.SetSize(lab1.GetBestSize())
imageSizer = wx.BoxSizer(wx.HORIZONTAL)
imageSizer.Add(lab1, 0, wx.ALL | wx.ALIGN_CENTRE_VERTICAL, 5)
lab2 = wx.StaticText(panel, -1, "Created by youonlylegoonce(cyrex)(Kommander000) for the glory " + \
"of the republic.")
panelSizer.Add(imageSizer, 0, wx.ALIGN_CENTRE)
panelSizer.Add((10, 10)) # Spacer.
panelSizer.Add(lab2, 0, wx.ALIGN_CENTRE)
panel.SetAutoLayout(True)
panel.SetSizer(panelSizer)
panelSizer.Fit(panel)
topSizer = wx.BoxSizer(wx.HORIZONTAL)
topSizer.Add(panel, 0, wx.ALL, 10)
dialog.SetAutoLayout(True)
dialog.SetSizer(topSizer)
topSizer.Fit(dialog)
dialog.Centre()
btn = dialog.ShowModal()
dialog.Destroy()
def __on_click(self,event):
event.Skip()
name = self.__sound_list[self.__list.GetSelection()]
sound = self.__sound_dict[name]
print("[ NOW PLAYING ] ... %s" % name)
pygame.mixer.Sound.play(sound)
def __quit(self, e):
self.Close()
def main():
app = wx.App()
windowClass(None, -1, style=wx.MAXIMIZE_BOX | wx.CAPTION | wx.CENTRE)
app.MainLoop()
main()
Before:
print("[ NOW PLAYING ] ... %s" % name)
input:
self.usertext.SetValue("[ NOW PLAYING ] ... %s" % name)
P.S. Your indentation is a mess