Skip to main content
gamedev.net
🔒 Locked

Saving files in Python

Started by Munchkin9 Aug 8, 2008 at 5:24 PM 44 replies 5.9k views
Original Post
Munchkin9
Munchkin9
Now that my last problem is solved I was wondering if anyone could recommend a way to save information from one program, place it in a different file, then take that information from the file and load it up in a separate program. Honestly nothing too complicated is needed at this point. Mainly I want a way to transfer information from a couple lists, to the actual game where it would load what was in those list. Not sure if this is very clear. But anything you can suggest would be great. Thanks in advance -Munchkin
Oluseyi
Oluseyi
You can write to file by using file objects:
fout = file("/path/to/file", "w")fout.write(...)

You can copy files by using the shutil module:
import shutilshutil.copyfile("/path/from", "/path/to")
doctorsixstring
doctorsixstring
Check out the pickle module for serializing objects into strings that can be read from and written to files. There is even an example showing how to write a serialized object to a file.
Zahlman
Zahlman
First: define "information". Does it have a particular format? Is there a reason to expect program B to be able to use information saved from program A?

It sounds like these are not big obstacles for you (you describe your program B as "the actual game"; is program A some kind of level editor?), but they bear consideration. Often it helps you figure out the best way to do things.

Your task can be approached in a variety of ways, but it fundamentally breaks down into two separate processes: converting the abstract "information" (presumably, some kind of Python object(s)) into a sequence of bytes that can be used to reconstruct the original (this is called "serialization"), and actually reading and writing those bytes. (Of course, there is also a "deserialization" step of converting read bytes back into objects.) You've received common answers for each, so you should be good to go - this post mostly exists to open your mind (and/or keep it open) rather than to solve your problem.

For more heavy-duty tasks, you might consider the 'shelve' module. The reasons for choosing this (or for not choosing it) are basically the same as for choosing or not choosing a full-fledged database, as opposed to a set of flat files.




Another approach to the serialization is to just use repr() to get a code representation of the data - basically, it looks exactly like the Python code for an expression (not statements) that would evaluate to the object - and eval() to evaluate that code representation and get the data back.

This requires that you provide an appropriate __repr__ for your own classes, which isn't always possible (typically, you'd want to output something that looks like 'ClassName(foo, bar, baz)', where the arguments, if fed to the __init__ of the class, would construct an exact copy of your object). It also works best if there is a natural "root" for your "tree" of objects. (For example, if you have a list of strings, that works fine: the serialized form will just be the Python syntax for an anonymous list of strings. But if you have several separate variables, each of which is a string, you will have a lot more work to do.)

The advantage you get from that, though, is that the output file will be much more human-readable, and amenable to editing by an external program (it's plain text, and looks just like Python, after all) - although you might want to put some effort into "pretty-printing" in that case. Making this system really robust will take a lot of effort, but the basics are trivial - and I personally use this approach quite a bit, especially for personal stuff where robustness doesn't matter so much :) Basically, you get all the benefits of XML (except of course the benefit of standard parsers existing for *other* languages ;) ), and none of the annoyances (like having to parse data and interpret what its data type should be; matching close tags to open tags etc.)
Munchkin9
Munchkin9
Wow quick good answers thanks.

To answer your questions:
1.Program A is indeed an editor, and program B is the game.

