A well designed application should probably support Copy and Paste. While it sounds simple, it actually entails a few features.
Copying from Your Application to Other Applications
This means that your application should offer up data formats that other applications can read. So a user should be able to copy in your application, and paste into another one. This may entail converting your internal data format into a new one that other programs will expect.
Copying from Other Applications into Your Application
This means reading other applications data formats and converting it your applications internal representation.
Copying and Pasting within Your Application or between Instances of Your Application
This entails marshaling all the data that your application needs. If a user can run multiple instances of your Application, it won't be sufficient to simply copy an instance of an object, since one instance won't have access to what is in the memory of another instances.
How Does Copy and Paste Work?
You start out by accessing the desktop's clipboard. Then, you tell that clipboard what kind of data your app can put on the clipboard, and then what kind of data your app can take off of the clipboard. Then, you right some functions to handle the following situations:
If the user selects a Copy command in your application, you tell the clipboard that data is available (but you don't actually put the data on the clipboard until the user selects paste).
If the user selects Paste in your application, you ask the clipboard for the data, and the application with the data then supplies it to the clipboard. Sometimes the application supplying the data is your application, and sometimes it is a different application.
If the user selects Paste in a different application, then the clipboard might ask your application to supply the data.
Set Up Variables
So, let's get to the code. First thing, we need to create a reference to desktop's clipboard, and also create a variable to store a reference to whatever item may be copied.
self.clipboard = gtk.Clipboard()
self.clipboard_item = None
I put this in finish_initializing.Handling the Copy Command
Then I wrote a function called "copy_menu" which is called when the user selects "Copy" fromt he menu. the first thing this function does is tells the clipboard what kinds of data is supported by passing in a list of tuples. Each tuple has a string that specifies the type, info regarding drag and drop, and a number that you can make up to specify what the type again. For Photobomb, only the strings are important. You need to use types that are commonly understood on the Linux desktop so that apps know that you can give them data they care about. I chose "UTF8_STRING" for putting strings on the clipboard, and the mime type "image/png" for images. For example, Gedit recongizes UTF8_STRING, and Gimp recognizes image/png, so Photobomb can copy data into both of those apps. Only Photobomb recognizes "PhotobombItem", of course. These strings are called "targets" in Gtk.
After specifying what data types you will support, you then tell the clipboard about those data types, along with what function to call if the user tries to paste, and what function to call when the clipboard gets cleared. Finally, the function stores a reference to the currently selected item. It's important to store this reference seperately, as the selection could change before the user pastes.
def copy_menu(self, widget, data=None):
paste_data = [("UTF8_STRING", 0, 0),
("image/png", 0, 1),
("PhotobombItem", 0, 2)]
self.clipboard.set_with_data(paste_data, self.format_for_clipboard, clipboard_cleared)
self.clipboard_item = self.selected_item
Handling Programs Pasting from PhotobombAfter telling the clipboard what kind of data you can put on it, the user may try to actually Paste! If this happens, you have to figure out what kind of data they are asking for, and then put the correct kind of data onto the clipboard. To figure out what is being asked for, you check the target. This info is stored in the selectiondata argument, that gets passed in.
Putting Text on the Clipboard
When an application is asking for a string, life is really simple. Every PhotobombItem has a text property, so it's simple to simply set the text of the clipboard with this data.
def format_for_clipboard(self, clipboard, selectiondata, info, data=None):
if selectiondata.get_target() == "UTF8_STRING":
self.clipboard.set_text(self.clipboard_item.text)
Putting an Image on the ClipboardBut what about when the app has asked to paste an image? The clipboard only supports text, so you can't paste an image, right? Actually, the contents of any binary stream or file can be converted to text, and that text can be put on the clipboard. However, you don't want to simply set the text for the clipboard, as binary data can have characters in it that will confuse a program that thinks it's a normal string. For example, when you read in a file of binary data, you can set that to a string in Python, but other programs not written in Python will probably get have errors if they try to treat it as a string type.
In fact, the clipboard will reject text that is binary data. So, if you have binary data, you need to:
- convert it into text
- set the selectiondata with the proper target
if selectiondata.get_target() == "image/png":
selectiondata.set("image/png", 8, self.image_for_item(self.clipboard_item))
Putting a PhotobombItem on the Clipboard
If an instance of Photobomb wants to paste, we need to provide enough information to create a proper PhotobombItem. A PhotobombItem knows a lot about itself, for example, it's clipping path, it's rotation, it's scale, etc... When copyng and pasting within Photobbomb or between instances of Photobomb, we don't want to lose all of that data.
So how do we paste a PhotobombItem if we can only put text on the clipboard? Python has built in support for turning objects into strings. This is called "pickling". So, in the simplest case, we could just convert the Python object to a string, put that string onto the clipboard, and then when convert it from the string back to a Python object.
So, first, import the pickle module:
import pickle
Then you can use "dumps" to dump the object to string:
if selectiondata.get_target() == "PhotobombItem":
s = pickle.dumps(self.clipboard_item)
self.clipboard.set_text(s)
Sadly, this doesn't work for Photobomb because the GooCanvas classes that PhotobombItems derive from don't support pickling. So we can't do this in the simple way. However, what we *can* do, is create a dictionary with enough information to create identical PhotobombItems, and then pickle that dictionary. For photobomb, this gets a bit annoying complex and is not relevant to the blog posting, so I snipped out most of the code. You can always look at the full code on launchpad, of course.This is, unfortunately, a lot more involved. However, it works just fine:
if selectiondata.get_target() == "PhotobombItem":
d = {"type":type(self.clipboard_item)}
d["clip"] = self.clipboard_item._clip_path
#snipped out all the other properties that need to be addeed to the dicitonary
s = pickle.dumps(d)
self.clipboard.set_text(s)
You don't have to use pickling. For example, you could represent this dictionary in JSON instead. However, pickling is the native serialization formatting for Python, so it's easiest to use it when you can.Now Photobomb can support copying and pasting into text editors, like Gedit, and image editors, such as the Gimp, as well as into Photobomb itself.
Handling Pasting in Photobomb
The essential paste function is run when the Photobomb user uses Photobomb's Paste command. This function tries to figure out if there are any interesting data types on the clipboard, and then uses them. Typically, such a function should ask for the richest and most intersting data it can handle first, and then less rich data types in order. For Photobomb, the richest data is a PhotobombItem, or course. Then an image, and then finally text. You may have noticed above that when a user pastes from a clipboard, it runs a function in some application. There is no gauruntee that these functions will be fast. For this reason, it's best to use the asyncronous "request" methods on the clipboard.
So, the paste function simply tries to figure out which is the best data type to paste, if any, and then asks the clipboard to call the correct function when the data is ready.
def paste_menu(self, widget, data=None):
targets = self.clipboard.wait_for_targets()
if "PhotobombItem" in targets:
self.clipboard.request_contents("PhotobombItem", self.paste_photobomb_item)
return
for t in targets:
if t.lower().find("image") > -1:
self.clipboard.request_image(self.paste_image)
return
for t in targets:
if t.lower().find("string") > -1 or t.lower().find("text") > -1:
self.clipboard.request_text(self.paste_text)
return
In the cases where another applications has put an image or text onto the clipboard, it's easy to use functions already built into Photobomb to paste those data types. Notice that the return functions already have all the data ready in the form of text or the pixbuf.
def paste_text(self, clipboard, text, data=None):
self.add_new_text(text)
def paste_image(self, clipboard, pixbuf, data=None):
self.add_image_from_pixbuf(self, pixbuf)
When an instance of Photobomb has put the data on the clipboard, it's a bit more complex, because Photobomb has to de-serialize the dictionary, and then create the proper item. Again, I snipped out most of the code for using the de-serialized dictionary, because it just unnecessarily complicated the sample: def paste_photobomb_item(self, clipboard, selectiondata, data=None):
item_dict = pickle.loads(clipboard.wait_for_text())
if item_dict["type"] is PhotobombPath:
new_item = PhotobombPath(self.__goo_canvas,
item_dict["path"],
item_dict["width"], None)
#snip out all the code to handle all of the other properties
new_item.set_clip_path(item_dict["clip"])
new_item.translate(10,10)
new_item.set_property("parent", self.__root)
self.z_order.append(new_item)
self.add_undo(new_item, None)
Persisting the Clipboard ItemYou may have noticed that an item isn't actually put on the clipboard until the user tries to paste. This is a great optimization, because pasting complex or big things can take a lot of time, and there is no point on the user waiting for a copy command to finish if they are never going to actually paste with it. However, what if the user selects "Copy" in your app, and then quits your app? How is the item going to get pasted?
Gtk handles this by letting you "store" data on the clipboard. This can actually be used at any time in your application, but to handle this particular situation, Photobomb just includes a few lines in it's on_destroy function. This causes the desktop's clipboard to store all of the data types, so all the functions for servicing a paste command get called, and the desktop's clipboard holds onto the data as long as it needs.
def on_destroy(self, widget, data=None):
"""on_destroy - called when the PhotobombWindow is close. """
#clean up code for saving application state should be added here
paste_data = [("UTF8_STRING", 0, 0),
("image/png", 0, 1),
("PhotobombItem", 0, 2)]
self.clipboard.set_can_store(paste_data)
self.clipboard.store()
gtk.main_quit()
Nice and quite informative post. I really look forward to your other posts.
ReplyDeleteAcer - 15.6" Aspire Notebook - 4 GB Memory - 640 GB Hard Drive - Black
Acer - Aspire AS5750-2456G50Mtkk 15.6" LED Notebook - Intel Core i5 i5-2450M 2.50 GHz - Black
Very great article and so informative thanks for sharing
ReplyDeleteclipping path service
You deserve a huge thanks for this wonderful post.This is really an excellent post.thanks
ReplyDeleteclipping path service provider
clipping path service
this is epson printer error code 0xe5 and epson error code 0xe5 as well as epson printer error 0xe5
ReplyDeletethis is epson printer error code 0xea and epson error code 0xea
ReplyDeletethis is epson printer error code 00041 and epson error code 00041 as well as Epson Error Code 1073
ReplyDeletethis is Epson Printer Error Code 00044 and also
ReplyDeleteepson error 000044 an at last epson printer error 000044
ReplyDeletethis is brother printer error code 30 and brother printer error 30
this is brother printer error code CC4-205 and brother error code CC4-205 and also brother printer error CC4-205
this is brother printer error code 32 and brother printer error 32 and at last brother printer 32 error
this is brother printer error code 50 and brother printer error 50
I really appreciate reading such kinds of blogs. Thanks for sharing such informative articles.
ReplyDeleteCut Out Image
Clipping Image Photoshop
Image Post Production