Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Malcolm XML
Aug 8, 2009

I always knew it would end like this.
Closures are a poor man's objects. Objects are a poor man's closures.

Adbot
ADBOT LOVES YOU

Dren
Jan 5, 2001

Pillbug

Dominoes posted:

For example, in an algorithmic stock trader I'm working on, I have a variable called transactions, which is a list of objects that describe previous stock transactions. This variable is referenced many times, in multiple functions. While I could keep it alive in the main run loop and pass it around as an argument, doing so would be somewhat confusing and awkward. It would lead to cases where functions with a logical parameter list take transactions as additional argument. Ie a function that places orders seems to just need two arguments: The stock symbol and quantity. Instead, it, like many other functions, requires transactions as well, since it calls a save function that requires the object list to be saved to disk after placing the order. It feels like a violation of DRY, and makes changing things more confusing.

Do you ever place an order and not want to log it in transactions? If the answer is no you probably want an object called TransactionManager or something that is one time initialized with historical data and from then on all order operations are run through its methods. e.g.
Python code:
class TransactionManager(object):
  def __init__(self, filename=None):
    self._transactions = []
    if filename is not None:
      # some code to load from file or db or whatever
      ...
  
  def save(self, filename):
    ...

  def place_an_order(self, symbol, amount):
    ...

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Dominoes posted:

For example, in an algorithmic stock trader I'm working on, I have a variable called transactions, which is a list of objects that describe previous stock transactions. This variable is referenced many times, in multiple functions. While I could keep it alive in the main run loop and pass it around as an argument, doing so would be somewhat confusing and awkward. It would lead to cases where functions with a logical parameter list take transactions as additional argument. Ie a function that places orders seems to just need two arguments: The stock symbol and quantity. Instead, it, like many other functions, requires transactions as well, since it calls a save function that requires the object list to be saved to disk after placing the order. It feels like a violation of DRY, and makes changing things more confusing.

Well, what happens if you want to have multiple transaction queues, e.g. if you're trading on both the NYSE and the NASDAQ? You need to be able to say "please put your transactions on this queue" somehow. I don't see what's wrong with passing it as an argument.

Dren's solution using objects is also acceptable. In here, you marry together the idea of a transaction queue with the methods for manipulating and appending to it, and it comes with a bit of nice syntax. But you said you aren't comfortable with OOP, so you probably don't like that one.

(also I hope you aren't writing a stock trader for actual reals. You won't get anywhere with Python.)

Dominoes
Sep 20, 2007

Dren posted:

Do you ever place an order and not want to log it in transactions? If the answer is no you probably want an object called TransactionManager or something that is one time initialized with historical data and from then on all order operations are run through its methods. e.g.
Python code:
class TransactionManager(object):
  def __init__(self, filename=None):
    self._transactions = []
    if filename is not None:
      # some code to load from file or db or whatever
      ...
  
  def save(self, filename):
    ...

  def place_an_order(self, symbol, amount):
    ...
Sweet; I'll try to make something like that work.

Suspicious Dish posted:

Well, what happens if you want to have multiple transaction queues, e.g. if you're trading on both the NYSE and the NASDAQ? You need to be able to say "please put your transactions on this queue" somehow. I don't see what's wrong with passing it as an argument.

Dren's solution using objects is also acceptable. In here, you marry together the idea of a transaction queue with the methods for manipulating and appending to it, and it comes with a bit of nice syntax. But you said you aren't comfortable with OOP, so you probably don't like that one.
The transactions variable/objects I mentioned are more like an activity log than an order queue. It's currently set to run in a linear fashion, making a single trade at a time, and has a 30 second cooldown after each order to allow it to settle. There's nothing wrong with passing it as an argument; but I think the code's a bit easier to read and change without passing transactions around like a whore.

quote:

(also I hope you aren't writing a stock trader for actual reals. You won't get anywhere with Python.)
Why? I've had it cooking for the past few months. It doesn't rely on making quick trades. Here's most of the modules on Github. I may post the screening logic module later.

