Connection list with add folder button and removed naming bug

The creation of folders was not clear and a bug could rename wrong
elements, which the user does not select.
Now connections and folders are handled in separat ways and the user can
add a new folder by pressing a button.
This commit is contained in:
2023-01-14 13:42:18 +01:00
parent d14b6dccdd
commit 15058feb7e
9 changed files with 3617 additions and 3282 deletions

View File

@@ -834,17 +834,31 @@ Ungesicherte Änderungen gehen verloren.</translation>
<translation type="obsolete">Neue Verbindung</translation>
</message>
<message>
<location filename="../revpiplclist.py" line="117"/>
<location filename="../revpiplclist.py" line="282"/>
<source>Question</source>
<translation>Frage</translation>
</message>
<message>
<location filename="../revpiplclist.py" line="117"/>
<location filename="../revpiplclist.py" line="121"/>
<source>Do you really want to quit?
Unsaved changes will be lost.</source>
<translation>Soll das Fenster wirklich geschlossen werden?
Ungesicherte Änderungen gehen verloren.</translation>
</message>
<message>
<location filename="../revpiplclist.py" line="282"/>
<source>If you remote this folder, all containing elements will be removed, too.
Do you want to delete folder and all elements?</source>
<translation>Wird dieser Ordner gelöscht, betrifft dies auch alle Elemente im Ordner.
Wollen sie den Ordner und alle Elemente löschen?</translation>
</message>
<message>
<location filename="../revpiplclist.py" line="324"/>
<source>New folder</source>
<translation>Neuer Ordner</translation>
</message>
</context>
<context>
<name>RevPiProgram</name>

View File

