This is a sort of ad hoc list of stuff that has taken me an inordinate amount of time to figure out because it is either :
Problem: If you bind the Listbox Click event <Button-1> to a callback and use cursorselection() in the callback, you get the previous cursorselection because the callback is invoked before the cursor position is updated.
Solution: Bind to the <<ListboxSelect>> event instead.
Problem: Clicking a task in one Listbox clears the selections in other listboxes.
Solution: To prevent this behavior, add exportselection=0 to the ListBox declaration. (No, I have no idea, what you lose by doing this.)
Problem: How to determine whether a listbox is empty or has no selection. The size attribute for an empty listbox is set to 1, not zero. int(curselection()[0] throws an Index Error exception if nothing is selected.
Solution: The listbox curselection() property will return an empty list [] if the listbox is empty or no entry is selected. Documentation that says an empty string will be returned appears to be wrong.
Problem: The default Clipboard bindings for a Tkinter Listbox work erratically. Shift-Ins works, Ctrl-Ins, Ctrl-Del, ^C and ^X work, but don't leave the copied/cut text selected even though it is in the Clipboard. ^V doesn't work at all. Apparently, the some of the keys are bound incorrectly and/or to possibly objects that do not generate key stroke events.
Solution: Bind the Clipboard keys to the event_generate methods of the textbox and specify <<Copy>>,<<Paste>>,or<<Cut>> events as appropriate. For example, if the Listbox name is t:
t.bind("<Control-Insert>",lambda x: local_event_generate("<<Copy>>"))
HOWEVER this may lead to other problems under Linux:
Problem: If clipboard key settings are overridden as suggested above one may find that the PASTE keys (e.g. shift-insert) work fine with text CUT or COPIED within Python-Tkinter but paste text twice when used with text CUT or COPIED from external programs. In addition, if there is marked text at the cursor, it is not overwritten as is normally the case for paste operations. And finally, cut operations disappear the first character after the text marked for cutting.
Solution: The double paste problem seems to be due to the internal paste logic being called twice referring to different clipboards -- presumably due to a previous binding on the keystrokes. The solution seems to be to return 'break' after generating the first PASTE event thus suppressing processing additional keystroke bindings. The marked text issue can be fixed by generating a CUT event before pasting. The extra character deletion can be fixed with a local_cut event handler similar to that used for PASTE. I suspect that these problems occur only on Unix platforms.
def local_paste(object) : temp = object.clipboard_get() #save the clipboard temporarily local_cut(object) #get rid of marked text object.clipboard_clear(); object.clipboard_append(temp) object.event_generate("<<Paste>>"); return 'break' def local_cut(object) : object.event_generate("<<Cut>>"); return "break" def rebind_clipboard(a): """Rebind CUA clipboard control keys to a specific object (Python's generic bindings don't always work properly).""" a.bind("<Control-Insert>",lambda x: a.event_generate("<<Copy>>")) a.bind("<Shift-Insert>",lambda x: local_paste(a)) a.bind("<Shift-Delete>",lambda x: local_cut(a)) a.bind("<Control-c>",lambda x: a.event_generate("<<Copy>>")) etc
Problem: Attempting to create new tkinter widget classes doesn't work right. Gets error messages at run time about a missing tk attribute.
Solution: The problem seems to be in the way Python and Tkinter are interfaced. Widget methods are acted upon by a method named tk that is apparently a Tcl interpreter. Just specifying inheritance does not suffice? (why not?) New widgets must explicitly initialize whatever existing widget class they inherit their properties from. They do that by calling the Widget's init method in order to get a tk attribute.
This leads to a second problem which is that multiple inheritance doesn't seem to work for tkinter widgets in Python. It is possible to work around that by inheriting as many attributes and methods impossible from one base class, then accessing any additional attributes and methods via their associated widgets within the object. If, for example, we create a widget x that has a listbox called l within a frame f and the widget inherits its properties from type Frame (and remembers to call Frame.init()), we can access pack() as either x.pack() or x.f.pack(). But we can only access listbox insert as x.l.insert(...whatever...) where ...whatever... is the parameter list. Alternatively, one could explicitly declare the methods that need to be provided, then simply call the appropriate component method internally. e.g. def insert(self,...whatever...): self.x.l.insert(...whatever...)
See http://mail.python.org/pipermail/tkinter-discuss/2008-August/001605.html
Problem: Tacking a vertical scrollbar onto a Tkinter Listbox widget is possible, but it's hard to remember how.
Solution: Here is a ScrolledList class that can be instantiated as a ScrolledList object. Accessing Listbox attributes and methods of the object requires a couple of extra characters in the attribute/method name, but otherwise using the class should be straightforward.
class ScrolledList(Frame) : """ Defines a Listbox with an associated Scrollbar. It seems not to be possible to use multiple inheritance with Tk widgets. This widget acts like a Frame. grid(),pack() and other Frame methods can be used directly. Listbox and Scrollbar methods must be accessed indirectly via their names in this class -- l and s. That is to say that if x is defined as x = ScrolledList(... that x.pack() is a legitimate operation, but that operations on the embedded Listbox or Scrollbar must take the form x.l.insert(... or x.s.whatever invocation is obj_name = ScrolledList(Tkobject, height=n, width=n) where Tkobject is the Tk object container where the scrolled list box will appear and height and width are the dimensions of the Listbox. """ #NOTE: **from tkinter import * ** should have been run before this #class is encountered. As I understand it # .Imports are normally done at the front of Python scripts # ."from x import .." is not desirable, but Tkinter is an exception # .A second import statement does no significant harm def __init__ (self, Tkobject, height=4, width=50) : """This code is executed whenever a new scrolled list object is created (instantiated) """ Frame.__init__(self) #Make sure we are a widget self.f = Frame(Tkobject) #We Create a frame self.s = Scrollbar(self.f,orient=VERTICAL) self.l = Listbox(self.f, height=height, width=width, yscrollcommand=self.s.set, exportselection=0) self.s.config(command=self.l.yview) #Hook scrollbar to listbox self.s.pack(side=RIGHT, fill=Y); self.l.pack() #Good to go # ---- Adding some methods ----------------------------------------------- def bind (self,event,action) : #define bind method self.l.bind (event,action) def curselection(self) : #define curselection method self.l.curselection() def focus_set() : #focus_set method self.l.focus_set() def insert (self,where,what) : #define an insert method self.l.insert (where,what) def pack (self) : #define a pack method self.f.pack() def see (self,which) : #define a see method self.l.see(which) def selection_set (self,which) #define a selection set method self.l.selection_set (which)
Problem: In Python Lambda operations are evaluated at run time -- which is OK most likely. And Callbacks with parameters require Lambda -- which is, I think, a weakness in the language. There are then three "times", compile time, run time when the callback is created, and a "callback time" when the lambda expression in the callback is actually evaluated. The result is that attempts to create an array of objects invoked via Callbacks will create Callbacks that (at best) all reference the final item in the array.
In my case, attempting to create a variable length array of menu items that simulated mouse clicks on an Array of Radio Buttons looked like this
for i in range(len(XF)) : mf.add_command(label="Tab "+str(i)+' '+XF[i], command=lambda: XT[i].event_generate("<Button-1>"), underline=4))
Runs OK, but all the menu items created act on the last entry in the XF array. It's actually a bit worse than that. They apparently all use the most recent value bound to i in the relevant namespace. Even if i was reused for something else entirely after being used to create widgets.
Solution: To get subscripts to reflect values they have to be decoded in advance then reflected by applying eval (or exec?) to the resulting expression.
for i in range(len(XF)) : eval('mf.add_command(label="Tab '+str(i)+' '+XF[i]+'", command=lambda: XT['+str(i)+'].event_generate("<Button-1>"), underline=4)')
Problem: Menu options and such in Tkinter generally call functions using a command=... option in the Menu, Button, Radiobutton, etc definition. Typically no parameters are passed to the invoked function which is convenient because passing parameters requires the use of lambda functions. Lambda functions cause most people's heads to hurt and have odd limitations. The same function can easily be bound to a keystroke event. Unfortunately, Python-Tkinter automatically passes a keystroke code to the called function when it is invoked by a bound keystroke. So now we need a called function which has no parameters when invoked from a Mousebutton press, but one parameter when invoked with a keystroke. An exception will be thrown if the number of parameters doesn't match the function definition.
Solution: Unlike many computer languages Python supports optional positional parameters.
def useful_function ( dummy = 0 ) : ... Radiobutton(FrameName, test="Useful",..., command=useful_function ) ... FrameName.bind("A",useful_function)
Will now run without throwing an exception whether the "Useful" menu item is clicked (no parameter passed) or the letter A is typed (one parameter passed).
Problem: Occasionally, one may desire to completely hide one "screen" and replace it with another. It's far from obvious how to do that.
Solution: The following will do that.
Initial setup -- displays A
... specify toplevel entity A ... A.protocol("WM_DELETE_WINDOW", A_quit) #Catch close by window manager ... display it (A.grid() or A.pack() ... specify toplevel entity B ... B.protocol("WM_DELETE_WINDOW", B_quit) #Catch close by window manager ... display it (B.grid() or B.pack() ... B.wm_withdraw() def B_quit() : B.wm_withdraw() A.wm_deiconify()
Switch to B
A.wm_withdraw() B.wm_deiconify()
Switch back to A
B.wm_withdraw() A.wm_deiconify()
Problem: If you try to loop through a string or list and delete specific elements based on some criteria, you will probably find (not necessarily immediately) that the element after the deleted element is not checked for deletion. That's because the iterator was created before the iteration starts. It has no way for it to know that if the (n)th element is deleted, the new (n)th element must be tested next. It simply moves on the the (n+1)th element. Simply reversing the list or string does not solve the problem, it just changes which elements are skipped.
Solution: You need to iterate backwards. One way to do this appears to be to use extended indexing syntax [::-1] for the loop control. e.g. for i in example_list[::-1] : I have not investigated whether using a generator expression for the iterator would work. I vaguely think it might. I think that for i in reversed(example_list): might work also, but I haven't tested it. It is also possible that there is some elegant solution using the list's iter() and next() methods. I think it is wonderful that Python allows access to the guts of the language in this way. But I'm nowhere near smart enough to take advantage of that access.
Problem: Although Python's CSV module will read a CSV (Comma Separated Variable) file that has empty fields that look like , "" , it won't write them. Converting a CSV module created file back requires reading the file back and inserting , "" , strings in place of ,, strings. But there are three pitfalls:
It is probably possible to create a tricky expression that appends extra fields to the front of the comma separated line, does the conversions, then removes any initial or terminal detritus. A simpler (and possibly faster) solution is simply to forget the CSV module and write desired line in the desired format.
Problem Unlike many computer languages, Python doesn't have a square root operator built into the language. That doesn't mean that you can't do square roots. To get the square root of x:
Problem If a python script launches tasks via spawn,etc. in Unix and sets P_NOWAIT in order to avoid blocking, the launched task will become a zombie when it terminates. That is to say that it will remain in the process tables and will be reported as "defunct". If the launching task is used a lot, it can create dozens or hundreds of these zombies. These processes will not go away until the Python script is terminated.
Solution This occurs because Python does not automatically handle the signal generated when a child process terminates. The Unix kernel is waiting for Python to acknowledge that it knows the child process has died. Adding
import signal signal.signal(signal.SIGCHLD, signal.SIG_IGN)
near the start of the launcher script will allow the kernel to kill the terminating child task normally. If the script also uses P_WAIT, it will be necessary to bracket the calls where P_WAIT is used with signal.signal(signal.SIGCHLD, signal.SIG_DFL) and signal.signal(signal.SIGCHLD, signal.SIG_IGN) calls to prevent the signal when the wait is completed from being ignored.
Problem: Python memory management is "different". Python works with objects, not data in the conventional sense. Even assignment operations that look the same as assignment in languages like FORTRAN or C really aren't. Technically, what looks like assignment (e.g. a = 2+2 )is actually binding of a "variable name" ( a ) to an object (that contains the integer value 4). And yes, now that you ask, a single object -- the integer 4 for example -- can be, and very likely is, bound to many names. This leads to some problems for some people sometimes.
It is said that Python does not support pointers and doesn't need them. That's largely true. Probably the most common use of pointers in languages that use them is to access sequential data. For the most part using Python's slice notation on sequence objects (strings,lists,etc) does the analogous thing in a possibly less mind bending fashion. Slice notation largely eliminates any confusion about the values of "variables" as opposed to the values of pointers to the variables.
For dealing with data tables or files laid out by C,FORTRAN,BASIC,etc programmers, there appear to be two not especially appealing alternatives -- treat them as strings accessed via slicing, or use real pointers via the CTYPES package or something similar. For memory mapped IO or shared data in memory, I think CTYPES or something similar would be the only choice. Accessing mixed data by slicing is klunky. WARNING: CTYPES has a steep learning curve.
One use of pointers in other languages is to minimize the use of complex expressions that have readability problems. For example, a pointer labeled text might be used in place of an expression like data_array[data_array_current_element][data_array_current_index]. There are ways to do this in Python.
A constant is something that will never be set, only used. These can be aliased in Python by binding a "constant name" to a lambda expression e.g. text = lambda : (data_array[data_array_current_element][data_array_current_index]). text can then be referenced normally e.g. print text
It is also possible to alias a variable that can be set (bound actually). It just isn't as easy. To do so, a class is needed.
class Object_Pointer() : """This class is used to assign simple names to database variables""" #See http://www2.lib.uchicago.edu/keith/courses/python/class/5/ def __getattr__(self, name): global data_array, data_array_current_element, data_array_current_index] if name == "db" : return (data_array[data_array_current_element][data_array_current_index]) elif name == ... etc, etc, etc def __setattr__(self, name, value): """Override the normal assign logic""" global XD,XX,X,x,XF if name == "db" : (data_array[data_array_current_element][data_array_current_index]) = value elif name == "db_cur" : ... etc, etc, etc # ---- p --------------------------------------------------------------------- p = Object_Pointer() #Now, create a pointer set #At this point p.text looks like a normal Python variable.
NOTE: If del p.text might be needed, a delattr function will need to be defined. See http://www2.lib.uchicago.edu/keith/courses/python/class/5/
NOTE: One might think that the variables would need to be defined as variables in the class. Indeed, exactly the reverse. They need not to be defined in the class.
NOTE: In a real implementation, one would probably want to print a error message or throw an exception if a variable name is encountered that is not recognized.
Python binding can bind expressions or functions as well as values. That can be confusing. a = b[3]+1 binds a to whatever value is in b[3] with 1 added to it, not to the expression b[3]+1. e.g. b[3]=2; a=b[3]+1 ;b[3]=3.1416; print a** prints 3, not 3.1416 or 4.1416. But a can also be bound to a function b[3]=2; a=lambda:b[3]+1 ;b[3]=3.1416; print a tells where the lambda function is (note that a acts like a pointer here) whereas b[3]=2; a=lambda:b[3]+1 ;b[3]=3.1416; print a() prints 4.1416. As can be seen, if one really misses the complexity of C pointers, there are ways to approximate it in Python.
While binding a name to a function creates a pointer like entity that can be used in expressions, the inverse binding b[3]=2; a=lambda:b[3] ;a=6; print a,b binds a to 6, not b[3] to 6. And using a()=6 doesn't work either. So one can use lambda functions to alias some horrendous name to a simple term, but only for evaluation of expressions, not rebinding (resetting) except in the special case where all the horror is in an expression in a slice term. Didn't follow all that? That's OK. The bottom line is that Python programmers tend to define get and set methods for objects where other languages might use pointers to access and set the values.
There is a lot of discussion of assignment in sections 5.3 and 6.3 of the Python Reference Manual The document seems not to exist for Python after version 2.6 It's not clear why not Most of the apparent complexity comes from the ability to assign lists of labels lists of values in a single operation and to augmented assignment operations such as +=
Solution: None -- that's the way Python works. Sometimes, it's the way you want it to work and it's a feature. Sometimes, it doesn't, and it's a nuisance.
A module can be pretty much any chunk of Python code. It can be imported and "compiled" with an import statement. Multiple import statements are harmless as an already loaded module will not be reloaded if a second import statement is encountered. There appear to be only two constraints:
If you wish to avoid having to put your local modules into libraries with "real" Python modules just to get it found, the following will load a module from the current directory:
import os,sys sys.path.append(os.path.abspath("")) import your_module
Problem: You can't drag and drop a file within it's own folder to make a copy of the file when you drop it as you can in Windows Explorer and many other shells.
Solution: Copy the file to the Clipboard, right click on the folder and paste.
Problem: The security model for personal computers sucks. Valuable though it might be in a business or school setting with professional system administration, it provides little security for a stand alone computer. Worse, well intentioned attempts to implement it actually makes the computers less reliable and more error prone -- something that they do not remotely need. This is certainly true of Windows and Unix which I am familiar with. I assume it is true of Apple as well since the current Apple software is based on Unix.
I would point out that the security model in Unix actually differs very little from the model that everyone detests in Windows Vista. Believing that Unix is more secure than Windows strikes me as being mostly wishful thinking.
Solution: So what's my better security model? I don't have one. But I am pretty sure that that no security model at all is at least as good as the model that is currently being touted. No security is about equally secure and substantially less aggravating.
If you really want a reasonably secure personal computer: . Keep the computer under lock and key . and use whole disk encryption with a password see http://tldp.org/HOWTO/html_single/Disk-Encryption-HOWTO/ . and do not ever connect the computer to a network -- any network.
If you do those things, the conventional security model is unnecessary. If you don't, it probably won't protect you.
If you must connect to a network, or cannot physically secure your computer, you can not, with current technology, have a secure system (communications based hackers break in taking advantage of flaws in the network interface software rather than by guessing your name and password). But, you may be able to improve your security by a few steps.
Problem: The man file for terminal provides virtually no information on the command line. The --help option does list the command line options, but the format shown for the -e (execute) option includes an extraneous equal sign and therefore doesn't work.
Solution: The command line for starting an instance of xfce4-terminal with a program running is:
xfce4-terminal -e "program param1 param2 param3"
Either single or double quote bracketing can be used for the program string.
on some (later?) versions something along the line of:
Terminal --execute program param1 param2 param3
Problem: How to start up processes automatically when XFCE Starts
Solution: Recommended: Make up a desktop file and put it in /etc/xdg/autostart/ or /etc/xfce/xdg/autostart/ (whichever your release wants). Use "Autostarted Applications" via the System Menu, or -- equivalently -- run /usr/bin/xfce4-autostart-editor from the command line and make sure that your desktop file is checked to be run.
Problem: How to Automatically log into a Linux system and start an X-Windows session. There are probably a hundred ways to accomplish an automatic logon into Linux. The problem is that there are probably ten thousand ways that won't work. The most common apparently is to use mingetty with it's autologin option. But not every Linux release includes autologin or will autologin with just mingetty added. The following works with Slackware and probably will work with any release using a bash shell.
if [ $HOME = "/" ];
then
cd /root
export HOME="/root"
export PWD="/root"
export USER="/root"
export AUTOLOG="TRUE"
fi
And at the end of /etc/profile, add
if [ $AUTOLOG = "TRUE" ];
then
export AUTOLOG=""
startx
fi
PROBLEM: For whatever reason, bash runs differently in a GUI terminal than in a console. For example, less when run in a console leaves the content of the last screen displayed. When run in a window, the content is erased.
ANALYSIS: You probably want to run different things when the bash shell is started as part of logging a user in than when it is run in a text console than when it is run in a terminal in the GUI. Linux accommodates that, but not very cleanly. And because it is difficult to see how the pieces fit together, Linux is likely to arrive with prompts and tasks like less behaving differently in a console than in the GUI. Different behavior may be necessary and desired. But it may also be unnecessary and annoying. It can be corrected. There are three scripts involved.
There are really three files that will be looked for in the order:
Only the first found of the three will be run. ~ is represents the user's root directory. ~/.bashrc is run when bash is run as a non-login shell.
Solution: My recommendation: Assuming that you don't actually want non-login shells to have a different user interface than do login shells -
# Use LS_OPTIONS with LS -- set defaults for less
export LS_OPTIONS=' -F -b -T 0 --color=auto -H'
alias ls='ls $LS_OPTIONS'
export MINICOM="-c on" #Enable ANSI escape color control in minicom
export MANPATH=/usr/local/man:/usr/man #Set search path for man requests
export LESSOPEN="|lesspipe.sh %s" #Define less preprocessor
export LESS="X" #disable termcap initialization when LESS is run
Problem: Listing plugins in use is not a menu option
Solution: To list Plugins, Enter about:plugins in the "Location Bar" and press ENTER (Yes, now that you ask, this IS non-intuitive and probably not in accordance with anyone's ideas of a good user interface.).
Problem: Konqueror randomly displays numerous small boxes with letters in them superimposed on web pages.
Solution The boxes are part of the Access Key capability. Access Keys are a capability added to HTML in HTML4 -- the primary descriptive language used on the World Wide Web. Access Keys allow keyboard access to various areas of the web page. In Konqueror, they are supposed to be enabled by pressing the Ctrl key. Unfortunately, there is a bug thought to be in the linux kernel, that randomly sends Ctrl press events to konqueror when playing video or audio media. That causes the boxes to randomly turn on and off which most users find to be very distracting. Studies have found that the access keystrokes themselves conflict with established usages on almost all platforms/browsers. The capability has been deprecated in recent specification drafts. http://en.wikipedia.org/wiki/Access_keys
It is possible to turn Access Key usage off completely in Konqueror by adding
[Access Keys] Enabled=false
to the .kde/share/config/khtmlrc file for the user who starts Konqueror, then restarting konqueror. This seems to work with Konqueror 3.9.5.
Problem: When citing a reference to a Google Book, the resulting URL can become really unwieldy
Solution: Link by ISBN Number (usually available on the About the book page) http://books.google.com/books?vid=ISBN{ISBN Number}
How can I link to specific Google Books
Problem: How to get a list of files sorted by size
Solution: To get a list of the entities on disk sorted by size largest first:
(Takes a while) Or check this article for other options.du -B M / | sort -nr | less
Problem: How to search large directories for files containing specific text
Solution:
for i in `find .`; do grep -H "text_to_search_for" $i; done
except that doesn't work right if you have files/directories with embedded blanks in the name (A practice that ought to be banned but probably can't be).
Problem: Compressed file structures for Linux (e.g. most software releases) are typically packed up into a single file (a "tarball") with tar then compressed with gzip. I can never remember how to unpack them.
Solution: for file whatever.tgz
gunzip -c whatever.tgz | tar xvf - or tar xvfz whatever.tgz
Unpacking can also be done as two operations:
gunzip whatever.tgz tar xvf whatever.tar
(Observe that gunzip eats the tgz file and replaces it with a -- generally much larger -- .tar file. Be aware also that gunzip is sometimes replaced with a different compressor/decompressor such as bzip/bunzip2)
Problem: df and other disk space analyzers may report far more disk space in use than is actually used
Solution: This is sometimes due to trash folders created by modern, generally GUI, OSes. Trash is a repository for "deleted" files that can be recovered if the user decides that discarding the file was a mistake. Files from trash are discarded automatically when disk space is needed. There does not seem to be a simple tool that quickly reports disk usage exclusive of trash files although it is possible to work out usage with the df and du commands if one knows the name(s) of the trash directories. Other causes are run away log files (typically in /var/log/) and purportedly defective files placed in the /lost+found directory when file system checks (fsck) are run.
Problem: The permanent clock in PCs was designed to be inexpensive, not accurate. Many of them drift by seconds a day. After a few months, the clock can be off by 5 or 10 minutes. It is possible to reset the clock from a Network Time Protocol server on the Internet. The problem is that one can search for hours to determine the proper Unix command.
Solution: A command that will probably work is
ntpdate pool.ntp.org
Problem: Unix, unlike MSDOS and Windows, does not have a pause command that will cause a script to stop and wait for user action
Solution: The unix equivalent to a DOS pause command is:
read -p "message"