Dominoes fucked around with this message at 22:54 on Oct 26, 2013

Dren
Jan 5, 2001

Pillbug
If you go with an object like I suggested keep in mind that a key part of the design is that the transaction state associated with the manager can only ever be set/loaded via the manager's constructor. (There is no "load" method, only a "save" method). This keeps the object's users (probably just you but w/e) from trying to reuse an instance, not properly resetting all of the state associated w/ it, and getting into trouble. I have seen too many avoidable bugs caused by reused objects not properly reset and every time it disappoints me because unless there is some resource reason to reuse objects a new one could be made instead, which is simpler and far less error prone.

Masa
Jun 20, 2003
Generic Newbie
Dumb PyQt question:

I have two spinboxes set up to change the size of an image, and when the user edits one of the spinboxes, the other one is automatically adjusted based on the image's aspect ratio. Both spinboxes are connected to the method that handles that using the valueChanged signal, and the method uses setText to change the value in the other spinbox. The problem being that setText emits a valueChanged signal, which calls the method again, and it keeps repeating until one of the spinboxes is at its minimum or maximum value. I'm sure there's a simple way to get around this, but I can't find anything.

QuarkJets
Sep 8, 2008

Masa posted:

Dumb PyQt question:

I have two spinboxes set up to change the size of an image, and when the user edits one of the spinboxes, the other one is automatically adjusted based on the image's aspect ratio. Both spinboxes are connected to the method that handles that using the valueChanged signal, and the method uses setText to change the value in the other spinbox. The problem being that setText emits a valueChanged signal, which calls the method again, and it keeps repeating until one of the spinboxes is at its minimum or maximum value. I'm sure there's a simple way to get around this, but I can't find anything.

If you're checking to make sure that the aspect ratio is the same, then there shouldn't be any issue. For instance, if the aspect ratio is 1 and I decrease a 100x100 image to 99x100, then the aspect ratio is no longer correct (0.99); I change the other value accordingly and the image becomes 99x99. This will cause the valueChanged signal to get emitted again, but now the aspect ratio is 1 again, so nothing should happen.

Make sure that your aspect ratio checking logic is working correctly. On non-unity aspect ratios you're going to have to do more complicated things (what happens when your aspect ratio is 0.3 and you change a 3x10 image to 3x9? You'll never get back to 0.3 aspect ratio just by decreasing the other image dimension)

Dominoes
Sep 20, 2007

Masa posted:

Dumb PyQt question:

I have two spinboxes set up to change the size of an image, and when the user edits one of the spinboxes, the other one is automatically adjusted based on the image's aspect ratio. Both spinboxes are connected to the method that handles that using the valueChanged signal, and the method uses setText to change the value in the other spinbox. The problem being that setText emits a valueChanged signal, which calls the method again, and it keeps repeating until one of the spinboxes is at its minimum or maximum value. I'm sure there's a simple way to get around this, but I can't find anything.
Stack overflow thread about the issue, in C++. I'm assuming you mean setValue, since afaik qSpinBox can't use setText.

Here's a standalone program that does what you ask. The key bits are the blockSignals calls in the set_width and set_height methods.

Python code:
import sys