2.The information I intend to pass along is lists of objects including all variables and position on the screen (I already have an engine that is used in both program A and program B, therefor the objects I want to pass along already exist I just want to make the same instances with the same minor changes.

3.That sounds more or less like what I need (but if something simpler can be found it would be great) could you direct me to some good official tutorials(though 1 might have already been posted).

And again thank you for the prompt and helpful responses.

EDIT: Never mind, just looked at the links this looks like exactly what I need thank you again.

EDIT2: Okay I tried it and I got a weird error: can't Pickle Surface objects. I have no idea what that means could someone tell me what to do.

[Edited by - Munchkin9 on August 9, 2008 2:46:16 PM]
dwahler
dwahler
Presumably you're using PyGame. A surface holds image data that may be in main memory or in hardware-accelerated video RAM, which means it doesn't really make sense to store and load it. You might want to save and load the surface's contents instead; if so, the surfarray module should be able to help you.
drakostar
drakostar
Pickle = awesome. The key to using it is to segregate your game world data from any graphics/sound/input resources, and pickle only your data. This requires good object-oriented design, or *some* kind of decoupling at least.
Munchkin9
Munchkin9
uh...what?

Sorry, but that is waaaaaay beyond me. Simple tutorial please. Yes I am using pygame, aka that is my problem, what do I do about it? And keep it simple.

No decoupling decompressing pressing and mish mash. Keep it simple please.

What is this surfarray module? sounds interesting. Link to a tutorial please.

Thank you in advance.
mikeman
mikeman
There are objects you can't pickle, like surfaces, because that would require access to resources you don't have. You should be smart though. Do you really want the entire surfaces to be pickled pixel-by-pixel, or would be enough to save/load the name of the file you loaded the picture from? If that's so, then you can just save a string(the filename) and load the image when you load that string. Also, take a look at how __getstate__ and __setstate__ work, they can help you customize the pickling. For example, this is a texture object that uses pygame surfaces and pickles/unpickles itself properly(saves the filename upon saving, loads the image from the file upon loading).

class Texture:    def __init__(self,fileName):        self.img=None        self.fileName=fileName        self.loadFromFile(self,fileName)    def loadFromFile(self,fileName):        self.img=pygame.loadimage(fileName)#or something similar, can't remember the exact function right now    def __getstate__(self):        return self.fileName    def __setstate__(self,state):        self.loadFromFile(self,state)
Munchkin9
Munchkin9
Am afraid I still don't really get it. I use a class that is made to have an image. It is what all my objects are made on. So I don't really know how to remove the image. I have tried setting the image to None then reload the image later. But it gives me the error: TypeNone has no get_rec or something like that. It is part of the class I assume it has something to do with getting the size of the image. (I did not make the engine I am using it was given to me).

If you are going to give me a tutorial please assume I know nothing what so ever, it is probably closer to the truth.
Oluseyi
Oluseyi
Quote:
Original post by Munchkin9
Am afraid I still don't really get it. I use a class that is made to have an image. It is what all my objects are made on. So I don't really know how to remove the image. I have tried setting the image to None then reload the image later. But it gives me the error: TypeNone has no get_rec or something like that. It is part of the class I assume it has something to do with getting the size of the image. (I did not make the engine I am using it was given to me).

To save an image to file using PyGame, assuming your image is in a pygame.Surface, use pygame.image.save:
pygame.image.save(yourImage, '/path/to/filename')

What this means is that, after saving the image to a file separately, the rest of your object should not contain the image data, but instead the name of the file.

Serialization is not hard, but it is detail-intensive. There are many, many options and approaches depending on what your preferences are. What follow is just one approach:
class MySprite(object): # new-style classes; see http://docs.python.org/ref/node33.html    def __init__(self, id, image):        self.id = id        self.x = 0        self.y = 0        self.image = pygame.image.load(image)    # rest of class...    def serialize(self):        # we serialize the sprite into an INI-style text file and a bitmap image file        pygame.image.save(self.image, self.id + '.png')        outf = file(self.id + '.sprite', 'w')        outf.write('id=%s\nx=%d\ny=%d\nimage=%s' % (self.id, self.x, self.y, self.id + .png'))    def deserialize(self):        inf = file(self.id + '.sprite')        for line in inf:            keyval = line.split('=')            if keyval[0] == 'image':                self.image = pygame.image.load(keyval[1])            elif keyval[0] == 'x':                self.x = int(keyval[1])            elif keyval[0] == 'y':                self.y = int(keyval[1])

The above is just example code; in production, deserialization might be moved out of the MySprite class so it can be used as part of an object factory, for instance. Further, deserialization might be generalized so that a.) you can serialize multiple objects to a file, b.) you can serialized and deserialize from archives, and c.) one serialization routine can handle multiple object types.

Hope that helps.
Munchkin9
Munchkin9
Is there a way of doing this outside of the class. As I said previously the engine I am using is not mine, aka I don't really know how it works (my skills are not that good) So I would prefer not to meddle with it (I tried a couple times but nothing turned out like I planned so obviously I still don't understand how it works.
drakostar
drakostar
I'll see if I can briefly explain one possible approach.

Forget PyGame for a moment. Let's say you have a GameBoard object, which contains Tiles for the background and GameObjects that can be moved around. Each thing has a position on the board, a unique ID number, and some kind of name which can be related to an image file, like "Player" or "PurpleSlime" or "GrassTile".

Now separately, you have a PyGameRenderer object, with a render(GameBoard) method. The first time render is called, it reads in the entire GameBoard, and creates a PyGame Sprite for every GameObject, using the same unique ID (eg, it has a dict which maps IDs to Sprites). The Tiles can be handled similarly. On subsequent calls to render, it goes through all the GameObjects again and updates the position of the Sprites, removes deleted objects, and adds new ones.

The key here is that GameBoard and the objects it contains knows absolutely nothing about PyGame. It can be safely pickled. You have a separate "interpreter" which translates a GameBoard into actual graphics. This doesn't get pickled, because it doesn't need to be. You can create a new one, feed in the same GameBoard, and get exactly the same screen displayed.

