controls/IDBrowser.py
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Wed, 13 Mar 2019 11:47:03 +0300
changeset 2537 eb4a4cc41914
parent 2492 7dd551ac2fa0
child 3750 f62625418bff
permissions -rw-r--r--
Fix various pylint and pep8 errors

Check basic code-style problems for PEP-8
pep8 version: 2.4.0
./connectors/PYRO/__init__.py:57:43: E261 at least two spaces before inline comment
./connectors/SchemeEditor.py:29:21: E128 continuation line under-indented for visual indent
./controls/IDBrowser.py:101:23: E127 continuation line over-indented for visual indent
./controls/IDBrowser.py:102:23: E127 continuation line over-indented for visual indent

Check for problems using pylint ...
No config file found, using default configuration
pylint 1.9.4,
astroid 1.6.5
Python 2.7.16rc1 (default, Feb 18 2019, 11:05:09)
[GCC 8.2.0]
Use multiple threads for pylint
Using config file /home/developer/WorkData/PLC/beremiz/beremiz/.pylint
************* Module connectors.PYRO_dialog
connectors/PYRO_dialog.py:9: [W0611(unused-import), ] Unused import wx
************* Module connectors
connectors/__init__.py:32: [W1652(deprecated-types-field), ] Accessing a deprecated fields on the types module
connectors/__init__.py:32: [C0411(wrong-import-order), ] standard import "from types import ClassType" should be placed before "from connectors.ConnectorBase import ConnectorBase"
************* Module connectors.PYRO.PSK_Adapter
connectors/PYRO/PSK_Adapter.py:7: [C0411(wrong-import-order), ] standard import "import ssl" should be placed before "import sslpsk"
************* Module connectors.SchemeEditor
connectors/SchemeEditor.py:29: [C0330(bad-continuation), ] Wrong continued indentation (add 1 space).
wx.ALIGN_CENTER_VERTICAL),
^|
connectors/SchemeEditor.py:42: [W0631(undefined-loop-variable), SchemeEditor.__init__] Using possibly undefined loop variable 'tag'
************* Module runtime.WampClient
runtime/WampClient.py:138: [W1612(unicode-builtin), WampSession.onJoin] unicode built-in referenced
runtime/WampClient.py:154: [W1612(unicode-builtin), WampSession.publishWithOwnID] unicode built-in referenced
runtime/WampClient.py:346: [W1612(unicode-builtin), PublishEvent] unicode built-in referenced
runtime/WampClient.py:351: [W1612(unicode-builtin), PublishEventWithOwnID] unicode built-in referenced
runtime/WampClient.py:31: [W0611(unused-import), ] Unused str imported from builtins as text
************* Module runtime.PLCObject
runtime/PLCObject.py:35: [W1648(bad-python3-import), ] Module moved in Python 3
runtime/PLCObject.py:35: [C0411(wrong-import-order), ] standard import "import md5" should be placed before "from six.moves import xrange"
runtime/PLCObject.py:36: [C0411(wrong-import-order), ] standard import "from tempfile import mkstemp" should be placed before "from six.moves import xrange"
runtime/PLCObject.py:37: [C0411(wrong-import-order), ] standard import "import shutil" should be placed before "from six.moves import xrange"
runtime/PLCObject.py:38: [C0411(wrong-import-order), ] standard import "from functools import wraps, partial" should be placed before "from six.moves import xrange"
************* Module runtime.Worker
runtime/Worker.py:12: [W1648(bad-python3-import), ] Module moved in Python 3
************* Module runtime.spawn_subprocess
runtime/spawn_subprocess.py:125: [C0325(superfluous-parens), ] Unnecessary parens after 'print' keyword
runtime/spawn_subprocess.py:130: [C0325(superfluous-parens), ] Unnecessary parens after 'print' keyword
runtime/spawn_subprocess.py:125: [E1601(print-statement), ] print statement used
runtime/spawn_subprocess.py:130: [E1601(print-statement), ] print statement used
************* Module controls.IDBrowser
controls/IDBrowser.py:101: [C0330(bad-continuation), ] Wrong continued indentation (remove 5 spaces).
if self.isManager
| ^
controls/IDBrowser.py:102: [C0330(bad-continuation), ] Wrong continued indentation (remove 5 spaces).
else dv.DATAVIEW_CELL_INERT),
| ^
************* Module Beremiz_service
Beremiz_service.py:34: [W0611(unused-import), ] Unused import __builtin__
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# See COPYING file for copyrights details.

