Exemple #1
0
def test():
    class XTest( XBase ):
        def __init__( self ):
            XBase.__init__( self )
            self.name = ""
            self.vorname = ""
            self.alter = 0
            self.haarfarbe = ""
            self.schuhgroesse = 0
            self.bemerkung = ""

    x1 = XTest()
    x1.name = "Kendel"
    x1.vorname = "Martin"
    x1.alter = 66
    x1.haarfarbe = "grau"
    x1.schuhgroesse = 44
    x1.bemerkung = "Das ist alles \nein riesengroßer Irrtum"

    x2 = XTest()
    x2.name = "Haaretz"
    x2.vorname = "Yosh"
    x2.alter = 56
    x2.haarfarbe = "schwarz"
    x2.schuhgroesse = 42

    tm = DefaultIccTableModel( (x1, x2 ) )
    tm.setSortable( True )
    tm.setKeyHeaderMappings( { "Nachname": "name", "Vorname": "vorname", "Alter": "alter", "Bemerkung": "bemerkung" } )
    tm.setNumColumnsIndexes( (2,) )

    from PySide2.QtWidgets import QApplication
    app = QApplication()

    tv = QTableView( )
    tv.setModel( tm )
    tv.setAlternatingRowColors( True )
    tv.setSortingEnabled( True )
    tv.resizeColumnsToContents()
    tv.resizeRowsToContents()
    tm.layoutChanged.connect( tv.resizeRowsToContents ) ## <======== WICHTIG bei mehrzeiligem Text in einer Zelle!
    tv.show()

    app.exec_()
Exemple #2
0
class MetricsDialog(PluginDialog):

    ENABLE = True

    def __init__(self, conn=None, parent=None):
        super().__init__(parent)
        self.conn = conn

        self.view = QTableView()
        self.model = MetricModel()
        self.buttons = QDialogButtonBox(QDialogButtonBox.Ok)

        self.view.setModel(self.model)
        self.view.setAlternatingRowColors(True)
        self.view.horizontalHeader().hide()
        self.view.verticalHeader().hide()
        self.view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)

        self.buttons.accepted.connect(self.accept)

        self.setWindowTitle(self.tr("Project metrics"))

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.view)
        v_layout.addWidget(self.buttons)
        self.setLayout(v_layout)

        # Async stuff
        self.metrics_runnable = None
        self.populate()

    def populate(self):
        """Async implementation to populate the view

        Notes:
            When closing the dialog window, the thread is not stopped.
        """
        def compute_metrics(conn):
            """Async function"""
            return {
                "variants": get_variant_count(conn),
                "snps": get_snp_count(conn),
                "transitions": get_variant_transition(conn),
                "transversions": get_variant_transversion(conn),
                "samples": get_sample_count(conn),
            }

        self.model.add_metrics(self.tr("Loading..."), self.tr("data..."))

        self.metrics_runnable = SqlRunnable(self.conn, compute_metrics)
        self.metrics_runnable.finished.connect(self.loaded)
        QThreadPool.globalInstance().start(self.metrics_runnable)

    def loaded(self):
        """Called at the end of the thread and populate data"""
        results = self.metrics_runnable.results

        self.model.clear()
        ratio = results["transitions"] / results["transversions"]

        self.model.add_metrics("Variant count", results["variants"])
        self.model.add_metrics("Snp count", results["snps"])
        self.model.add_metrics("Transition count", results["transitions"])
        self.model.add_metrics("Transversion count", results["transversions"])
        self.model.add_metrics("Tr/tv ratio", ratio)
        self.model.add_metrics("Sample count", results["variants"])

        self.view.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.view.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.Stretch)