I'm glossing over a lot of details and improvements because I assume you don't speak Patterns, but this is my general approach. If this doesn't make any sense to you, there's nothing inherently wrong with Oluseyi's approach, except that it doesn't really take advantage of the automagic goodness of pickle. I'm assuming your image files are stored separately, in some kind of shared location accessible by both the editor and the game.
Munchkin9
Munchkin9
Ya are right about that the images, the editor, and the game are all in the same folder. And no, you are also right that I know nothing about patterns. But for what you suggested if you could give me an easy tutorial (I don't need to understand it as long as it works) to use this with minimum problems it would be great (aka a way to separate the gameboard and the interpreter.

I hope you can help, I am getting seriously behind in schedule. And I can't continue the game till I have done this. (As a last option do you think it would help if I sent what I have of the game and of the engine to who ever wants to help, so that they could find a way to make it work (I am getting desperate) of course I would have to explain how it works and what I want saved but should only take a lengthy post (or I could finish the manual, either way) as long as this problem gets fixed somehow).
Oluseyi
Oluseyi
Quote:
Original post by Munchkin9
Is there a way of doing this outside of the class.

If you can obtain all of the properties of the objects you're using, then it's basically the same.
Munchkin9
Munchkin9
Please I know it might seem weird and it is potentially asking a lot of time, I don't need (or want) theoretics. I need something clean and easy show me examples, look at the game, anything that works fast and efficiently (I would of course mention any help I get as credits) but need something fast.

Sorry if I seem a little rude but I am tired and don't feel too good. So something fast, the faster the better.
Kylotan
Kylotan
How on earth is anybody going to magic up examples that work exactly the way you want with code they've never seen? People are willing to help you learn to do what you need to do, but generally they're not willing to do your work for you.

Probably the quickest way to do what you want is to pickle everything that can be pickled, and for those things that can't, make copies of them that can be pickled, and work out a way to transform them back on the other side. The method you tried with replacing the image was probably along the right lines, but if you expect any help with that, you must post the exact error message you get, and show the code that causes it.
Oluseyi
Oluseyi
Quote:
Original post by Munchkin9
Please I know it might seem weird and it is potentially asking a lot of time, I don't need (or want) theoretics. I need something clean and easy show me examples, look at the game, anything that works fast and efficiently (I would of course mention any help I get as credits) but need something fast.

I need a BMW 5-series and a blowjob. Amazing how we don't always get the things we "need."

Stop being lazy. Learn something for yourself.
mikeman
mikeman
Yeah, I think you can understand that we can't give you the exact solution you need, because we don't know the code you're using, and even if we did, I don't think many would have the time to do the work for you. Have you read all the material this thread has provided you? Serialization is a pretty complex issue. You must try to read them and understand, then you can solve the problem yourself.
Munchkin9
Munchkin9
I am sorry for what I said last post. I was having a bad day and did not notice that much showed on the post. The thing is that I need that system working soon as I am running behind on schedule (not that I exactly have a schedule since I am doing this for my self and some friends, but that is the thing I want it done before one of my friends gets back (or at least a playable version)). And the problem is, I have read the posts, but I don't understand even the things you are saying outside of the code boxes (btw, how do you do those). My knowledge of this stuff is completely non existent.

Know back on topic the exact error I got and the code I wrote is here:

The error:
Traceback (most recent call last):File "editor.py", line 1048, in (module)ship_image.image = NoneFile "C:\hp\bin\Python\lib\site-package\livewires\beginners.py", line 533 in set_imageself._replace(new_image)File "C:\hp\bin\Python\lib\site-package\livewires\beginners.py", line 408, _replaceself._rect = self._surface.get_rect()AttributeError: 'NoneType' object has no attribute 'get_rect'


Now the code inside the 'editor':
if ship_image != False:            ship_image.image = None        player.image = None        for f in floors:            f.image = None        for d in doors:            d.image = None        for w in walls:            w.image = None        for w in wires:            w.image = None        for s in splitters:            s.image = None        for o in outlets:            o.image = None        for c in consoles:            c.image = None        for b in buttons:            b.image = None        for p in powersources:            p.image = None                #the data        data_floors = floors        data_doors = doors        data_walls = walls        data_wires = wires        data_splitters = splitters        data_outlets = outlets        data_consoles = consoles        data_buttons = buttons        data_powersources = powersources        data_player = player                #output        output = open('ship1.pkl', 'wb')                #dumping(saving)        pickle.dump(data_floors, output)        pickle.dump(data_doors, output)        pickle.dump(data_walls, output)        pickle.dump(data_wires, output)        pickle.dump(data_splitters, output)        pickle.dump(data_outlets, output)        pickle.dump(data_consoles, output)        pickle.dump(data_buttons, output)        pickle.dump(data_powersources, output)        pickle.dump(data_player, output)                #done        output.close()


and the code in the engine 'livewires.beginners' (if any one has used it before) around line 533:

    def set_image(self, new_image):         # if image is a string, then load corresponding file to produce image object        if type(new_image) == str:            self._image_name = new_image            new_image = Image.load(self._image_name)        else:            self._image_name = ""              self._orig_surface = new_image         if self._angle != 0:             self._rotate()         else:             self._replace(new_image)     image = property(get_image, set_image)


and around 408:

    def _replace(self, new_surface):         x, y = self.position         self._surface = new_surface         self._rect = self._surface.get_rect()         self.position = (x, y)


I hope this helps you help me. And again I am sorry about my last post. I will defend my self by saying that I did not (would never have want) to have the work done for me. But something elementary that worked for all scenarios (however, if this does not exist, my mistake).

Edit: Added [source] and [code] tags for readability. - Oluseyi

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.