from __future__ import absolute_import
import wx
import wx.dataview as dv
import PSKManagement as PSK
from PSKManagement import *
from dialogs.IDMergeDialog import IDMergeDialog


class IDBrowserModel(dv.PyDataViewIndexListModel):
    def __init__(self, project_path, columncount):
        self.project_path = project_path
        self.columncount = columncount
        self.data = PSK.GetData(project_path)
        dv.PyDataViewIndexListModel.__init__(self, len(self.data))

    def _saveData(self):
        PSK.SaveData(self.project_path, self.data)

    def GetColumnType(self, col):
        return "string"

    def GetValueByRow(self, row, col):
        return self.data[row][col]

    def SetValueByRow(self, value, row, col):
        self.data[row][col] = value
        self._saveData()

    def GetColumnCount(self):
        return len(self.data[0]) if self.data else self.columncount

    def GetCount(self):
        return len(self.data)

    def Compare(self, item1, item2, col, ascending):
        if not ascending:  # swap sort order?
            item2, item1 = item1, item2
        row1 = self.GetRow(item1)
        row2 = self.GetRow(item2)
        if col == 0:
            return cmp(int(self.data[row1][col]), int(self.data[row2][col]))
        else:
            return cmp(self.data[row1][col], self.data[row2][col])

    def DeleteRows(self, rows):
        rows = list(rows)
        rows.sort(reverse=True)

        for row in rows:
            PSK.DeleteID(self.project_path, self.data[row][COL_ID])
            del self.data[row]
            self.RowDeleted(row)
        self._saveData()

    def AddRow(self, value):
        self.data.append(value)
        self.RowAppended()
        self._saveData()

    def Import(self, filepath, sircb):
        data = PSK.ImportIDs(self.project_path, filepath, sircb)
        if data is not None:
            self.data = data
            self.Reset(len(self.data))

    def Export(self, filepath):
        PSK.ExportIDs(self.project_path, filepath)


colflags = dv.DATAVIEW_COL_RESIZABLE | dv.DATAVIEW_COL_SORTABLE