Exemple #3
0
class DictWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__()

        self.view = QTableView()
        self.model = DictModel()
        self.proxy_model = QSortFilterProxyModel()
        self.search_bar = QLineEdit()
        self._show_loading = False

        self.proxy_model.setSourceModel(self.model)

        self.view.setModel(self.proxy_model)
        self.view.setAlternatingRowColors(True)
        self.view.horizontalHeader().setStretchLastSection(True)
        self.view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.view.setSortingEnabled(True)
        self.view.verticalHeader().hide()

        self.search_bar.textChanged.connect(
            self.proxy_model.setFilterRegularExpression)
        self.search_bar.setVisible(False)

        self._show_search_action = QAction("show search bar")
        self._show_search_action.setCheckable(True)
        self._show_search_action.setShortcutContext(Qt.WidgetShortcut)
        self._show_search_action.setShortcut(QKeySequence.Find)
        self._show_search_action.triggered.connect(self._on_show_search)

        self._close_search_action = QAction()
        self._close_search_action.setShortcut(QKeySequence(Qt.Key_Escape))
        self._close_search_action.setShortcutContext(Qt.WidgetShortcut)
        self._close_search_action.triggered.connect(self._on_close_search)

        self.view.addAction(self._show_search_action)
        self.search_bar.addAction(self._close_search_action)

        _layout = QVBoxLayout()
        _layout.addWidget(self.view)
        _layout.addWidget(self.search_bar)
        _layout.setContentsMargins(0, 0, 0, 0)

        self.setLayout(_layout)
        print("init")

    def set_dict(self, data: dict):

        self.model.set_dict(data)

    def _on_show_search(self):
        self.search_bar.setVisible(True)
        self.search_bar.setFocus(Qt.ShortcutFocusReason)

    def _on_close_search(self):
        self.search_bar.hide()
        self.search_bar.clear()
        self.view.setFocus(Qt.ShortcutFocusReason)

    def set_header_visible(self, visible=True):
        self.view.horizontalHeader().setVisible(visible)

    def clear(self):
        self.model.clear()

    def paintEvent(self, event: QPaintEvent):

        if self._show_loading:
            painter = QPainter(self)
            painter.drawText(self.rect(), Qt.AlignCenter,
                             self.tr("Loading ..."))
        else:
            super().paintEvent(event)

    def set_loading(self, show=True):
        self._show_loading = True
        self.view.setVisible(not show)
        self.update()