@@ -37,13 +37,30 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.lbl_port.setText(self.lbl_port.text().format(self.__default_port))
self.sbx_port.setValue(self.__default_port)
# Dirty workaround to remove default button to prevent action on ENTER key, while user edit texts
self.__btn_dummy = QtWidgets.QPushButton(self)
self.__btn_dummy.setVisible(False)
self.__btn_dummy.setDefault(True)
def _load_cbb_folder(self):
"""Clean up all entries and reload existing ones from treeview."""
self.cbb_folder.blockSignals(True)
self.cbb_folder.clear()
self.cbb_folder.addItem("")
for i in range(self.tre_connections.topLevelItemCount()):
item = self.tre_connections.topLevelItem(i)
if item.type() != NodeType.DIR:
continue
self.cbb_folder.addItem(item.text(0))
self.cbb_folder.blockSignals(False)
def _load_settings(self):
"""Load values to GUI widgets."""
pi.logger.debug("RevPiPlcList._load_settings")
self.tre_connections.clear()
self.cbb_folder.clear()
self.cbb_folder.addItem("")
# Get length of array and close it, the RevPiSettings-class need it
count_settings = helper.settings.beginReadArray("connections")
@@ -67,7 +84,6 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
sub_folder.setText(0, folder)
self.tre_connections.addTopLevelItem(sub_folder)
self.cbb_folder.addItem(folder)
sub_folder.addChild(con_item)
else:
@@ -76,8 +92,7 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.tre_connections.expandAll()
self.changes = False
if self.tre_connections.topLevelItemCount() == 0:
self._edit_state()
self._edit_state()
def accept(self) -> None:
pi.logger.debug("RevPiPlcList.accept")
@@ -150,6 +165,8 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
def _edit_state(self):
"""Set enabled status of all controls, depending on selected item."""
pi.logger.debug("RevPiPlcList._edit_state")
item = self.tre_connections.currentItem()
if item is None:
up_ok = False
@@ -171,12 +188,16 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.btn_up.setEnabled(up_ok)
self.btn_down.setEnabled(down_ok)
self.btn_delete.setEnabled(con_item)
self.btn_delete.setEnabled(con_item or dir_item)
self.txt_name.setEnabled(con_item)
self.txt_address.setEnabled(con_item)
self.sbx_port.setEnabled(con_item)
self.sbx_timeout.setEnabled(con_item)
self.cbb_folder.setEnabled(con_item or dir_item)
self.cbb_folder.setEditable(dir_item)
if self.cbb_folder.isEditable():
# Disable auto complete, this would override a new typed name with existing one
self.cbb_folder.setCompleter(None)
self.cbx_ssh_use_tunnel.setEnabled(con_item)
self.sbx_ssh_port.setEnabled(con_item)
@@ -203,22 +224,25 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
if 0 <= new_index < dir_item.childCount():
item = dir_item.takeChild(index)
dir_item.insertChild(new_index, item)
self.tre_connections.expandItem(dir_item)
else:
index = self.tre_connections.indexOfTopLevelItem(item)
new_index = index + count
if 0 <= index < self.tre_connections.topLevelItemCount():
item = self.tre_connections.takeTopLevelItem(index)
self.tre_connections.insertTopLevelItem(new_index, item)
if item.type() == NodeType.DIR:
# Expand a moved dir node, it would be collapsed after move
self.tre_connections.expandItem(item)
self.tre_connections.setCurrentItem(item)
self._edit_state()
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, QtWidgets.QTreeWidgetItem)
def on_tre_connections_currentItemChanged(
self, current: QtWidgets.QTreeWidgetItem, previous: QtWidgets.QTreeWidgetItem):
self._edit_state()
self._load_cbb_folder()
if current and current.type() == NodeType.CON:
self.__current_item = current
@@ -255,9 +279,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
@QtCore.pyqtSlot()
def on_btn_delete_clicked(self):
"""Remove selected entry."""
item = self.tre_connections.currentItem()
if item and item.type() == NodeType.CON:
def remove_item(item: QtWidgets.QTreeWidgetItem):
"""Remove CON item and save keyring actions for save action."""
revpi_settings = item.data(0, WidgetData.revpi_settings) # type: RevPiSettings
if revpi_settings.ssh_saved_password:
# Cleans up keyring in save function
@@ -270,7 +294,27 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
index = self.tre_connections.indexOfTopLevelItem(item)
self.tre_connections.takeTopLevelItem(index)
self._edit_state()
item_to_remove = self.tre_connections.currentItem()
if item_to_remove and item_to_remove.type() == NodeType.DIR:
if item_to_remove.childCount():
rc = QtWidgets.QMessageBox.question(
self, self.tr("Question"), self.tr(
"If you remote this folder, all containing elements will be removed, too. \n\n"
"Do you want to delete folder and all elements?"
),
)
if rc != QtWidgets.QMessageBox.Yes:
return
while item_to_remove.childCount() > 0:
remove_item(item_to_remove.child(0))
item_index = self.tre_connections.indexOfTopLevelItem(item_to_remove)
self.tre_connections.takeTopLevelItem(item_index)
elif item_to_remove and item_to_remove.type() == NodeType.CON:
remove_item(item_to_remove)
@QtCore.pyqtSlot()
def on_btn_add_clicked(self, settings_preset: RevPiSettings = None):
@@ -291,6 +335,20 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.txt_name.setFocus()
self.txt_name.selectAll()
@QtCore.pyqtSlot()
def on_btn_add_dir_clicked(self):
"""Add a new folder."""
folder_text = self.tr("New folder")
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
sub_folder.setText(0, folder_text)
self.tre_connections.addTopLevelItem(sub_folder)
self.tre_connections.setCurrentItem(sub_folder)
self.cbb_folder.setFocus()
self.changes = True
@QtCore.pyqtSlot(str)
def on_txt_name_textEdited(self, text):
if self.__current_item.type() != NodeType.CON:
@@ -349,65 +407,47 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
settings.ssh_user = text
self.changes = True
@QtCore.pyqtSlot(str)
def on_cbb_folder_currentIndexChanged(self, text: str):
pi.logger.debug("RevPiPlcList.on_cbb_folder_currentIndexChanged({0})".format(text))
if self.__current_item.type() == NodeType.CON:
new_dir_node = self._get_folder_item(text)
current_dir_node = self.__current_item.parent()
if current_dir_node == new_dir_node:
# No change required, both nodes are the same
return
change_item = self.__current_item
self.tre_connections.blockSignals(True)
self.changes = True
if current_dir_node:
# Move an element to other folder or root
index = current_dir_node.indexOfChild(change_item)
change_item = current_dir_node.takeChild(index)
else:
# Move a root element to a folder
index = self.tre_connections.indexOfTopLevelItem(change_item)
change_item = self.tre_connections.takeTopLevelItem(index)
if text == "":
self.tre_connections.addTopLevelItem(change_item)
else:
new_dir_node.addChild(change_item)
self.tre_connections.blockSignals(False)
self.tre_connections.setCurrentItem(change_item)
@QtCore.pyqtSlot(str)
def on_cbb_folder_editTextChanged(self, text: str):
pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text))
if self.__current_item.type() == NodeType.DIR:
if self.__current_item.type() == NodeType.DIR and self.__current_item.text(0) != text:
# We just have to rename the dir node
self.__current_item.setText(0, text)
elif self.__current_item.type() == NodeType.CON:
sub_folder = self._get_folder_item(text)
dir_node = self.__current_item.parent()
if dir_node:
if dir_node.text(0) == text:
# It is the same folder
return
if text != "" and dir_node.childCount() == 1 and not sub_folder:
# The folder hold just one item, so we can rename that
for i in range(self.cbb_folder.count()):
if self.cbb_folder.itemText(i) == dir_node.text(0):
self.cbb_folder.setItemText(i, text)
break
dir_node.setText(0, text)
return
index = dir_node.indexOfChild(self.__current_item)
self.__current_item = dir_node.takeChild(index)
elif text != "":
# Move root to folder
index = self.tre_connections.indexOfTopLevelItem(self.__current_item)
self.__current_item = self.tre_connections.takeTopLevelItem(index)
else:
# Root stays root
return
if text == "":
self.tre_connections.addTopLevelItem(self.__current_item)
else:
if sub_folder is None:
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
sub_folder.setText(0, text)
self.tre_connections.addTopLevelItem(sub_folder)
self.cbb_folder.addItem(text)
sub_folder.addChild(self.__current_item)
if dir_node and dir_node.childCount() == 0:
# Remove empty folders
for i in range(self.cbb_folder.count()):
if self.cbb_folder.itemText(i) == dir_node.text(0):
self.cbb_folder.removeItem(i)
break
index = self.tre_connections.indexOfTopLevelItem(dir_node)
self.tre_connections.takeTopLevelItem(index)
self.tre_connections.setCurrentItem(self.__current_item)
self.cbb_folder.setFocus()
self.changes = True
# endregion # # # # #

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,7 @@ class Ui_diag_connections(object):
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
self.cbb_folder = QtWidgets.QComboBox(self.tab_connection)
self.cbb_folder.setEditable(True)
self.cbb_folder.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.cbb_folder.setObjectName("cbb_folder")
self.cbb_folder.addItem("")
self.cbb_folder.setItemText(0, "")
@@ -115,39 +116,41 @@ class Ui_diag_connections(object):
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.vl_edit.addItem(spacerItem)
self.btn_up = QtWidgets.QPushButton(diag_connections)
self.btn_up.setText("")
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/action/ico/arrow-up.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_up.setIcon(icon)
self.btn_up.setObjectName("btn_up")
self.vl_edit.addWidget(self.btn_up)
self.btn_down = QtWidgets.QPushButton(diag_connections)
self.btn_down.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/action/ico/arrow-down.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_down.setIcon(icon1)
self.btn_down.setObjectName("btn_down")
self.vl_edit.addWidget(self.btn_down)
self.btn_delete = QtWidgets.QPushButton(diag_connections)
self.btn_delete.setText("")
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/action/ico/edit-delete.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon2.addPixmap(QtGui.QPixmap(":/action/ico/edit-delete-6.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_delete.setIcon(icon2)
self.btn_delete.setObjectName("btn_delete")
self.vl_edit.addWidget(self.btn_delete)
self.btn_add = QtWidgets.QPushButton(diag_connections)
self.btn_add.setText("")
self.btn_add_dir = QtWidgets.QPushButton(diag_connections)
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/action/ico/edit-add.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_add.setIcon(icon3)
icon3.addPixmap(QtGui.QPixmap(":/action/ico/folder-open.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_add_dir.setIcon(icon3)
self.btn_add_dir.setObjectName("btn_add_dir")
self.vl_edit.addWidget(self.btn_add_dir)
self.btn_add = QtWidgets.QPushButton(diag_connections)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/action/ico/edit-add.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.btn_add.setIcon(icon4)
self.btn_add.setObjectName("btn_add")
self.vl_edit.addWidget(self.btn_add)
self.gridLayout.addLayout(self.vl_edit, 0, 1, 1, 1)
self.retranslateUi(diag_connections)
self.tab_properties.setCurrentIndex(0)
self.btn_box.accepted.connect(diag_connections.accept) # type: ignore
self.btn_box.rejected.connect(diag_connections.reject) # type: ignore
self.btn_box.accepted.connect(diag_connections.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(diag_connections)
def retranslateUi(self, diag_connections):

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
ui_dev/ico/process-stop.ico Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -9,6 +9,7 @@
<file>ico/file-python.ico</file>
</qresource>
<qresource prefix="action">
<file>ico/edit-delete-6.ico</file>
<file>ico/applications-utilities.ico</file>
<file>ico/edit-find.ico</file>
<file>ico/process-stop.ico</file>

View File

@@ -108,6 +108,9 @@
<property name="editable">
<bool>true</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string/>
@@ -218,9 +221,6 @@
</item>
<item>
<widget class="QPushButton" name="btn_up">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/action/ico/arrow-up.ico</normaloff>:/action/ico/arrow-up.ico</iconset>
@@ -229,9 +229,6 @@
</item>
<item>
<widget class="QPushButton" name="btn_down">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/action/ico/arrow-down.ico</normaloff>:/action/ico/arrow-down.ico</iconset>
@@ -240,20 +237,22 @@
</item>
<item>
<widget class="QPushButton" name="btn_delete">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/action/ico/edit-delete.ico</normaloff>:/action/ico/edit-delete.ico</iconset>
<normaloff>:/action/ico/edit-delete-6.ico</normaloff>:/action/ico/edit-delete-6.ico</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_add_dir">
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/action/ico/folder-open.ico</normaloff>:/action/ico/folder-open.ico</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_add">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/action/ico/edit-add.ico</normaloff>:/action/ico/edit-add.ico</iconset>
@@ -268,22 +267,6 @@
<include location="ressources.qrc"/>
</resources>
<connections>
<connection>
<sender>btn_box</sender>
<signal>accepted()</signal>
<receiver>diag_connections</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>btn_box</sender>
<signal>rejected()</signal>
@@ -291,8 +274,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
<x>322</x>
<y>564</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -300,5 +283,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>btn_box</sender>
<signal>accepted()</signal>
<receiver>diag_connections</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>254</x>
<y>564</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>