from PyQt5 import QtCore, QtWidgets


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.resize(200, 200)
        self.sb_width = QtWidgets.QSpinBox(self)
        self.sb_width.setGeometry(QtCore.QRect(40, 50, 75, 25))
        self.sb_width.setMaximum(10000)
        self.sb_height = QtWidgets.QSpinBox(self)
        self.sb_height.setGeometry(QtCore.QRect(40, 100, 75, 25))
        self.sb_height.setMaximum(10000)
        
        self.aspect_ratio = 4/3
        self.sb_width.setValue(640)
        self.set_width()
    
        self.sb_width.valueChanged.connect(self.set_width)
        self.sb_height.valueChanged.connect(self.set_height)        
        
    def set_width(self):
        width = self.sb_width.value()
        self.sb_height.blockSignals(True)
        self.sb_height.setValue(width // self.aspect_ratio)
        # Function call for changing image here
        self.sb_height.blockSignals(False)
        
    def set_height(self):
       height = self.sb_height.value()
       self.sb_width.blockSignals(True)
       self.sb_width.setValue(height * self.aspect_ratio)
       # Function call for changing image here
       self.sb_width.blockSignals(False)
    
    
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main = Main()
    main.show()
    sys.exit(app.exec_())
Unrelated: I've been trying to get my programs to work on Ubuntu linux. I have a program that generates and plays midi using pygame.midi. It doesn't work on Ubuntu. After researching this a bit, it seems that it's because linux generally doesn't come with a midi playback capability, and I couldn't find an obvious way to make it work. Is there a way around this, ie if I wanted to release a linux version?

Dominoes fucked around with this message at 14:38 on Oct 27, 2013

SurgicalOntologist
Jun 17, 2004

SurgicalOntologist posted:

I just finished the first working prototype of a small project; is it kosher to post [a link to] my code here looking for critiques? Or should that go in project.log?

Whatever, I'm just posting it here. If this should go somewhere else, please let me know:

https://bitbucket.org/hharrison/experimentator

This is my first Python project that is meant to be used by someone else, but it's still mostly a learning exercise so I would really appreciate any critique/feedback. I could also use some guidance writing tests for this. I don't even know where to start so maybe some reading on the topic would be most helpful.

JetsGuy
Sep 17, 2003

science + hockey
=
LASER SKATES

M31 posted:

It's not a strange rounding issue, it's a limitation of using floating point numbers. More specifically, 0.1 can't be represented as a floating point number, so you get something that is very close, but not actually 0.1.
code:
>>> (0.3 - 0.2) == 0.1
False
This is what the documentation of numpy.arrange says:

Thanks for the explanation.

I've really never run into a problem with using floating point steps in arange, so that's why I called it odd.

thorf
Jun 26, 2013

SurgicalOntologist posted:

This is my first Python project that is meant to be used by someone else, but it's still mostly a learning exercise so I would really appreciate any critique/feedback. I could also use some guidance writing tests for this. I don't even know where to start so maybe some reading on the topic would be most helpful.

A couple of minor remarks:

Line 74, change 'value' to '__getitem__'. It's confusing when
code:
len(object)
works but
code:
object[index]
doesn't.

Look at the abc module (http://docs.python.org/2/library/abc.html). Use it for the Experiment class and make people override run_trial

Line 148, I would express this as

code:
self.constants = filter(lambda v: isinstance(v, ConstantVariable), self.variables)
but that's more a personal preference.

SurgicalOntologist
Jun 17, 2004

thorf posted:

A couple of minor remarks:

Thanks, much appreciated! I think I understanding the abstract class thing, is it basically:
- let the user know that they shouldn't make instances of a class but instead subclass it (and enforce this?)
- enforce which methods must be overridden.

If that's correct, it seems to make sense to make Variable an abstract class too. My example still works, so things are good. I guess my next move is to make a more complex example that uses more features, so I can use that as a test also - at least until I figure out how unit testing is supposed to happen.

Randel Candygram
Jun 21, 2008

College Slice

Suspicious Dish posted:

(also I hope you aren't writing a stock trader for actual reals. You won't get anywhere with Python.)

This is very wrong. A number of very successful firms have used trading platforms written in Python.

low quality jpeg
Mar 10, 2012

Has anyone tried using pyfacebook or similar?

Say I wanted to make a markov chain generator which posts to Facebook statuses/comments, is that doable?

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb
Is there an easy way to crop and resize animated gifs in Python? I found images2gif but I'm wondering if there's something that's...a little better supported. Maybe I'm better off going with some external utility like ImageMagick?

ahmeni
May 1, 2005

It's one continuous form where hardware and software function in perfect unison, creating a new generation of iPhone that's better by any measure.
Grimey Drawer

low quality jpeg posted:

Has anyone tried using pyfacebook or similar?

Say I wanted to make a markov chain generator which posts to Facebook statuses/comments, is that doable?

I've never used it but the last time I tried Facebook integration it was basic OAuth token generation and was pretty easy to work with.

User-Friendly
Apr 27, 2008

Is There a God? (Pt. 9)
If I'm using downloaded Python on a Mac and not the Apple-supplied one, is installing modules as big of a pain in the rear end as it seems? I only really use it to automate work tasks and make dumb games (which modules like pygame or easygui would help with).

I've googled around a little, and it seems to involve downloading a bunch of things with a bunch of jargon I don't understand. I have zero background in computer science.

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

User-Friendly posted:

If I'm using downloaded Python on a Mac and not the Apple-supplied one, is installing modules as big of a pain in the rear end as it seems? I only really use it to automate work tasks and make dumb games (which modules like pygame or easygui would help with).

I've googled around a little, and it seems to involve downloading a bunch of things with a bunch of jargon I don't understand. I have zero background in computer science.

No, it's actually not that bad. Follow this guide. http://docs.python-guide.org/en/latest/starting/install/osx/

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

I decided to finally start using Python 3 where I can. The very first time I decide to use it on a project, I find out my favorite green threading library, eventlet, doesn't support it.

Any good alternatives for that?

OnceIWasAnOstrich
Jul 22, 2006

Thermopyle posted:

I decided to finally start using Python 3 where I can. The very first time I decide to use it on a project, I find out my favorite green threading library, eventlet, doesn't support it.

Any good alternatives for that?

Circuits?

I know gevent and concurence are also not ready for Python 3.

Every year I say I'll try to switch to Python 3, but then I hit yet another library that still doesn't have a good alternative.

SurgicalOntologist
Jun 17, 2004

Wall of text incoming:

So I decided my 'turn code for a single trial into code for an experiment by looping over combinations of independent variables' project would be a good opportunity to play with recursive generators and ChainMaps. So I'm in the process of rewriting it, and it's good fun.

The idea is that an experiment is defined by sections: Participants contain sessions, sessions contain blocks, and blocks contain trials (by default). An independent variable can live at any level of the hierarchy. It can change every trial (as in a fully randomized within-subjects experiment), every session (in a before-and-after experiment), every participant (in a between-subjects experiment), etc. Each section figures out which sections it contains and runs them.

Python code:
class Experiment(metaclass=collections.abc.ABCMeta):
    def __init__(self, variables, settings, hierarchy=('participant', 'session', 'block', 'trial'),
                 output_names=None, output_file=None):
        self.hierarchy = hierarchy
        self.variables = variables
        self.settings = settings
        self.output_names = output_names
        self.output_file = output_file

    def run(self):
        root_context = collections.ChainMap()
        results = pd.DataFrame.from_records(self.run_experiment_section(root_context, self.hierarchy),
                                            columns=self.output_names)
        if self.output_file:
            logging.debug('Saving results to {}...'.format(self.output_file))
            results.to_pickle(self.output_file)
        else:
            return results

    def run_section(self, parent_context, hierarchy):
        next_level = hierarchy[0]
        section_variables = self.variables.get(next_level, [])
        section_settings = self.settings.get(next_level, {'sort': None, 'repeats': 1})

        variable_idxs = itertools.product(*[range(len(v)) for v in section_variables])
        next_section_types = [{v.name: v.value(c[i]) for i, v in enumerate(section_variables)}
                              for c in variable_idxs]
        next_sections = section_sort(next_section_types, method=section_settings['sort'],
                                     n=section_settings['repeats'])

        for i, section in enumerate(next_sections):
            child_context = parent_context.new_child().update(section)
            child_context[next_level] = i
            logging.debug('Running {}'.format(child_context))
            if i > 0:
                self.inter_section(next_level, **child_context)
            if next_level == 'trial':
                child_context.update({v.name: v.value(0) for v in self.variables['other']})
                yield self.run_trial(**child_context)
            else:
                self.section_start(next_level, **child_context)
                yield from self.run_section(child_context, hierarchy[1:])
                self.section_end(next_level, **child_context)

    def section_start(self, section, **kwargs):
        pass

    def section_end(self, section, **kwargs):
        pass

    def inter_section(self, section, **kwargs):
        pass

    @collections.abc.abstractmethod
    def run_trial(self, **kwargs):
        pass
So I've managed to use yield from to get recursion, finally run the trial when the bottom of the hierarchy is reached, and run custom methods at the beginning, end, and in-between each section.

There's one more thing I can't figure out: Is it possible to break the recursive loop, save the state to disk, and re-enter it later? If session 0 is ending, the whole experiment needs to suspend and I need to later start again at session 1.

If I do
Python code:
if next_level == 'session' or next_level == 'participant:
# needs to be generalized to 'or next_level == any level above session'
    raise StopIteration()
right after calling self.section_end() this stops the execution. But how can I restart the whole generator tree in the same spot it left off? Is there a way to pickle the entire state of the interpreter?

E: To avoid having to mentally parse that middle part, next_sections is a list of dictionaries, each describing a section one level down (e.g., in a block, each dict contains the variables that need to be set for a trial).

SurgicalOntologist fucked around with this message at 22:50 on Oct 31, 2013

Dren
Jan 5, 2001

Pillbug
SurgicalOntologist, it seems like there's a decent amount of code you left out. There also aren't any examples of what the output is supposed to look like. Perhaps I'm thick but having all the code and having examples would help me. Alternatively, a narrower example (even if contrived) could work.

To answer your question the best I can, I know of no way to save the state of the interpreter in order to capture your place in the running of your recursion. I have two general suggestions for how to stop and resume your routine. The first is to alter your processing code so that it can accept a partially filled out root_context, skip the parts that have already been filled out, and start back where you left off. This approach has the disadvantage of needing to run through all of the things you've already processed upon resumption in order to skip them. The second is to ditch the actual recursion and do the recursion bookkeeping yourself by using a queue as the recursion stack. Then you'll have access to your recursion stack (the queue) when you want to stop execution and serialize your state. With access to all the state you need you can pick back up right where you left off.

Your code led me to learn about yield from and chainmap. I think I hate yield from but that's just because I hate recursion. And chainmap, I guess that's ok. Not sure I've ever needed it.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

So...config files and versioning.

Anyone have any favorite methods they use to track and migrate config files as their application evolves?

I'm thinking about stuff like User is using your application at version 1.0. You add new keys/values to your config files and restructure how it's laid out for version 2.0. User upgrades to version 2.0. How do you migrate?

Custom code to handle all the possible upgrade paths is hell, but I don't know of any other way. Something like Django's South for config files would be neat.

deedee megadoodoo
Sep 28, 2000
Two roads diverged in a wood, and I, I took the one to Flavortown, and that has made all the difference.


For our backend services we've recently started using zookeeper for all of our configs. When an application starts up it checks into zookeeper and checks out a properties file for it's specific version and environment. We never have to worry about what the upgrade path is like because all of the configs are kept in one place. It also makes managing configs super easy. We've got some custom tools that let us batch update and clone configs to new versions. It was a lot of work to setup, but worth it if your environment can support that.

Haystack
Jan 23, 2005





Thermopyle posted:

So...config files and versioning.

Anyone have any favorite methods they use to track and migrate config files as their application evolves?

I'm thinking about stuff like User is using your application at version 1.0. You add new keys/values to your config files and restructure how it's laid out for version 2.0. User upgrades to version 2.0. How do you migrate?

Custom code to handle all the possible upgrade paths is hell, but I don't know of any other way. Something like Django's South for config files would be neat.

So, basically you have a situation where your application's configuration format/schema is prone to change, and you'd like to provide automation for updating (and possibly downgrading) your third party users' config files to the corresponding version?

SurgicalOntologist
Jun 17, 2004

Dren posted:

SurgicalOntologist, it seems like there's a decent amount of code you left out. There also aren't any examples of what the output is supposed to look like. Perhaps I'm thick but having all the code and having examples would help me. Alternatively, a narrower example (even if contrived) could work.

To answer your question the best I can, I know of no way to save the state of the interpreter in order to capture your place in the running of your recursion. I have two general suggestions for how to stop and resume your routine. The first is to alter your processing code so that it can accept a partially filled out root_context, skip the parts that have already been filled out, and start back where you left off. This approach has the disadvantage of needing to run through all of the things you've already processed upon resumption in order to skip them. The second is to ditch the actual recursion and do the recursion bookkeeping yourself by using a queue as the recursion stack. Then you'll have access to your recursion stack (the queue) when you want to stop execution and serialize your state. With access to all the state you need you can pick back up right where you left off.

Your code led me to learn about yield from and chainmap. I think I hate yield from but that's just because I hate recursion. And chainmap, I guess that's ok. Not sure I've ever needed it.

Thanks for your help. Here's what I left out:
Python code:
import numpy as np
import pandas as pd
import itertools
import collections
import logging


def section_sort(array, method=None, n=1):
    if method == 'random':
        return np.random.permutation(n*array)
    #TODO: More sorts (e.g. counterbalance)
    elif isinstance(method, str):
        raise ValueError('Unrecognized sort method {}.'.format(method))
    elif not method:
        return n*array
    else:
        return n * np.array(array)[method]


class Variable(metaclass=collections.abc.ABCMeta):
    def __init__(self, name):
        logging.debug('Creating variable {} of type {}.'.format(name, type(self)))
        self.name = name

    def __str__(self):
        return self.name

    @collections.abc.abstractmethod
    def value(self, *args, **kwargs):
        return None


class ConstantVariable(Variable):
    def __init__(self, name, value):
        super(ConstantVariable, self).__init__(name)
        self._value = value

    def value(self, idx):
        return self._value


class IndependentVariable(Variable):
    """
    Additional args: levels (list of values for the IV)
    Additional kwargs: design (between or within), change_by (trial, block, session, participant)
        design='between' is equivalent to change_by='participant'
    """
    def __init__(self, name, levels, design='within', change_by='trial'):
        super(IndependentVariable, self).__init__(name)
        self.levels = levels
        self.design = design
        if self.design == 'between':
            self.change_by = 'participant'
        elif self.design == 'within' and change_by == 'participant':
            raise ValueError('Cannot have a within-subjects IV change by participant.')
        else:
            self.change_by = change_by

    def value(self, idx, *args, **kwargs):
        return self.levels[idx]

    def __len__(self):
        return len(self.levels)

    def __getitem__(self, item):
        return self.levels[item]


class CustomVariable(Variable):
    def __init__(self, name, func):
        super(CustomVariable, self).__init__(name)
        self.func = func

    def value(self, idx):
        return self.func()


class RandomVariable(CustomVariable):
    def __init__(self, name, lower, upper):
        super(RandomVariable, self).__init__(name, lambda: (upper - lower)*np.random.random() + lower)
I'm not sure I understand the second option you mention. Do you mean this: perform the whole recursion, but instead of actually calling run_trial each time, just record the inputs to run_trial. Run through the whole thing and save them all collapsed into a flat list. Then, in a separate method, run through only a select portion of the list. Is that what you mean by queue, or is there a specific programming concept there that I'm not familiar with?

E: anyone know why the Python code formatting didn't work on my post above but works here? It looks so ugly without it...

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

HatfulOfHollow posted:

For our backend services we've recently started using zookeeper for all of our configs. When an application starts up it checks into zookeeper and checks out a properties file for it's specific version and environment. We never have to worry about what the upgrade path is like because all of the configs are kept in one place. It also makes managing configs super easy. We've got some custom tools that let us batch update and clone configs to new versions. It was a lot of work to setup, but worth it if your environment can support that.

zookeeper seems pretty cool, but I'm afraid it's way overkill for me.


Haystack posted:

So, basically you have a situation where your application's configuration format/schema is prone to change, and you'd like to provide automation for updating (and possibly downgrading) your third party users' config files to the corresponding version?

Yeah, that's basically it.

So, say the application has a config.ini that looks like:

code:
[section1]
foo=bar
buzz=baz
a_key=The user input this string which overrides the default value.
In version 2 of the application the config file has changed:
code:
[section1]
foo=bar
buzz=butts

[section2]
a_key_with_a_better_name=We should transfer over the users previously configured value for `a_key` to this new place.
Currently I just have code that manages this upgrade process, but that's fragile and tedious.

Mainly just looking for strategies or tools or config file formats or any ideas really.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
I doubt there's any sort of generalized schema migration system that supports arbitrary sorts of config files, because it'd have to be so general that it doesn't actually do much of anything. Assuming you can't just use a config format which already has schema migration tools, I don't think there's really any better answer than creating one.

redleader
Aug 18, 2005

Engage according to operational parameters
You could store config settings in an XML file and use XSLT to convert between versions. I don't know if I would recommend this though.

Dren
Jan 5, 2001

Pillbug
1. Put Config settings in flat file w/ version field (ini file, json, not XML)
2. Write module that converts from old settings to next version up, only ever allow latest version of config to run (users must convert their configs when they upgrade)
3. Profit

Buller
Nov 6, 2010
Anyone know a good way to teach yourself how to do GUI? I tried googling around a bit but theres a lack of really explaining how to set it up.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
GUI is not a thing Python is good at.

There are multiple solutions and they're all quite bad.

Dominoes
Sep 20, 2007

Buller posted:

Anyone know a good way to teach yourself how to do GUI? I tried googling around a bit but theres a lack of really explaining how to set it up.
Download PyQt or PySide. Look at the examples folder - use the examples as templates, and look up things on the Qt documentation website.

So, no. If you want to dive into Qt and have questions, ask here.

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Does anyone have experience getting wxpython to run on OS X? I'm trying to use RIDE (Robot Framework IDE) on OS X, and it depends on wxpython. However, I keep getting segfaults :(

More info at https://groups.google.com/forum/#!topic/robotframework-users/jNUj1ytQXK0

QuarkJets
Sep 8, 2008

Buller posted:

Anyone know a good way to teach yourself how to do GUI? I tried googling around a bit but theres a lack of really explaining how to set it up.

PyQt and PySide have a wealth of resources on the internet and in published materials that are aimed at GUI design, and they're both based on Qt, which is widely recognized as a good GUI-building framework, so I don't know what the gently caress Suspicious Dish is talking about

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Buller posted:

Anyone know a good way to teach yourself how to do GUI? I tried googling around a bit but theres a lack of really explaining how to set it up.

In my experience, tkinter is the easiest Python GUI framework for just hopping in and getting your hands dirty. Its API and documentation are pretty awful, and it's generally a pain to use, but it's built-in and doing things is generally fairly simple.

Keep in mind, the first few GUIs you write will be really horrible. The next few might be horrible too.

Pollyanna
Mar 5, 2005

Milk's on them.


All GUIs are horrible. Some are just less horrible than others.

Dominoes
Sep 20, 2007

Pollyanna posted:

All GUIs are horrible. Some are just less horrible than others.
Go on.

Pollyanna
Mar 5, 2005

Milk's on them.



I just hate everything :v:

Adbot
ADBOT LOVES YOU

Lurchington
Jan 2, 2003

Forums Dragoon

I suggest this in this thread every 40 pages or so: use python with a thin server like cherrypy or flask, make a user's web-browser the way to interact. Templating libraries like Jinja write out html and use javascript where appropriate, hitting methods on the server to do the filesystem things.

This is how both http://sabnzbd.org/ and http://sickbeard.com/ do it and they're pretty sweet and usable apps. It's also a lot easier to write automated tests since testing webpages is a bit more developed problem space than native apps in the free/open source only world

  • Locked thread