Exemple #4
0
class IncomeSpendingWidget(AbstractOperationDetails):
    def __init__(self, parent=None):
        AbstractOperationDetails.__init__(self, parent)
        self.name = "Income/Spending"

        self.details_model = None
        self.category_delegate = CategorySelectorDelegate()
        self.tag_delegate = TagSelectorDelegate()
        self.float_delegate = FloatDelegate(2)

        self.date_label = QLabel(self)
        self.details_label = QLabel(self)
        self.account_label = QLabel(self)
        self.peer_label = QLabel(self)

        self.main_label.setText(g_tr("IncomeSpendingWidget", "Income / Spending"))
        self.date_label.setText(g_tr("IncomeSpendingWidget", "Date/Time"))
        self.details_label.setText(g_tr("IncomeSpendingWidget", "Details"))
        self.account_label.setText(g_tr("IncomeSpendingWidget", "Account"))
        self.peer_label.setText(g_tr("IncomeSpendingWidget", "Peer"))

        self.timestamp_editor = QDateTimeEdit(self)
        self.timestamp_editor.setCalendarPopup(True)
        self.timestamp_editor.setTimeSpec(Qt.UTC)
        self.timestamp_editor.setFixedWidth(self.timestamp_editor.fontMetrics().width("00/00/0000 00:00:00") * 1.25)
        self.timestamp_editor.setDisplayFormat("dd/MM/yyyy hh:mm:ss")
        self.account_widget = AccountSelector(self)
        self.peer_widget = PeerSelector(self)
        self.a_currency = OptionalCurrencyComboBox(self)
        self.a_currency.setText(g_tr("IncomeSpendingWidget", "Paid in foreign currency:"))
        self.add_button = QPushButton(self)
        self.add_button.setText(" +️ ")
        self.add_button.setFont(self.bold_font)
        self.add_button.setFixedWidth(self.add_button.fontMetrics().width("XXX"))
        self.del_button = QPushButton(self)
        self.del_button.setText(" — ️")
        self.del_button.setFont(self.bold_font)
        self.del_button.setFixedWidth(self.del_button.fontMetrics().width("XXX"))
        self.copy_button = QPushButton(self)
        self.copy_button.setText(" >> ️")
        self.copy_button.setFont(self.bold_font)
        self.copy_button.setFixedWidth(self.copy_button.fontMetrics().width("XXX"))
        self.details_table = QTableView(self)
        self.details_table.horizontalHeader().setFont(self.bold_font)
        self.details_table.setAlternatingRowColors(True)
        self.details_table.verticalHeader().setVisible(False)
        self.details_table.verticalHeader().setMinimumSectionSize(20)
        self.details_table.verticalHeader().setDefaultSectionSize(20)

        self.layout.addWidget(self.date_label, 1, 0, 1, 1, Qt.AlignLeft)
        self.layout.addWidget(self.details_label, 2, 0, 1, 1, Qt.AlignLeft)

        self.layout.addWidget(self.timestamp_editor, 1, 1, 1, 4)
        self.layout.addWidget(self.add_button, 2, 1, 1, 1)
        self.layout.addWidget(self.copy_button, 2, 2, 1, 1)
        self.layout.addWidget(self.del_button, 2, 3, 1, 1)

        self.layout.addWidget(self.account_label, 1, 5, 1, 1, Qt.AlignRight)
        self.layout.addWidget(self.peer_label, 2, 5, 1, 1, Qt.AlignRight)

        self.layout.addWidget(self.account_widget, 1, 6, 1, 1)
        self.layout.addWidget(self.peer_widget, 2, 6, 1, 1)

        self.layout.addWidget(self.a_currency, 1, 7, 1, 1)

        self.layout.addWidget(self.commit_button, 0, 9, 1, 1)
        self.layout.addWidget(self.revert_button, 0, 10, 1, 1)

        self.layout.addWidget(self.details_table, 4, 0, 1, 11)
        self.layout.addItem(self.horizontalSpacer, 1, 8, 1, 1)

        self.add_button.clicked.connect(self.addChild)
        self.del_button.clicked.connect(self.delChild)

        super()._init_db("actions")
        self.mapper.setItemDelegate(IncomeSpendingWidgetDelegate(self.mapper))

        self.details_model = DetailsModel(self.details_table, db_connection())
        self.details_model.setTable("action_details")
        self.details_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.details_table.setModel(self.details_model)
        self.details_model.dataChanged.connect(self.onDataChange)

        self.account_widget.changed.connect(self.mapper.submit)
        self.peer_widget.changed.connect(self.mapper.submit)
        self.a_currency.changed.connect(self.mapper.submit)
        self.a_currency.updated.connect(self.details_model.setAltCurrency)

        self.mapper.addMapping(self.timestamp_editor, self.model.fieldIndex("timestamp"))
        self.mapper.addMapping(self.account_widget, self.model.fieldIndex("account_id"))
        self.mapper.addMapping(self.peer_widget, self.model.fieldIndex("peer_id"))
        self.mapper.addMapping(self.a_currency, self.model.fieldIndex("alt_currency_id"))

        self.details_table.setItemDelegateForColumn(2, self.category_delegate)
        self.details_table.setItemDelegateForColumn(3, self.tag_delegate)
        self.details_table.setItemDelegateForColumn(4, self.float_delegate)
        self.details_table.setItemDelegateForColumn(5, self.float_delegate)

        self.model.select()
        self.details_model.select()
        self.details_model.configureView()

    def setId(self, id):
        super().setId(id)
        self.details_model.setFilter(f"action_details.pid = {id}")

    @Slot()
    def addChild(self):
        new_record = self.details_model.record()
        new_record.setNull("tag_id")
        new_record.setValue("amount", 0)
        new_record.setValue("amount_alt", 0)
        if not self.details_model.insertRecord(-1, new_record):
            logging.fatal(
                g_tr('AbstractOperationDetails', "Failed to add new record: ") + self.details_model.lastError().text())
            return

    @Slot()
    def delChild(self):
        idx = self.details_table.selectionModel().selection().indexes()
        selected_row = idx[0].row()
        self.details_model.removeRow(selected_row)
        self.details_table.setRowHidden(selected_row, True)

    @Slot()
    def saveChanges(self):
        if not self.model.submitAll():
            logging.fatal(
                g_tr('AbstractOperationDetails', "Operation submit failed: ") + self.model.lastError().text())
            return
        pid = self.model.data(self.model.index(0, self.model.fieldIndex("id")))
        if pid is None:  # we just have saved new action record and need last inserted id
            pid = self.model.query().lastInsertId()
        for row in range(self.details_model.rowCount()):
            self.details_model.setData(self.details_model.index(row, self.details_model.fieldIndex("pid")), pid)
        if not self.details_model.submitAll():
            logging.fatal(g_tr('AbstractOperationDetails', "Operation details submit failed: ")
                          + self.details_model.lastError().text())
            return
        self.modified = False
        self.commit_button.setEnabled(False)
        self.revert_button.setEnabled(False)
        self.dbUpdated.emit()
        return

    def createNew(self, account_id=0):
        super().createNew(account_id)
        self.details_model.setFilter(f"action_details.pid = 0")

    def prepareNew(self, account_id):
        new_record = self.model.record()
        new_record.setNull("id")
        new_record.setValue("timestamp", int(datetime.now().replace(tzinfo=tz.tzutc()).timestamp()))
        new_record.setValue("account_id", account_id)
        new_record.setValue("peer_id", 0)
        new_record.setValue("alt_currency_id", None)
        return new_record

    def copyNew(self):
        old_id = self.model.record(self.mapper.currentIndex()).value(0)
        super().copyNew()
        self.details_model.setFilter(f"action_details.pid = 0")
        query = executeSQL("SELECT * FROM action_details WHERE pid = :pid ORDER BY id DESC",
                           [(":pid", old_id)])
        while query.next():
            new_record = query.record()
            new_record.setNull("id")
            new_record.setNull("pid")
            assert self.details_model.insertRows(0, 1)
            self.details_model.setRecord(0, new_record)

    def copyToNew(self, row):
        new_record = self.model.record(row)
        new_record.setNull("id")
        new_record.setValue("timestamp", int(datetime.now().replace(tzinfo=tz.tzutc()).timestamp()))
        return new_record