class IDBrowser(wx.Panel):
    def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
        big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None
        wx.Panel.__init__(self, parent, -1, size=(800 if big else 450,
                                                  600 if big else 200))

        self.SelectURICallBack = SelectURICallBack
        self.SelectIDCallBack = SelectIDCallBack

        dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
        if self.isManager:
            # no multiple selection in selector mode
            dvStyle |= dv.DV_MULTIPLE
        self.dvc = dv.DataViewCtrl(self, style=dvStyle)

        def args(*a, **k):
            return (a, k)

        ColumnsDesc = [
            args(_("ID"), COL_ID, width=70),
            args(_("Last URI"), COL_URI, width=300 if big else 80),
            args(_("Description"), COL_DESC, width=300 if big else 200,
                 mode=dv.DATAVIEW_CELL_EDITABLE
                 if self.isManager
                 else dv.DATAVIEW_CELL_INERT),
            args(_("Last connection"), COL_LAST, width=120),
        ]

        self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc))
        self.dvc.AssociateModel(self.model)

        col_list = []
        for a, k in ColumnsDesc:
            col_list.append(
                self.dvc.AppendTextColumn(*a, **dict(k, flags=colflags)))
        col_list[COL_LAST].SetSortOrder(False)

        # TODO : sort by last bvisit by default

        self.Sizer = wx.BoxSizer(wx.VERTICAL)
        self.Sizer.Add(self.dvc, 1, wx.EXPAND)

        btnbox = wx.BoxSizer(wx.HORIZONTAL)
        if self.isManager:

            # deletion of secret and metadata
            deleteButton = wx.Button(self, label=_("Delete ID"))
            self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
            btnbox.Add(deleteButton, 0, wx.LEFT | wx.RIGHT, 5)

            # export all
            exportButton = wx.Button(self, label=_("Export all"))
            self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
            btnbox.Add(exportButton, 0, wx.LEFT | wx.RIGHT, 5)

            # import with a merge -> duplicates are asked for
            importButton = wx.Button(self, label=_("Import"))
            self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
            btnbox.Add(importButton, 0, wx.LEFT | wx.RIGHT, 5)

        else:
            # selector mode
            self.useURIButton = wx.Button(self, label=_("Use last URI"))
            self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton)
            self.useURIButton.Disable()
            btnbox.Add(self.useURIButton, 0, wx.LEFT | wx.RIGHT, 5)

        self.Sizer.Add(btnbox, 0, wx.TOP | wx.BOTTOM, 5)
        self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc)

    def OnDeleteButton(self, evt):
        items = self.dvc.GetSelections()
        rows = [self.model.GetRow(item) for item in items]

        # Ask if user really wants to delete
        if wx.MessageBox(_('Are you sure to delete selected IDs?'),
                         _('Delete IDs'),
                         wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
            return

        self.model.DeleteRows(rows)

    def OnSelectionChanged(self, evt):
        if not self.isManager:
            items = self.dvc.GetSelections()
            somethingSelected = len(items) > 0
            self.useURIButton.Enable(somethingSelected)
            if somethingSelected:
                row = self.model.GetRow(items[0])
                ID = self.model.GetValueByRow(row, COL_ID)
                self.SelectIDCallBack(ID)

    def OnUseURIButton(self, evt):
        row = self.model.GetRow(self.dvc.GetSelections()[0])
        URI = self.model.GetValueByRow(row, COL_URI)
        if URI:
            self.SelectURICallBack(URI)

    def OnExportButton(self, evt):
        dialog = wx.FileDialog(self, _("Choose a file"),
                               wildcard=_("PSK ZIP files (*.zip)|*.zip"),
                               style=wx.SAVE | wx.OVERWRITE_PROMPT)
        if dialog.ShowModal() == wx.ID_OK:
            self.model.Export(dialog.GetPath())

    # pylint: disable=unused-variable
    def ShouldIReplaceCallback(self, existing, replacement):
        ID, URI, DESC, LAST = existing
        _ID, _URI, _DESC, _LAST = replacement
        dlg = IDMergeDialog(
            self,
            _("Import IDs"),
            (_("Replace information for ID {ID} ?") + "\n\n" +
             _("Existing:") + "\n    " +
             _("Description:") + " {DESC}\n    " +
             _("Last known URI:") + " {URI}\n    " +
             _("Last connection:") + " {LAST}\n\n" +
             _("Replacement:") + "\n    " +
             _("Description:") + " {_DESC}\n    " +
             _("Last known URI:") + " {_URI}\n    " +
             _("Last connection:") + " {_LAST}\n").format(**locals()),
            _("Do the same for following IDs"),
            [_("Replace"), _("Keep"), _("Cancel")])

        answer = dlg.ShowModal()  # return value ignored as we have "Ok" only anyhow
        if answer == wx.ID_CANCEL:
            return CANCEL

        if dlg.OptionChecked():
            if answer == wx.ID_YES:
                return REPLACE_ALL
            return KEEP_ALL
        else:
            if answer == wx.ID_YES:
                return REPLACE
            return KEEP

    def OnImportButton(self, evt):
        dialog = wx.FileDialog(self, _("Choose a file"),
                               wildcard=_("PSK ZIP files (*.zip)|*.zip"),
                               style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
        if dialog.ShowModal() == wx.ID_OK:
            self.model.Import(dialog.GetPath(),
                              self.ShouldIReplaceCallback)