dicomsort/dicomsort

View on GitHub
dicomsort/gui/preferences.py

Summary

Maintainability
B
4 hrs
Test Coverage
A
93%
import wx
import wx.py
import configobj

from dicomsort import config
from dicomsort.gui.anonymizer import AnonymizeList


class PreferencePanel(wx.Panel):

    def __init__(self, parent, shortname, title, config):
        wx.Panel.__init__(self, parent, -1)
        self.shortname = shortname
        self.config = config
        self.title = title

    def GetState(self):
        raise TypeError('Abstract Method!')

    def SaveState(self, *evnt):
        # Load since last saved version
        tmpconfig = configobj.ConfigObj(self.config.filename)
        self.StoreState(config=tmpconfig)

        # Write just the change from this panel
        tmpconfig.write()

    def UpdateFromConfig(self, config=None):
        raise TypeError('Abstract Method!')

    def RevertState(self, *evnt):
        # Load a temporary copy
        tmpconfig = configobj.ConfigObj(self.config.filename)
        self.config[self.shortname] = tmpconfig[self.shortname]
        # TODO: See if all of these configobjs need to be closed()

    def StoreState(self, config=None):
        if config is None:
            config = self.config

        config[self.shortname] = self.GetState()


class MiscPanel(PreferencePanel):
    def __init__(self, parent, config):
        super(MiscPanel, self).__init__(parent, 'Miscpanel',
                                        'Miscellaneous', config)
        self.create()

    def GetState(self):
        return {
            'KeepSeries': str(self.keepSeries.IsChecked()),
            'SeriesFirst': str(self.seriesFirst.IsChecked()),
            'KeepOriginal': str(self.keepOriginal.IsChecked())
        }

    def RevertState(self, *evnt):
        super(MiscPanel, self).RevertState()
        savedConfig = configobj.ConfigObj(self.config.filename)
        self.UpdateFromConfig(savedConfig)

    def UpdateFromConfig(self, config):
        tmp = config[self.shortname]
        if 'KeepSeries' not in tmp:
            tmp['KeepSeries'] = 'True'

        if 'SeriesFirst' not in tmp:
            tmp['SeriesFirst'] = 'False'

        if 'KeepOriginal' not in tmp:
            tmp['KeepOriginal'] = 'True'

        if tmp['KeepSeries']:
            self.keepSeries.SetValue(eval(tmp['KeepSeries']))

        if tmp['SeriesFirst']:
            self.seriesFirst.SetValue(eval(tmp['SeriesFirst']))

        if tmp['KeepOriginal']:
            self.keepOriginal.SetValue(eval(tmp['KeepOriginal']))

    def create(self):
        vbox = wx.BoxSizer(wx.VERTICAL)

        self.keepSeries = wx.CheckBox(
            self, -1,
            'Automatically include SeriesDescription'
        )

        # Vertical spacing
        vbox.Add((30, 40), 0, wx.ALL | 30)
        vbox.Add(self.keepSeries, 0, wx.ALIGN_LEFT | wx.ALL | 15)

        self.seriesFirst = wx.CheckBox(self, -1, "Series Name First")

        # Vertical spacing
        vbox.Add((30, 20), 0, wx.ALL | 30)
        vbox.Add(self.seriesFirst, 0, wx.ALIGN_LEFT | wx.ALL | 15)

        self.keepOriginal = wx.CheckBox(self, -1, "Keep Original Files")

        vbox.Add((30, 20), 0, wx.ALL | 30)
        vbox.Add(self.keepOriginal, 0, wx.ALIGN_LEFT | wx.ALL | 15)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.store = wx.Button(self, -1, "Set as Default", size=(120, -1))
        self.revert = wx.Button(self, -1, "Restore Default", size=(120, -1))
        self.Bind(wx.EVT_BUTTON, self.RevertState, self.revert)
        self.Bind(wx.EVT_BUTTON, self.SaveState, self.store)

        hbox.Add(self.store, 0, wx.ALL, 5)
        hbox.Add(self.revert, 0, wx.ALL, 5)

        vbox.Add(hbox, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, 25)

        self.SetSizer(vbox)


class FileNamePanel(PreferencePanel):

    def __init__(self, parent, config):
        PreferencePanel.__init__(self, parent, 'FilenameFormat',
                                 'Filename Format', config)

        self.create()

    def create(self):
        vbox = wx.BoxSizer(wx.VERTICAL)

        boxChoices = [
            'Image (0001)',
            'Keep Original Filename',
            'Custom Filename Format:'
        ]

        self.radioBox = wx.RadioBox(self, -1,
                                    style=wx.VERTICAL | wx.BORDER_NONE,
                                    choices=boxChoices)

        self.Bind(wx.EVT_RADIOBOX, self.OnChange, self.radioBox)

        vbox.Add(self.radioBox, 0, wx.TOP | wx.LEFT, 15)

        self.custom = wx.TextCtrl(self, -1, '',
                                  size=(300, -1))
        vbox.Add(self.custom, 0, wx.LEFT, 50)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.store = wx.Button(self, -1, "Set as Default", size=(120, -1))
        self.revert = wx.Button(self, -1, "Restore Default", size=(120, -1))
        self.Bind(wx.EVT_BUTTON, self.RevertState, self.revert)
        self.Bind(wx.EVT_BUTTON, self.SaveState, self.store)

        hbox.Add(self.store, 0, wx.ALL, 5)
        hbox.Add(self.revert, 0, wx.ALL, 5)

        vbox.Add(hbox, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, 25)

        self.SetSizer(vbox)

    def RevertState(self, *evnt):
        super(FileNamePanel, self).RevertState()
        savedConfig = configobj.ConfigObj(self.config.filename)
        self.UpdateFromConfig(savedConfig)

    def OnChange(self, *evnt):
        index = self.radioBox.GetSelection()

        # TODO: Make this more robust in the future
        if index != 2:
            self.custom.Disable()
        else:
            self.custom.Enable()

    def UpdateFromConfig(self, config):

        config.interpolation = False

        data = config[self.shortname]

        if 'Selection' in data:
            self.radioBox.SetSelection(int(data['Selection']))
        else:
            self.radioBox.SetSelection(0)

        self.custom.SetValue(data['FilenameString'])

        self.OnChange()

    def GetState(self):
        return {
            'FilenameString': self.custom.GetValue(),
            'Selection': self.radioBox.GetSelection()
        }