Exemple #5
0
class RemoverDialog(QDialog):
    def __init__(self, parent=None):
        super(RemoverDialog, self).__init__(parent)

        self.setupUi()

    def setupUi(self):
        self.menuBar = QMenuBar()
        self.menuBar.show()

        self.pathInputBox = QLineEdit(self)
        self.pathInputBox.setEnabled(False)
        self.pathInputBox.setToolTip(
            'Input a path or drag a directory here...')

        self.openPathButton = QPushButton('Open...', self)
        self.openPathButton.clicked.connect(self.openPath)

        inputLayout = QHBoxLayout()
        inputLayout.addWidget(QLabel('Path:', self))
        inputLayout.addWidget(self.pathInputBox)
        inputLayout.addWidget(self.openPathButton)

        self.filterFolderCheckBox = QCheckBox('Folders', self)
        self.filterFolderCheckBox.setChecked(True)
        self.filterFolderCheckBox.toggled.connect(self.filter)
        self.filterFileCheckBox = QCheckBox('Files', self)
        self.filterFileCheckBox.setChecked(True)
        self.filterFileCheckBox.toggled.connect(self.filter)
        self.filterSuffixCheckBox = QCheckBox('Suffixes', self)
        self.filterSuffixCheckBox.setChecked(True)
        self.filterSuffixCheckBox.toggled.connect(self.filter)

        filterLayout = QHBoxLayout()
        filterLayout.addWidget(self.filterFolderCheckBox)
        filterLayout.addWidget(self.filterFileCheckBox)
        filterLayout.addWidget(self.filterSuffixCheckBox)
        filterLayout.addStretch()

        self.trashButton = QPushButton('Trash', self)
        self.trashButton.clicked.connect(self.trash)
        self.deleteButton = QPushButton('Delete', self)
        self.deleteButton.clicked.connect(self.delete)

        confirmLayout = QHBoxLayout()
        confirmLayout.addStretch()
        confirmLayout.addWidget(self.trashButton)
        confirmLayout.addWidget(self.deleteButton)

        layout = QVBoxLayout()
        layout.addLayout(inputLayout)
        layout.addLayout(filterLayout)
        layout.addWidget(self.createResultView())
        layout.addLayout(confirmLayout)

        self.setAcceptDrops(True)
        self.setLayout(layout)
        self.setMinimumWidth(600)
        self.setWindowTitle('Remover')
        self.setWindowIcon(QApplication.style().standardIcon(
            QStyle.SP_DirIcon))

    def createResultView(self):
        self.resultModel = ResultModel(self)
        self.resultView = QTableView(self)
        self.resultView.setSortingEnabled(True)
        self.resultView.setShowGrid(False)
        self.resultView.setAlternatingRowColors(True)
        self.resultView.verticalHeader().hide()
        self.resultView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.resultView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.sortFilterModel = SortFilterResultModel(self.resultModel, self)
        self.resultView.setModel(self.sortFilterModel)
        return self.resultView

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls() and len(event.mimeData().urls()) == 1:
            event.acceptProposedAction()

    def dropEvent(self, event):
        if event.mimeData().hasUrls() and len(event.mimeData().urls()) == 1:
            self.pathInputBox.setText(event.mimeData().urls()[0].toLocalFile())
            self.reloadPath(self.pathInputBox.text())

    def openPath(self):
        path = QFileDialog.getExistingDirectory(
            parent=self,
            caption='Open',
            options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        if path:
            self.pathInputBox.setText(path)
            self.reloadPath(path)

    def filter(self):
        filters = []
        if self.filterFolderCheckBox.isChecked():
            filters.append('folder')
        if self.filterFileCheckBox.isChecked():
            filters.append('file')
        if self.filterSuffixCheckBox.isChecked():
            filters.append('suffix')
        self.sortFilterModel.setFilterRegularExpression('|'.join(filters))
        self.sortFilterModel.filterRegularExpression()
        self.resultView.resizeRowsToContents()

    def trash(self):
        folders, files = self.collectSelectedFolderFiles()

        try:
            for file in files:
                send2trash(file)
            for folder in folders:
                if QFile(folder).exists():
                    send2trash(folder)
        except:
            QMessageBox.warning(self, 'Failed',
                                'Failed to trash selected files/folders')
            return

        self.reloadPath(self.pathInputBox.text())

    def delete(self):
        folders, files = self.collectSelectedFolderFiles()

        try:
            for file in files:
                os.remove(file)
            for folder in folders:
                shutil.rmtree(folder)
        except:
            QMessageBox.warning(self, 'Failed',
                                'Failed to delete selected files/folders')
            return

        self.reloadPath(self.pathInputBox.text())

    def collectSelectedFolderFiles(self):
        folders, files, suffixes = [], [], []
        for index in self.resultView.selectedIndexes():
            if index.column() != 0:
                # 忽略第二列的selection
                continue
            item = self.sortFilterModel.data(index, Qt.UserRole)
            if 'folder' == item.type:
                folders.append(item.name)
            elif 'file' == item.type:
                files.append(item.name)
            elif 'suffix' == item.type:
                suffixes.append(item.name[2:])

        # 将后缀符合选中条件的文件添加到files中
        path = self.pathInputBox.text()
        iterator = QDirIterator(path,
                                filter=QDir.Files | QDir.Dirs | QDir.Hidden
                                | QDir.NoDotAndDotDot,
                                flags=QDirIterator.Subdirectories)
        folderPaths, filePaths = set(), set()
        while iterator.hasNext():
            file = iterator.next()
            if '.' == file[-1] or '..' == file[-2]:
                continue
            fileInfo = QFileInfo(file)
            if fileInfo.isDir():
                if fileInfo.fileName() in folders:
                    folderPaths.add(fileInfo.absoluteFilePath())
            if fileInfo.isFile():
                if fileInfo.fileName() in files:
                    filePaths.add(fileInfo.absoluteFilePath())
                if fileInfo.suffix() in suffixes:
                    filePaths.add(fileInfo.absoluteFilePath())
        return sorted(folderPaths), filePaths

    def reloadPath(self, path):
        self.resultModel.reload(path)
        self.resultView.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.Stretch)
        self.resultView.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.filter()
        self.resultView.sortByColumn(1, Qt.DescendingOrder)
