def on_changed(self, *args): current_values = Combobox.cget(self.vcb, "values") cur = Combobox.current(self.vcb) new_values = list(current_values) new_values[self.idx] = self.var.get() Combobox.config(self.vcb, values = new_values) if cur == self.idx: Combobox.current(self.vcb, cur)
class MemorySettingsWidget(SettingsWidget): def __init__(self, mem, *args, **kw): SettingsWidget.__init__(self, mem, *args, **kw) self.mem = mem self.mem_fr = fr = GUIFrame(self) fr.pack(fill=BOTH, expand=False) fr.columnconfigure(0, weight=0) fr.columnconfigure(1, weight=1) fr.rowconfigure(0, weight=0) row = 0 l = VarLabel(fr, text=_("Region type")) l.grid(row=row, column=0, sticky="NES") memtype2str = { MemoryNode: _("Container"), MemorySASNode: _("SAS"), MemoryAliasNode: _("Alias"), MemoryRAMNode: _("RAM"), MemoryROMNode: _("ROM") } l = VarLabel(fr, text=memtype2str[type(mem)]) l.grid(row=row, column=1, sticky="NEWS") row += 1 if not isinstance(mem, MemorySASNode): l = VarLabel(fr, text=_("Parent region")) l.grid(row=row, column=0, sticky="NES") self.var_parent = StringVar() self.cb_parent = Combobox(fr, textvariable=self.var_parent, state="readonly") self.cb_parent.grid(row=row, column=1, sticky="NEWS") row += 1 self.fields = [(_("Name"), "name", CConst), (_("Size"), "size", CConst), (_("Offset"), "offset", CConst), (_("May overlap"), "may_overlap", bool), (_("Priority"), "priority", CConst)] if type(mem) is MemoryAliasNode: self.fields.extend([(_("Alias offset"), "alias_offset", CConst)]) if isinstance(mem, MemorySASNode): self.fields = [(_("Name"), "name", str)] for text, field, _type in self.fields: if _type is bool: l = None v = BooleanVar() w = VarCheckbutton(fr, text=text, variable=v) else: l = VarLabel(fr, text=text) v = StringVar() w = HKEntry(fr, textvariable=v) fr.rowconfigure(row, weight=0) if l is None: w.grid(row=row, column=0, sticky="NWS", columnspan=2) else: l.grid(row=row, column=0, sticky="NES") l.gi = l.grid_info() w.grid(row=row, column=1, sticky="NEWS") w.gi = w.grid_info() row += 1 if l: setattr(self, "l_" + field, l) setattr(self, "w_" + field, w) setattr(self, "var_" + field, v) self.var_name.trace_variable("w", self.__on_name_var_changed) if type(mem) is MemoryAliasNode: l = VarLabel(fr, text=_("Alias region")) l.grid(row=row, column=0, sticky="NES") self.var_alias_to = StringVar() self.cb_alias_to = Combobox(fr, textvariable=self.var_alias_to, state="readonly") self.cb_alias_to.grid(row=row, column=1, sticky="NEWS") if not isinstance(mem, MemorySASNode): if not mem.parent: self.l_offset.grid_forget() self.w_offset.grid_forget() def __apply_internal__(self): if not isinstance(self.mem, MemorySASNode): new_parent = self.find_node_by_link_text(self.var_parent.get()) cur_parent = self.mem.parent if new_parent is None: new_parent_id = -1 else: new_parent_id = new_parent.id if cur_parent is None: cur_parent_id = -1 else: cur_parent_id = cur_parent.id if not new_parent_id == cur_parent_id: if not cur_parent_id == -1: self.mht.stage(MOp_RemoveMemChild, self.mem.id, cur_parent_id) if not new_parent_id == -1: self.mht.stage(MOp_AddMemChild, self.mem.id, new_parent_id) for text, field, _type in self.fields: new_val = getattr(self, "var_" + field).get() if _type is CConst: try: new_val = CConst.parse(new_val) except: continue cur_val = getattr(self.mem, field) if new_val == cur_val: continue self.mht.stage(MOp_SetMemNodeAttr, field, new_val, self.mem.id) if type(self.mem) is MemoryAliasNode: new_alias_to = self.find_node_by_link_text(self.var_alias_to.get()) cur_alias_to = self.mem.alias_to if not new_alias_to == cur_alias_to: self.mht.stage(MOp_SetMemNodeAlias, "alias_to", new_alias_to, self.mem.id) self.mht.set_sequence_description( _("Memory '%s' (%d) configuration.") % (self.mem.name, self.mem.id)) def refresh(self): SettingsWidget.refresh(self) if not isinstance(self.mem, MemorySASNode): values = [ DeviceSettingsWidget.gen_node_link_text(mem) for mem in ([ mem for mem in self.mach.mems if (not isinstance(mem, MemoryLeafNode) and mem != self.mem) ] + [None]) ] self.cb_parent.config(values=values) self.var_parent.set( DeviceSettingsWidget.gen_node_link_text(self.mem.parent)) for text, field, _type in self.fields: var = getattr(self, "var_" + field) cur_val = getattr(self.mem, field) var.set(cur_val) if type(self.mem) is MemoryAliasNode: values = [ DeviceSettingsWidget.gen_node_link_text(mem) for mem in ( [mem for mem in self.mach.mems if (mem != self.mem)]) ] self.cb_alias_to.config(values=values) self.var_alias_to.set( DeviceSettingsWidget.gen_node_link_text(self.mem.alias_to)) if not isinstance(self.mem, MemorySASNode): if self.mem.parent is None: self.l_offset.grid_forget() self.w_offset.grid_forget() else: self.l_offset.grid(self.l_offset.gi) self.w_offset.grid(self.w_offset.gi) def on_changed(self, op, *args, **kw): if not isinstance(op, MachineNodeOperation): return if op.writes_node() and self.mem.id == -1: self.destroy() else: self.refresh() def __on_name_var_changed(self, *args): vvb = self.v_var_base vb = vvb.get() try: prev_n = self.__prev_name except AttributeError: # name was not edited yet prev_n = self.mem.name.v if vb == "mem" or vb == name_to_var_base(prev_n): """ If current variable name base is default or corresponds to previous name then auto suggest new variable name base with account of just entered name. """ n = self.var_name.get() vvb.set(name_to_var_base(n)) self.__prev_name = n
class BusLineDesc(object): def __init__(self, device_settings_widget, idx): self.dsw = device_settings_widget self.idx = idx def gen_row(self): p = self.dsw.buses_lf p.rowconfigure(self.idx, weight = 1) self.v = StringVar() # When a bus is selected all Combobox values lists should be updated # to prevent selecting of this bus in another Combobox self._on_var_changed = self.v.trace_variable("w", self.on_var_changed) self.cb = Combobox(p, textvariable = self.v, state = "readonly" ) self.cb.grid(row = self.idx, column = 0, sticky = "NEWS") self._on_bus_selected = self.dsw.bind( DeviceSettingsWidget.EVENT_BUS_SELECTED, self.on_bus_selected, "+") def on_var_changed(self, *args): self.dsw.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) def update(self): try: cur_bus = self.dsw.dev.buses[self.idx] except IndexError: cur_bus = None bus_text = DeviceSettingsWidget.gen_node_link_text(cur_bus) self.v.set(bus_text) def delete(self): self.v.trace_vdelete("w", self._on_var_changed) self.dsw.unbind(DeviceSettingsWidget.EVENT_BUS_SELECTED, self._on_bus_selected) if "obs" in self.__dict__: self.v.trace_vdelete("w", self.obs) del self.obs self.cb.destroy() def update_values(self): sel_buses = self.dsw.get_selected_buses() values = [ DeviceSettingsWidget.gen_node_link_text(b) for b in ( [ b for b in self.dsw.mach.buses if (\ ( b.parent_device is None \ or b.parent_device == self.dsw.dev) and (not b.id in sel_buses)) ] + [ None ] ) ] self.cb.config(values = values) def on_bus_selected(self, event): self.update_values() def on_dsw_destroy(self, event): self.delete()
class DeviceSettingsWidget(SettingsWidget): EVENT_BUS_SELECTED = "<<DSWBusSelected>>" prop_type_name_map = { QOMPropertyTypeInteger: ("Integer", ), QOMPropertyTypeLink: ("Link", ), QOMPropertyTypeString: ("String", ), QOMPropertyTypeBoolean: ("Boolean", ) } prop_name_type_map = { "Integer": QOMPropertyTypeInteger, "Link": QOMPropertyTypeLink, "String": QOMPropertyTypeString, "Boolean": QOMPropertyTypeBoolean } def __init__(self, device, *args, **kw): SettingsWidget.__init__(self, device, *args, **kw) self.dev = device common_fr = GUIFrame(self) common_fr.pack(fill = BOTH, expand = False) common_fr.columnconfigure(0, weight = 0) common_fr.columnconfigure(1, weight = 1) common_fr.rowconfigure(0, weight = 0) l = VarLabel(common_fr, text = _("QOM type")) v = self.qom_type_var = StringVar() e = HKEntry(common_fr, textvariable = v) v.trace_variable("w", self.__on_qom_type_var_changed) l.grid(row = 0, column = 0, sticky = "W") e.grid(row = 0, column = 1, sticky = "EW") b = VarButton(common_fr, text = _("Select"), command = self.on_press_select_qom_type ) b.grid(row = 0, column = 2, sticky = "EW") # Check for device tree bp = self.mach.project.build_path if bp is None: b["state"] = "disabled" else: qvd = qvd_get(bp, version = self.mach.project.target_version) if qvd.qvc is None or qvd.qvc.device_tree is None: # TODO: watch "qvc_available" signal b["state"] = "disabled" # parent bus editing widgets l = VarLabel(common_fr, text = _("Parent bus")) self.bus_var = StringVar() self.bus_var.trace_variable("w", self.on_parent_bus_var_changed) self.bus_cb = Combobox( common_fr, textvariable = self.bus_var, state = "readonly" ) l.grid(row = 1, column = 0, sticky = "W") self.bus_cb.grid(row = 1, column = 1, sticky = "EW") common_fr.rowconfigure(1, weight = 0) self.buses_lf = lf = VarLabelFrame( common_fr, text = _("Child buses") ) lf.grid(row = 2, column = 0, columns = 3, sticky = "NEWS") self.rowconfigure(2, weight = 1) lf.columnconfigure(0, weight = 1) self.child_buses_rows = [] # pack properties inside a frame with scrolling outer_frame = VarLabelFrame(self, text = _("Properties")) outer_frame.pack(fill = BOTH, expand = True) self.props_lf = add_scrollbars(outer_frame, GUIFrame) self.props_lf.columnconfigure(0, weight = 1) self.props_lf.columnconfigure(1, weight = 0) self.props_lf.columnconfigure(2, weight = 1) self.props_lf.columnconfigure(3, weight = 0) self.prop2field = {} self.bt_add_prop = VarButton( self.props_lf, text = _("Add"), command = self.on_prop_add ) self.bt_add_prop.grid( column = 3, sticky = "NEWS" ) def __on_destroy__(self, *args): for bld in self.child_buses_rows: bld.delete() SettingsWidget.__on_destroy__(self, *args) def __on_qom_type_var_changed(self, *args): vvb = self.v_var_base vb = vvb.get() try: prev_qt = self.__prev_qom_type except AttributeError: # QOM type was not edited yet prev_qt = self.node.qom_type if vb == "dev" or vb == qom_type_to_var_base(prev_qt): """ If current variable name base is default or corresponds to previous QOM type name then auto suggest new variable name base with account of just entered QOM type. """ qt = self.qom_type_var.get() vvb.set(qom_type_to_var_base(qt)) self.__prev_qom_type = qt def on_parent_bus_var_changed(self, *args): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) def on_press_select_qom_type(self): DeviceTreeWidget(self) def gen_uniq_prop_name(self): for x in count(0, 1): name = "name-of-new-property-" + str(x) for prop in self.prop2field: if name == prop.prop_name: name = None break if name: return name def on_prop_add(self): p = QOMPropertyValue( QOMPropertyTypeLink, self.gen_uniq_prop_name(), None ) lpd = PropLineDesc(self, p) row = len(self.prop2field) lpd.gen_row(row) self.prop2field[p] = lpd # Move add button bottom self.bt_add_prop.grid(row = row + 1) def on_changed(self, op, *args, **kw): if isinstance(op, MOp_SetChildBus): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) if isinstance(op, MOp_SetDevParentBus): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) if isinstance(op, MachineNodeOperation): if op.writes_node() and self.dev.id == -1: self.destroy() elif op.node_id == self.dev.id: self.refresh() @staticmethod def gen_prop_type_optionmenu(parent, current = None): var = StringVar() keys = [] for ptn in DeviceSettingsWidget.prop_type_name_map.values(): keys.append(ptn[0]) om = OptionMenu(parent, var, *keys) if current: current = DeviceSettingsWidget.prop_type_name_map[current][0] else: current = next(itervalues(DeviceSettingsWidget.prop_type_name_map)) var.set(current) return om, var @staticmethod def gen_node_link_text(node): # TODO: localize? if node is None: return "-1: NULL" ret = "%s: " % node.id if isinstance(node, BusNode): ret = ret + "Bus, %s" % node.gen_child_name_for_bus() elif isinstance(node, IRQLine): ret = ret + "IRQ: %s" \ % DeviceSettingsWidget.gen_node_link_text(node.src[0]) \ + " -> %s" \ % DeviceSettingsWidget.gen_node_link_text(node.dst[0]) elif isinstance(node, IRQHub): ret = ret + "IRQ Hub" elif isinstance(node, DeviceNode): ret = ret + "Device, %s" % node.qom_type elif isinstance(node, MemoryNode): ret = ret + "Memory, %s" % node.name return ret def refresh(self): SettingsWidget.refresh(self) self.qom_type_var.set(self.dev.qom_type) for p, desc in self.prop2field.items(): desc.e_name.destroy() desc.om_type.destroy() desc.w_val.destroy() desc.bt_del.destroy() self.prop2field = {} # If self.dev.properties is empty the row variable will remain # undefined. row = -1 for row, p in enumerate(self.dev.properties): lpd = PropLineDesc(self, p) lpd.gen_row(row) # Do not use different QOMPropertyValue as the key for the # PropLineDesc of corresponding device-stored QOMPropertyValue # The QOMPropertyValue is used to apply deletion of device # property. self.prop2field[p] = lpd self.bt_add_prop.grid(row = row + 1) # refresh parent bus buses = [ DeviceSettingsWidget.gen_node_link_text(None) ] for n in self.mach.id2node.values(): if not isinstance(n, BusNode): continue buses.append(DeviceSettingsWidget.gen_node_link_text(n)) self.bus_cb.config(values = buses) self.bus_var.set( DeviceSettingsWidget.gen_node_link_text(self.dev.parent_bus) ) bus_row_count = len(self.child_buses_rows) bus_count = len(self.dev.buses) + 1 if bus_row_count < bus_count: if bus_row_count: bld = self.child_buses_rows[-1] bld.v.trace_vdelete("w", bld.obs) del bld.obs for idx in xrange(bus_row_count, bus_count): bld = BusLineDesc(self, idx) self.child_buses_rows.append(bld) bld.gen_row() bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) if bus_count < bus_row_count: for idx in xrange(bus_count, bus_row_count): bld = self.child_buses_rows.pop() bld.delete() bld = self.child_buses_rows[-1] bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) for bld in self.child_buses_rows: bld.update() def on_last_child_bus_changed(self, *args): bld = self.child_buses_rows[-1] bus = self.find_node_by_link_text(bld.v.get()) if not bus is None: # Selecting not NULL child bus means that a child bus was added. # Add new NULL bus string for consequent bus addition. bld.v.trace_vdelete("w", bld.obs) del bld.obs bld = BusLineDesc(self, len(self.child_buses_rows)) self.child_buses_rows.append(bld) bld.gen_row() bld.update() bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) def get_selected_child_buses(self): child_buses = [ bld.v.get() for bld in self.child_buses_rows ] ret = [ self.find_node_by_link_text(t) for t in child_buses if t ] return [ b.id for b in ret if not b is None ] def get_selected_buses(self): ret = self.get_selected_child_buses() parent_bus = self.find_node_by_link_text(self.bus_var.get()) if not parent_bus is None: parent_bus = parent_bus.id if not parent_bus in ret: ret.append(parent_bus) return ret def __apply_internal__(self): # apply parent bus new_bus_text = self.bus_var.get() new_bus = self.find_node_by_link_text(new_bus_text) if not self.dev.parent_bus == new_bus: self.mht.stage(MOp_SetDevParentBus, new_bus, self.dev.id) qom = self.qom_type_var.get() if not self.dev.qom_type == qom: self.mht.stage(MOp_SetDevQOMType, qom, self.dev.id) # Do property removing before addition to prevent conflicts of # property recreation. for p in self.dev.properties: if not p in self.prop2field: self.mht.stage(MOp_DelDevProp, p, self.dev.id) for p, desc in list(self.prop2field.items()): cur_name, cur_type, cur_val = desc.get_current_name(), \ desc.get_current_type(), desc.get_current_val() if p in self.dev.properties.values(): if cur_name != p.prop_name: # Name of property was changed. Recreate it. new_p = QOMPropertyValue(cur_type, cur_name, cur_val) self.mht.stage(MOp_DelDevProp, p, self.dev.id) self.mht.stage(MOp_AddDevProp, new_p, self.dev.id) del self.prop2field[p] self.prop2field[new_p] = desc desc.prop = new_p elif not ( p.prop_type == cur_type and p.prop_val == cur_val ): self.mht.stage( MOp_SetDevProp, cur_type, cur_val, p, self.dev.id ) else: # A completely new property. It was added using # the 'Add' button. new_p = QOMPropertyValue(cur_type, cur_name, cur_val) self.mht.stage(MOp_AddDevProp, new_p, self.dev.id) new_buses = self.get_selected_child_buses() # Changing of buses is made in two steps to allow reordering of buses # during single iteration. step2 = [] # The child bus list is reversed to remove buses from the end to to the # begin. After removing bus from middle consequent indexes becomes # incorrect. for i, bus in reversed([ x for x in enumerate(self.dev.buses) ]): try: new_bus = new_buses.pop(i) except IndexError: # remove i-th bus self.mht.stage( MOp_SetChildBus, self.dev.id, i, -1 ) else: if bus.id == new_bus: continue # change i-th bus (1-st step: remove) self.mht.stage( MOp_SetChildBus, self.dev.id, i, -1 ) # step 2 should be done in increasing index order step2.insert(0, (i, new_bus)) adding = [ x for x in zip(count(len(self.dev.buses)), new_buses) ] for i, new_bus in step2 + adding: # add i-th bus self.mht.stage( MOp_SetChildBus, self.dev.id, i, new_bus ) self.mht.set_sequence_description(_("Device configuration."))
class BusSettingsWidget(SettingsWidget): def __init__(self, bus, *args, **kw): SettingsWidget.__init__(self, bus, *args, **kw) self.bus = bus self.bus_fr = fr = GUIFrame(self) fr.pack(fill=BOTH, expand=False) fr.columnconfigure(0, weight=0) fr.columnconfigure(1, weight=1) fr.rowconfigure(0, weight=0) l = VarLabel(fr, text=_("Parent device")) l.grid(row=0, column=0, sticky="NES") self.var_parent = StringVar() self.cb_parent = Combobox(fr, textvariable=self.var_parent, state="readonly") self.cb_parent.grid(row=0, column=1, sticky="NEWS") self.fields = [] if type(bus) is BusNode: self.fields.extend([(_("C type"), "c_type", str), (_("Casting macro"), "cast", str), (_("Child name"), "child_name", str), (_("Always show index"), "force_index", bool)]) # Common bus type for row, (text, field, _type) in enumerate(self.fields, start=1): if _type is str: l = VarLabel(fr, text=text) v = StringVar() w = HKEntry(fr, textvariable=v) elif _type is bool: l = None v = BooleanVar() w = VarCheckbutton(fr, text=text, variable=v) fr.rowconfigure(row, weight=0) if l is None: w.grid(row=row, column=0, sticky="NEWS", columnspan=2) else: l.grid(row=row, column=0, sticky="NES") w.grid(row=row, column=1, sticky="NEWS") setattr(self, "w_" + field, w) setattr(self, "var_" + field, v) def __apply_internal__(self): new_parent = self.find_node_by_link_text(self.var_parent.get()) cur_parent = self.bus.parent_device if new_parent is None: new_parent_id = -1 else: new_parent_id = new_parent.id if cur_parent is None: cur_parent_id = -1 else: cur_parent_id = cur_parent.id if not new_parent_id == cur_parent_id: if new_parent_id == -1: if not cur_parent_id == -1: self.mht.disconnect_child_bus(self.bus.id) else: self.mht.append_child_bus(new_parent_id, self.bus.id) for (text, field, _type) in self.fields: new_val = getattr(self, "var_" + field).get() cur_val = getattr(self.bus, field) if new_val == cur_val: continue self.mht.stage(MOp_SetBusAttr, field, new_val, self.bus.id) self.mht.set_sequence_description( _("Bus %d configuration.") % self.bus.id) def refresh(self): SettingsWidget.refresh(self) values = [ DeviceSettingsWidget.gen_node_link_text(dev) for dev \ in ( self.mach.devices + [ None ] ) ] self.cb_parent.config(values=values) self.var_parent.set( DeviceSettingsWidget.gen_node_link_text(self.bus.parent_device)) for (text, field, _type) in self.fields: var = getattr(self, "var_" + field) cur_val = getattr(self.bus, field) var.set(cur_val) def on_changed(self, op, *args, **kw): if isinstance(op, MOp_SetChildBus): if self.bus.id in [op.prev_bus_id, op.bus_id]: self.refresh() return if not isinstance(op, MachineNodeOperation): return if op.writes_node(): if not self.bus.id in self.mach.id2node: self.destroy() else: self.refresh()
def config(self, cnf = None, **kw): if "values" in kw: self.set_var_values(kw["values"]) kw["values"] = [ b.var.get() for b in self.bindings ] return Combobox.config(self, cnf, **kw)