class PreferenceDlg(wx.Dialog):

    def __init__(self, *args, **kwargs):
        if 'config' in kwargs:
            self.config = kwargs.pop('config')
        else:
            self.config = configobj.ConfigObj(config.configuration_file)

        wx.Dialog.__init__(self, *args, **kwargs)

        # We want to make this a dict instead of list
        self.pages = dict()
        self.create()

        # Initialize from Config on Create
        self.UpdateFromConfig(self.config)

    def UpdateFromConfig(self, config=None):
        if config is None:
            config = self.config

        [val.UpdateFromConfig(config) for val in self.pages.values()]

    def Show(self, *args):
        # Call superclass constructor
        wx.Dialog.Show(self, *args)

    def create(self):
        self.nb = wx.Notebook(self)
        self.AddModule(AnonymousPanel)
        self.AddModule(FileNamePanel)
        self.AddModule(MiscPanel)

        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.nb, 1, wx.EXPAND)

        hbox = wx.BoxSizer(wx.HORIZONTAL)

        self.cancel = wx.Button(self, -1, 'Cancel')
        self.apply = wx.Button(self, -1, 'Apply')

        self.cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
        self.apply.Bind(wx.EVT_BUTTON, self.OnApply)

        hbox.Add(self.cancel, 0, 0, 10)
        hbox.Add(self.apply, 0, wx.LEFT, 10)

        vbox.Add(hbox, 0, wx.ALIGN_RIGHT | wx.TOP, 5)
        vbox.Add((10, 10), 0, wx.ALL | 30)

        self.SetSizer(vbox)

    def AddModule(self, panel):
        newPage = panel(self.nb, self.config)

        self.pages[newPage.shortname] = newPage
        self.nb.AddPage(newPage, newPage.title)

    def OnApply(self, *evnt):
        [val.StoreState() for val in self.pages.values()]

        # For now don't write this to the file because it's a local default
        self.Close()
        return self.config

    def OnCancel(self, *evnt):
        # Return configobj that we came in with
        self.Close()
        self.UpdateFromConfig(self.config)
        return self.config

    def ShowModal(self, *args):
        wx.Dialog.ShowModal(self, *args)
        return self.config


class AnonymousPanel(PreferencePanel):

    def __init__(self, parent, config):
        super(AnonymousPanel, self).__init__(parent, 'Anonymization',
                                             'Anonymizing Fields', config)

        self.create()

    def GetState(self):
        # Get all fields that are stored in config but not present in current
        defFields = self.config[self.shortname]['Fields']

        fields = list()
        # Keep only the empty ones
        for i, val in enumerate(self.anonList.FindStrings(defFields)):
            if val is None:
                fields.append(defFields[i])

        # Add to this list the newly checked ones
        fields.extend(self.anonList.GetCheckedStrings(0))

        return {
            'Fields': fields,
            'Replacements': self.anonList.GetReplacementDict()
        }

    def RevertState(self, *evnt):
        # Update self.config
        super(AnonymousPanel, self).RevertState()
        savedConfig = configobj.ConfigObj(self.config.filename)
        savedConfig.interpolation = False
        self.UpdateFromConfig(savedConfig)

    def SetDicomFields(self, values):
        self.anonList.SetStringItems(values)
        self.UpdateFromConfig(self.config)

    def UpdateFromConfig(self, config):
        data = config[self.shortname]

        # The fields that we care about are "Fields" and "Replacements"
        fields = data['Fields']
        self.anonList.UnCheckAll()
        self.anonList.CheckStrings(fields, col=0)
        self.anonList.ClearColumn(1)

        # Now put in substitutes
        self.anonList.SetReplacementDict(data['Replacements'])

    def create(self):
        vbox = wx.BoxSizer(wx.VERTICAL)

        title = wx.StaticText(self, -1, "Fields to Omit")
        vbox.Add(title, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.BOTTOM, 10)

        self.anonList = AnonymizeList(self)

        hbox = wx.BoxSizer(wx.HORIZONTAL)

        self.store = wx.Button(self, -1, "Set as Default", size=(120, -1))
        self.revert = wx.Button(self, -1, "Restore Default", size=(120, -1))
        self.revert.Bind(wx.EVT_BUTTON, self.RevertState)
        self.store.Bind(wx.EVT_BUTTON, self.SaveState)

        hbox.Add(self.store, 0, wx.TOP | wx.LEFT, 10)
        hbox.Add(self.revert, 0, wx.TOP | wx.LEFT, 10)

        vbox.Add(self.anonList, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 10)
        vbox.Add(hbox, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM, 15)
        self.SetSizer(vbox)