Exemple #6
0
class SydTableWidget(QtWidgets.QWidget, Ui_SydTableWidget):
    table_reloaded = Signal()

    def __init__(self, filename, table_name, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        # internal members
        self._filename = filename
        self._table_name = table_name
        self._db = None
        self._data = None
        self._model = None
        self._filter_proxy_model = None
        self._header = None
        self._toggle_width_menus = []
        self.button_reload.clicked.connect(self.slot_on_reload)
        self.button_view.hide()
        self.button_view.clicked.connect(self.slot_on_view)

        # initial UI
        # self.setAutoFillBackground(True)
        self.scrollArea.setVisible(False)
        self.button_view.setEnabled(False)

        # pop-up window
        self.w = None

    def table_name(self):
        return self._table_name

    def model(self):
        return self._model

    def set_data(self, data):
        self._data = data
        db = self._db
        table_name = self._table_name

        # define and set the model
        self._model = SydTableModel(self._filename, db, table_name, data)

        # remove previous widget
        self.table_view.setParent(None)
        del self.table_view

        # create new one
        self.table_view = QTableView(self)
        self.verticalLayout.addWidget(self.table_view)

        # define own header (with column filter)
        self._header = SydColumnFilterHeader(self.table_view)
        ncol = self._model.columnCount(0)

        # define and set the filter/sort proxy
        self._filter_proxy_model = SydTableSortFilterProxyModel(self._header)
        self._filter_proxy_model.setSourceModel(self._model)
        self._filter_proxy_model.setSortLocaleAware(True)
        self._filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive)
        self.table_view.setModel(self._filter_proxy_model)

        # selection model for the button_view
        self.table_view.setSelectionBehavior(QTableView.SelectRows)
        selection = self.table_view.selectionModel()
        selection.selectionChanged.connect(self.on_selection_change)

        # set the columns filters
        self._header.set_filter_editors(ncol, self._filter_proxy_model)

        # setup the context menu
        self._header.setContextMenuPolicy(Qt.CustomContextMenu)
        self._header.customContextMenuRequested.connect(
            self.slot_on_column_header_popup)
        self._header.filterActivated.connect(self.slot_on_col_filter_changed)

        self.table_view.setHorizontalHeader(self._header)

        # remove previous col buttons
        c = self.layout_col_buttons.takeAt(0)
        while c and c.widget():
            c.widget().setParent(None)
            del c
            c = self.layout_col_buttons.takeAt(0)

        # create new col buttons
        def create_lambda(icol):
            return lambda: self.slot_on_col_button_clicked(icol)

        self.col_buttons = []
        for i in range(0, ncol):
            s = self._model._headers[i]
            b = QPushButton(s)  # , parent=self.layout_col_buttons)
            b.clicked.connect(create_lambda(i))
            b.setFlat(True)
            b.setVisible(False)
            self.layout_col_buttons.addWidget(b)
            self.col_buttons.append(b)
        h = QSpacerItem(878, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.layout_col_buttons.addItem(h)

        # create menu col width
        for i in range(0, ncol):
            b = QAction(self)
            b.setText('Toggle adjust width')
            b.setCheckable(True)
            b.setChecked(False)
            self._toggle_width_menus.append(b)
            # special case for first column ('id')
            if i == 0:
                b.setChecked(True)
                self.slot_on_auto_width_column(i)

        # make the area invisible first
        self.scrollArea.setVisible(False)
        # self.scrollArea.setAutoFillBackground(True)
        # self.table_view.setAutoFillBackground(True)
        self.table_view.setAlternatingRowColors(True)

        # initial nb of elements
        self.slot_on_col_filter_changed()

        # double click header
        self.table_view.horizontalHeader().sectionDoubleClicked. \
            connect(self.slot_on_toggle_auto_width_column)

        # global filter
        self.edit_filter.textChanged.connect(self.slot_on_filter_changed)
        self.edit_filter.setClearButtonEnabled(True)

        # allow sorting
        self.table_view.setSortingEnabled(True)
        self._filter_proxy_model.sort(0, Qt.AscendingOrder)
        self._filter_proxy_model.invalidateFilter()
        self._header.updateGeometries()

    def slot_on_column_header_popup(self, pos):
        idx = self.table_view.horizontalHeader().logicalIndexAt(pos)
        menu = QMenu(self.table_view)
        a = QAction(self)
        name = self._model._col_names[idx]
        a.setText(f'Hide {name}')
        a.triggered.connect(lambda col_name=idx: self.slot_on_hide_column(idx))
        b = self._toggle_width_menus[idx]
        b.triggered.connect(
            lambda col_name=idx: self.slot_on_auto_width_column(idx))
        menu.addAction(a)
        menu.addAction(b)
        menu.exec_(self.table_view.mapToGlobal(pos))

    def slot_on_hide_column(self, idx):
        # hide the column
        self.table_view.setColumnHidden(idx, True)
        # show the button
        self.scrollArea.setVisible(True)
        self.col_buttons[idx].setVisible(True)
        self._header.updateGeometries()

    def slot_on_col_button_clicked(self, idx):
        # show the column
        self.table_view.setColumnHidden(idx, False)
        # hide the button
        self.col_buttons[idx].setVisible(False)
        for b in self.col_buttons:
            if b.isVisible():
                return
        self.scrollArea.setVisible(False)
        self._header.updateGeometries()

    def slot_on_toggle_auto_width_column(self, idx):
        b = self._toggle_width_menus[idx]
        b.setChecked(not b.isChecked())
        self.slot_on_auto_width_column(idx)

    def slot_on_auto_width_column(self, idx):
        b = self._toggle_width_menus[idx]
        if b.isChecked():
            self._header.setSectionResizeMode(
                idx, QtWidgets.QHeaderView.ResizeToContents)
        else:
            self._header.setSectionResizeMode(
                idx, QtWidgets.QHeaderView.Interactive)
        self._header.updateGeometries()

    def slot_on_col_filter_changed(self):
        n = self._filter_proxy_model.rowCount()
        t = self._model.rowCount(None)
        self.label_tablename.setText(f'{self._table_name}')
        self.label_status.setText(f'{n}/{t}')
        if self._table_name == 'Image' or self._table_name == 'DicomSeries':
            self.button_view.show()
        elif self._table_name == "DicomSeries_default" or self._table_name == "Image_default":
            self.button_view.show()

    def slot_on_filter_changed(self):
        n = self._filter_proxy_model.rowCount()
        t = self._model.rowCount(None)
        self.label_status.setText(f'{n}/{t}')
        f = self.edit_filter
        self._filter_proxy_model.set_global_filter(f.text())

    def slot_on_reload(self):
        # later -> keep filters if the columns are identical.
        # not clear why I need to reopen the db here
        # (not needed for tables, needed for view)
        self._db = syd.open_db(self._filename)
        t = self._db.load_table(self._table_name)
        elements = syd.find_all(t)
        self.set_data(elements)
        # indicate that the table has been reloaded
        self.table_reloaded.emit()
        n = self._filter_proxy_model.rowCount()
        t = self._model.rowCount(None)
        self.label_status.setText(f'{n}/{t}')
        self.button_view.setText("view in vv")
        self.button_view.setEnabled(False)

    def slot_on_view(self):
        data = []
        path = []
        rows = set(index.row() for index in self.table_view.selectedIndexes())
        for row in rows:
            data.append(self._data[row])
        self.w = SydCTWindow(data, self._filename, self._table_name)
        self.w.button_ct_on.setEnabled(False)
        db = syd.open_db(self._filename)
        for d in data:
            e = self.w.get_ct_path(db, d)
            if e is not None and len(rows) == 1 and d['modality'] != 'CT':
                self.w.button_ct_on.setEnabled(True)
                self.w.show()
            else:
                if self._table_name == 'DicomSeries' or self._table_name == 'DicomSeries_default':
                    db = syd.open_db(self._filename)
                    dicom_file = syd.find_one(db['DicomFile'],
                                              dicom_series_id=d['id'])
                    file = syd.find_one(db['File'], id=dicom_file['file_id'])
                    tmp = db.absolute_data_folder + '/' + file[
                        'folder'] + '/' + file['filename']
                    path.append(tmp)
                elif self._table_name == 'Image' or self._table_name == 'Image_default':
                    db = syd.open_db(self._filename)
                    file = syd.find_one(db['File'], id=d['file_mhd_id'])
                    path.append(db.absolute_data_folder + '/' +
                                file['folder'] + '/' + file['filename'])
                else:
                    print('La table séléctionnée ne correspond pas')
        if path != []:
            path = ' '.join(path)
            cmd = f'vv {path}'
            os.system(cmd)
        else:
            print('Path to image has no corresponding file')

    def on_selection_change(self):
        rows = set(index.row() for index in self.table_view.selectedIndexes())
        if len(rows) == 0:
            self.button_view.setEnabled(False)
            self.button_view.setText("view in vv")
        else:
            t = self._model.rowCount(None)
            self.button_view.setText(f"view in vv {len(rows)}/{t}")
            self.button_view.setEnabled(True)