class DBServersWidget(QWidget): """Displays a list of servers""" def __init__(self, parent=None): QWidget.__init__(self, parent) self.debug = False self.connections = {} self.setWindowTitle("Servers") #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.setLayout(self.mainLayout) #============================================= ## Top Toolbar topBar = QToolBar() topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.mainLayout.addWidget(topBar) ## Add the action buttons topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add) self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit), "Edit", self.on_server_edit) self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete), "Delete", self.on_server_delete) #============================================= ## Tree self.tree = QTreeWidget() self.mainLayout.addWidget(self.tree) self.tree.setUniformRowHeights(True) self.tree.setRootIsDecorated(True) self.tree.setHeaderLabels(["Server", "Butt"]) # set header, but hide anyway self.tree.header().hide() self.tree.header().setResizeMode(C.node, QHeaderView.Stretch) self.tree.setColumnWidth(C.butt, 20) self.connect(self.tree, SIGNAL('itemSelectionChanged()'), self.on_tree_selection_changed) self.connect(self.tree, SIGNAL('itemDoubleClicked (QTreeWidgetItem *,int)'), self.on_tree_double_clicked) self.buttGroup = QButtonGroup(self) self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"), self.on_open_server) self.on_tree_selection_changed() self.load_servers() #======================================= ##== Tree Events def on_tree_selection_changed(self): disabled = self.tree.selectionModel().hasSelection() == False self.actionServerEdit.setDisabled(disabled) self.actionServerDelete.setDisabled(disabled) def on_tree_double_clicked(self): self.actionServerEdit.trigger() #======================================= ## Server Actions def on_server_add(self): self.show_server_dialog(None) def on_server_edit(self): item = self.tree.currentItem() if item == None: return server = str(item.text(C.server)) self.show_server_dialog(server) def show_server_dialog(self, server=None): d = DBServerDialog.DBServerDialog(self, server) if d.exec_(): self.load_servers() def load_servers(self): """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """ self.tree.clear() for butt in self.buttGroup.buttons(): self.buttGroup.removeButton(butt) for srv in G.settings.get_servers_list(): item = QTreeWidgetItem() item.setText(C.node, srv['server']) #item.setText(C.user, srv['user']) self.tree.addTopLevelItem(item) butt = QToolButton() butt.setIcon(Ico.icon(Ico.Connect)) butt.setProperty("server", srv['server']) self.tree.setItemWidget(item, C.butt, butt) self.buttGroup.addButton(butt) def on_server_delete(self): item = self.tree.currentItem() if item == None: return srv = str(item.text(C.server)) G.settings.delete_server(srv) self.load_servers() def on_open_server(self, butt): # self.emit(SIGNAL("open_server"), butt.property("server").toString()) srv_ki = str(butt.property("server").toString()) server = G.settings.get_server(srv_ki) db = QSqlDatabase.addDatabase("QMYSQL", srv_ki) db.setHostName(server['server']) db.setUserName(server['user']) db.setPassword(server['passwd']) ok = db.open() if ok: #self.connections[srv_ki] = self.load_databases(srv_ki) print "open", ok def load_databases(self, srv_ki): """Load databases into tree node for server; executes 'show databases;' or aslike """ sql = "show databases;" query = QSqlQuery(QSqlDatabase.database(srv_ki)) ok = query.exec_(sql) print ok, sql, query.result() # Get the parent node, ie the server node pItem = self.tree.findItems(srv_ki, Qt.MatchExactly, C.node)[0] ## Assumed value(0) is the table.. we need the defs (ie mysql case) while query.next(): table_name = query.value(0).toString() nuItem = QTreeWidgetItem(pItem) nuItem.setText(C.node, table_name) #print table_name self.tree.setItemExpanded(pItem, True)
class TabBarWidget(QWidget): """ A tab bar widget using tool buttons as tabs. """ # TODO: A uniform size box layout. currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed ) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=None, toolTip=None): """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=None, toolTip=None): """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): self.layout().takeItem(index) tab = self.__tabs.pop(index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=icon) self.__updateTab(index) def setTabToolTip(self, index, toolTip): """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace(palette=palette) self.__updateTab(index) def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): """ Return the current index. """ return self.__currentIndex def button(self, index): """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): if self.__iconSize != size: self.__iconSize = size for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width()+10, 0) p4 = self.pos() + QPoint(self.width()+10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return QWidget.eventFilter(self, receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)
class DBDatabasesWidget(QWidget): """Displays a list of Databases""" def __init__(self, parent=None): QWidget.__init__(self, parent) self.debug = False self.db = None self.setWindowTitle("Databases") #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.setLayout(self.mainLayout) #============================================= ## Top Toolbar topBar = QToolBar() topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.mainLayout.addWidget(topBar) ## Add the action buttons topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add) self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit), "Edit", self.on_server_edit) self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete), "Delete", self.on_server_delete) #============================================= ## Tree self.tree = QTreeWidget() self.mainLayout.addWidget(self.tree) self.tree.setHeaderLabels(["Server", "User", ""]) self.tree.setUniformRowHeights(True) self.tree.setRootIsDecorated(False) self.tree.setColumnWidth(C.widget, 20) self.connect(self.tree, SIGNAL('itemSelectionChanged()'), self.on_tree_selection_changed) self.connect(self.tree, SIGNAL('itemDoubleClicked (QTreeWidgetItem *,int)'), self.on_tree_double_clicked) self.buttGroup = QButtonGroup(self) self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"), self.on_open_server) self.on_tree_selection_changed() self.load_servers() #======================================= ##== Tree Events def on_tree_selection_changed(self): disabled = self.tree.selectionModel().hasSelection() == False self.actionServerEdit.setDisabled(disabled) self.actionServerDelete.setDisabled(disabled) def on_tree_double_clicked(self): self.actionServerEdit.trigger() #======================================= ## Server Actions def on_server_add(self): self.show_server_dialog(None) def on_server_edit(self): item = self.tree.currentItem() if item == None: return server = str(item.text(C.server)) self.show_server_dialog(server) def show_server_dialog(self, server=None): d = DBServerDialog.DBServerDialog(self, server) if d.exec_(): self.load_servers() def on_open_server(self, butt): self.emit(SIGNAL("open_server"), butt.property("server").toString()) def load_servers(self): """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """ self.tree.clear() for butt in self.buttGroup.buttons(): self.buttGroup.removeButton(butt) for srv in G.settings.get_servers_list(): item = QTreeWidgetItem() item.setText(C.server, srv['server']) item.setText(C.user, srv['user']) self.tree.addTopLevelItem(item) butt = QToolButton() butt.setIcon(Ico.icon(Ico.Connect)) butt.setProperty("server", srv['server']) self.tree.setItemWidget(item, C.widget, butt) self.buttGroup.addButton(butt) def on_server_delete(self): item = self.tree.currentItem() if item == None: return srv = str(item.text(C.server)) G.settings.delete_server(srv) self.load_servers()
class TabBarWidget(QWidget): """ A tab bar widget using tool buttons as tabs. """ # TODO: A uniform size box layout. currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=None, toolTip=None): """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=None, toolTip=None): """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): self.layout().takeItem(index) tab = self.__tabs.pop(index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=icon) self.__updateTab(index) def setTabToolTip(self, index, toolTip): """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace(palette=palette) self.__updateTab(index) def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): """ Return the current index. """ return self.__currentIndex def button(self, index): """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): if self.__iconSize != size: self.__iconSize = size for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width() + 10, 0) p4 = self.pos() + QPoint(self.width() + 10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return QWidget.eventFilter(self, receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)
class OptionWidget(EditorWidget): widgettype = 'Option Row' def __init__(self, *args, **kwargs): super(OptionWidget, self).__init__(*args, **kwargs) self._bindvalue = None self.group = QButtonGroup() self.group.buttonClicked.connect(self.emitvaluechanged) def createWidget(self, parent=None): widget = QWidget(parent) return widget def _buildfromlist(self, listconfig, multiselect): def chunks(l, n): """ Yield successive n-sized chunks from l. """ for i in xrange(0, len(l), n): yield l[i:i+n] items = listconfig['items'] wrap = self.config.get('wrap', 0) showcolor = self.config.get('always_color', False) if wrap > 0: rows = list(chunks(items, wrap)) else: rows = [items] for rowcount, row in enumerate(rows): for column, item in enumerate(row): parts = item.split(';') data = parts[0] try: desc = parts[1] except IndexError: desc = data button = QPushButton() button.setCheckable(multiselect) self.group.setExclusive(not multiselect) icon = QIcon() try: path = parts[2] if path.startswith("#"): # Colour the button with the hex value. # If show color is enabled we always show the color regardless of selection. print showcolor if not showcolor: style = """ QPushButton::checked {{ border: 3px solid rgb(137, 175, 255); background-color: {colour}; }}""".format(colour=path) else: style = """ QPushButton::checked {{ border: 3px solid rgb(137, 175, 255); }} QPushButton {{ background-color: {colour}; }}""".format(colour=path) button.setStyleSheet(style) elif path.endswith("_icon"): icon = QIcon(":/icons/{}".format(path)) else: icon = QIcon(path) except: icon = QIcon() button.setCheckable(True) button.setText(desc) button.setProperty("value", data) button.setIcon(icon) button.setIconSize(QSize(24, 24)) if isinstance(self.widget.layout(), QBoxLayout): self.widget.layout().addWidget(button) else: self.widget.layout().addWidget(button, rowcount, column) self.group.addButton(button) def initWidget(self, widget, config): if not widget.layout(): widget.setLayout(QGridLayout()) widget.layout().setContentsMargins(0, 0, 0, 0) def updatefromconfig(self): super(OptionWidget, self).updatefromconfig() for button in self.group.buttons(): self.group.removeButton(button) self.widget.layout().removeWidget(button) button.deleteLater() button.setParent(None) listconfig = self.config['list'] multiselect = self.config.get('multi', False) self._buildfromlist(listconfig, multiselect) super(OptionWidget, self).endupdatefromconfig() def validate(self, *args): button = self.group.checkedButton() if button: return True return False @property def buttons(self): return self.group.buttons() @property def nullvalues(self): return ['NULL'] @property def multioption(self): return self.config.get('multi', False) def setvalue(self, value): def set_button(setvalue): for button in self.group.buttons(): buttonvalue = button.property("value") if (setvalue is None and buttonvalue in self.nullvalues) or buttonvalue == str(setvalue): button.setChecked(True) self.emitvaluechanged() return if value in self.nullvalues: value = None if self.multioption and value: values = value.split(';') else: values = [value] for value in values: set_button(value) def value(self): def _returnvalue(): if self.multioption: _values = [] checked = [button for button in self.group.buttons() if button.isChecked()] for button in checked: value = button.property("value") _values.append(value) if not _values: return None return ";".join(_values) else: checked = self.group.checkedButton() if not checked: return None value = checked.property("value") return value returnvalue = _returnvalue() if returnvalue in self.nullvalues: returnvalue = None return returnvalue
class OptionWidget(EditorWidget): widgettype = 'Option Row' def __init__(self, *args): super(OptionWidget, self).__init__(*args) self._bindvalue = None self.group = QButtonGroup() self.group.setExclusive(True) self.group.buttonClicked.connect(self.emitvaluechanged) def createWidget(self, parent): widget = QWidget(parent) return widget def _buildfromlist(self, listconfig): items = listconfig['items'] for item in items: parts = item.split(';') data = parts[0] try: desc = parts[1] except IndexError: desc = data button = QPushButton() icon = QIcon() try: path = parts[2] if path.startswith("#"): # Colour the button with the hex value style = """ QPushButton:checked {{ border: 3px solid rgb(137, 175, 255); background-color: {colour}; }}""".format(colour=path) button.setStyleSheet(style) elif path.endswith("_icon"): icon = QIcon(":/icons/{}".format(path)) else: icon = QIcon(path) except: icon = QIcon() button.setCheckable(True) button.setText(desc) button.setProperty("value", data) button.setIcon(icon) button.setIconSize(QSize(24, 24)) self.widget.layout().addWidget(button) self.group.addButton(button) def initWidget(self, widget): if not widget.layout(): widget.setLayout(QHBoxLayout()) widget.layout().setContentsMargins(0, 0, 0, 0) def updatefromconfig(self): super(OptionWidget, self).updatefromconfig() for button in self.group.buttons(): self.group.removeButton(button) self.widget.layout().removeWidget(button) button.deleteLater() button.setParent(None) listconfig = self.config['list'] self._buildfromlist(listconfig) super(OptionWidget, self).endupdatefromconfig() def validate(self, *args): button = self.group.checkedButton() if button: return True return False @property def nullvalues(self): return ['NULL'] def setvalue(self, value): if value in self.nullvalues: value = None for button in self.group.buttons(): buttonvalue = button.property("value") if (value is None and buttonvalue in self.nullvalues) or buttonvalue == str(value): button.setChecked(True) self.emitvaluechanged() return def value(self): button = self.group.checkedButton() if not button: return None value = button.property("value") if value in self.nullvalues: value = None return value
class XNavigationEdit(XLineEdit): """ """ navigationChanged = Signal() __designer_icon__ = projexui.resources.find('img/ui/navigate.png') def __init__( self, parent = None ): super(XNavigationEdit, self).__init__( parent ) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape( QScrollArea.NoFrame ) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect( self.navigateToIndex ) self._buttonGroup.buttonClicked.connect( self.handleButtonClick ) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts ) def acceptEdit( self ): """ Accepts the current text and rebuilds the parts widget. """ if ( self._partsWidget.isVisible() ): return False use_completion = self.completer().popup().isVisible() completion = self.completer().currentCompletion() self._completerTree.hide() self.completer().popup().hide() if ( use_completion ): self.setText(completion) else: self.rebuild() return True def cancelEdit( self ): """ Rejects the current edit and shows the parts widget. """ if ( self._partsWidget.isVisible() ): return False self._completerTree.hide() self.completer().popup().hide() self.setText(self._originalText) return True def currentItem( self ): """ Returns the current navigation item from the current path. :return <XNavigationItem> || None """ model = self.navigationModel() if ( not model ): return None return model.itemByPath(self.text()) def eventFilter( self, object, event ): """ Filters the events for the inputed object through this edit. :param object | <QObject> event | <QEvent> :return <bool> | consumed """ if ( event.type() == event.KeyPress ): if ( event.key() == Qt.Key_Escape ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return True elif ( event.key() == Qt.Key_Tab ): if ( self.completer().popup().isVisible() ): text = str(self.completer().currentCompletion()) super(XNavigationEdit, self).setText(text) return True else: self.acceptEdit() return False elif ( event.type() == event.MouseButtonPress ): if ( not self._completerTree.rect().contains(event.pos()) ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() return False def focusOutEvent( self, event ): """ Overloads the focus out event to cancel editing when the widget loses focus. :param event | <QFocusEvent> """ super(XNavigationEdit, self).focusOutEvent(event) self.cancelEdit() def handleButtonClick( self, button ): """ Handle the event when a user clicks on one of the part buttons. :param button | <QToolButton> """ path = button.property('path') is_completer = button.property('is_completer') # popup a completion menu if ( unwrapVariant(is_completer) ): model = self.navigationModel() if ( not model ): return sep = self.separator() path = str(unwrapVariant(path)) item = model.itemByPath(path, includeRoot = True) if ( not item ): return curr_path = str(self.text()).strip(self.separator()) curr_path = curr_path.replace(path, '').strip(self.separator()) child_name = '' if ( curr_path ): child_name = curr_path.split(self.separator())[0] index = model.indexFromItem(item) self._completerTree.move(QCursor.pos()) self._completerTree.setRootIndex(index) self._completerTree.verticalScrollBar().setValue(0) if ( child_name ): child_item = None for i in range(item.rowCount()): child = item.child(i) if ( child.text() == child_name ): child_item = child break if ( child_item ): child_index = model.indexFromItem(child_item) self._completerTree.setCurrentIndex(child_index) self._completerTree.scrollTo(child_index) self._completerTree.show() self._completerTree.setUpdatesEnabled(True) else: self.setText(unwrapVariant(path)) def keyPressEvent( self, event ): """ Overloads the key press event to listen for escape calls to cancel the parts editing. :param event | <QKeyPressEvent> """ if ( self.scrollWidget().isHidden() ): if ( event.key() == Qt.Key_Escape ): self.cancelEdit() return elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return elif ( event.key() == Qt.Key_A and event.modifiers() == Qt.ControlModifier ): self.startEdit() super(XNavigationEdit, self).keyPressEvent(event) def mouseDoubleClickEvent( self, event ): """ Overloads the system to enable editing when a user double clicks. :param event | <QMouseEvent> """ super(XNavigationEdit, self).mouseDoubleClickEvent(event) self.startEdit() def navigationModel( self ): """ Returns the navigation model linked with this edit. :return <XNavigationModel> || None """ return self._navigationModel def navigateToIndex( self, index ): """ Navigates to the inputed action's path. :param action | <QAction> """ self._completerTree.hide() item = self._navigationModel.itemFromIndex(index) self.setText(self._navigationModel.itemPath(item)) def parts( self ): """ Returns the parts that are used for this system. :return [<str>, ..] """ path = str(self.text()).strip(self.separator()) if ( not path ): return [] return path.split(self.separator()) def partsWidget( self ): """ Returns the widget that contains the parts system. :return <QScrollArea> """ return self._partsWidget def startEdit( self ): """ Rebuilds the pathing based on the parts. """ self._originalText = self.text() self.scrollWidget().hide() self.setFocus() self.selectAll() def rebuild( self ): """ Rebuilds the parts widget with the latest text. """ navitem = self.currentItem() if ( navitem ): navitem.initialize() self.setUpdatesEnabled(False) self.scrollWidget().show() self._originalText = '' partsw = self.partsWidget() for button in self._buttonGroup.buttons(): self._buttonGroup.removeButton(button) button.close() button.setParent(None) button.deleteLater() # create the root button layout = partsw.layout() parts = self.parts() button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant('')) button.setProperty('is_completer', wrapVariant(True)) last_button = button self._buttonGroup.addButton(button) layout.insertWidget(0, button) # check to see if we have a navigation model setup if ( self._navigationModel ): last_item = self._navigationModel.itemByPath(self.text()) show_last = last_item and last_item.rowCount() > 0 else: show_last = False # load the navigation system count = len(parts) for i, part in enumerate(parts): path = self.separator().join(parts[:i+1]) button = QToolButton(partsw) button.setAutoRaise(True) button.setText(part) if ( self._navigationModel ): item = self._navigationModel.itemByPath(path) if ( item ): button.setIcon(item.icon()) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(False)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 1, button) # determine if we should show the final button if ( show_last or i < (count - 1) ): button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(True)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 2, button) last_button = button if ( self.scrollWidget().width() < partsw.width() ): self.scrollParts(partsw.width() - self.scrollWidget().width()) self.setUpdatesEnabled(True) self.navigationChanged.emit() def resizeEvent( self, event ): """ Resizes the current widget and its parts widget. :param event | <QResizeEvent> """ super(XNavigationEdit, self).resizeEvent(event) w = self.width() h = self.height() self._scrollWidget.resize(w - 4, h - 4) if ( self._scrollWidget.width() < self._partsWidget.width() ): self.scrollParts( self._partsWidget.width() - self._scrollWidget.width() ) def scrollParts( self, amount ): """ Scrolls the parts to offset the scrolling amount. :param amount | <int> """ change = self._scrollAmount - amount self._partsWidget.scroll(change, 0) self._scrollAmount = amount def scrollWidget( self ): """ Returns the scrolling widget. :return <QScrollArea> """ return self._scrollWidget def separator( self ): """ Returns the separation character that is used for this edit. :return <str> """ return self._separator def setTopLevelItems( self, items ): """ Initializes the navigation system to start with the inputed root \ item. :param item | <XNavigationItem> """ if ( not self._navigationModel ): self.setNavigationModel(XNavigationModel(self)) self._navigationModel.setTopLevelItems(items) def setNavigationModel( self, model ): """ Sets the navigation model for this edit. :param model | <XNavigationModel> """ self._navigationModel = model self._completerTree.setModel(model) if ( model ): model.setSeparator(self.separator()) completer = XNavigationCompleter(model, self) self.setCompleter(completer) completer.popup().installEventFilter(self) else: self.setCompleter(None) self.rebuild() def setParts( self, parts ): """ Sets the path for this edit widget by providing the parts to the path. :param parts | [<str>, ..] """ self.setText(self.separator().join(map(str, parts))) def setSeparator( self, separator ): """ Sets the separator to the inputed character. :param separator | <str> """ self._separator = separator if ( self._navigationModel ): self._navigationModel.setSeparator(separator) self.rebuild() def setText( self, text ): """ Sets the text for this edit to the inputed text. :param text | <str> """ super(XNavigationEdit, self).setText(text) self.scrollWidget().show() if ( text == '' or self._originalText != text ): self.rebuild()
class DBDatabasesWidget(QWidget): """Displays a list of Databases""" def __init__(self, parent=None): QWidget.__init__(self, parent) self.debug = False self.db = None self.setWindowTitle("Databases") #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.setLayout(self.mainLayout) #============================================= ## Top Toolbar topBar = QToolBar() topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.mainLayout.addWidget(topBar) ## Add the action buttons topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add) self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit), "Edit", self.on_server_edit) self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete), "Delete", self.on_server_delete) #============================================= ## Tree self.tree = QTreeWidget() self.mainLayout.addWidget(self.tree) self.tree.setHeaderLabels(["Server", "User", ""]) self.tree.setUniformRowHeights(True) self.tree.setRootIsDecorated(False) self.tree.setColumnWidth(C.widget, 20) self.connect( self.tree, SIGNAL( 'itemSelectionChanged()' ), self.on_tree_selection_changed ) self.connect( self.tree, SIGNAL( 'itemDoubleClicked (QTreeWidgetItem *,int)' ), self.on_tree_double_clicked ) self.buttGroup = QButtonGroup(self) self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"), self.on_open_server) self.on_tree_selection_changed() self.load_servers() #======================================= ##== Tree Events def on_tree_selection_changed(self): disabled = self.tree.selectionModel().hasSelection() == False self.actionServerEdit.setDisabled(disabled) self.actionServerDelete.setDisabled(disabled) def on_tree_double_clicked(self): self.actionServerEdit.trigger() #======================================= ## Server Actions def on_server_add(self): self.show_server_dialog(None) def on_server_edit(self): item = self.tree.currentItem() if item == None: return server = str(item.text(C.server)) self.show_server_dialog(server) def show_server_dialog(self, server=None): d = DBServerDialog.DBServerDialog(self, server) if d.exec_(): self.load_servers() def on_open_server(self, butt): self.emit(SIGNAL("open_server"), butt.property("server").toString()) def load_servers(self): """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """ self.tree.clear() for butt in self.buttGroup.buttons(): self.buttGroup.removeButton(butt) for srv in G.settings.get_servers_list(): item = QTreeWidgetItem() item.setText(C.server, srv['server']) item.setText(C.user, srv['user']) self.tree.addTopLevelItem(item) butt = QToolButton() butt.setIcon(Ico.icon(Ico.Connect)) butt.setProperty("server", srv['server']) self.tree.setItemWidget(item, C.widget, butt) self.buttGroup.addButton(butt) def on_server_delete(self): item = self.tree.currentItem() if item == None: return srv = str(item.text(C.server)) G.settings.delete_server(srv) self.load_servers()
class DBServersWidget(QWidget): """Displays a list of servers""" def __init__(self, parent=None): QWidget.__init__(self, parent) self.debug = False self.connections = {} self.setWindowTitle("Servers") #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.setLayout(self.mainLayout) #============================================= ## Top Toolbar topBar = QToolBar() topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.mainLayout.addWidget(topBar) ## Add the action buttons topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add) self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit), "Edit", self.on_server_edit) self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete), "Delete", self.on_server_delete) #============================================= ## Tree self.tree = QTreeWidget() self.mainLayout.addWidget(self.tree) self.tree.setUniformRowHeights(True) self.tree.setRootIsDecorated(True) self.tree.setHeaderLabels(["Server", "Butt"]) # set header, but hide anyway self.tree.header().hide() self.tree.header().setResizeMode(C.node, QHeaderView.Stretch) self.tree.setColumnWidth(C.butt, 20) self.connect( self.tree, SIGNAL( 'itemSelectionChanged()' ), self.on_tree_selection_changed ) self.connect( self.tree, SIGNAL( 'itemDoubleClicked (QTreeWidgetItem *,int)' ), self.on_tree_double_clicked ) self.buttGroup = QButtonGroup(self) self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"), self.on_open_server) self.on_tree_selection_changed() self.load_servers() #======================================= ##== Tree Events def on_tree_selection_changed(self): disabled = self.tree.selectionModel().hasSelection() == False self.actionServerEdit.setDisabled(disabled) self.actionServerDelete.setDisabled(disabled) def on_tree_double_clicked(self): self.actionServerEdit.trigger() #======================================= ## Server Actions def on_server_add(self): self.show_server_dialog(None) def on_server_edit(self): item = self.tree.currentItem() if item == None: return server = str(item.text(C.server)) self.show_server_dialog(server) def show_server_dialog(self, server=None): d = DBServerDialog.DBServerDialog(self, server) if d.exec_(): self.load_servers() def load_servers(self): """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """ self.tree.clear() for butt in self.buttGroup.buttons(): self.buttGroup.removeButton(butt) for srv in G.settings.get_servers_list(): item = QTreeWidgetItem() item.setText(C.node, srv['server']) #item.setText(C.user, srv['user']) self.tree.addTopLevelItem(item) butt = QToolButton() butt.setIcon(Ico.icon(Ico.Connect)) butt.setProperty("server", srv['server']) self.tree.setItemWidget(item, C.butt, butt) self.buttGroup.addButton(butt) def on_server_delete(self): item = self.tree.currentItem() if item == None: return srv = str(item.text(C.server)) G.settings.delete_server(srv) self.load_servers() def on_open_server(self, butt): # self.emit(SIGNAL("open_server"), butt.property("server").toString()) srv_ki = str(butt.property("server").toString()) server = G.settings.get_server(srv_ki) db = QSqlDatabase.addDatabase("QMYSQL", srv_ki) db.setHostName(server['server']) db.setUserName(server['user']) db.setPassword(server['passwd']) ok = db.open() if ok: #self.connections[srv_ki] = self.load_databases(srv_ki) print "open", ok def load_databases(self, srv_ki): """Load databases into tree node for server; executes 'show databases;' or aslike """ sql = "show databases;" query = QSqlQuery(QSqlDatabase.database(srv_ki)) ok = query.exec_(sql) print ok, sql, query.result() # Get the parent node, ie the server node pItem = self.tree.findItems(srv_ki, Qt.MatchExactly, C.node)[0] ## Assumed value(0) is the table.. we need the defs (ie mysql case) while query.next(): table_name = query.value(0).toString() nuItem = QTreeWidgetItem(pItem) nuItem.setText(C.node, table_name) #print table_name self.tree.setItemExpanded(pItem, True)