def drawGUI(self): form_layout = QFormLayout(self) form_layout.setFormAlignment(Qt.AlignLeft) title = QLabel(tr("<H1>DHCP Configuration</H1>")) form_layout.addRow(title) self.enable = QCheckBox(tr("Enable DHCP Server on configured LANs")) form_layout.addRow(self.enable) self.main_window.writeAccessNeeded(self.enable) self.add_button = AddButton() self.add_button.setText(tr("Add a range")) self.main_window.writeAccessNeeded(self.add_button) form_layout.addRow(self.add_button, QLabel()) if not self.main_window.readonly: self.connect(self.enable, SIGNAL('stateChanged(int)'), self.setDHCPEnabled) self.connect(self.add_button, SIGNAL('clicked()'), self.addRange) self.setLayout(form_layout)
def __init__(self, parent=None, isChild=False): QWidget.__init__(self, parent) QGridLayout(self) # FIXME: call self.setLayout(layout)? self._readonly = False self.up_down = QFrame() self.up = UpButton() self.down = DownButton() self.add = AddButton() self.rem = RemButton() self.listEditView = ListEditView(isChild, parent=self) self.connect(self.listEditView, SIGNAL('itemAdded'), SIGNAL('itemAdded')) self.connect(self.listEditView, SIGNAL('itemModified'), SIGNAL('itemModified')) self.connect(self.listEditView.model, SIGNAL('dataChanged(QModelIndex,QModelIndex)'), SIGNAL('dataChanged(QModelIndex,QModelIndex)')) self.connect(self.listEditView.model, SIGNAL('rowsRemoved(QModelIndex,int,int)'), SIGNAL('rowsRemoved(QModelIndex,int,int)')) self.connect(self.listEditView.model, SIGNAL('headerDataChanged(Qt::Orientation,int,int)'), SIGNAL('headerDataChanged(Qt::Orientation,int,int)')) self.connect(self.listEditView, SIGNAL('itemDeleted'), SIGNAL('itemDeleted')) self.connect(self.listEditView, SIGNAL('itemSorted'), SIGNAL('itemSorted')) self.connect(self.listEditView.horizontalHeader(), SIGNAL('sectionClicked(int)'), SIGNAL('sectionClicked(int)')) self.connect(self.listEditView, SIGNAL('clicked(QModelIndex)'), SIGNAL('clicked(QModelIndex)')) self.layout().addWidget(self.listEditView, 0, 0) self.buildUpDown() if not isChild: self.buildAddRem() self.setDisplayUpDown(False) self.setReadOnly(False)
class ListEdit(QWidget): def __init__(self, parent=None, isChild=False): QWidget.__init__(self, parent) QGridLayout(self) # FIXME: call self.setLayout(layout)? self._readonly = False self.up_down = QFrame() self.up = UpButton() self.down = DownButton() self.add = AddButton() self.rem = RemButton() self.listEditView = ListEditView(isChild, parent=self) self.connect(self.listEditView, SIGNAL('itemAdded'), SIGNAL('itemAdded')) self.connect(self.listEditView, SIGNAL('itemModified'), SIGNAL('itemModified')) self.connect(self.listEditView.model, SIGNAL('dataChanged(QModelIndex,QModelIndex)'), SIGNAL('dataChanged(QModelIndex,QModelIndex)')) self.connect(self.listEditView.model, SIGNAL('rowsRemoved(QModelIndex,int,int)'), SIGNAL('rowsRemoved(QModelIndex,int,int)')) self.connect(self.listEditView.model, SIGNAL('headerDataChanged(Qt::Orientation,int,int)'), SIGNAL('headerDataChanged(Qt::Orientation,int,int)')) self.connect(self.listEditView, SIGNAL('itemDeleted'), SIGNAL('itemDeleted')) self.connect(self.listEditView, SIGNAL('itemSorted'), SIGNAL('itemSorted')) self.connect(self.listEditView.horizontalHeader(), SIGNAL('sectionClicked(int)'), SIGNAL('sectionClicked(int)')) self.connect(self.listEditView, SIGNAL('clicked(QModelIndex)'), SIGNAL('clicked(QModelIndex)')) self.layout().addWidget(self.listEditView, 0, 0) self.buildUpDown() if not isChild: self.buildAddRem() self.setDisplayUpDown(False) self.setReadOnly(False) # Qt Properties ... def getReadOnly(self): return self._readonly def setReadOnly(self, readonly): self._readonly = readonly self.up.setEnabled(not self.readOnly) self.down.setEnabled(not self.readOnly) self.add.setEnabled(not self.readOnly) self.rem.setEnabled(not self.readOnly) self.listEditView.setReadOnly(self.readOnly) def resetReadOnly(self): self._readonly = False readOnly = pyqtProperty('bool', getReadOnly, setReadOnly, resetReadOnly) def getAcceptDrops(self): return self.listEditView.acceptDrops() def setAcceptDrops(self, mode): self.listEditView.setAcceptDrops(mode) flags = self.listEditView.model.getFlags() if mode: flags |= Qt.ItemIsDropEnabled else: flags &= ~Qt.ItemIsDropEnabled self.listEditView.model.setFlags(flags) def resetAcceptDrops(self): self.setAcceptDrops(False) acceptDrops = pyqtProperty('bool', getAcceptDrops, setAcceptDrops, resetAcceptDrops) def getDragDropMode(self): return self.listEditView.dragDropMode() Q_ENUMS('QAbstractItemView.DragDropMode') def setDragDropMode(self, mode): self.listEditView.setDragDropMode(mode) def resetDragDropMode(self): self.listEditView.setDragDropMode(QAbstractItemView.NoDragDrop) dragDropMode = pyqtProperty('QAbstractItemView::DragDropMode', getDragDropMode, setDragDropMode, resetDragDropMode) def getShowDropIndicator(self): return self.listEditView.showDropIndicator() def setShowDropIndicator(self, mode): self.listEditView.setShowDropIndicator(mode) def resetShowDropIndicator(self): self.listEditView.setShowDropIndicator(False) showDropIndicator = pyqtProperty('bool', getShowDropIndicator, setShowDropIndicator, resetShowDropIndicator) def getDisplayUpDown(self): return self.up_down.isVisible() def setDisplayUpDown(self, displayUpDown): self.up_down.setVisible(displayUpDown) def resetDisplayUpDown(self): self.up_down.setVisible(False) displayUpDown = pyqtProperty('bool', getDisplayUpDown, setDisplayUpDown, resetDisplayUpDown) def getEditBoxDescription(self): return self.listEditView.getEditBoxDescription() def setEditBoxDescription(self, description): self.listEditView.setEditBoxDescription(description) def resetEditBoxDescription(self): self.listEditView.resetEditBoxDescription() editBoxDescription = pyqtProperty('QString', getEditBoxDescription, setEditBoxDescription, resetEditBoxDescription) def getHeaders(self): return self.listEditView.getHeaders() def setHeaders(self, headers): self.listEditView.setHeaders(headers) def resetHeaders(self): self.listEditView.resetHeaders() headers = pyqtProperty('QStringList', getHeaders, setHeaders, resetHeaders) def getEditInPopup(self): return self.listEditView.editInPopup def setEditInPopup(self, in_popup): self.listEditView.editInPopup = in_popup def resetEditInPopup(self): self.listEditView.editInPopup = True editInPopup = pyqtProperty('bool', getEditInPopup, setEditInPopup, resetEditInPopup) def getEditInPlace(self): return self.listEditView.editInPlace def setEditInPlace(self, edit_in_place): self.listEditView.editInPlace = edit_in_place def resetEditInPlace(self): self.listEditView.editInPlace = False editInPlace = pyqtProperty('bool', getEditInPlace, setEditInPlace, resetEditInPlace) # ... Qt Properties def setDropMimeData(self, callback): self.listEditView.model.dropMimeData_cb = callback def setEditBox(self, editBox): """allow to customize edit popup""" # box = editBox([row1, row2, row3], listEditOption, windowTitle) # ret = box.exec_() # if QDialog.Accepted == ret: # newData = box.getData() # # newData : [ modifiedRow1, modifiedRow2, modifiedRow3 ] # box must return QDialog.Accepted if data have been modified / created # then data must be returned by box.getData() self.listEditView.editBox = editBox def setColDelegate(self, callback): """callback prototype: createDelegate(view, column)""" self.listEditView.setColDelegate(callback) def buildUpDown(self): up_down_layout = QVBoxLayout(self.up_down) up_down_layout.addWidget(self.up) up_down_layout.addWidget(self.down) up_down_layout.insertStretch(0) up_down_layout.insertStretch(-1) self.layout().addWidget(self.up_down, 0, 1) self.connect(self.up, SIGNAL('clicked()'), self.listEditView.upItem) self.connect(self.down, SIGNAL('clicked()'), self.listEditView.downItem) def buildAddRem(self): buttons = QFrame() buttons_layout = QHBoxLayout(buttons) buttons_layout.insertStretch(1) self.connect(self.add, SIGNAL('clicked()'), self.listEditView.addItem) self.connect(self.rem, SIGNAL('clicked()'), self.listEditView.removeItem) buttons_layout.addWidget(self.add) buttons_layout.addWidget(self.rem) self.layout().addWidget(buttons, 1, 0) def hideRow(self, row): self.listEditView.verticalHeader().setSectionHidden(row, True) def showRow(self, row): self.listEditView.verticalHeader().setSectionHidden(row, False) def hideColumn(self, col): self.listEditView.horizontalHeader().setSectionHidden(col, True) def showColumn(self, col): self.listEditView.horizontalHeader().setSectionHidden(col, False) def reset(self, data): """ TODO call clean & setData & reset """ self.listEditView.model.newData(data) self.listEditView.model.emit(SIGNAL("modelReset()")) def rawData(self): # TODO use model.data(...) return deepcopy(self.listEditView.model._data)
class DHCPWidget(QFrame): def __init__(self, client, main_window, parent): QFrame.__init__(self, parent) self._parent = parent self._loading = True self._modified = False self.client = client self.main_window = main_window self.addToInfoArea = self.main_window.addToInfoArea self.q_netobject = QNetObject.getInstance() # try: # self.q_netobject.registerCallbacks(self.acceptNetworkChange, self.handleNetworkChange) # except Exception, err: # raise self.dhcpcfg = None self.range_widgets = set() self.drawGUI() self.resetConf() self._loading = False def drawGUI(self): form_layout = QFormLayout(self) form_layout.setFormAlignment(Qt.AlignLeft) title = QLabel(tr("<H1>DHCP Configuration</H1>")) form_layout.addRow(title) self.enable = QCheckBox(tr("Enable DHCP Server on configured LANs")) form_layout.addRow(self.enable) self.main_window.writeAccessNeeded(self.enable) self.add_button = AddButton() self.add_button.setText(tr("Add a range")) self.main_window.writeAccessNeeded(self.add_button) form_layout.addRow(self.add_button, QLabel()) if not self.main_window.readonly: self.connect(self.enable, SIGNAL('stateChanged(int)'), self.setDHCPEnabled) self.connect(self.add_button, SIGNAL('clicked()'), self.addRange) self.setLayout(form_layout) def addRange(self): self.setModified(True) self.addRangeFrontend() def fetchCfg(self): dhcpcfg_repr = self.main_window.init_call("dhcp", "getDhcpConfig") if dhcpcfg_repr is None: #Failing to fetch dhcp config return None netcfg = self.q_netobject.netcfg if netcfg is None: return None return deserialize(dhcpcfg_repr, netcfg) def setDHCPEnabled(self, state): self.setModified(True) if state == Qt.Unchecked: self.dhcpcfg.enabled = DISABLED else: self.dhcpcfg.enabled = ENABLED def isModified(self): return self._modified def setModified(self, bool=True): if self._loading: return if bool == True: self.main_window.setModified(self._parent, True) if self._modified is False and bool is True: self.addToInfoArea(tr("DHCP Server configuration edited.")) self._modified = bool def resetConf(self): self.dhcpcfg = self.fetchCfg() if self.dhcpcfg is None: self._clearWidgets() msg_area = MessageArea() msg_area.setMessage( tr("Problem loading the network or dhcp configuration!"), tr("A problem occured while loading the network " "or dhcp config from the appliance!"), "critical" ) self.layout().insertRow(2, msg_area) self.enable.setEnabled(False) self.add_button.setEnabled(False) else: self.fillView() self._modified = False def _clearWidgets(self): for widget in self.range_widgets: # sometimes the widget stay visible widget.close() self.range_widgets.clear() def fillView(self): self._clearWidgets() if self.dhcpcfg is None: return for range in self.dhcpcfg.ranges: self.addRangeFrontend(range=range) if self.dhcpcfg.enabled == ENABLED: check_state = Qt.Checked else: check_state = Qt.Unchecked self.enable.setCheckState(check_state) def addRangeFrontend(self, range=None): # called by resetConf so we can access to QNetObject.getInstance().netcfg range_widget = NewRangeFrontend(dhcprange=range) self.main_window.writeAccessNeeded(range_widget) self.range_widgets.add(range_widget) self.connect(range_widget, SIGNAL('deleted'), self.delRangeFrontend) self.connect(range_widget, SIGNAL('modified'), self.setModified) if range is None: self.dhcpcfg.ranges.add(range_widget.dhcprange) layout = self.layout() layout.insertRow(2, range_widget) range_widget.setParent(self) def getWidgetFromRange(self, range): for widget in self.range_widgets: if widget.range is range: return widget def delRangeFrontend(self, range_widget): self.dhcpcfg.ranges.discard(range_widget.dhcprange) self.range_widgets.discard(range_widget) self.setModified(True) def isValid(self): to_remove = set() for range_widget in self.range_widgets: dhcprange = range_widget.dhcprange if dhcprange not in self.dhcpcfg.ranges or dhcprange.is_fully_unset(): to_remove.add(range_widget) continue for range_widget in to_remove: self.delRangeFrontend(range_widget) range_widget.close() return self.dhcpcfg.isValid() def saveConf(self, message): dhcpcfg_repr = self.dhcpcfg.serialize() self.client.call("dhcp", 'setDhcpConfig', dhcpcfg_repr, message) self.setModified(False) self.addToInfoArea(tr("DHCP server configuration uploaded to server.")) def acceptNetworkChange(self): deletable = set() translatable = set() translated_ranges = {} if self.dhcpcfg is None: return True, translated_ranges, deletable deletable, translatable = self.dhcpcfg.computeChanges(self.q_netobject.netcfg) if not (deletable | translatable): #Nothing to do (yay!) we agree with proposed changes return True #oh, questions arrive accept = True generic_text = "<h2>%s</h2>" % tr("This change affects the DHCP server configuration") #TODO: add questions and return for range in translatable: if not accept: break if not isinstance(range.router, IP): new_router_ip = UNSET else: new_router_ip = adjust_ip(range.net.net, range.router) widget_range = self.getWidgetFromRange(range) simulation = { 'start': adjust_ip(range.net.net, range.start), 'end': adjust_ip(range.net.net, range.end), 'router': new_router_ip, 'custom_dns': widget_range.custom_dns, } cur_router = _valuefromrouter(range.router) new_router = _valuefromrouter(simulation['router']) help_items = [] help_items.append(tr("Clicking \"Yes\" will change the DHCP range.")) help_items.append(tr("Clicking \"Ignore\" will not change the DHCP range. Double-check the DHCP server configuration before saving.")) help_items.append(tr("Clicking \"Cancel\" will cancel your changes to the network configuration")) help_text = unicode(htmlList(help_items)) title = tr("DHCP: Translate a range?") html = u"""<table> %(title)s<br /> %(ask)s<br /> <tr> <td><h2>%(start)s</h2></td> <td>%(cur_start)s</td> <td><img src=":/icons-32/go-next"/></td> <td>%(simu_start)s</td> </tr> <tr> <td><h2>%(end)s</h2></td> <td>%(cur_end)s</td> <td><img src=":/icons-32/go-next"/></td> <td>%(simu_end)s</td> </tr> <tr> <td><h2>%(router)s</h2></td> <td>%(cur_router)s</td> <td><img src=":/icons-32/go-next"/></td> <td>%(simu_router)s</td> </tr> </table> """ % { 'title' : tr("You changed the network address for the '<i>%s</i>' network") % range.net.label, 'ask' : tr("Do you want to adjust the range?"), 'start' : tr("Start IP"), 'cur_start' : range.start, 'simu_start' : unicode(simulation['start']), 'end' : tr("End IP"), 'cur_end' : range.end, 'simu_end' : unicode(simulation['end']), 'router' : tr("Router IP"), 'cur_router' : cur_router, 'simu_router' : unicode(new_router), } html += help_text message_box = QMessageBox(self) message_box.setWindowTitle(title) message_box.setText(generic_text) message_box.setInformativeText(html) message_box.setStandardButtons( QMessageBox.Yes | QMessageBox.Ignore | QMessageBox.Cancel ) clicked_button = message_box.exec_() accept = clicked_button in (QMessageBox.Yes, QMessageBox.Ignore) if clicked_button == QMessageBox.Yes: translated_ranges[range] = simulation deletable_ranges = frozenset() if accept and deletable: deletable_tr = tr("Consequence: the following DHCP range will be deleted:") deletable_html = u"<ul>" for range in deletable: deletable_html += "<li>%s %s: %s > %s</li>" % ( deletable_tr, unicode(range.net), unicode(range.start), unicode(range.end) ) deletable_html += u"</ul>" title = tr("DHCP configuration") message_box = QMessageBox(self) message_box.setWindowTitle(title) message_box.setText(generic_text) message_box.setInformativeText(deletable_html) message_box.setStandardButtons( QMessageBox.Yes | QMessageBox.Cancel ) clicked_button = message_box.exec_() accept = (clicked_button == QMessageBox.Yes) if accept: deletable_ranges = deletable if not accept: return False return True, translated_ranges, deletable_ranges def handleNetworkChange(self, *args): if args: translated_ranges, deleted_ranges = args changed = False for range in deleted_ranges: self.dhcpcfg.ranges.remove(range) changed = True for range, translation in translated_ranges.iteritems(): range.start = translation['start'] range.end = translation['end'] range.router = translation['router'] changed = True if changed: self.setModified(True) self.fillView()