Hi I am new to python and I am self taught programmer otherwise.
I am trying to get the copy and paste methods working for the clipboard in wxpython.
I have found and implemented what I have found on the topic, but there is an issue when used on my mac computer (OS X 10.10.5).
The attached code is a sample application that works fine within itself (given limits of grid). It also works fine for copying from the grid cells and pasting to an external notepad or spreadsheet. Meaning to me that the SetData is getting and maintaining the tab delimiters and new lines when building the clipboard data.
However, if I select tab delimited and multiline data from the same notepad or spreadsheet and proceed to paste into the grid, I get a single column of data. This means to me that the tab delimiters and newline characters are lost in the GetData.
With a data selection of
1 2 3
4 5 6
in a spreadsheet.
Using print repr(data) to get what the clipboard is holding, as suggested,
When copying and pasting within application results in
pasting data - print repr(data) = u'1\t2\t3\n4\t5\t6\n'
When data is copied from an external source and pasting seems to only have \r return characters and ufeff ?? which I don't know about? Perhaps thats a key? (on the mac)
print repr(data) = u'\ufeff\r1\r2\r3\r4\r5\r6\r\r'
Now this works fine on my Windows machine, but not on my Mac.
Is this a known issue? Is there a workaround, or is there a setting that I am missing or that I don't understand?
Much appreciate any help or direction.
Thanks
Frank
import wx
import wx.grid as dg
class cp(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY, size = (600,600))
self.dgGrid = dg.Grid(self, size = (500,500))
self.dgGrid.CreateGrid(10,5)
self.dgGrid.Bind(wx.EVT_KEY_DOWN, self.OnKeyPress)
def OnKeyPress(self, event):
# If Ctrl+V is pressed...
if event.ControlDown() and event.GetKeyCode() == 86:
print "Ctrl+V"
# Call paste method
self.Paste()
if event.ControlDown() and event.GetKeyCode() == 67:
print "Ctrl+C"
# Call copy method
self.copy()
event.Skip()
def copy(self):
print "Copy method"
# Number of rows and cols
rows = self.dgGrid.GetSelectionBlockBottomRight()[0][0] - self.dgGrid.GetSelectionBlockTopLeft()[0][0] + 1
cols = self.dgGrid.GetSelectionBlockBottomRight()[0][1] - self.dgGrid.GetSelectionBlockTopLeft()[0][1] + 1
# data variable contain text that must be set in the clipboard
data = ''
# For each cell in selected range append the cell value in the data variable
# Tabs '\t' for cols and '\r' for rows
for r in range(rows):
for c in range(cols):
data = data + str(self.dgGrid.GetCellValue(self.dgGrid.GetSelectionBlockTopLeft()[0][0] + r, self.dgGrid.GetSelectionBlockTopLeft()[0][1] + c))
if c < cols - 1:
data = data + '\t'
data = data + '\n'
# Create text data object
clipboard = wx.TextDataObject()
# Set data object value
clipboard.SetText(data)
# Put the data in the clipboard
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(clipboard)
wx.TheClipboard.Close()
else:
wx.MessageBox("Can't open the clipboard", "Error")
def Paste(self):
print "Paste method"
clipboard = wx.TextDataObject()
if wx.TheClipboard.Open():
wx.TheClipboard.GetData(clipboard)
wx.TheClipboard.Close()
else:
wx.MessageBox("Can't open the clipboard", "Error")
return
data = clipboard.GetText()
y = -1
# Convert text in a array of lines
for r in data.splitlines():
y = y +1
x = -1
print r
# Convert c in a array of text separated by tab
for c in r.split('\t'):
x = x +1
print c
self.dgGrid.SetCellValue(self.dgGrid.GetGridCursorRow() + y, self.dgGrid.GetGridCursorCol() + x, c)
if __name__ == '__main__':
print ' running locally not imported '
app = wx.App(False)
MainFrame = wx.Frame(None, title = "TestingCopy and Paste", size = (600,600))
cppanel = cp(MainFrame)
MainFrame.Refresh()
MainFrame.Show()
app.MainLoop()
Related
I am building a user interface with several (as many as the user wants) tabular (spreadsheet-like) forms of user-specified size (but the size won't change once initialized). The user populates these tables either by copy-pasting data (usually from excel) or directly typing data to the cells. I am using the Tksheet Tkinter add-on.
It seems that there are several options in Tksheet to achieve the goal of opening an empty table of i rows and j columns:
a) set_sheet_data_and_display_dimensions(total_rows = None, total_columns = None).
This routine throws a TypeError. The error is raised in:
GetLinesHeight(self, n, old_method = False)
The subroutine expects the parameter n to be an integer, but receives a tuple.
The calling routine is sheet_display_dimensions, and the relevant line is:
height = self.MT.GetLinesHeight(self.MT.default_rh).
MT.default_rh is apparently a complex object, it can be an integer, but also a string or a tuple. Other routines that use it in Tksheet perform elaborate manipulation to make sure it is handed to the subroutine in integer form, but not so sheet_display_dimensions.
b) sheet_data_dimensions(total_rows = None, total_columns = None)
This seems to work programmatically, but does not display the table to the user.
One may add the line sheet_display_dimensions(i,j) but--you guessed it--this raises an error...
Sample code:
from tksheet import Sheet
import tkinter as tk
from tkinter import ttk
# This class builds and displays a test table. It is not part of the question but merely used to illustrate it
class SeriesTable(tk.Frame):
def __init__(self, master):
super().__init__(master) # call super class init to build frame
self.grid_columnconfigure(0, weight=1) # This configures the window's escalators
self.grid_rowconfigure(0, weight=1)
self.grid(row=0, column=0, sticky="nswe")
self.sheet = Sheet(self, data=[[]]) # set up empty table inside the frame
self.sheet.grid(row=0, column=0, sticky="nswe")
self.sheet.enable_bindings(bindings= # enable table behavior
("single_select",
"select_all",
"column_select",
"row_select",
"drag_select",
"arrowkeys",
"column_width_resize",
"double_click_column_resize",
"row_height_resize",
"double_click_row_resize",
"right_click_popup_menu",
"rc_select", # rc = right click
"copy",
"cut",
"paste",
"delete",
"undo",
"edit_cell"
))
# Note that options that change the structure/size of the table (e.g. insert/delete col/row) are disabled
# make sure that pasting data won't change table size
self.sheet.set_options(expand_sheet_if_paste_too_big=False)
# bind specific events to my own functions
self.sheet.extra_bindings("end_edit_cell", func=self.cell_edited)
self.sheet.extra_bindings("end_paste", func=self.cells_pasted)
label = "Change column name" # Add option to the right-click menu for column headers
self.sheet.popup_menu_add_command(label, self.column_header_change, table_menu=False, index_menu=False, header_menu=True)
# Event functions
def cell_edited(self, info_tuple):
r, c, key_pressed, updated_value = info_tuple # break the info about the event to individual variables
'''
updated_value checked here
'''
# passed tests
pass # go do stuff
def cells_pasted(self, info_tuple):
key_pressed, rc_tuple, updated_array = info_tuple # break the info about the event to individual variables
r, c = rc_tuple # row & column where paste begins
err_flag = False # will be switched if errors are encountered
'''
updated_array is checked here
'''
# passed tests
if err_flag: # error during checks is indicated
self.sheet.undo() # undo change
else:
pass # go do stuff
def column_header_change(self):
r, c = self.sheet.get_currently_selected()
col_name = sd.askstring("User Input", "Enter column name:")
if col_name is not None and col_name != "": # if user cancelled (or didn't enter anything), do nothing
self.sheet.headers([col_name], index=c) # This does not work - it always changes the 1st col
self.sheet.redraw()
# from here down is test code
tk_win = tk.Tk() # establish the root tkinter window
tk_win.title("Master Sequence")
tk_win.geometry("600x400")
tk_win.config(bg='red')
nb = ttk.Notebook(tk_win) # a notebook in ttk is a [horizontal] list of tabs, each associated with a page
nb.pack(expand=True, fill='both') # widget packing strategy
settings_page = tk.Frame(nb) # initiate 1st tab object in the notebook
nb.add(settings_page, text = "Settings") # add it as top page in the notebook
test = SeriesTable(nb) # creates a 1 row X 0 column table
nb.add(test, text = "Table Test") # add it as second page in the notebook
i = 4
j = 3
#test.sheet.set_sheet_data_and_display_dimensions(total_rows=i, total_columns=j) # raises TypeError
#test.sheet.sheet_data_dimensions(total_rows=i, total_columns=j) # extends the table to 4 X 3, but the display is still 1 X 0
#test.sheet.sheet_display_dimensions(total_rows=i, total_columns=j) # raises TypeError
test.sheet.insert_columns(j) # this works
test.sheet.insert_rows(i - 1) # note that we insert i-1 because table already has one row
test.mainloop()
I figured out a work-around with:
c)
insert_columns(j)
insert_rows(i - 1)
Note that you have to insert i-1 rows. This is because the sheet object is initiated with 1 row and 0 columns. (But does it say so in the documentation? No it does not...)
I created a table widget and added a contextmenu to it. When I right click the cell,I want to get a file directory and put it into the cell. I've got the directory and pass it to a variable, but i failed to display it in the cell,because I can't get the index of the cell.How to get index of a cell in QTableWidget? Is there any orther method to figure out this qusstion? I'm using Python and PyQt5.
enter image description here
#pyqtSlot()
def on_actionAddFolder_triggered(self):
# TODO: Open filedialog and get directory
filedir = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
return filedir
#pyqtSlot(QPoint)
def on_tableWidget_customContextMenuRequested(self, pos):
# TODO: get directory and display it in the cell
x = self.tableWidget.currentRow
y = self.tableWidget.currentColumn
RightClickMenu = QMenu()
AddFolder = RightClickMenu.addAction('Add Folder')
FolderAction = RightClickMenu.exec_(self.tableWidget.mapToGlobal(pos))
if FolderAction == AddFolder:
NewItem = QTableWidgetItem(self.on_actionAddFolder_triggered())
self.tableWidget.setItem(x,y, NewItem)
hahaha, I find the mistake!
x = self.tableWidget.currentRow
y = self.tableWidget.currentColumn
replace these two lines
x = self.tableWidget.currentRow()
y = self.tableWidget.currentColumn()
then it works.
I have written a program in Python that allow me to change the names of many files all at once. I have one issue that is quite odd.
When I use raw_input to get my desired extension, the GUI will not launch. I don't get any errors, but the window will never appear.
I tried using raw_input as a way of getting a file extension from the user to build the file list. This program will works correctly when raw_input is not used.The section of code that I am referring to is in my globList function. For some reason when raw_imput is used the window will not launch.
import os
import Tkinter
import glob
from Tkinter import *
def changeNames(dynamic_entry_list, filelist):
for index in range(len(dynamic_entry_list)):
if(dynamic_entry_list[index].get() != filelist[index]):
os.rename(filelist[index], dynamic_entry_list[index].get())
print "The files have been updated!"
def drawWindow(filelist):
dynamic_entry_list = []
my_row = 0
my_column = 0
for name in filelist:
my_column = 0
label = Tkinter.Label(window, text = name, justify = RIGHT)
label.grid(row = my_row, column = my_column)
my_column = 1
entry = Entry(window, width = 50)
dynamic_entry_list.append(entry)
entry.insert(0, name)
entry.grid(row = my_row, column = my_column)
my_row += 1
return dynamic_entry_list
def globList(filelist):
#ext = raw_input("Enter the file extension:")
ext = ""
desired = '*' + ext
for name in glob.glob(desired):
filelist.append(name)
filelist = []
globList(filelist)
window = Tkinter.Tk()
user_input = drawWindow(filelist)
button = Button(window, text = "Change File Names", command = (lambda e=user_input: changeNames(e, filelist)))
button.grid(row = len(filelist) + 1 , column = 1)
window.mainloop()
Is this a problem with raw_input?
What would be a good solution to the problem?
This is how tkinter is defined to work. It is single threaded, so while it's waiting for user input it's truly waiting. mainloop must be running so that the GUI can respond to events, including internal events such as requests to draw the window on the screen.
Generally speaking, you shouldn't be mixing a GUI with reading input from stdin. If you're creating a GUI, get the input from the user via an entry widget. Or, get the user input before creating the GUI.
A decent tutorial on popup dialogs can be found on the effbot site: http://effbot.org/tkinterbook/tkinter-dialog-windows.htm
I am writing a converter code for our Data Department to convert fixed width files into delmited files. Normally we use import the file into Excel, use the text import wizard to set the field lengths, and then just save as a csv. However we have run into the limitation where we have started getting files that are millions of records long, and thus cant be imported into Excel. The files do not always have spaces in between the fields, espicially so between value fields like phone numbers or zip codes. The headers are also often filled completely in with no spaces.
A sample of a typical fixed width file we are dealing with:
SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar
000001T1 P1 Sample A Sample 123 Any Street Anytown 12345-6789 12345678900
000002T1 P1 Sample A Sample Director of Medicine 123 Any Street Po Box 1234 Anytown 12345-6789 12345678900
The program needs to break file into the following delimited fields:
Sequen
Sack and Pa
Full name
Job Title
Hosp Name
Delivery Address
Alternate Address 1
Calculated Text
POSTNET Bar
Each file as a slightly different width of each field depending on the rest of the job. What i am looking for is a GUI oriented delimiter much like the Excel import wizard for fixed width files. I am writing this tool in Python as a part of a larger tool that does many other file operations such as breaking up files into multiple up, reversing a file, converting from delimited to fixed width and check digit checking. I am using Tkinter for the rest of the tools and it would be ideal if the solution use it as well.
Any help appreciated
If I understand the problem correctly (and there's a good chance I don't...), the simplest solution might be to use a text widget.
Make the first line be a series of spaces the same length as the row. Use a couple of alternating tags (eg: "even" and "odd") to give each character an alternate color so they stand out from one another. The second line would be the header, and any remaining lines would be a couple lines of sample data.
Then, set up bindings on the first row to convert a space into an "x" when the user clicks on a character. If they click on an "x", convert it back to a space. They can then go and click on the character that is the start of each column. When the user is done, you can get the first line of the text widget and it will have an "x" for each column. You then just need a little function that translates that into whatever format you need.
It would look roughly like this (though obviously the colors would be different than what appears on this website)
x x x ...
SequenSack and PaFull Name****************************]JOB...
000001T1 P1 Sample A Sample ...
Here's a quick hack to illustrate the general idea. It's a little sloppy but I think it illustrates the technique. When you run it, click on an area in the first row to set or clear a marker. This will cause the header to be highlighted in alternate colors for each marker.
import sys
import Tkinter as tk
import tkFont
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
header = "SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar"
sample = "000001T1 P1 Sample A Sample 123 Any Street Anytown 12345-6789 12345678900"
widget = DelimiterWidget(self, header, sample)
hsb = tk.Scrollbar(orient="horizontal", command=widget.xview)
widget.configure(xscrollcommand=hsb.set)
hsb.pack(side="bottom", fill="x")
widget.pack(side="top", fill="x")
class DelimiterWidget(tk.Text):
def __init__(self, parent, header, samplerow):
fixedFont = tkFont.nametofont("TkFixedFont")
tk.Text.__init__(self, parent, wrap="none", height=3, font=fixedFont)
self.configure(cursor="left_ptr")
self.tag_configure("header", background="gray")
self.tag_configure("even", background="#ffffff")
self.tag_configure("header_even", background="bisque")
self.tag_configure("header_odd", background="lightblue")
self.tag_configure("odd", background="#eeeeee")
markers = " "*len(header)
for i in range(len(header)):
tag = "even" if i%2==0 else "odd"
self.insert("end", " ", (tag,))
self.insert("end", "\n")
self.insert("end", header+"\n", "header")
self.insert("end", samplerow, "sample")
self.configure(state="disabled")
self.bind("<1>", self.on_click)
self.bind("<Double-1>", self.on_click)
self.bind("<Triple-1>", self.on_click)
def on_click(self, event):
'''Handle a click on a marker'''
index = self.index("#%s,%s" % (event.x, event.y))
current = self.get(index)
self.configure(state="normal")
self.delete(index)
(line, column) = index.split(".")
tag = "even" if int(column)%2 == 0 else "odd"
char = " " if current == "x" else "x"
self.insert(index, char, tag)
self.configure(state="disabled")
self.highlight_header()
return "break"
def highlight_header(self):
'''Highlight the header based on marker positions'''
self.tag_remove("header_even", 1.0, "end")
self.tag_remove("header_odd", 1.0, "end")
markers = self.get(1.0, "1.0 lineend")
i = 0
start = "2.0"
tag = "header_even"
while True:
try:
i = markers.index("x", i+1)
end = "2.%s" % i
self.tag_add(tag, start, end)
start = self.index(end)
tag = "header_even" if tag == "header_odd" else "header_odd"
except ValueError:
break
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
edit: I now see that you are looking for a gui. I'll leave this incorrect answer for posterity.
import csv
def fixedwidth2csv(fw_name, csv_name, field_info, headings=None):
with open(fw_name, 'r') as fw_in:
with open(csv_name, 'rb') as csv_out: # 'rb' => 'r' for python 3
wtr = csv.writer(csv_out)
if headings:
wtr.writerow(headings)
for line in fw_in:
wtr.writerow(line[pos:pos+width].strip() for pos, width in field_info)
I would like to be able to save my session state within the PythonWin editor (e.g. these three files are opened and positioned in these particular locations within the PythonWin window). I can get handles to each of the child windows within PythonWin using win32gui, as well as the titles of each of the files and the positions/sizes of the windows. I'm unclear though in how to get the full path for the file listed as the child window name (i.e. if child window name is test.py and test.py lives at c:\python\test.py, I don't know how to get c:\python). I was thinking I would write out which files were opened plus their window positions to a small file that I would then call at PythonWin start time for loading.
Any ideas on how to get the full paths to the child window names?
Alternatively if someone already has a more elegant solution for saving session state in PythonWin please pass it along.
Below is the code I'm using right now (thanks to Michal Niklas for the starter code for using win32gui).
import win32gui
import re
MAIN_HWND = 0
def is_win_ok(hwnd, starttext):
s = win32gui.GetWindowText(hwnd)
if s.startswith(starttext):
global MAIN_HWND
MAIN_HWND = hwnd
return None
return 1
def find_main_window(starttxt):
global MAIN_HWND
win32gui.EnumChildWindows(0, is_win_ok, starttxt)
return MAIN_HWND
def winPos(hwnd):
if type(hwnd) == type(1): ( left, top, right, bottom ) = win32gui.GetWindowRect(hwnd)
return "%i, %i, %i, %i" % (left, right, top, bottom)
def winName(hwnd, children):
s = win32gui.GetWindowText(hwnd)
rePy = re.compile(r'[a-zA-Z1-9_ ]*.py')
rePySearch = rePy.search(s)
if rePySearch is not None:
if rePySearch.group()[0:7] != "Running":
s = s + ',' + winPos(hwnd) + '\n'
children.append(s)
return 1
def main():
children = []
main_app = 'PythonWin'
hwnd = win32gui.FindWindow(None, main_app)
if hwnd < 1:
hwnd = find_main_window(main_app)
if hwnd:
win32gui.EnumChildWindows(hwnd, winName, children)
filename = "sessionInfo.txt"
sessionFile = os.path.join(sys.path[0],filename)
fp=open(sessionFile, 'wb')
for i in range(len(children)):
fp.write(children[i])
fp.close()
main()
I could be wrong, but isn't PythonWin written in Python?
Have you tried reading the source to the "Save" command to figure out where it stores its full paths?
(I'd take a look myself, but I haven't used Windows in half a decade)