def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) #tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, group)
class MainWindow(QWidget): def __init__(self): QWidget.__init__(self) self.resize(640, 480) vbox = QVBoxLayout() self.setWindowTitle('TableDemo') self.setLayout(vbox) self.table = QTableView() self.table.setAlternatingRowColors(True) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) cards = [Card(u'巫医', u'战吼:恢复2点生命值。', 1, 2, 1), Card(u'狼骑兵', u'冲锋', 3, 3, 1), Card(u'石牙野猪', u'冲锋', 1, 1, 1), Card(u'森金持盾卫士', u'嘲讽', 4, 3, 5), ] cardModel = CardModel(cards) sortModel = QSortFilterProxyModel() sortModel.setSourceModel(cardModel) self.table.setModel(sortModel) vbox.addWidget(self.table) def printItem(self, idx): print idx.model().persons[idx.row()]
def __init__(self): super(PhoneFrame, self).__init__() self.setWindowTitle('Phone Book.') self.name = QLineEdit() self.number = QLineEdit() entry = QFormLayout() entry.addRow(QLabel('Name'), self.name) entry.addRow(QLabel('Number'), self.number) buttons = QHBoxLayout() button = QPushButton('&Add') button.clicked.connect(self._addEntry) buttons.addWidget(button) button = QPushButton('&Update') button.clicked.connect(self._updateEntry) buttons.addWidget(button) button = QPushButton('&Delete') button.clicked.connect(self._deleteEntry) buttons.addWidget(button) dataDisplay = QTableView() dataDisplay.setModel(PhoneDataModel()) layout = QVBoxLayout() layout.addLayout(entry) layout.addLayout(buttons) layout.addWidget(dataDisplay) self.setLayout(layout) self.show()
def testSetModel(self): model = VirtualList() model._getItemCalled = False table = QTableView() table.setModel(model) table.show() self.assertFalse(model._getItemCalled)
class QuickOrderViewWidget(SubFrame): def __init__(self, parent): super(QuickOrderViewWidget, self).__init__("Quick view", None, parent) self._table_model = QStandardItemModel(1, 2, None) self.table_view = QTableView(self) self.table_view.setModel(self._table_model) self.layout().addWidget(self.table_view) self._table_model.setHorizontalHeaderLabels( ['Part Nr.', 'Description']) self.table_view.verticalHeader().hide() headers_view = self.table_view.horizontalHeader() headers_view.setResizeMode(0, QHeaderView.ResizeToContents) headers_view.setResizeMode(1, QHeaderView.Stretch) def selected_order(self, cur, prev): if cur.isValid(): order_id = cur.model().index(cur.row(), 0).data(Qt.UserRole) self._table_model.removeRows(0, self._table_model.rowCount()) row = 0 for label, description in dao.order_dao.load_quick_view(order_id): self._table_model.appendRow( [QStandardItem(label), QStandardItem(description)])
def _addtabondemand(self, thread): """Adds a new tab for the given thread, if it doesn't exist yet. If it does, it will be made the current tab. @param thread Instance of FourChanThreahHeader """ i = thread.id_() if not i in self._openthreads.keys(): table = QTableView(self) table.setModel(LiveFeedModel(self)) table.setAlternatingRowColors(True) table.setEditTriggers(QAbstractItemView.NoEditTriggers) table.setIconSize(QSize(192, 192)) table.horizontalHeader().setVisible(False) table.horizontalHeader().setDefaultSectionSize(192) table.horizontalHeader().setStretchLastSection(True) table.verticalHeader().setVisible(False) table.verticalHeader().setDefaultSectionSize(192) table.doubleClicked.connect(self._showlivepost) idx = self.tabPosts.addTab(table, "/%s/%s" % (thread.forum(), i)) self._openthreads[i] = (idx, thread, table) fct = FourChanThread( FourChanThreadUrl(thread.url()), self._masterobserver) fct.postadded.connect(self._updatethread) self._masterobserver.addobserver( FourChanThreadObserver(fct, parent=self)) fct.refresh(True) else: idx = self._openthreads[i][0] self.tabPosts.setCurrentIndex(idx)
def __init__(self, parent, header, device_settings, firmware_settings, *args): QDialog.__init__(self, parent, *args) self.setGeometry(300, 200, 570, 450) self.setWindowTitle("Device information") table_model = DeviceInformationTable(self, header, device_settings) dev_settings_table = QTableView() dev_settings_table.setModel(table_model) table_model = DeviceInformationTable(self, header, firmware_settings) fw_settings_table = QTableView() fw_settings_table.setModel(table_model) # set font # font = QFont("monospace", 10) font = QFont("", 10) dev_settings_table.setFont(font) fw_settings_table.setFont(font) # set column width to fit contents (set font first!) dev_settings_table.resizeColumnsToContents() fw_settings_table.resizeColumnsToContents() tab_view = QTabWidget() tab_view.addTab(dev_settings_table, "User settings") tab_view.addTab(fw_settings_table, "Firmware settings") layout = QVBoxLayout(self) layout.addWidget(tab_view) self.setLayout(layout)
def __init__(self, parent): super(FilteredTable, self).__init__(parent) self.model = QStandardItemModel(600, 600) for i in range(self.model.rowCount()): for j in range(self.model.columnCount()): self.model.setData(self.model.index(i, j), str(i * j), Qt.DisplayRole) self.m = FilteringModel(self) self.m.setSourceModel(self.model) d = ['a'] * 300 + ['b'] * 200 + ['c'] * 99 + ['d'] self.m.setIndexData(d) t = QTableView(self) t.setModel(self.m) self.line_in = QLineEdit(self) self.line_in.textChanged.connect(self.m.setFilterFixedString) l = QVBoxLayout() l.addWidget(self.line_in) l.addWidget(t) self.setLayout(l)
def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, group)
def testReferenceCountingWhenDeletingReferrer(self): '''Tests reference count of model object referred by deceased view object.''' model = TestModel() refcount1 = getrefcount(model) view = QTableView() view.setModel(model) self.assertEqual(getrefcount(view.model()), refcount1 + 1) del view self.assertEqual(getrefcount(model), refcount1)
class MainWindow(QWidget): def __init__(self): super(MainWindow, self).__init__() self.layout = QVBoxLayout(self) self.grid = QTableView() self.grid.setModel(TaskTableModel(TaskView())) self.layout.addWidget(self.grid)
class skillsWindow(QDialog): def __init__(self, dataModel, oberTablo): QDialog.__init__(self) self.setWindowTitle('All Skills') self.setWindowIcon(QIcon('elance.ico')) self.oberTablo = oberTablo #create & configure tablewiew self.table_view = QTableView() self.table_view.setModel(dataModel) self.table_view.setSortingEnabled(True) self.table_view.sortByColumn(1, Qt.DescendingOrder) self.table_view.resizeColumnsToContents() self.table_view.resizeRowsToContents() #http://stackoverflow.com/questions/7189305/set-optimal-size-of-a-dialog-window-containing-a-tablewidget #http://stackoverflow.com/questions/8766633/how-to-determine-the-correct-size-of-a-qtablewidget w = 0 w += self.table_view.contentsMargins().left() +\ self.table_view.contentsMargins().right() +\ self.table_view.verticalHeader().width() w += qApp.style().pixelMetric(QStyle.PM_ScrollBarExtent) for i in range(len(self.table_view.model().header)): w += self.table_view.columnWidth(i) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.setMinimumWidth(w) # create two buttons self.findEntries = QPushButton('Find entries') self.findEntries.clicked.connect(self.ApplyFilterToMainList) self.cancel = QPushButton('Cancel') self.cancel.clicked.connect(self.winClose) self.mainLayout = QGridLayout() self.mainLayout.addWidget(self.table_view, 0,0,1,3) self.mainLayout.addWidget(self.findEntries, 1,0) self.mainLayout.addWidget(self.cancel, 1,2) self.setLayout(self.mainLayout) self.show() def ApplyFilterToMainList(self): selection = self.table_view.selectionModel().selection() skills = [] for selRange in selection: for index in selRange.indexes(): skill = index.model().data(index, Qt.DisplayRole) skills.append(skill) self.oberTablo.model().emit(SIGNAL("modelAboutToBeReset()")) self.oberTablo.model().criteria = {'Skills':skills} self.oberTablo.model().emit(SIGNAL("modelReset()")) self.close() def winClose(self): self.close()
def __init__(self, parent=None): super(MainWindow, self).__init__(parent) database = QSqlDatabase.addDatabase('QSQLITE') database.setDatabaseName('astatsscraper.db') # Better lookup logic needed if not database.open(): print('Error opening database!') model = QSqlTableModel(db=database) model.setTable('steam_apps') table = QTableView() table.setEditTriggers(QAbstractItemView.NoEditTriggers) table.setModel(model) self.setCentralWidget(table) table.show()
def __init__(self, parent=None): super(MainWindow, self).__init__(parent) database = QSqlDatabase.addDatabase('QSQLITE') database.setDatabaseName( 'astatsscraper.db') # Better lookup logic needed if not database.open(): print('Error opening database!') model = QSqlTableModel(db=database) model.setTable('steam_apps') table = QTableView() table.setEditTriggers(QAbstractItemView.NoEditTriggers) table.setModel(model) self.setCentralWidget(table) table.show()
def test_database_view(qtbot, session): session.add(Person(name="Roger Milla")) session.add(Person(name="François Biyik")) session.add(Person(name="Salvatore Schillaci")) session.commit() persons = DatabaseObjectModel(Person, session) persons.addAttributeColumn("name", "Name") assert persons.data(ModelIndexDuck(0, 0), Qt.DisplayRole) == "Roger Milla" view = QTableView() view.setModel(persons) persons.appendObject(Person(name="Claudio Cannigia")) persons.removeObject(0) qtbot.addWidget(view) view.show()
class Blew(QWidget): def __init__(self): super(Blew, self).__init__() self.setWindowModality(Qt.NonModal) self.setWindowFlags( Qt.FramelessWindowHint) # Qt.Tool | Qt.FramelessWindowHint l = QHBoxLayout() self.items_view = QTableView() # Make the dropdown nice l.setContentsMargins(0, 0, 0, 0) self.items_view.horizontalHeader().setStretchLastSection(True) self.items_view.horizontalHeader().hide() self.items_view.verticalHeader().hide() self.items_view.setShowGrid(False) self.setMinimumSize(300, 100) self.model = QuickComboModel(self) self.items_view.setModel(self.model) l.addWidget(self.items_view) self.setLayout(l) def get_current_completion(self): """ Get the currently selected completion """ return self.items_view.currentIndex().data() @Slot(list) def set_possible_completions(self, completions): if completions: self.model.buildModelFromArray(completions) # self.show() else: self.model.clear() self.hide() completion_discarded = Signal() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: event.ignore() self.hide() self.completion_discarded.emit() return super(Blew, self).keyPressEvent(event)
def __init__(self, table_model, _completerSetupFunction=None, *args): QWidget.__init__(self, *args) # setGeometry(x_pos, y_pos, width, height) self.setGeometry(300, 200, 570, 450) self.setWindowTitle("QTableView + completer example") table_view = QTableView() table_view.setModel(table_model) # set column width to fit contents (set font first!) table_view.resizeColumnsToContents() table_view.resizeRowsToContents() if _completerSetupFunction is not None: delegate = CompleterDelegate(table_view, _completerSetupFunction) table_view.setItemDelegateForColumn(1, delegate) layout = QVBoxLayout(self) layout.addWidget(table_view) self.setLayout(layout)
def setup_ui(self): splitter = QSplitter(self) left = QWidget(splitter) left.setLayout(QVBoxLayout(left)) left.layout().addWidget(QLabel('QTableView', left)) tableview = QTableView(left) tableview.setModel(TableModel(tableview)) left.layout().addWidget(tableview) splitter.addWidget(left) right = QWidget(splitter) right.setLayout(QVBoxLayout(right)) right.layout().addWidget(QLabel('QTableWidget', right)) # create a table widget for DATA tablewidget = QTableWidget(len(DATA), len(DATA[1]), right) right.layout().addWidget(tablewidget) splitter.addWidget(right) self.setCentralWidget(splitter) # add tablewidget data self.add_data(tablewidget)
def testReferenceCounting(self): '''Tests reference count of model object referred by view objects.''' model1 = TestModel() refcount1 = getrefcount(model1) view1 = QTableView() view1.setModel(model1) self.assertEqual(getrefcount(view1.model()), refcount1 + 1) view2 = QTableView() view2.setModel(model1) self.assertEqual(getrefcount(view2.model()), refcount1 + 2) model2 = TestModel() view2.setModel(model2) self.assertEqual(getrefcount(view1.model()), refcount1 + 1)
class BrowserWindow(QMainWindow): MO_ROLE = Qt.UserRole+1 def __init__(self, conn): super(BrowserWindow, self).__init__() self._conn = conn self._resolver = AsyncResolver() self._resolver.object_resolved.connect(self._data_resolved) self._resolver.start() self._init_models() self._init_gui() self._init_data() self._init_connections() def __del__(self): self._resolver.stop_work() self._resolver.terminate() def _init_models(self): self._hierarchy_model = QStandardItemModel() self._hierarchy_model.setColumnCount(2) self._hierarchy_model.setHorizontalHeaderLabels(['class', 'dn']) self._details_model = QStandardItemModel() self._details_model.setColumnCount(2) self._details_model.setHorizontalHeaderLabels(['Property', 'Value']) def _init_gui(self): self._widget = QSplitter(self, Qt.Horizontal) self._hierarchy_view = QTreeView(self._widget) self._details_view = QTableView(self._widget) self._widget.addWidget(self._hierarchy_view) self._widget.addWidget(self._details_view) self._widget.setStretchFactor(0, 2) self._widget.setStretchFactor(1, 1) self.setCentralWidget(self._widget) self._hierarchy_view.setModel(self._hierarchy_model) self._details_view.setModel(self._details_model) self._hierarchy_view.expanded.connect(self._mo_item_expand) def _init_data(self): item = self._row_for_mo(self._conn.resolve_dn('')) self._hierarchy_model.insertRow(0, item) def _init_connections(self): self.connect(self._resolver, SIGNAL('object_resolved(QVariant)'), self, SLOT('_data_resolved(QVariant)')) self._hierarchy_view.activated.connect(self._item_activated) #self.connect(self._hierarchy_view.selectionModel(), # SIGNAL('currentChanged(QModelIndex,QModelIndex)'), # self, # SLOT('_current_changed(QModelIndex, QModelIndex)')) self.connect(self._hierarchy_view.selectionModel(), SIGNAL('activated(QModelIndex)'), self, SLOT('_item_activated(QModelIndex)')) def _row_for_mo(self, mo): row = [QStandardItem(mo.ucs_class), QStandardItem(mo.dn)] for item in row: item.setEditable(False) row[0].appendColumn([QStandardItem('Loading...')]) row[0].setData(mo, self.MO_ROLE) return row def _add_mo_in_tree(self, mo, index=QtCore.QModelIndex()): item = None if index.isValid(): item = self._hierarchy_model.itemFromIndex(index) else: item = self._get_item_for_dn(self._parent_dn(mo.dn)) if item: item.appendColumn([self._row_for_mo(mo)[0]]) self.auto_width() def _add_mos_in_tree(self, mos, index=QtCore.QModelIndex()): item = None if index.isValid(): item = self._hierarchy_model.itemFromIndex(index) else: if not mos: return item = self._get_item_for_dn(self._parent_dn(mos[0].dn)) while item.columnCount(): item.removeColumn(0) items = map(self._row_for_mo, mos) if items: for x in xrange(len(items[0])): item.appendColumn([row[x] for row in items]) self.auto_width() @staticmethod def _parent_dn(dn): parent_dn, _, rn = dn.rpartition('/') return parent_dn def _get_item_for_dn(self, dn): parent_dn = dn items = self._hierarchy_model.findItems(parent_dn, column=1) if items: return self._hierarchy_model.item(items[0].row()) return None @QtCore.Slot('_data_resolved(QVariant)') def _data_resolved(self, datav): print 'Data resolved: ', datav index, data = datav if isinstance(data, UcsmObject): self._add_mo_in_tree(data, index=index) else: self._add_mos_in_tree(data, index=index) @QtCore.Slot('_current_changed(QModelIndex,QModelIndex)') def _current_changed(self, curr, prev): self._item_activated(curr) @QtCore.Slot('_item_activated(QModelIndex)') def _item_activated(self, index): print 'Activated: %s data %s' % (index, index.data(self.MO_ROLE)) if index.sibling(0, 0).isValid(): index = index.sibling(0, 0) data = index.data(self.MO_ROLE) self.set_detail_object(data) def _mo_item_expand(self, index): obj = index.data(self.MO_ROLE) print 'Expanded object: %s' % obj try: self._resolver.add_task(lambda: (index, self._conn.resolve_children(obj.dn))) except (KeyError, AttributeError): QtGui.QMessageBox.critical(0, 'Error', 'Object does not have dn') def auto_width(self): for view in [self._hierarchy_view, self._details_view]: for col in xrange(view.model().columnCount()): view.resizeColumnToContents(col) def set_detail_object(self, object): self._details_model.removeRows(0, self._details_model.rowCount()) for k, v in object.attributes.iteritems(): row = [QStandardItem(k), QStandardItem(v)] for item in row: item.setEditable(False) self._details_model.appendRow(row) self.auto_width()
class uiManager(QMainWindow): def __init__(self): super(uiManager, self).__init__() self.setMinimumSize(900,500) self.setWindowTitle("Manage") self.aFileModel = FileModel() # Add files self.sFileModel = FileModel() # Queried files self.eFileModel = FileModel() # Editing files self.tabWidget = QTabWidget() self.setCentralWidget(self.tabWidget) self.defActions() self.addTab() self.searchTab() self.editTab() self.testtab() self.statusBar().showMessage("Hi.") def defActions(self): self.findAct = QAction("+", self, triggered=self.findFilesDlg) self.dropAddTabAct = QAction("-", self, shortcut="Delete", triggered=self.dropAddTabRows) self.dropEditTabAct = QAction("-", self, shortcut="Delete", triggered=self.dropEditTabRows) def dropEditTabRows(self): self.dropFiles(self.eFileTable, self.eFileModel) def dropAddTabRows(self): self.dropFiles(self.aFileTable, self.aFileModel) def testtab(self): self.test = QWidget() self.tabWidget.addTab(self.test, "WORDS") self.tabWidget.setCornerWidget(self.test) def addTab(self): self.aFileTable = QTableView() self.aFileTable.setModel(self.aFileModel) self.aFileTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.setColumnsWidths(self.aFileTable) self.addWidget = QWidget() vLayout = QVBoxLayout() vLayout.addWidget(self.aFileTable) addBtn = QToolButton() addBtn.setDefaultAction(self.findAct) # addBtn.clicked.connect(self.findFilesDlg) dropBtn = QToolButton() dropBtn.setDefaultAction(self.dropAddTabAct) # rmvBtn.clicked.connect(self.rmvFiles) insBtn = QPushButton("Track") insBtn.clicked.connect(self.insert) hLayout = QHBoxLayout() hLayout.addWidget(addBtn) hLayout.addWidget(dropBtn) hLayout.addStretch(1) hLayout.addWidget(insBtn) vLayout.addLayout(hLayout) self.addWidget.setLayout(vLayout) self.tabWidget.addTab(self.addWidget, u"Add") def searchTab(self): # sFileModel + view tab self.sFileTable = QTableView() self.sFileTable.setModel(self.sFileModel) self.sFileTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.sFileTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.setColumnsWidths(self.sFileTable) self.searchWidget = QWidget() # Horizontal layout # Button, Line Edit, Button hLayout = QHBoxLayout() self.searchBox = QLineEdit() self.searchBox.returnPressed.connect(self.searchdb) hLayout.addWidget(self.searchBox) searchBtn = QPushButton("Search") searchBtn.clicked.connect(self.searchdb) hLayout.addWidget(searchBtn) # Vertical Layout vLayout = QVBoxLayout() vLayout.addLayout(hLayout) vLayout.addWidget(self.sFileTable) # sub horiz box subHLayout = QHBoxLayout() subHLayout.addStretch(1) editBtn = QPushButton("Edit -->") editBtn.clicked.connect(self.addToEditTab) subHLayout.addWidget(editBtn) vLayout.addLayout(subHLayout) self.searchWidget.setLayout(vLayout) self.tabWidget.addTab(self.searchWidget, u"Retrieve") def editTab(self): self.eFileTable = QTableView() self.eFileTable.setModel(self.eFileModel) self.eFileTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.setColumnsWidths(self.eFileTable) self.editWidget = QWidget() vLayout = QVBoxLayout() vLayout.addWidget(self.eFileTable) dropBtn = QToolButton() dropBtn.setDefaultAction(self.dropEditTabAct) undBtn = QPushButton("Undo Delete") undBtn.clicked.connect(self.undeleteFiles) rmvBtn = QPushButton("Delete") rmvBtn.clicked.connect(self.deleteFiles) cmtBtn = QPushButton("Update") cmtBtn.clicked.connect(self.update) hLayout = QHBoxLayout() hLayout.addWidget(dropBtn) hLayout.addStretch(1) hLayout.addWidget(undBtn) hLayout.addWidget(rmvBtn) hLayout.addWidget(cmtBtn) vLayout.addLayout(hLayout) self.editWidget.setLayout(vLayout) self.tabWidget.addTab(self.editWidget, u"Edit") def setColumnsWidths(self, table): table.setColumnWidth(0, 438) table.horizontalHeader().setStretchLastSection(True) def undeleteFiles(self): delfiles = self.eFileModel.delfiles for file in delfiles: path, tags = file.path, file.tags self.addToModel(self.eFileModel, path, tags, True) self.eFileModel.delfiles = [] # Adds files to the models delete list # for removal from the DB on next update() def deleteFiles(self): indexes = self.getSRIndexes(self.eFileTable) for index in indexes: self.eFileModel.delList(index.row()) self.eFileModel.removeRows(index.row()) self.statusBar().showMessage("Pending Changes") # Drop files from a table and model def dropFiles(self, table, model): indexes = self.getSRIndexes(table) for index in indexes: model.removeRows(index.row()) # Inserts files into the DB from the add model def insert(self): p = self.aFileModel.insert() if p: msgDuplBox = QMessageBox() msgDuplBox.setText('\n'.join(p) + "\n\n...file(s) already being tracked.") msgDuplBox.exec_() def update(self): self.eFileModel.update() ''' Order of user selection matters in the view. As a user selects an item it is entered into the list of selected rows. To remove the correct items first you have to sort the list, then reverse it to remove items from the bottom up to prevent a row number pointing at the wrong entry because an entry above it was already removed.''' def getSRIndexes(self, table): indexes = table.selectionModel().selectedRows() indexes.sort() indexes.reverse() return indexes def findFilesDlg(self): finder = getFilesDlg(self) finder.sendPaths.connect(self.addToAddTab) finder.show() @Slot(list) def addToAddTab(self, list): for path in list: if self.colCheck(self.aFileModel, path): self.addToModel(self.aFileModel, path) def addToSearchTab(self, qresults): if qresults: self.sFileModel.reset() for file in qresults: path,tags = file self.addToModel(self.sFileModel, path, tags) def addToEditTab(self): # Need to make sure what they are adding isn't already # present in the edit model's files or delfiles lists indexes = self.getSRIndexes(self.sFileTable) # save original path as update reference for db oldpath = True for index in indexes: path, tags = self.sFileModel.dataTuple(index) if self.colCheck(self.eFileModel, path): self.addToModel(self.eFileModel, path, tags, oldpath) self.sFileModel.removeRows(index.row()) def addToModel(self, model, path, tags=None, oldpath=False): model.insertRows(0) ix = model.index(0, 0, QModelIndex()) model.setData(ix, path, Qt.EditRole, oldpath) ix = model.index(0, 1, QModelIndex()) model.setData(ix, tags, Qt.EditRole, oldpath) def colCheck(self, model, path): # THIS IS REALLY SLOW # Linear search, if model.files: for file in model.files: if path == file.path: return False return True def searchdb(self): thedb = myDB() query = thedb.query(self.searchBox.text()) self.addToSearchTab(query)
class ConsoleWidget(QMainWindow): def __init__(self): super(ConsoleWidget, self).__init__() self.setWindowTitle('1c query') self._connection = None self._home = os.path.expanduser('~/%s' % QApplication.applicationName()) if not os.path.isdir(self._home): os.mkdir(self._home) self.queryToolBar = self.addToolBar('Query') self.queryAction = self.queryToolBar.addAction('Run', self.executeQuery) self.queryAction.setDisabled(True) uri_history = list() path = os.path.join(self._home, 'uri_history.txt') if os.path.isfile(path): uri_history = open(path, 'r').read().split('\n') self.connectionToolBar = self.addToolBar('Connection') self.connectionUriCombo = QComboBox(self) self.connectionUriCombo.setEditable(True) if not uri_history: self.connectionUriCombo.addItem('File="";usr="";pwd="";') self.connectionUriCombo.addItem('Srvr="{host}";Ref="{ref}";Usr="******";Pwd="{password}";') else: self.connectionUriCombo.addItems(uri_history) self.connectionUriCombo.setCurrentIndex(len(uri_history) - 1) self.connectionUriCombo.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) self.connectionToolBar.addWidget(self.connectionUriCombo) self.onesVersionCombo = QComboBox(self) self.onesVersionCombo.addItems(['8.3', '8.2', '8.1', '8.0']) self.onesVersionCombo.setCurrentIndex(0) self.connectionToolBar.addWidget(self.onesVersionCombo) self.connectAction = self.connectionToolBar.addAction('Connect', self.connectOneS) self.disconnectAction = self.connectionToolBar.addAction('Disconnect', self.disconnectOneS) self.disconnectAction.setDisabled(True) self.logEdit = QPlainTextEdit(self) self.logDock = QDockWidget('Log', self) self.logDock.setWidget(self.logEdit) self.addDockWidget(Qt.BottomDockWidgetArea, self.logDock, Qt.Horizontal) self.splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.splitter) self.sqlEdit = QTextEdit(self) self.sqlEdit.setLineWrapMode(QTextEdit.NoWrap) path = os.path.join(self._home, 'last-sql.txt') if os.path.isfile(path): sql = open(path, 'r').read() self.sqlEdit.setText(sql) self.model = QStandardItemModel(self) self.tableView = QTableView(self) self.tableView.setModel(self.model) self.splitter.addWidget(self.sqlEdit) self.splitter.addWidget(self.tableView) self.splitter.setStretchFactor(0, 3) self.splitter.setStretchFactor(1, 2) def query(self, sql): if not self._connection: self.logEdit.appendPlainText('No connection') return None try: query = self._connection.NewObject('Query', sql) result = query.Execute() except Exception as e: self.logEdit.appendPlainText(str(e)) return None return result def refresh(self, result): self.model.clear() columns = list() result_columns = result.Columns for index in range(result_columns.Count()): name = result_columns.Get(index).Name columns.append(name) self.model.setColumnCount(len(columns)) for section, name in enumerate(columns): self.model.setHeaderData(section, Qt.Horizontal, name) select = result.Choose() self.logEdit.appendPlainText('Selected %d records' % select.Count()) while select.Next(): items = list() for index in range(len(columns)): value = select.Get(index) item = QStandardItem('') if isinstance(value, bool): item.setText(value and 'Yes' or 'No') elif isinstance(value, (int, str)): item.setText(str(value)) elif isinstance(value, datetime.datetime): item.setText(value.strftime('%Y.%m.%d %H:%M:%S')) else: item.setText(str(value)) items.append(item) self.model.appendRow(items) @Slot() def executeQuery(self): sql = self.sqlEdit.toPlainText() result = self.query(sql) if result: path = os.path.join(self._home, 'last-sql.txt') open(path, 'w').write(sql) self.refresh(result) @Slot() def connectOneS(self): uri = self.connectionUriCombo.currentText().strip() if not uri: self.logEdit.appendPlainText('Need a connection string') return version = self.onesVersionCombo.currentText() comName = "V%s.COMConnector" % str(version).replace('.', '') pythoncom.CoInitialize() try: obj = win32com.client.Dispatch(comName) self._connection = obj.Connect(uri) except Exception as e: self.logEdit.appendPlainText(str(e)) return self.connectAction.setDisabled(True) self.disconnectAction.setEnabled(True) self.queryAction.setEnabled(True) uri_history = list() for i in range(self.connectionUriCombo.count()): uri_history.append(self.connectionUriCombo.itemText(i)) if uri not in uri_history: self.connectionUriCombo.clearEditText() self.connectionUriCombo.addItem(uri) self.connectionUriCombo.setCurrentIndex(len(uri_history)) uri_history.append(uri) path = os.path.join(self._home, 'uri_history.txt') open(path, 'w').write('\n'.join(uri_history)) @Slot() def disconnectOneS(self): pythoncom.CoUninitialize() self._connection = None self.connectAction.setEnabled(True) self.disconnectAction.setDisabled(True) self.queryAction.setDisabled(True)
class TimeTracksOverviewWidget(QWidget): @Slot(QModelIndex) def cell_entered(self,ndx): chrono = datetime.now() employee = self.employees[ndx.row()] a = max(1,ndx.column()) d = date(self.base_date.year,self.base_date.month,min(calendar.monthrange(self.base_date.year,self.base_date.month)[1], max(1,ndx.column()))) # print employee, d # Fill in the timetracks report panel ---------------------------- # First we read the timetrackss and their associated OrderPart t_start = datetime(d.year,d.month,d.day,0,0,0,0) t_end = datetime(d.year,d.month,d.day,23,59,59,999999) # FIXME not abstract enough # FIXME This should be in the DAO tt = session().query(TimeTrack,OrderPart).\ join(TaskOnOperation).join(Operation).join(ProductionFile).join(OrderPart).\ filter(and_(TimeTrack.employee == employee, TimeTrack.start_time >= t_start, TimeTrack.start_time <= t_end)).order_by(asc(TimeTrack.start_time)).all() tt += session().query(TimeTrack,Order).join(TaskOnOrder).join(Order).filter(and_(TimeTrack.employee == employee, TimeTrack.start_time >= t_start, TimeTrack.start_time <= t_end)).order_by(asc(TimeTrack.start_time)).all() tt += session().query(TimeTrack,OperationDefinition).join(TaskOnNonBillable).join(OperationDefinition).filter(and_(TimeTrack.employee == employee, TimeTrack.start_time >= t_start, TimeTrack.start_time <= t_end)).order_by(asc(TimeTrack.start_time)).all() # Fill in the report panel --------------------------------------- self.hours_on_day_subframe.set_title(_("Hours worked on the {} by {}").format(date_to_s(d,True),employee.fullname)) self.controller.model._buildModelFromObjects(map(lambda o : o[0],tt)) # dao.timetrack_dao.all_for_employee(employee)) # self.controller.model.row_protect_func = lambda obj,row: obj is not None and obj.managed_by_code ndx = 0 for row in self.controller.model.table: if row[1]: row[0] = tt[ndx][1] # First column becomes the order ndx += 1 self.controller.view.resizeColumnsToContents() # Fill in the time report panel --------------------------------- self.pointage_timestamp_prototype.set_fix_date(d) self.controller_actions.model._buildModelFromObjects( dao.task_action_report_dao.get_reports_for_employee_on_date(employee,d)) @Slot() def refresh_action(self): global dao self.title_box.set_title(_("Time Records Overview - {}").format(date_to_my(self.base_date,True))) self.employees = dao.employee_dao.all() day_max = calendar.monthrange(self.base_date.year,self.base_date.month)[1] t_start = datetime(self.base_date.year,self.base_date.month,1) t_end = datetime(self.base_date.year,self.base_date.month,day_max,23,59,59,999999) self._table_model.setRowCount( len(self.employees)) self._table_model.setColumnCount( 1+day_max) headers = QStandardItemModel(1, 1+day_max) headers.setHeaderData(0, Qt.Orientation.Horizontal, _("Employee")) for i in range(day_max): headers.setHeaderData(i+1, Qt.Orientation.Horizontal, "{}".format(i+1)) self.headers_view.setModel(headers) # qt's doc : The view does *not* take ownership self.header_model = headers row = 0 for employee in self.employees: # mainlog.debug(u"refresh action employee {}".format(employee)) self._table_model.setData(self._table_model.index(row,0),employee.fullname,Qt.DisplayRole) # FIXME Use a delegate self._table_model.setData(self._table_model.index(row,0),employee,Qt.UserRole) # FIXME Use a delegate tracks = session().query(TimeTrack).filter(and_(TimeTrack.employee_id == employee.employee_id, TimeTrack.start_time >= t_start,TimeTrack.start_time <= t_end)).all() # One bucket per day buckets = [0] * day_max for t in tracks: mainlog.debug("Bucket {}".format(t)) buckets[t.start_time.day - 1] += t.duration for b in range(len(buckets)): if buckets[b] != 0: self._table_model.setData(self._table_model.index(row,b+1),duration_to_s(buckets[b]),Qt.DisplayRole) else: self._table_model.setData(self._table_model.index(row,b+1),None,Qt.DisplayRole) if buckets[b] >= 0: # Clear the background self._table_model.setData(self._table_model.index(row,b+1),None,Qt.BackgroundRole) else: self._table_model.setData(self._table_model.index(row,b+1),QBrush(QColor(255,128,128)),Qt.TextColorRole) self._table_model.setData(self._table_model.index(row,b+1),Qt.AlignRight,Qt.TextAlignmentRole) row += 1 # Compute all mondays indices monday = 0 if t_start.weekday() > 0: monday = 7 - t_start.weekday() all_mondays = [] while monday < day_max: all_mondays.append(monday) monday += 7 today = date.today() for row in range(len(self.employees)): # Mark mondays for col in all_mondays: # col + 1 to account for the employee column self._table_model.setData(self._table_model.index(row,col + 1),QBrush(QColor(230,230,255)),Qt.BackgroundRole) # Mark today if today.month == self.base_date.month and today.year == self.base_date.year: self._table_model.setData(self._table_model.index(row,today.day),QBrush(QColor(255,255,128)),Qt.BackgroundRole) #for i in range(len(all_mondays)): self.table_view.resizeColumnsToContents() # mainlog.debug("Reset selection") ndx = self.table_view.currentIndex() self.table_view.selectionModel().clear() # self.table_view.selectionModel().clearSelection() # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) self.table_view.selectionModel().setCurrentIndex( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) # self.cell_entered(self.table_view.currentIndex()) @Slot() def month_today(self): self.base_date = date.today() self.refresh_action() @Slot() def month_before(self): m = self.base_date.month if m > 1: self.base_date = date(self.base_date.year,m - 1,self.base_date.day) else: self.base_date = date(self.base_date.year - 1,12,self.base_date.day) self.refresh_action() @Slot() def month_after(self): m = self.base_date.month if self.base_date.year < date.today().year or m < date.today().month: if m < 12: self.base_date = date(self.base_date.year,m + 1,self.base_date.day) else: self.base_date = date(self.base_date.year + 1,1,self.base_date.day) self.refresh_action() @Slot() def edit_timetrack_no_ndx(self): ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: self.edit_timetrack(ndx) else: showWarningBox(_("Can't edit"),_("You must first select a day/person.")) @Slot(QModelIndex) def edit_timetrack(self,ndx): global dao global user_session if not user_session.has_any_roles(['TimeTrackModify']): return m = self.base_date.month edit_date = date(self.base_date.year,m,ndx.column()) # +1 already in because of employee's names employee = self._table_model.data(self._table_model.index(ndx.row(),0),Qt.UserRole) # FIXME Use a delegate d = EditTimeTracksDialog(self,dao,edit_date) d.set_employee_and_date(employee,edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() d.deleteLater() @Slot() def editTaskActionReports(self): if not user_session.has_any_roles(['TimeTrackModify']): return m = self.base_date.month ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: edit_date = date(self.base_date.year,m,ndx.column()) # +1 already in because of employee's names employee = self._table_model.data(self._table_model.index(ndx.row(),0),Qt.UserRole) # FIXME Use a delegate d = EditTaskActionReportsDialog(dao,self,edit_date) d.set_employee_date(employee, edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() d.deleteLater() else: showWarningBox(_("Can't edit"),_("You must first select a day/person.")) @Slot(QModelIndex) def timetrack_changed(self,ndx): selected_timetrack = self.controller.model.object_at(ndx) # Update the colors in the timetrack views # to show what action reports correspond to the # selected timetrack self.controller_actions.model.current_timetrack = selected_timetrack self.controller_actions.model.beginResetModel() self.controller_actions.model.endResetModel() # Make sure the first of the action reports is shown in the # table action_reports = self.controller_actions.model.objects for i in range(len(action_reports)-1,-1,-1): if action_reports[i] and action_reports[i].timetrack == selected_timetrack: self.controller_actions.view.scrollTo(self.controller_actions.model.index(i,0)) break def __init__(self,parent): super(TimeTracksOverviewWidget,self).__init__(parent) self.base_date = date.today() headers = QStandardItemModel(1, 31 + 1) headers.setHeaderData(0, Qt.Orientation.Horizontal, _("Employee")) for i in range(31): headers.setHeaderData(i+1, Qt.Orientation.Horizontal, "{}".format(i+1)) self._table_model = QStandardItemModel(1, 31+1, None) self.headers_view = QHeaderView(Qt.Orientation.Horizontal,self) self.header_model = headers self.headers_view.setResizeMode(QHeaderView.ResizeToContents) self.headers_view.setModel(self.header_model) # qt's doc : The view does *not* take ownership self.table_view = QTableView(None) self.table_view.setModel(self._table_model) self.table_view.setHorizontalHeader(self.headers_view) self.table_view.verticalHeader().hide() self.table_view.setAlternatingRowColors(True) # self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) navbar = NavBar(self, [ (_("Month before"), self.month_before), (_("Today"),self.month_today), (_("Month after"), self.month_after)]) self.title_box = TitleWidget(_("Time Records Overview"),self,navbar) self.vlayout = QVBoxLayout(self) self.vlayout.setObjectName("Vlayout") self.vlayout.addWidget(self.title_box) self.setLayout(self.vlayout) self.hours_per_pers_subframe = SubFrame(_("Hours worked per person"), self.table_view, self) self.vlayout.addWidget(self.hours_per_pers_subframe) hlayout = QHBoxLayout() prototype = [] prototype.append( OrderPartOnTaskPrototype(None, _('Order Part'))) prototype.append( TaskOnOrderPartPrototype('task', _('Task'),on_date=date.today())) prototype.append( DurationPrototype('duration',_('Duration'))) prototype.append( TimestampPrototype('start_time',_('Start time'),fix_date=date.today())) prototype.append( DatePrototype('encoding_date',_('Recorded at'),editable=False)) self.controller = PrototypeController(self,prototype) self.controller.setModel(TrackingProxyModel(self,prototype)) self.controller.view.setColumnWidth(1,300) self.controller.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) self.controller.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch) navbar = NavBar(self, [ (_("Edit"), self.edit_timetrack_no_ndx)]) self.hours_on_day_subframe = SubFrame(_("Total times on day"), self.controller.view, self,navbar) hlayout.addWidget(self.hours_on_day_subframe) prototype = [] # prototype.append( EmployeePrototype('reporter', _('Description'), dao.employee_dao.all())) prototype.append( TaskDisplayPrototype('task', _('Task'))) self.pointage_timestamp_prototype = TimestampPrototype('time',_('Hour'),editable=False,fix_date=date.today()) prototype.append( self.pointage_timestamp_prototype) prototype.append( TaskActionTypePrototype('kind',_('Action'))) prototype.append( TextLinePrototype('origin_location',_('Origin'))) prototype.append( TextLinePrototype('editor',_('Editor'),editable=False,default='master')) self.controller_actions = PrototypeController(self,prototype) self.controller_actions.setModel(ActionReportModel(self,prototype)) navbar = NavBar(self, [ (_("Edit"), self.editTaskActionReports)]) hlayout.addWidget(SubFrame(_("Time records"),self.controller_actions.view,self,navbar)) self.controller_actions.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.controller_actions.view.doubleClicked.connect(self.editTaskActionReports) self.controller_actions.view.horizontalHeader().setResizeMode(0,QHeaderView.Stretch) self.controller_actions.view.horizontalHeader().setResizeMode(3,QHeaderView.ResizeToContents) self.controller_actions.view.horizontalHeader().setResizeMode(4,QHeaderView.ResizeToContents) self.vlayout.addLayout(hlayout) self.vlayout.setStretch(0,0) self.vlayout.setStretch(1,300) self.vlayout.setStretch(2,200) # self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) #self.table_view.entered.connect(self.cell_entered) self.table_view.selectionModel().currentChanged.connect(self.cell_entered) self.table_view.doubleClicked.connect(self.edit_timetrack) self.controller.view.selectionModel().currentChanged.connect(self.timetrack_changed) self.controller.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.refresh_action()
class ModelTableWidget(QWidget): dataChanged = Signal(QModelIndex, QModelIndex) class _ModelTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._models = [] def rowCount(self, *args, **kwargs): return len(self._models) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or not (0 <= index.row() < len(self._models)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter model = self._models[index.row()] if model is None: return "" if role == Qt.DisplayRole or role == Qt.ToolTipRole: column = index.column() if column == 0: return str(model.type) elif column == 1: return str(model) return None def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return "Type" elif section == 1: return "Model" elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or not (0 <= index.row() < len(self._models)): return False row = index.row() self._models[row] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for _ in range(count): self._models.insert(row, None) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) for index in reversed(range(row, row + count)): self._models.pop(index) self.endRemoveRows() return True def append(self, model): self.insert(self.rowCount(), model) def insert(self, index, model): self.insertRows(index) self.setData(self.createIndex(index, 0), model) def remove(self, model): index = self._models.index(model) self.removeRows(index) def clear(self): self.removeRows(0, self.rowCount()) def models(self): models = set(self._models) models.discard(None) return models def model(self, index): return self._models[index.row()] def __init__(self, parent=None): QWidget.__init__(self, parent) # Variables model = self._ModelTableModel() # Widgets self._tbl_models = QTableView() self._tbl_models.setModel(model) header = self._tbl_models.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) policy = self._tbl_models.sizePolicy() policy.setVerticalStretch(True) self._tbl_models.setSizePolicy(policy) self._tbl_models.setSelectionMode(QTableView.SelectionMode.MultiSelection) # Layouts layout = QVBoxLayout() layout.addWidget(self._tbl_models) self.setLayout(layout) # Signals model.dataChanged.connect(self.dataChanged) def addModel(self, model): self._tbl_models.model().append(model) def addModels(self, models): for model in models: self.addModel(model) def removeModel(self, model): self._tbl_models.model().remove(model) def clear(self): self._tbl_models.model().clear() def models(self): return self._tbl_models.model().models() def currentModels(self): tbl_model = self._tbl_models.model() models = [] for index in self._tbl_models.selectionModel().selection().indexes(): models.append(tbl_model.model(index)) return models
class WarningWizardPage(_OptionsWizardPage): class _WarningTableModel(QAbstractTableModel): def __init__(self, warns=None): QAbstractTableModel.__init__(self) if warns is None: warns = [] self._warns = warns def rowCount(self, *args, **kwargs): return len(self._warns) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < len(self._warns)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None return str(self._warns[index.row()][index.column()]) def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Program' elif section == 1: return 'Warning' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) def __init__(self, options, parent=None): _OptionsWizardPage.__init__(self, options, parent=parent) self.setTitle("Conversion warnings") # Widgets self._lbl_count = SimulationCountLabel() self._lbl_count.setAlignment(Qt.AlignCenter) # Layouts layout = self.layout() frm_line = QFrame() frm_line.setFrameStyle(QFrame.HLine) frm_line.setFrameShadow(QFrame.Sunken) layout.addWidget(frm_line) sublayout = QHBoxLayout() sublayout.setContentsMargins(10, 0, 10, 0) sublayout.addWidget(self._lbl_count) layout.addLayout(sublayout) def _initUI(self): # Widgets self._tbl_warnings = QTableView() self._tbl_warnings.setModel(self._WarningTableModel()) header = self._tbl_warnings.horizontalHeader() header.setResizeMode(1, QHeaderView.Stretch) policy = self._tbl_warnings.sizePolicy() policy.setVerticalStretch(True) self._tbl_warnings.setSizePolicy(policy) # Layouts layout = _OptionsWizardPage._initUI(self) layout.addRow(self._tbl_warnings) return layout def initializePage(self): count = 0 warns = [] for program in self.options().programs: converter = program.converter_class() warnings.simplefilter("always") with warnings.catch_warnings(record=True) as ws: list_options = converter.convert(self.options()) count += len(list_options) for w in ws: warns.append((program, w.message)) model = self._WarningTableModel(warns) self._tbl_warnings.setModel(model) self._lbl_count.setValue(count) def validatePage(self): return self._lbl_count.value() > 0
class LimitWizardPage(_ExpandableOptionsWizardPage): class _LimitComboBoxModel(QAbstractListModel): def __init__(self, limits_text=None): QAbstractListModel.__init__(self) if limits_text is None: limits_text = {} self._limits_text = limits_text.copy() self._limits = list(limits_text.keys()) def rowCount(self, *args, **kwargs): return len(self._limits) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None limit_class = self._limits[index.row()] return self._limits_text[limit_class] def add(self, limit_class): if limit_class not in self._limits_text: raise ValueError('No text defined for limit: %s' % limit_class) self._limits.append(limit_class) self.reset() def remove(self, limit_class): self._limits.remove(limit_class) self.reset() def limitClass(self, index): return self._limits[index] class _LimitTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._limits = [] def rowCount(self, *args, **kwargs): return len(self._limits) def columnCount(self, *args, **kwargs): return 1 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < len(self._limits)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role == Qt.DisplayRole or role == Qt.ToolTipRole: limit = self._limits[index.row()] return str(limit) if limit is not None else '' return None def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._limits)): return False row = index.row() self._limits[row] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for _ in range(count): self._limits.insert(row, None) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) for index in reversed(range(row, row + count)): self._limits.pop(index) self.endRemoveRows() return True def append(self, limit): self.insert(self.rowCount(), limit) def insert(self, index, limit): self.insertRows(index) self.setData(self.createIndex(index, 0), limit) def remove(self, limit): index = self._limits.index(limit) self.removeRows(index) def modify(self, index, limit): self.setData(self.createIndex(index, 0), limit) def clear(self): self.removeRows(0, self.rowCount()) def limits(self): limits = set(self._limits) limits.discard(None) return limits def limit(self, index): return self._limits[index.row()] class _LimitTableDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): return None def setEditorData(self, editor, index): pass def setModelData(self, editor, model, index): return None def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Limit') def _initUI(self): # Variables self._widgets = {} tbl_model = self._LimitTableModel() # Widgets self._cb_limit = QComboBox() self._cb_limit.setModel(self._LimitComboBoxModel()) btn_limit_add = QPushButton() btn_limit_add.setIcon(getIcon("list-add")) self._tbl_limit = QTableView() self._tbl_limit.setModel(tbl_model) self._tbl_limit.setItemDelegate(self._LimitTableDelegate()) header = self._tbl_limit.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) header.hide() policy = self._tbl_limit.sizePolicy() policy.setVerticalStretch(True) self._tbl_limit.setSizePolicy(policy) tlb_limit = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_limit.addWidget(spacer) act_remove = tlb_limit.addAction(getIcon("list-remove"), "Remove limit") act_clear = tlb_limit.addAction(getIcon("edit-clear"), "Clear") # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) sublayout = QHBoxLayout() sublayout.addWidget(self._cb_limit, 1) sublayout.addWidget(btn_limit_add) layout.addRow("Select", sublayout) layout.addRow(self._tbl_limit) layout.addRow(tlb_limit) # Signals btn_limit_add.released.connect(self._onLimitAdd) act_remove.triggered.connect(self._onLimitRemove) act_clear.triggered.connect(self._onLimitClear) self._tbl_limit.doubleClicked.connect(self._onLimitDoubleClicked) tbl_model.dataChanged.connect(self.valueChanged) tbl_model.rowsInserted.connect(self.valueChanged) tbl_model.rowsRemoved.connect(self.valueChanged) return layout def _onLimitAdd(self): tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() index = self._cb_limit.currentIndex() try: limit_class = cb_model.limitClass(index) except IndexError: return widget_class = self._widgets[limit_class] wdg_limit = widget_class() dialog = _LimitDialog(wdg_limit) if not dialog.exec_(): return limit = dialog.limit() tbl_model.append(limit) # Insert table row cb_model.remove(limit.__class__) # Remove limit from combo box def _onLimitRemove(self): selection = self._tbl_limit.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Limit", "Select a row") return tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): limit = tbl_model.limit(tbl_model.createIndex(row, 0)) cb_model.add(limit.__class__) # Show limit to combo box tbl_model.removeRow(row) # Remove row if self._cb_limit.currentIndex() < 0: self._cb_limit.setCurrentIndex(0) def _onLimitClear(self): tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() for row in reversed(range(tbl_model.rowCount())): limit = tbl_model.limit(tbl_model.createIndex(row, 0)) cb_model.add(limit.__class__) # Show limit to combo box tbl_model.removeRow(row) # Remove row if self._cb_limit.currentIndex() < 0: self._cb_limit.setCurrentIndex(0) def _onLimitDoubleClicked(self, index): tbl_model = self._tbl_limit.model() limit = tbl_model.limit(index) widget_class = self._widgets[limit.__class__] wdg_limit = widget_class() wdg_limit.setValue(limit) dialog = _LimitDialog(wdg_limit) if not dialog.exec_(): return tbl_model.modify(index.row(), dialog.limit()) def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) # Clear self._widgets.clear() limits_text = {} # Populate combo box it = self._iter_widgets('pymontecarlo.ui.gui.options.limit', 'LIMITS') for limit_class, widget_class, programs in it: widget = widget_class() self._widgets[limit_class] = widget_class program_text = ', '.join(map(attrgetter('name'), programs)) text = '{0} ({1})'.format(widget.accessibleName(), program_text) limits_text[limit_class] = text del widget cb_model = self._LimitComboBoxModel(limits_text) self._cb_limit.setModel(cb_model) self._cb_limit.setCurrentIndex(0) # Add limit(s) tbl_model = self._tbl_limit.model() tbl_model.clear() for limit in self.options().limits: tbl_model.append(limit) def validatePage(self): tbl_model = self._tbl_limit.model() self.options().limits.clear() for limit in tbl_model.limits(): if not limit.__class__ in self._widgets: continue self.options().limits.add(limit) return True def expandCount(self): if self._tbl_limit.model().rowCount() == 0: return 0 try: count = 1 for limit in self._tbl_limit.model().limits(): count *= len(expand(limit)) return count except: return 0
def testModelWithParent(self): view = QTableView() model = TestModel(None) view.setModel(model) samemodel = view.model() self.assertEqual(model, samemodel)
class PhotonIntensityResultWidget(_SaveableResultWidget): def _initUI(self): # Variables model = _PhotonIntensityResultTableModel(self.result()) # Widgets self._table = QTableView() self._table.setModel(model) header = self._table.horizontalHeader() for i in range(1, 9): header.setResizeMode(i, QHeaderView.Stretch) # Layouts layout = _SaveableResultWidget._initUI(self) layout.addWidget(self._table) return layout def _initToolbox(self): toolbox = _SaveableResultWidget._initToolbox(self) self._itm_abbrev = _PhotonIntensityResultAbbreviationToolItem(self) toolbox.addItem(self._itm_abbrev, "Abbreviations") itm_detector = DetectorToolItem(self) toolbox.addItem(itm_detector, "Detector") self._itm_options = _PhotonIntensityResultOptionsToolItem(self) self._itm_options.stateChanged.connect(self._onOptionsChanged) toolbox.addItem(self._itm_options, "Options") return toolbox def _onOptionsChanged(self): model = self._table.model() model.setShowUncertainty(self._itm_options.showUncertainty()) pg, cg, bg, tg = self._itm_options.showGenerated() pe, ce, be, te = self._itm_options.showEmitted() self._table.setColumnHidden(1, not pg) self._table.setColumnHidden(2, not pe) self._table.setColumnHidden(3, not cg) self._table.setColumnHidden(4, not ce) self._table.setColumnHidden(5, not bg) self._table.setColumnHidden(6, not be) self._table.setColumnHidden(7, not tg) self._table.setColumnHidden(8, not te) factor = self._itm_options.factor() model.setFactor(factor) def dump(self): rows = [] rows.append( [ "Transition", "PG", "PG unc", "PE", "PE unc", "CG", "CG unc", "CE", "CE unc", "BG", "BG unc", "BE", "BE unc", "TG", "TG unc", "TE", "TE unc", ] ) result = self.result() for transition in result: row = [transition] row.extend(result.intensity(transition, False, False)) row.extend(result.intensity(transition, True, False)) row.extend(result.characteristic_fluorescence(transition, False)) row.extend(result.characteristic_fluorescence(transition, True)) row.extend(result.bremsstrahlung_fluorescence(transition, False)) row.extend(result.bremsstrahlung_fluorescence(transition, True)) row.extend(result.intensity(transition, False, True)) row.extend(result.intensity(transition, True, True)) rows.append(row) return rows
class Ui_MainWindow(QMainWindow): """Cette classe contient tous les widgets de notre application.""" defaultPalette = QPalette() defaultPalette.setColor(QPalette.Base, QColor("#151515")) defaultPalette.setColor(QPalette.Text, Qt.white) def __init__(self): super(Ui_MainWindow, self).__init__() # initialise la GUI avec un exemple self.text = "Ceci est un petit texte d'exemple." # les variables sont en place, initialise la GUI self.initUI() # exécute l'exemple self.orgText.setText(self.text) self.encode_text(False) self.logLab.setText( u"Saisir du texte ou importer un fichier, puis pousser \n" u"le bouton correspondant à l'opération souhaitée.") def initUI(self): """Met en place les éléments de l'interface.""" # -+++++++------------------- main window -------------------+++++++- # self.setWindowTitle(u"Encodage / Décodage de Huffman") self.centerAndResize() centralwidget = QWidget(self) mainGrid = QGridLayout(centralwidget) mainGrid.setColumnMinimumWidth(0, 450) # -+++++++------------------ groupe analyse -----------------+++++++- # analysGroup = QGroupBox(u"Analyse", centralwidget) self.analysGrid = QGridLayout(analysGroup) # ----------- groupe de la table des codes ---------- # codeTableGroup = QGroupBox(u"Table des codes", analysGroup) codeTableGrid = QGridLayout(codeTableGroup) # un tableau pour les codes self.codesTableModel = MyTableModel() self.codesTable = QTableView(codeTableGroup) self.codesTable.setModel(self.codesTableModel) self.codesTable.setFont(QFont("Mono", 8)) self.codesTable.resizeColumnsToContents() self.codesTable.setSortingEnabled(True) codeTableGrid.addWidget(self.codesTable, 0, 0, 1, 1) self.analysGrid.addWidget(codeTableGroup, 1, 0, 1, 1) # ----------- label du ratio de compression ---------- # self.ratioLab = QLabel(u"Ratio de compression: ", analysGroup) font = QFont() font.setBold(True) font.setWeight(75) font.setKerning(True) self.ratioLab.setFont(font) self.analysGrid.addWidget(self.ratioLab, 2, 0, 1, 1) # -+++++++-------- groupe de la table de comparaison --------+++++++- # self.compGroup = QGroupBox(analysGroup) self.compGroup.setTitle(u"Comparaisons") compGrid = QGridLayout(self.compGroup) # un tableau pour le ratio self.compTable = QTableWidget(self.compGroup) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.compTable.sizePolicy().hasHeightForWidth()) self.compTable.setSizePolicy(sizePolicy) self.compTable.setBaseSize(QSize(0, 0)) font = QFont() font.setWeight(50) self.compTable.setFont(font) self.compTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.compTable.setShowGrid(True) self.compTable.setGridStyle(Qt.SolidLine) # lignes / colonnes self.compTable.setColumnCount(2) self.compTable.setRowCount(3) self.compTable.setVerticalHeaderItem(0, QTableWidgetItem("Taille (bits)")) self.compTable.setVerticalHeaderItem(1, QTableWidgetItem("Entropie")) self.compTable.setVerticalHeaderItem(2, QTableWidgetItem("Taille moy. (bits)")) for i in range(2): self.compTable.verticalHeaderItem(i).setTextAlignment( Qt.AlignRight) self.compTable.setHorizontalHeaderItem(0, QTableWidgetItem("ASCII")) self.compTable.setHorizontalHeaderItem(1, QTableWidgetItem("Huffman")) # nom des items self.compTabASCIIMem = QTableWidgetItem() self.compTable.setItem(0, 0, self.compTabASCIIMem) self.compTabASCIIEnt = QTableWidgetItem() self.compTable.setItem(1, 0, self.compTabASCIIEnt) self.compTabASCIIAvg = QTableWidgetItem() self.compTable.setItem(2, 0, self.compTabASCIIAvg) self.compTabHuffMem = QTableWidgetItem() self.compTable.setItem(0, 1, self.compTabHuffMem) self.compTabHuffEnt = QTableWidgetItem() self.compTable.setItem(1, 1, self.compTabHuffEnt) self.compTabHuffAvg = QTableWidgetItem() self.compTable.setItem(2, 1, self.compTabHuffAvg) # parem du tableau self.compTable.horizontalHeader().setCascadingSectionResizes(False) self.compTable.verticalHeader().setVisible(True) font = QFont("Mono", 8) self.compTable.setFont(font) compGrid.addWidget(self.compTable, 1, 0, 1, 1) self.analysGrid.addWidget(self.compGroup, 0, 0, 1, 1) mainGrid.addWidget(analysGroup, 0, 1, 1, 1) # -+++++++----------------- groupe du texte -----------------+++++++- # groupBox = QGroupBox(u"Texte", centralwidget) textGrid = QGridLayout(groupBox) # -+++++++------------- groupe du texte original ------------+++++++- # orgTextGroup = QGroupBox(u"Texte original (Ctrl+T)", groupBox) orgTextGrid = QGridLayout(orgTextGroup) self.orgText = QTextEdit(orgTextGroup) self.orgText.setPalette(self.defaultPalette) orgTextGrid.addWidget(self.orgText, 0, 0, 1, 1) textGrid.addWidget(orgTextGroup, 0, 0, 1, 2) # -+++++++------------ groupe du texte compressé ------------+++++++- # compressedTextGroup = QGroupBox(u"Texte compressé (Ctrl+H)", groupBox) compressedTextGrid = QGridLayout(compressedTextGroup) self.compressedText = QTextEdit(compressedTextGroup) self.compressedText.setPalette(self.defaultPalette) compressedTextGrid.addWidget(self.compressedText, 0, 0, 1, 1) textGrid.addWidget(compressedTextGroup, 1, 0, 1, 2) # -+++++++------------ groupe pour le texte ascii -----------+++++++- # asciiTextGroup = QGroupBox(u"Texte ASCII", groupBox) asciiTextGrid = QGridLayout(asciiTextGroup) self.asciiText = QTextBrowser(asciiTextGroup) self.asciiText.setPalette(self.defaultPalette) asciiTextGrid.addWidget(self.asciiText, 0, 0, 1, 1) textGrid.addWidget(asciiTextGroup, 2, 0, 1, 2) # -+++++++-------------------- label de log -----------------+++++++- # self.logLab = QLabel(analysGroup) textGrid.addWidget(self.logLab, 3, 0, 1, 2) # -+++++++----------- bouton pour encoder le texte ----------+++++++- # self.encodeBut = QPushButton(groupBox) self.encodeBut.setStatusTip( u"Cliquez sur ce bouton pour encoder le texte original.") self.encodeBut.setText(u"ENCODER") self.encodeBut.clicked.connect(self.encode_text) textGrid.addWidget(self.encodeBut, 4, 0, 1, 1) # -+++++++----------- bouton pour décoder le texte ----------+++++++- # self.decodeBut = QPushButton(groupBox) self.decodeBut.setStatusTip( u"Cliquez sur ce bouton pour décoder le texte compressé.") self.decodeBut.setText(u"DÉCODER") self.decodeBut.clicked.connect(self.decode_text) textGrid.addWidget(self.decodeBut, 4, 1, 1, 1) mainGrid.addWidget(groupBox, 0, 0, 1, 1) self.setCentralWidget(centralwidget) # -+++++++--------------- une barre de statut ---------------+++++++- # self.setStatusBar(QStatusBar(self)) # -+++++++--------------------- le menu ---------------------+++++++- # self.fileMenu = QMenu(u"Fichier") self.fileMenu.addAction(u"Importer un texte...", self.open_text) self.fileMenu.addAction( u"Importer un texte encodé...", lambda: self.open_text(True)) self.fileMenu.addAction(u"Importer un dictionnaire...", self.open_dict) self.fileMenu.addAction(u"Enregistrer le dictionnaire...", self.save_dict) self.fileMenu.addAction(u"Quitter", self.close) self.menuBar().addMenu(self.fileMenu) QMetaObject.connectSlotsByName(self) def open_text(self, compressed=False): """Ouvrir un fichier contenant un texte compressé ou non.""" fname, _ = QFileDialog.getOpenFileName(self, u'Ouvrir') f = open(fname, 'r') with f: data = f.read() if compressed: self.compressedText.setText(data) else: self.orgText.setText(data) def open_dict(self): """Ouvrir un dictionnaire de codes Huffman.""" fname, _ = QFileDialog.getOpenFileName(self, u'Ouvrir') self.occ = {} self.hCodes = {} self.aCodes = {} self.hCost = {} self.aCost = {} self.hCodes = create_dict_from_file(fname) self.update_codes_table() self.logLab.setText(u"Dictionnaire de Huffman importé.") return self.hCodes def save_dict(self): """Enregistrer le dictionnaire de codes Huffman.""" fname, _ = QFileDialog.getSaveFileName(self, "Enregistrer sous") save_dict_to_file(fname, self.hCodes) def make_tab_rows(self): """Génère le remplissage des lignes du tableau des codes.""" dictList = [self.occ, self.aCodes, self.hCodes, self.aCost, self.hCost] tabList = [] charList = self.hCodes.keys() if self.hCodes else self.occ.keys() for char in charList: l = ["'" + char + "'"] for dic in dictList: try: l.append(dic[char]) except KeyError: l.append('') tabList.append(l) return tabList def encode_text(self, wizard=True): """Encode le texte original.""" self.text = self.orgText.toPlainText().encode('utf-8') if not self.text: self.compressedText.setText(u"") self.asciiText.setText(u"") self.logLab.setText( u"<font color=#A52A2A>Rien à compresser.</font>") return self.occ = {} self.tree = () self.hCodes = {} self.aCodes = {} self.hCost = {} self.aCost = {} self.hSqueezed = [] self.aSqueezed = [] self.stringHSqueezed = '' self.stringASqueezed = '' if wizard: self.launch_wizard( EncodeWizard(self), u"<font color=#008000>Compression achevée.</font>") else: self.make_occ() self.make_tree() self.make_codes() self.make_cost() self.make_encoded_text() self.make_comp() def decode_text(self, wizard=True): """Décode le texte compressé.""" self.codeString = str( self.compressedText.toPlainText().replace(' ', '')) if not self.codeString or not self.hCodes: self.orgText.setText(u"") self.asciiText.setText(u"") if not self.codeString: self.logLab.setText( u"<font color=#A52A2A>Rien à décompresser.</font>") if not self.hCodes: self.logLab.setText( u"<font color=#A52A2A>Dictionnaire indisponible pour la décompression.</font>") return self.text = '' self.stringASqueezed = '' if wizard: self.launch_wizard( DecodeWizard(self), u"<font color=#008000>Texte décompressé.</font>") else: self.make_code_map() self.make_decoded_text() def launch_wizard(self, wizard, finishString): """Lance l'assistant d'en/décodage pour guider l'utilisateur. Cache les options inaccessibles pendant l'assistant. """ self.logLab.setText( u"<font color=#9E6A00>Opération en cours. " u"Suivre les indications.</font>") disItems = [self.orgText, self.compressedText, self.encodeBut, self.decodeBut, self.fileMenu.actions()[1], self.fileMenu.actions()[2], self.fileMenu.actions()[3]] for item in disItems: item.setEnabled(False) self.compGroup.setVisible(False) self.analysGrid.addWidget(wizard, 0, 0, 1, 1) res = wizard.exec_() if res: self.logLab.setText(finishString) else: self.logLab.setText( u"<font color=#A52A2A>Opération interrompue.</font>") for item in disItems: item.setEnabled(True) self.compGroup.setVisible(True) def update_ratio_lab(self): """Replace la valeur du label du ratio de compression.""" if not self.stringASqueezed: val = '/' else: val = (len(self.stringHSqueezed.replace(' ', '')) / float(len(self.stringASqueezed.replace(' ', '')))) self.ratioLab.setText(unicode('Taux de compression: ' + str(val))) def update_comp_table(self): """Met à jour le tableau de comparaison ASCII VS Huffman.""" self.compTabASCIIMem.setText(unicode(len(''.join(self.aSqueezed)))) self.compTabHuffMem.setText(unicode(len(''.join(self.hSqueezed)))) # entropie ? self.compTabASCIIEnt.setText('0') self.compTabHuffEnt.setText('0') self.compTabASCIIAvg.setText(unicode(8)) self.compTabHuffAvg.setText(unicode( average_code_length(self.hSqueezed))) self.compTable.resizeColumnsToContents() def update_codes_table(self, hlRow=[]): """Met à jour le tableau des codes et surligne les lignes de hlRow.""" self.codesTableModel.hlRow = hlRow self.codesTableModel.emptyTable() self.codesTableModel.fillTable(self.make_tab_rows()) self.codesTable.resizeColumnsToContents() def centerAndResize(self): """Centre et redimmensionne le widget.""" screen = QDesktopWidget().screenGeometry() self.resize(screen.width() / 1.6, screen.height() / 1.4) size = self.geometry() self.move( (screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) #===================== METHODS FOR EN/DECODE WIZARDS =====================# def make_encode_init(self): self.compressedText.setText(unicode(self.stringHSqueezed)) self.asciiText.setText(unicode(self.stringASqueezed)) self.orgText.setTextColor(QColor(Qt.darkGreen)) self.orgText.setText(unicode(self.text.decode('utf-8'))) self.update_codes_table() def make_occ(self): self.orgText.setTextColor(QColor(Qt.white)) self.orgText.setText(unicode(self.text.decode('utf-8'))) self.occ = occurences(self.text) self.update_codes_table([0, 1]) def make_tree(self): self.tree = make_trie(self.occ) self.update_codes_table() def make_codes(self): self.hCodes = make_codes(self.tree, {}) self.aCodes = make_ascii_codes(self.occ.keys(), {}) self.update_codes_table([2, 3]) def make_cost(self): self.hCost = tree_cost(self.hCodes, self.occ) self.aCost = tree_cost(self.aCodes, self.occ) self.update_codes_table([4, 5]) def make_encoded_text(self): self.hSqueezed = squeeze(self.text, self.hCodes) self.aSqueezed = squeeze(self.text, self.aCodes) self.stringHSqueezed = ' '.join(self.hSqueezed) self.stringASqueezed = ' '.join(self.aSqueezed) self.compressedText.setTextColor(QColor(Qt.darkGreen)) self.asciiText.setTextColor(QColor(Qt.darkYellow)) self.compressedText.setText(unicode(self.stringHSqueezed)) self.asciiText.setText(unicode(self.stringASqueezed)) self.update_codes_table() def make_comp(self): self.compressedText.setTextColor(QColor(Qt.white)) self.asciiText.setTextColor(QColor(Qt.white)) self.compressedText.setText(unicode(self.stringHSqueezed)) self.asciiText.setText(unicode(self.stringASqueezed)) self.update_codes_table() self.update_comp_table() self.update_ratio_lab() def make_decode_init(self): self.orgText.setText(unicode(self.text)) self.asciiText.setText(unicode(self.stringASqueezed)) self.compressedText.setTextColor(QColor(Qt.darkGreen)) self.compressedText.setText(self.compressedText.toPlainText()) def make_code_map(self): self.compressedText.setTextColor(QColor(Qt.white)) self.compressedText.setText(self.compressedText.toPlainText()) self.orgText.setText(unicode(self.text)) self.asciiText.setText(unicode(self.stringASqueezed)) self.codeMap = dict(zip(self.hCodes.values(), self.hCodes.keys())) self.update_codes_table([0, 3]) def make_decoded_text(self): self.unSqueezed = unsqueeze(self.codeString, self.codeMap) self.text = ''.join(self.unSqueezed) self.orgText.setTextColor(QColor(Qt.darkGreen)) self.orgText.setText(unicode(self.text.decode('utf-8'))) self.asciiText.setText(unicode(self.stringASqueezed)) self.update_codes_table() def make_ascii_decode(self): self.orgText.setTextColor(QColor(Qt.white)) self.orgText.setText(unicode(self.text.decode('utf-8'))) self.aCodes = make_ascii_codes(self.codeMap.values(), {}) self.aSqueezed = squeeze(self.text, self.aCodes) self.stringASqueezed = ' '.join(self.aSqueezed) self.occ = occurences(self.text) self.hCost = tree_cost(self.hCodes, self.occ) self.aCost = tree_cost(self.aCodes, self.occ) self.asciiText.setTextColor(QColor(Qt.darkGreen)) self.asciiText.setText(unicode(self.stringASqueezed)) self.update_codes_table([1, 2, 4, 5])
class PresenceOverviewWidget(HorsePanel): @Slot(QModelIndex) def cell_entered(self, ndx): employee_id = self._employee_id_on_row(ndx) if not employee_id: self._show_totals_day_off(None) elif employee_id: chrono.chrono_start() employee = None for i in self.employees: if i.employee_id == employee_id: employee = i break self.detail_subframe.set_title(employee.fullname) self._show_totals_day_off(employee_id) d = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, ndx.column()))) chrono.chrono_click("Retrieveing data") tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date( employee_id, d) work_timetracks = dao.timetrack_dao.all_work_for_employee_date_manual( employee_id, d) presence_timetracks = dao.timetrack_dao.all_presence_for_employee_date_managed_by_code_full( employee_id, d) # employee = dao.employee_dao.find_by_id(employee_id) special_activities = dao.special_activity_dao.find_on_day( employee_id, d) chrono.chrono_click("Redrawing") self.time_report_view.redraw(datetime(d.year, d.month, d.day, 6, 0), tars, employee_id, work_timetracks, presence_timetracks, special_activities, view_title=_("Work on {}").format( date_to_dmy(d, full_month=True))) session().close( ) # FIXME Put his one line above; but that's tough ! SQLA doesn't help us much here ! chrono.chrono_click("Session closed") # for event_type, duration in self.day_event_totals[employee_id].items(): # mainlog.debug("{}{}".format(event_type, duration)) self._toggle_days_off_actions() def _employee_id_on_row(self, row_or_ndx): r = row_or_ndx if type(r) != int: r = row_or_ndx.row() return self._table_model.data(self._table_model.index(r, 0), Qt.UserRole) DAY_EVENT_PALETTE = { DayEventType.holidays: (Qt.GlobalColor.white, Qt.GlobalColor.red), DayEventType.day_off: (Qt.GlobalColor.white, Qt.GlobalColor.darkRed), DayEventType.unpaid_day_off: (Qt.GlobalColor.black, Qt.GlobalColor.magenta), DayEventType.free_day: (Qt.GlobalColor.white, Qt.GlobalColor.darkMagenta), DayEventType.overtime: (Qt.GlobalColor.black, Qt.GlobalColor.green), DayEventType.recuperation: (Qt.GlobalColor.white, Qt.GlobalColor.darkGreen), DayEventType.unemployment: (Qt.GlobalColor.white, Qt.GlobalColor.blue), DayEventType.unemployment_short: (Qt.GlobalColor.white, Qt.GlobalColor.darkBlue), DayEventType.work_accident: (Qt.GlobalColor.black, Qt.GlobalColor.yellow), DayEventType.sick_leave: (Qt.GlobalColor.white, Qt.GlobalColor.darkYellow) } MONTH_EVENT_COLUMN = 2 YEAR_EVENT_COLUMN = 3 def _make_total_days_off_panel(self): widget = QFrame() widget.setObjectName('HorseRegularFrame') widget.setFrameShape(QFrame.Panel) widget.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout() #layout.addWidget(QLabel(_("Days off to date"))) self.day_off_total_duration_labels = dict() self.day_off_month_duration_labels = dict() self.day_off_labels = dict() self._day_off_table_model = QStandardItemModel(10, 3) self._day_off_table_model.setHorizontalHeaderLabels( [None, None, _("This\nmonth"), _("Before")]) self.day_off_table_view = QTableView(None) self.day_off_table_view.setModel(self._day_off_table_model) # self.day_off_table_view.setHorizontalHeader(self.headers_view) self.day_off_table_view.verticalHeader().hide() self.day_off_table_view.setAlternatingRowColors(True) self.day_off_table_view.setEditTriggers( QAbstractItemView.NoEditTriggers) self.day_off_table_view.hide() row = 0 for det in DayEventType.symbols(): ndx = self._day_off_table_model.index(row, 0) self._day_off_table_model.setData(ndx, det.description, Qt.DisplayRole) ndx = self._day_off_table_model.index(row, 1) fg, bg = self.DAY_EVENT_PALETTE[det] self._day_off_table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) self._day_off_table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._day_off_table_model.setData(ndx, DayEventType.short_code(det), Qt.DisplayRole) row += 1 layout.addWidget(self.day_off_table_view) grid = QGridLayout() self.days_off_layout = grid grid.setColumnStretch(3, 1) row = 0 grid.addWidget(QLabel(_('Year')), row, self.YEAR_EVENT_COLUMN) grid.addWidget(QLabel(_('Month')), row, self.MONTH_EVENT_COLUMN) row += 1 for det in DayEventType.symbols(): self.day_off_total_duration_labels[det] = QLabel("-") self.day_off_month_duration_labels[det] = QLabel("-") self.day_off_labels[det] = QLabel(det.description) hlayout = QHBoxLayout() sl = QLabel() fg, bg = self.DAY_EVENT_PALETTE[det] def to_html_rgb(color): i = color.red() * 256 * 256 + color.green() * 256 + color.blue( ) return "#{:06X}".format(i) p = QPalette() p.setColor(QPalette.Window, QColor(bg)) p.setColor(QPalette.WindowText, QColor(fg)) sl.setPalette(p) sl.setAlignment(Qt.AlignCenter) sl.setStyleSheet("border: 2px solid black; background: {}".format( to_html_rgb(QColor(bg)))) t = DayEventType.short_code(det) mainlog.debug(t) sl.setAutoFillBackground(True) sl.setText(t) grid.addWidget(sl, row, 0) grid.addWidget(self.day_off_labels[det], row, 1) grid.addWidget(self.day_off_total_duration_labels[det], row, self.YEAR_EVENT_COLUMN) grid.addWidget(self.day_off_month_duration_labels[det], row, self.MONTH_EVENT_COLUMN) hlayout.addStretch() row += 1 layout.addLayout(grid) layout.addStretch() self.day_off_table_view.resizeColumnsToContents() # self.day_off_table_view.setMinimumWidth( self.day_off_table_view.width()) # self.day_off_table_view.resize( self.day_off_table_view.minimumWidth(), # self.day_off_table_view.minimumHeight(),) widget.setLayout(layout) return widget def _show_totals_day_off(self, employee_id): mainlog.debug("_show_totals_day_off : {}".format(employee_id)) def form_layout_row_set_visible(layout, row_ndx, is_visible): for i in range(layout.columnCount()): l = layout.itemAtPosition(row_ndx, i) if l and l.widget(): l.widget().setVisible(is_visible) det_to_show = dict() row = 0 for det in DayEventType.symbols(): yearly = 0 if employee_id in self.all_events_in_year: if det in self.all_events_in_year[employee_id]: yearly = nice_round( self.all_events_in_year[employee_id][det]) monthly = 0 if employee_id in self.day_event_totals: if det in self.day_event_totals[employee_id]: monthly = nice_round( self.day_event_totals[employee_id][det]) # ndx = self._day_off_table_model.index(row,self.YEAR_EVENT_COLUMN) # self._day_off_table_model.setData(ndx, v, Qt.DisplayRole) if yearly or monthly: det_to_show[det] = {'monthly': monthly, 'yearly': yearly} if det_to_show: # If there are some days spent on some counters, then we display # those counters *only* mainlog.debug("_show_totals_day_off : showing some events ".format( str(det_to_show))) row = 0 for det in DayEventType.symbols(): if det in det_to_show: monthly = det_to_show[det]['monthly'] yearly = det_to_show[det]['yearly'] form_layout_row_set_visible(self.days_off_layout, row + 1, True) self.day_off_total_duration_labels[det].setText(yearly or '-') self.day_off_month_duration_labels[det].setText(monthly or '-') else: form_layout_row_set_visible(self.days_off_layout, row + 1, False) else: # If there are no days spent on any counter, then we display # all counters at the 0 mark. mainlog.debug("_show_totals_day_off : showing no event") row = 0 for det in DayEventType.symbols(): form_layout_row_set_visible(self.days_off_layout, row + 1, True) row += 1 # self.day_off_table_view.resizeColumnsToContents() self.days_off_panel.parent().update() @Slot() def refresh_action(self): global dao # mainlog.debug("refresh action started") self.hours_per_pers_subframe.set_title(date_to_my( self.base_date, True)) chrono.chrono_start() all_events_in_month = people_admin_service.events_for_month( self.base_date) employee_with_events = [ event.employee_id for event in all_events_in_month ] # mainlog.debug(all_events_in_month) self.all_events_in_year = people_admin_service.events_for_year( self.base_date.year) self.all_presences = all_presences = dao.employee_dao.presence_overview_for_month( self.base_date) all_correction_times = dict() for s in dao.month_time_synthesis_dao.load_all_synthesis( self.base_date.year, self.base_date.month): all_correction_times[s.employee_id] = s.correction_time special_activities = dao.special_activity_dao.find_on_month( self.base_date) employees = list( filter( lambda e: e.is_active or e.employee_id in all_presences or e. employee_id in all_correction_times or e.employee_id in special_activities or e.employee_id in employee_with_events, dao.employee_dao.list_overview())) self.employees = employees chrono.chrono_click() day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] t_start = datetime(self.base_date.year, self.base_date.month, 1) t_end = datetime(self.base_date.year, self.base_date.month, day_max, 23, 59, 59, 999999) self._table_model.setRowCount(len(employees)) self._table_model.setColumnCount(1 + day_max + 3) headers = QStandardItemModel(1, 1 + day_max + 3) headers.setHeaderData(0, Qt.Orientation.Horizontal, _("Employee")) for i in range(day_max): headers.setHeaderData(i + 1, Qt.Orientation.Horizontal, "{}".format(i + 1)) headers.setHeaderData(day_max + 1, Qt.Orientation.Horizontal, _("Correction")) headers.setHeaderData(day_max + 2, Qt.Orientation.Horizontal, _("Total")) headers.setHeaderData(day_max + 3, Qt.Orientation.Horizontal, _("Days off")) self.headers_view.setModel( headers) # qt's doc : The view does *not* take ownership self.header_model = headers self.headers_view.setModel( self.header_model) # qt's doc : The view does *not* take ownership # Compute all mondays indices monday = 0 if t_start.weekday() > 0: monday = 7 - t_start.weekday() all_mondays = [] while monday < day_max: all_mondays.append(monday) monday += 7 today = date.today() # mainlog.debug("Running on employees") for row in range(self._table_model.rowCount()): # Clear the line for col in range(0, 32): ndx = self._table_model.index(row, col) self._table_model.setData(ndx, None, Qt.BackgroundRole) self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black), Qt.TextColorRole) self._table_model.setData(ndx, None, Qt.DisplayRole) self._table_model.setData(ndx, None, Qt.UserRole) self._table_model.setData(ndx, None, Qt.UserRole + 1) # else: # self._table_model.setData(ndx,None,Qt.BackgroundRole) # else: # self._table_model.setData(ndx,None,Qt.DisplayRole) # self._table_model.setData(ndx,None,Qt.BackgroundRole) # Mark mondays for col in all_mondays: # col + 1 to account for the employee column self._table_model.setData( self._table_model.index(row, col + 1), QBrush(QColor(230, 230, 255)), Qt.BackgroundRole) # Mark today if today.month == self.base_date.month and today.year == self.base_date.year: self._table_model.setData( self._table_model.index(row, today.day), QBrush(QColor(255, 255, 128)), Qt.BackgroundRole) row = 0 for employee in employees: # employees are sorted self._table_model.setData(self._table_model.index(row, 0), employee.fullname, Qt.DisplayRole) # FIXME Use a delegate self._table_model.setData(self._table_model.index(row, 0), employee.employee_id, Qt.UserRole) # FIXME Use a delegate correction = 0 if employee.employee_id in all_correction_times: correction = all_correction_times[employee.employee_id] self._table_model.setData( self._table_model.index(row, day_max + 1), duration_to_hm(correction, short_unit=True), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 1), None, Qt.DisplayRole) presence = 0 if employee.employee_id in all_presences and len( all_presences[employee.employee_id]) > 0: import functools presence = functools.reduce( lambda acc, s: acc + s, all_presences[employee.employee_id], 0) presence += correction if presence != 0: self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black), Qt.TextColorRole) self._table_model.setData( self._table_model.index(row, day_max + 2), duration_to_hm(presence, short_unit=True), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 2), None, Qt.DisplayRole) if employee.employee_id in all_presences and len( all_presences[employee.employee_id]) > 0: for b in range(len(all_presences[employee.employee_id])): ndx = self._table_model.index(row, b + 1) p = all_presences[employee.employee_id][b] if p > 0: self._table_model.setData( ndx, duration_to_hm(p, short_unit=True), Qt.DisplayRole) self._table_model.setData(ndx, p, Qt.UserRole) if p >= 4 and p <= 8: # Regular work load self._table_model.setData( ndx, QBrush(QColor(192, 255, 192)), Qt.BackgroundRole) elif p > 8 or (p < 4 and p > 0): # Problematic work load self._table_model.setData( ndx, QBrush(QColor(255, 192, 192)), Qt.BackgroundRole) if employee.employee_id in special_activities: sa_of_employee = special_activities[employee.employee_id] for sa in sa_of_employee: start = max(t_start, sa.start_time) end = min(t_end, sa.end_time) for i in range(start.day, end.day + 1): ndx = self._table_model.index(row, i) self._table_model.setData(ndx, QBrush(QColor(255, 128, 0)), Qt.BackgroundRole) # self._table_model.setData(self._table_model.index(row,b+1),Qt.AlignRight | Qt.AlignVCenter,Qt.TextAlignmentRole) row += 1 # Display day events employee_id_to_row = dict() # little accelerator for row in range(len(employees)): employee_id_to_row[employees[row].employee_id] = row # Compute days off totals and show them self.day_event_totals = dict([(e.employee_id, dict()) for e in employees]) for day_event in all_events_in_month: # mainlog.debug("employee_id = {}".format(day_event.employee_id)) # if day_event.employee_id not in self.day_event_totals: # mainlog.debug(self.day_event_totals) t = self.day_event_totals[day_event.employee_id] if day_event.event_type not in t: t[day_event.event_type] = day_event.duration else: t[day_event.event_type] += day_event.duration for employee in employees: # employees are sorted t = self.day_event_totals[employee.employee_id] mainlog.debug(t) total_off = sum(t.values()) mainlog.debug(total_off) row = employee_id_to_row[employee.employee_id] mainlog.debug(row) if total_off: self._table_model.setData( self._table_model.index(row, day_max + 3), nice_round(total_off), Qt.DisplayRole) else: self._table_model.setData( self._table_model.index(row, day_max + 3), None, Qt.DisplayRole) # Show days off for day_event in all_events_in_month: row = employee_id_to_row[day_event.employee_id] col = day_event.date.day fg = bg = None if day_event.event_type in self.DAY_EVENT_PALETTE: fg, bg = self.DAY_EVENT_PALETTE[day_event.event_type] else: fg, bg = Qt.GlobalColor.red, Qt.GlobalColor.gray ndx = self._table_model.index(row, col) self._table_model.setData(ndx, day_event.day_event_id, Qt.UserRole + 1) # The problem here is to nicely blend the fact # that you can have a day event mixed with actual work # the very same day. Here's a poor man solution. active_time = self._table_model.data(ndx, Qt.UserRole) if not active_time: self._table_model.setData( ndx, DayEventType.short_code(day_event.event_type), Qt.DisplayRole) self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) else: self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole) self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole) self._table_model.setData( ndx, duration_to_hm(active_time, short_unit=True) + DayEventType.short_code(day_event.event_type), Qt.DisplayRole) chrono.chrono_click() #for i in range(len(all_mondays)): self.table_view.resizeColumnsToContents() # mainlog.debug("Reset selection") ndx = self.table_view.currentIndex() self.table_view.selectionModel().clear() # self.table_view.selectionModel().clearSelection() # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select) self.table_view.selectionModel().setCurrentIndex( self.table_view.model().index(ndx.row(), ndx.column()), QItemSelectionModel.Select) self.cell_entered(self.table_view.currentIndex()) @Slot() def edit_tars(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) d = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, ndx.column()))) dialog = TimeReportingScannerDialog(self) dialog.set_data(datetime(d.year, d.month, d.day, 6, 0), employee_id) dialog.exec_() if dialog.result() == QDialog.Accepted: # pub.sendMessage('time_report.changed') self.timetrack_changed.emit() self.refresh_action() @Slot() def show_actions(self): button = self.action_menu.parent() p = button.mapToGlobal(QPoint(0, button.height())) self.action_menu.exec_(p) @Slot() def delete_holidays(self): ndx = self.table_view.currentIndex() employee_id = self._employee_id_on_row(ndx) d = date(self.base_date.year, self.base_date.month, ndx.column()) if dao.special_activity_dao.delete_by_employee_and_date( employee_id, d): self.refresh_panel() @Slot() def create_holidays(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) left_col = 1000 right_col = 0 for ndx in self.table_view.selectionModel().selectedIndexes(): c = ndx.column() left_col = min(c, left_col) right_col = max(c, right_col) d_start = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, left_col))) d_end = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, right_col))) dialog = HolidaysDialog(self) sa = SpecialActivity() sa.employee_id = employee_id sa.reporter_id = user_session.user_id sa.encoding_date = date.today() sa.start_time = datetime(d_start.year, d_start.month, d_start.day, 6, 0) sa.end_time = datetime(d_end.year, d_end.month, d_end.day, 14, 0) dialog.setup(sa, dao.employee_dao.find_by_id(employee_id).fullname) # dialog.set_data(employee,self.base_date,c) dialog.exec_() if dialog.result() == QDialog.Accepted: dao.special_activity_dao.save(sa) self.refresh_action() @Slot() def edit_month_correction(self): employee_id = self._employee_id_on_row(self.table_view.currentIndex()) if employee_id: employee = dao.employee_dao.find_by_id(employee_id) c = dao.month_time_synthesis_dao.load_correction_time( employee_id, self.base_date.year, self.base_date.month) dialog = MonthTimeCorrectionDialog(self) dialog.set_data(employee.fullname, self.base_date, c) dialog.exec_() if dialog.result() == QDialog.Accepted: c = dao.month_time_synthesis_dao.save(employee_id, self.base_date.year, self.base_date.month, dialog.correction_time) self.refresh_action() @Slot() def month_today(self): self.base_date = date.today() self.refresh_action() @Slot() def month_before(self): m = self.base_date.month if m > 1: self.base_date = date(self.base_date.year, m - 1, 1) else: self.base_date = date(self.base_date.year - 1, 12, 1) self.refresh_action() @Slot() def month_after(self): m = self.base_date.month if self.base_date.year < date.today().year + 1 \ or m < date.today().month: if m < 12: self.base_date = date(self.base_date.year, m + 1, 1) else: self.base_date = date(self.base_date.year + 1, 1, 1) self.refresh_action() @Slot() def edit_timetrack_no_ndx(self): ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: self.edit_timetrack(ndx) else: showWarningBox(_("Can't edit"), _("You must first select a day/person.")) timetrack_changed = Signal() @Slot(QModelIndex) def edit_timetrack(self, ndx): global dao global user_session if ndx.column() >= 1: edit_date = date( self.base_date.year, self.base_date.month, ndx.column()) # +1 already in because of employee's names employee_id = self._employee_id_on_row(ndx) tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date( employee_id, edit_date) if len(tars) == 0: d = EditTimeTracksDialog(self, dao, edit_date) d.set_employee_and_date(employee_id, edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() self.timetrack_changed.emit() d.deleteLater() else: edit_date = datetime(self.base_date.year, self.base_date.month, ndx.column(), hour=6) from koi.TimeReportingScanner import TimeReportingScannerDialog d = TimeReportingScannerDialog(self) d.set_data(edit_date, employee_id) # or 16 d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() self.timetrack_changed.emit() d.deleteLater() @Slot() def editTaskActionReports(self): if not user_session.has_any_roles(['TimeTrackModify']): return m = self.base_date.month ndx = self.table_view.currentIndex() if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0: edit_date = date( self.base_date.year, m, ndx.column()) # +1 already in because of employee's names employee = self._table_model.data( self._table_model.index(ndx.row(), 0), Qt.UserRole) # FIXME Use a delegate d = EditTaskActionReportsDialog(dao, self, edit_date) d.set_employee_date(employee, edit_date) d.exec_() if d.result() == QDialog.Accepted: self.refresh_action() d.deleteLater() else: showWarningBox(_("Can't edit"), _("You must first select a day/person.")) # @Slot(QModelIndex) # def timetrack_changed(self,ndx): # selected_timetrack = self.controller.model.object_at(ndx) # # Update the colors in the timetrack views # # to show what action reports correspond to the # # selected timetrack # self.controller_actions.model.current_timetrack = selected_timetrack # self.controller_actions.model.beginResetModel() # self.controller_actions.model.endResetModel() # # Make sure the first of the action reports is shown in the # # table # action_reports = self.controller_actions.model.objects # for i in range(len(action_reports)-1,-1,-1): # if action_reports[i] and action_reports[i].timetrack == selected_timetrack: # self.controller_actions.view.scrollTo(self.controller_actions.model.index(i,0)) # break def _make_table_header(self): pass def __init__(self, parent, find_order_action_slot): super(PresenceOverviewWidget, self).__init__(parent) self.set_panel_title(_("Presence overview")) self.base_date = date.today() headers = QStandardItemModel(1, 31 + 3) self._table_model = QStandardItemModel(1, 31 + 3, None) self.headers_view = QHeaderView(Qt.Orientation.Horizontal, self) self.header_model = headers self.headers_view.setResizeMode(QHeaderView.ResizeToContents) self.headers_view.setModel( self.header_model) # qt's doc : The view does *not* take ownership self.table_view = TableViewSignaledEvents(None) self.table_view.setModel(self._table_model) self.table_view.setHorizontalHeader(self.headers_view) self.table_view.verticalHeader().hide() self.table_view.setAlternatingRowColors(True) self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_view.setContextMenuPolicy(Qt.CustomContextMenu) self.table_view.customContextMenuRequested.connect( self.popup_context_menu) self.copy_action = QAction(_("Copy order parts"), self.table_view) self.copy_action.triggered.connect(self.copy_slot) self.copy_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C)) self.copy_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.table_view.addAction(self.copy_action) self.select_all_action = QAction(_("Select all"), self.table_view) self.select_all_action.triggered.connect(self.select_all_slot) self.select_all_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_A)) self.select_all_action.setShortcutContext( Qt.WidgetWithChildrenShortcut) self.table_view.addAction(self.select_all_action) # self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems) # self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) navbar = NavBar(self, [(_("Month before"), self.month_before), (_("Today"), self.month_today), (_("Action"), self.show_actions), (_("Month after"), self.month_after), (_("Find"), find_order_action_slot)]) self.action_menu = QMenu(navbar.buttons[2]) navbar.buttons[2].setObjectName("specialMenuButton") navbar.buttons[4].setObjectName("specialMenuButton") self._make_days_off_menu_and_action_group() list_actions = [ # (_("Edit"),self.edit_tars, None, None), (_("Edit"), self.edit_timetrack_no_ndx, None, None), (_("Month correction"), self.edit_month_correction, None, [RoleType.modify_monthly_time_track_correction]), (self.days_off_menu, None), (self.copy_action, None), (self.select_all_action, None) ] # (_("Insert holidays"),self.create_holidays, None, None), # (_("Delete holidays"),self.delete_holidays, None, None) ] populate_menu(self.action_menu, self, list_actions) # mainlog.debug("tile widget") self.title_box = TitleWidget(_("Presence Overview"), self, navbar) self.vlayout = QVBoxLayout(self) self.vlayout.setObjectName("Vlayout") self.vlayout.addWidget(self.title_box) self.hours_per_pers_subframe = SubFrame(_("Overview"), self.table_view, self) self.vlayout.addWidget(self.hours_per_pers_subframe) self.time_report_view = TimeReportView(self) self.days_off_panel = self._make_total_days_off_panel() vbox = QVBoxLayout() vbox.addWidget(self.days_off_panel) vbox.addStretch() vbox.setStretch(0, 0) vbox.setStretch(1, 1) hlayout = QHBoxLayout() hlayout.addWidget(self.time_report_view) hlayout.addLayout(vbox) hlayout.setStretch(0, 1) self.detail_subframe = SubFrame(_("Day"), hlayout, self) self.vlayout.addWidget(self.detail_subframe) self.setLayout(self.vlayout) # dbox = QVBoxLayout() # dbox.addWidget(QLabel("kjkljkj")) # self.total_active_hours = LabeledValue(_("Total activity")) # dbox.addWidget(self.total_active_hours) # hbox = QHBoxLayout() # hbox.addWidget(self.table_view) # hbox.addLayout(dbox) # self.selection_model = self.table_view.selectionModel() # mainlog.debug(m) #sm = QItemSelectionModel(self.table_view.model()) #sm.setModel(self.table_view.model()) # self.table_view.setSelectionModel(self.selection_model) self.table_view.selectionModel().currentChanged.connect( self.cell_entered) self.table_view.doubleClickedCell.connect(self.edit_timetrack) def _selection_to_period(self): left_col = 1000 right_col = 0 for ndx in self.table_view.selectionModel().selectedIndexes(): c = ndx.column() left_col = min(c, left_col) right_col = max(c, right_col) d_start = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, left_col))) d_end = date( self.base_date.year, self.base_date.month, min( calendar.monthrange(self.base_date.year, self.base_date.month)[1], max(1, right_col))) return d_start, d_end def _toggle_days_off_actions(self): day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] ndx = self.table_view.currentIndex() can_add = can_remove = False if ndx.column() >= 1 and ndx.column() <= day_max: day_event_id = ndx.data(Qt.UserRole + 1) can_add = True # if not day_event_id: # day = ndx.column() - 1 # # employee_id = self._employee_id_on_row(ndx) # if employee_id in self.all_presences: # if not self.all_presences[employee_id][day]: # can_add = True # else: # can_add = True # else: # can_add = True can_remove = day_event_id is not None self.days_off_add_submenu.setEnabled(can_add) for actions in self.days_off_action_group.actions(): actions.setEnabled(can_add) self.day_off_remove_action.setEnabled(can_remove) def _add_day_off(self, action): if action.data() != 'Remove': day_event_type, day_event_duration = action.data() day_event_type = DayEventType.from_str(day_event_type) mainlog.debug("selected action {} {}".format( day_event_type, day_event_duration)) ndx = self.table_view.currentIndex() day_event_id = ndx.data(Qt.UserRole + 1) # if day_event_id: # showWarningBox(_("There's already a day off here")) # return employee_id = self._employee_id_on_row(ndx) if employee_id in self.all_presences: day = ndx.column() - 1 mainlog.debug("_add_day_off : employee_id={}, day={}".format( employee_id, day)) mainlog.debug(self.all_presences[employee_id]) mainlog.debug(type(self.all_presences[employee_id])) # if self.all_presences[employee_id][day]: # showWarningBox(_("One can't add day off where there is activity")) # return else: mainlog.debug( "_add_day_off : employee_id={} not yet known".format( employee_id)) day_event = DayEvent() day_event.employee_id = employee_id day_event.event_type = day_event_type day, last_day = self._selection_to_period() if day_event_duration in (0.5, 1): days_durations = [(day, day_event_duration)] else: days_durations = [] day, last_day = self._selection_to_period() while day <= last_day: days_durations.append( (day, 1)) # One full work day on the day day += timedelta(1) # mainlog.debug(days_durations) mainlog.debug("Creating day event of type {}".format( day_event.event_type)) try: people_admin_service.set_event_on_days(day_event, days_durations) except ServerException as ex: showErrorBox(ex.translated_message) self.refresh_action() def _remove_day_off(self): # Grab all the selected events event_ids = [] for ndx in self.table_view.selectionModel().selectedIndexes(): day_event_id = ndx.data(Qt.UserRole + 1) if day_event_id: mainlog.debug("Removing event: {}".format(day_event_id)) event_ids.append(day_event_id) # Remove them if event_ids: people_admin_service.remove_events(event_ids) self.refresh_action() def _make_days_off_menu_and_action_group(self): # We use a action group to be able to use the data() of actions # when action is tigerred # Call this ONLY ONCE because there are signal/slot connections. self.days_off_menu = QMenu(_("Day off")) self.days_off_add_submenu = QMenu(_("Set day off")) self.days_off_action_group = QActionGroup(self) for det in DayEventType.symbols(): a_one = QAction(_("Set one day"), self.days_off_action_group) a_one.setData((det.value, 1)) a_half = QAction(_("Set half day"), self.days_off_action_group) a_half.setData((det.value, 0.5)) a_period = QAction(_("Set period"), self.days_off_action_group) a_period.setData((det.value, 2)) self.days_off_action_group.addAction(a_one) self.days_off_action_group.addAction(a_half) self.days_off_action_group.addAction(a_period) m = QMenu(_("Set time off for {}").format(det.description)) m.addAction(a_one) m.addAction(a_half) m.addAction(a_period) self.days_off_add_submenu.addMenu(m) self.days_off_action_group.triggered.connect(self._add_day_off) self.day_off_remove_action = QAction(_("Remove day off"), self) self.day_off_remove_action.triggered.connect(self._remove_day_off) # Now we have the actions, we build the menu self.days_off_menu.addMenu(self.days_off_add_submenu) self.days_off_menu.addAction(self.day_off_remove_action) @Slot(QPoint) def popup_context_menu(self, position): self.days_off_menu.exec_(QCursor.pos()) @Slot() def select_all_slot(self): m = self.table_view.model() all = QItemSelection(m.index(0, 0), m.index(m.rowCount() - 1, m.columnCount() - 1)) self.table_view.selectionModel().select(all, QItemSelectionModel.Select) @Slot() def copy_slot(self): # Collect the rows indices indices = self.table_view.selectedIndexes() if not indices: return min_row = max_row = indices[0].row() min_col = max_col = indices[0].column() def min_max(minimum, v, maximum): if v < minimum: return v, maximum elif v > maximum: return minimum, v else: return minimum, maximum for ndx in self.table_view.selectedIndexes(): min_row, max_row = min_max(min_row, ndx.row(), max_row) min_col, max_col = min_max(min_col, ndx.column(), max_col) mainlog.debug("Copy from {},{} to {},{}".format( min_row, min_col, max_row, max_col)) day_max = calendar.monthrange(self.base_date.year, self.base_date.month)[1] s = "" for r in range(min_row, max_row + 1): d = [] for c in range(min_col, max_col + 1): ndx = self._table_model.item(r, c) if c == 0 or c > day_max: d.append(ndx.data(Qt.DisplayRole) or "") else: t = ndx.data(Qt.UserRole + 1) # Day off if t: t = ndx.data(Qt.DisplayRole) else: hours = ndx.data(Qt.UserRole) # Activity hours if hours is not None: t = str(hours).replace('.', ',') else: t = "" d.append(t) s += "\t".join(d) + u"\n" QApplication.clipboard().setText(s)
class RunnerDialog(QDialog): options_added = Signal(Options) options_running = Signal(Options) options_simulated = Signal(Options) options_error = Signal(Options, Exception) results_saved = Signal(Results, str) results_error = Signal(Results, Exception) def __init__(self, parent=None): QDialog.__init__(self, parent) self.setWindowTitle('Runner') self.setMinimumWidth(750) # Runner self._runner = None self._running_timer = QTimer() self._running_timer.setInterval(500) # Widgets self._dlg_progress = QProgressDialog() self._dlg_progress.setRange(0, 100) self._dlg_progress.setModal(True) self._dlg_progress.hide() lbl_outputdir = QLabel("Output directory") self._txt_outputdir = DirBrowseWidget() max_workers = cpu_count() #@UndefinedVariable lbl_workers = QLabel('Number of workers') self._spn_workers = QSpinBox() self._spn_workers.setRange(1, max_workers) self._spn_workers.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) lbl_max_workers = QLabel('(max: %i)' % max_workers) self._chk_overwrite = QCheckBox("Overwrite existing results in output directory") self._chk_overwrite.setChecked(True) self._lbl_available = QLabel('Available') self._lst_available = QListView() self._lst_available.setModel(_AvailableOptionsListModel()) self._lst_available.setSelectionMode(QListView.SelectionMode.MultiSelection) tlb_available = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_available.addWidget(spacer) act_open = tlb_available.addAction(getIcon("document-open"), "Open") act_open.setShortcut(QKeySequence.Open) tlb_available.addSeparator() act_remove = tlb_available.addAction(getIcon("list-remove"), "Remove") act_clear = tlb_available.addAction(getIcon("edit-clear"), "Clear") self._btn_addtoqueue = QPushButton(getIcon("go-next"), "") self._btn_addtoqueue.setToolTip("Add to queue") self._btn_addtoqueue.setEnabled(False) self._btn_addalltoqueue = QPushButton(getIcon("go-last"), "") self._btn_addalltoqueue.setToolTip("Add all to queue") self._btn_addalltoqueue.setEnabled(False) self._lbl_options = QLabel('Queued/Running/Completed') self._tbl_options = QTableView() self._tbl_options.setModel(_StateOptionsTableModel()) self._tbl_options.setItemDelegate(_StateOptionsItemDelegate()) self._tbl_options.setSelectionMode(QListView.SelectionMode.NoSelection) self._tbl_options.setColumnWidth(1, 60) self._tbl_options.setColumnWidth(2, 80) header = self._tbl_options.horizontalHeader() header.setResizeMode(0, QHeaderView.Interactive) header.setResizeMode(1, QHeaderView.Fixed) header.setResizeMode(2, QHeaderView.Fixed) header.setResizeMode(3, QHeaderView.Stretch) self._btn_start = QPushButton(getIcon("media-playback-start"), "Start") self._btn_cancel = QPushButton("Cancel") self._btn_cancel.setEnabled(False) self._btn_close = QPushButton("Close") self._btn_import = QPushButton("Import") self._btn_import.setEnabled(False) # Layouts layout = QVBoxLayout() sublayout = QGridLayout() sublayout.addWidget(lbl_outputdir, 0, 0) sublayout.addWidget(self._txt_outputdir, 0, 1) sublayout.addWidget(lbl_workers, 1, 0) subsublayout = QHBoxLayout() subsublayout.addWidget(self._spn_workers) subsublayout.addWidget(lbl_max_workers) sublayout.addLayout(subsublayout, 1, 1) layout.addLayout(sublayout) sublayout.addWidget(self._chk_overwrite, 2, 0, 1, 3) sublayout = QGridLayout() sublayout.setColumnStretch(0, 1) sublayout.setColumnStretch(2, 3) sublayout.addWidget(self._lbl_available, 0, 0) sublayout.addWidget(self._lst_available, 1, 0) sublayout.addWidget(tlb_available, 2, 0) subsublayout = QVBoxLayout() subsublayout.addStretch() subsublayout.addWidget(self._btn_addtoqueue) subsublayout.addWidget(self._btn_addalltoqueue) subsublayout.addStretch() sublayout.addLayout(subsublayout, 1, 1) sublayout.addWidget(self._lbl_options, 0, 2) sublayout.addWidget(self._tbl_options, 1, 2) layout.addLayout(sublayout) sublayout = QHBoxLayout() sublayout.addStretch() sublayout.addWidget(self._btn_import) sublayout.addWidget(self._btn_start) sublayout.addWidget(self._btn_cancel) sublayout.addWidget(self._btn_close) layout.addLayout(sublayout) self.setLayout(layout) # Signal self._running_timer.timeout.connect(self._onRunningTimer) act_open.triggered.connect(self._onOpen) act_remove.triggered.connect(self._onRemove) act_clear.triggered.connect(self._onClear) self._btn_addtoqueue.released.connect(self._onAddToQueue) self._btn_addalltoqueue.released.connect(self._onAddAllToQueue) self._btn_start.released.connect(self._onStart) self._btn_cancel.released.connect(self._onCancel) self._btn_close.released.connect(self._onClose) self._btn_import.released.connect(self._onImport) self.options_added.connect(self._onOptionsAdded) self.options_running.connect(self._onOptionsRunning) self.options_simulated.connect(self._onOptionsSimulated) self.options_error.connect(self._onOptionsError) self.results_error.connect(self._onResultsError) # Defaults settings = get_settings() section = settings.add_section('gui') if hasattr(section, 'outputdir'): self._txt_outputdir.setPath(section.outputdir) if hasattr(section, 'maxworkers'): self._spn_workers.setValue(int(section.maxworkers)) if hasattr(section, 'overwrite'): state = True if section.overwrite.lower() == 'true' else False self._chk_overwrite.setChecked(state) def _onDialogProgressProgress(self, progress, status): self._dlg_progress.setValue(progress * 100) self._dlg_progress.setLabelText(status) def _onDialogProgressCancel(self): self._dlg_progress.hide() if self._options_reader_thread is None: return self._options_reader_thread.cancel() self._options_reader_thread.quit() self._options_reader_thread.wait() def _onDialogProgressException(self, ex): self._dlg_progress.hide() self._options_reader_thread.quit() self._options_reader_thread.wait() messagebox.exception(self, ex) def _onRunningTimer(self): self._tbl_options.model().reset() def _onOpen(self): settings = get_settings() curdir = getattr(settings.gui, 'opendir', os.getcwd()) filepath, namefilter = \ QFileDialog.getOpenFileName(self, "Open", curdir, 'Options [*.xml] (*.xml)') if not filepath or not namefilter: return settings.gui.opendir = os.path.dirname(filepath) if not filepath.endswith('.xml'): filepath += '.xml' self._options_reader_thread = _OptionsReaderWrapperThread(filepath) self._dlg_progress.canceled.connect(self._onDialogProgressCancel) self._options_reader_thread.resultReady.connect(self._onOpened) self._options_reader_thread.progressUpdated.connect(self._onDialogProgressProgress) self._options_reader_thread.exceptionRaised.connect(self._onDialogProgressException) self._options_reader_thread.start() self._dlg_progress.reset() self._dlg_progress.show() def _onOpened(self, options): self._dlg_progress.hide() self._options_reader_thread.quit() self._options_reader_thread.wait() self._options_reader_thread = None try: self._lst_available.model().addOptions(options) except Exception as ex: messagebox.exception(self, ex) def _onRemove(self): selection = self._lst_available.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Queue", "Select an options") return model = self._lst_available.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.popOptions(row) def _onClear(self): self._lst_available.model().clearOptions() def _onAddToQueue(self): selection = self._lst_available.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Queue", "Select an options") return model = self._lst_available.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): options = model.options(row) try: self._runner.put(options) except Exception as ex: messagebox.exception(self, ex) return def _onAddAllToQueue(self): model = self._lst_available.model() for row in reversed(range(0, model.rowCount())): options = model.options(row) try: self._runner.put(options) except Exception as ex: messagebox.exception(self, ex) return def _onStart(self): outputdir = self._txt_outputdir.path() if not outputdir: QMessageBox.critical(self, 'Start', 'Missing output directory') return max_workers = self._spn_workers.value() overwrite = self._chk_overwrite.isChecked() self.start(outputdir, overwrite, max_workers) def _onCancel(self): self.cancel() def _onClose(self): if self._runner is not None: self._runner.close() self._running_timer.stop() self.close() def _onImport(self): list_options = self._lst_available.model().listOptions() if not list_options: return # Select options dialog = _OptionsSelector(list_options) if not dialog.exec_(): return options = dialog.options() # Start importer outputdir = self._runner.outputdir max_workers = self._runner.max_workers importer = LocalImporter(outputdir, max_workers) importer.start() importer.put(options) self._dlg_progress.show() try: while importer.is_alive(): if self._dlg_progress.wasCanceled(): importer.cancel() break self._dlg_progress.setValue(importer.progress * 100) finally: self._dlg_progress.hide() def _onOptionsAdded(self, options): logging.debug('runner: optionsAdded') self._tbl_options.model().addOptions(options) def _onOptionsRunning(self, options): logging.debug('runner: optionsRunning') self._tbl_options.model().resetOptions(options) def _onOptionsSimulated(self, options): logging.debug('runner: optionsSimulated') self._tbl_options.model().resetOptions(options) def _onOptionsError(self, options, ex): logging.debug('runner: optionsError') self._tbl_options.model().resetOptions(options) def _onResultsError(self, results, ex): logging.debug('runner: resultsError') self._tbl_options.model().reset() def closeEvent(self, event): if self.is_running(): message = 'Runner is running. Do you want to continue?' answer = QMessageBox.question(self, 'Runner', message, QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.No: event.ignore() return self.cancel() self._dlg_progress.close() settings = get_settings() section = settings.add_section('gui') path = self._txt_outputdir.path() if path: section.outputdir = path section.maxworkers = str(self._spn_workers.value()) section.overwrite = str(self._chk_overwrite.isChecked()) settings.write() event.accept() def addAvailableOptions(self, options): self._lst_available.model().addOptions(options) def removeAvailableOptions(self, options): self._lst_available.model().removeOptions(options) def clearAvailableOptions(self): self._lbl_available.model().clearOptions() def start(self, outputdir, overwrite, max_workers): self._runner = LocalRunner(outputdir=outputdir, overwrite=overwrite, max_workers=max_workers) self._tbl_options.setModel(_StateOptionsTableModel(self._runner)) self._spn_workers.setEnabled(False) self._txt_outputdir.setEnabled(False) self._chk_overwrite.setEnabled(False) self._btn_addtoqueue.setEnabled(True) self._btn_addalltoqueue.setEnabled(True) self._btn_start.setEnabled(False) self._btn_cancel.setEnabled(True) self._btn_close.setEnabled(False) self._btn_import.setEnabled(True) self._runner.options_added.connect(self.options_added.emit) self._runner.options_running.connect(self.options_running.emit) self._runner.options_simulated.connect(self.options_simulated.emit) self._runner.options_error.connect(self.options_error.emit) self._runner.results_saved.connect(self.results_saved.emit) self._runner.results_error.connect(self.results_error.emit) self._running_timer.start() self._runner.start() def cancel(self): if self._runner is None: return self._runner.cancel() self._running_timer.stop() self._runner.options_added.disconnect(self.options_added.emit) self._runner.options_running.disconnect(self.options_running.emit) self._runner.options_simulated.disconnect(self.options_simulated.emit) self._runner.options_error.disconnect(self.options_error.emit) self._runner.results_saved.disconnect(self.results_saved.emit) self._runner.results_error.disconnect(self.results_error.emit) self._runner = None self._spn_workers.setEnabled(True) self._txt_outputdir.setEnabled(True) self._chk_overwrite.setEnabled(True) self._btn_addtoqueue.setEnabled(False) self._btn_addalltoqueue.setEnabled(False) self._btn_start.setEnabled(True) self._btn_cancel.setEnabled(False) self._btn_close.setEnabled(True) self._btn_import.setEnabled(False) def is_running(self): return self._runner is not None and self._runner.is_alive()
class LayerListWidget(_ParameterWidget): def __init__(self, parameter, parent=None): _ParameterWidget.__init__(self, parameter, parent) # Variables model = _LayerModel() self._material_class = Material # Actions act_add = QAction(getIcon("list-add"), "Add layer", self) act_remove = QAction(getIcon("list-remove"), "Remove layer", self) act_clean = QAction(getIcon('edit-clear'), "Clear", self) # Widgets self._cb_unit = UnitComboBox('m') self._cb_unit.setUnit('um') self._tbl_layers = QTableView() self._tbl_layers.setModel(model) self._tbl_layers.setItemDelegate(_LayerDelegate()) header = self._tbl_layers.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) header.setStyleSheet('color: blue') self._tlb_layers = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._tlb_layers.addWidget(spacer) self._tlb_layers.addAction(act_add) self._tlb_layers.addAction(act_remove) self._tlb_layers.addAction(act_clean) # Layouts layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) sublayout = QHBoxLayout() sublayout.addStretch() sublayout.addWidget(QLabel('Thickness unit')) sublayout.addWidget(self._cb_unit) layout.addLayout(sublayout) layout.addWidget(self._tbl_layers) layout.addWidget(self._tlb_layers) self.setLayout(layout) # Signals self.valuesChanged.connect(self._onChanged) self.validationRequested.connect(self._onChanged) act_add.triggered.connect(self._onAdd) act_remove.triggered.connect(self._onRemove) act_clean.triggered.connect(self._onClear) self._tbl_layers.doubleClicked.connect(self._onDoubleClicked) model.dataChanged.connect(self.valuesChanged) model.rowsInserted.connect(self.valuesChanged) model.rowsRemoved.connect(self.valuesChanged) self.validationRequested.emit() def _onChanged(self): if self.hasAcceptableInput(): self._tbl_layers.setStyleSheet("background: none") else: self._tbl_layers.setStyleSheet("background: pink") def _onDoubleClicked(self, index): if index.column() != 0: return model = self._tbl_layers.model() materials = model.materials(index) if len(materials) == 0: dialog = get_material_dialog_class(self._material_class)() elif len(materials) == 1: dialog = get_material_dialog_class(self._material_class)() dialog.setValue(materials[0]) else: dialog = MaterialListDialog() dialog.setMaterialClass(self._material_class) dialog.setValues(materials) dialog.setReadOnly(self.isReadOnly()) if not dialog.exec_(): return model.setData(index, dialog.values()) def _onAdd(self): index = self._tbl_layers.selectionModel().currentIndex() model = self._tbl_layers.model() model.insertRows(index.row() + 1) # Show material dialog right away index = model.createIndex(index.row() + 1, 0) self._onDoubleClicked(index) def _onRemove(self): selection = self._tbl_layers.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Layer", "Select a row") return model = self._tbl_layers.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def _onClear(self): model = self._tbl_layers.model() for row in reversed(range(model.rowCount())): model.removeRow(row) def values(self): factor = self._cb_unit.factor() layers = [] for material, thickness in self._tbl_layers.model().layers(): if not material or not thickness: continue thickness_m = np.array(thickness, ndmin=1) * factor layers.append(_MockLayer(material, thickness_m)) return layers def setValues(self, layers): layers = np.array(layers, ndmin=1) factor = self._cb_unit.factor() model = self._tbl_layers.model() model.removeRows(0, model.rowCount()) model.insertRows(0, len(layers)) for i, layer in enumerate(layers): model.setData(model.index(i, 0), layer.material) model.setData(model.index(i, 1), layer.thickness_m / factor) def isReadOnly(self): return not self._cb_unit.isEnabled() and \ not self._tlb_layers.isVisible() def setReadOnly(self, state): self._cb_unit.setEnabled(not state) self._tlb_layers.setVisible(not state) style = 'color: none' if state else 'color: blue' self._tbl_layers.horizontalHeader().setStyleSheet(style) self._tbl_layers.itemDelegate().setReadOnly(state) def setMaterialClass(self, clasz): self._material_class = clasz
class DetectorWizardPage(_ExpandableOptionsWizardPage): class _DetectorComboBoxModel(QAbstractListModel): def __init__(self): QAbstractListModel.__init__(self) self._detectors = [] def rowCount(self, *args, **kwargs): return len(self._detectors) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None return self._detectors[index.row()]['text'] def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return False row = index.row() self._detectors[row] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(QModelIndex(), row, row + count - 1) for _ in range(count): value = {'text': '', 'class': None, 'widget_class': None} self._detectors.insert(row, value) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(QModelIndex(), row, row + count - 1) for index in reversed(range(row, row + count)): self._detectors.pop(index) self.endRemoveRows() return True def append(self, text, clasz, widget_class): self.insert(self.rowCount(), text, clasz, widget_class) def insert(self, row, text, clasz, widget_class): self.insertRows(row) value = {'text': text, 'class': clasz, 'widget_class': widget_class} self.setData(self.createIndex(row, 0), value) def clear(self): self.removeRows(0, self.rowCount()) def widget_class(self, index): return self._detectors[index]['widget_class'] class _DetectorTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._detectors = [] def rowCount(self, *args, **kwargs): return len(self._detectors) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role == Qt.DisplayRole or role == Qt.ToolTipRole: key, detector = self._detectors[index.row()] column = index.column() if column == 0: return key elif column == 1: return str(detector) if detector is not None else '' return None def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Key' elif section == 1: return 'Detector' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return False row = index.row() column = index.column() self._detectors[row][column] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for _ in range(count): self._detectors.insert(row, ['untitled', None]) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) for index in reversed(range(row, row + count)): self._detectors.pop(index) self.endRemoveRows() return True def append(self, key, detector): self.insert(self.rowCount(), key, detector) def insert(self, index, key, detector): self.insertRows(index) self.setData(self.createIndex(index, 0), key) self.setData(self.createIndex(index, 1), detector) def modify(self, index, key, detector): self.setData(self.createIndex(index, 0), key) self.setData(self.createIndex(index, 1), detector) def clear(self): self.removeRows(0, self.rowCount()) def detectors(self): detectors = {} for key, detector in self._detectors: if detector is not None: detectors.setdefault(key, []).append(detector) return detectors def detector(self, index): return self._detectors[index.row()][1] def key(self, index): return self._detectors[index.row()][0] class _DetectorTableDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: editor = QLineEdit(parent) editor.setValidator(QRegExpValidator(QRegExp(r"^(?!\s*$).+"))) return editor elif column == 1: return None def setEditorData(self, editor, index): column = index.column() if column == 0: key = index.model().data(index, Qt.DisplayRole) editor.setText(key) def setModelData(self, editor, model, index): column = index.column() if column == 0: if not editor.hasAcceptableInput(): return model.setData(index, editor.text()) def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Detector') def _initUI(self): # Variables self._widgets = {} tbl_model = self._DetectorTableModel() # Widgets self._cb_detector = QComboBox() self._cb_detector.setModel(self._DetectorComboBoxModel()) btn_detector_add = QPushButton() btn_detector_add.setIcon(getIcon("list-add")) self._tbl_detector = QTableView() self._tbl_detector.setModel(tbl_model) self._tbl_detector.setItemDelegate(self._DetectorTableDelegate()) header = self._tbl_detector.horizontalHeader() header.setResizeMode(1, QHeaderView.Stretch) policy = self._tbl_detector.sizePolicy() policy.setVerticalStretch(True) self._tbl_detector.setSizePolicy(policy) tlb_detector = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_detector.addWidget(spacer) act_remove = tlb_detector.addAction(getIcon("list-remove"), "Remove detector") act_clear = tlb_detector.addAction(getIcon("edit-clear"), "Clear") # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) sublayout = QHBoxLayout() sublayout.addWidget(self._cb_detector, 1) sublayout.addWidget(btn_detector_add) layout.addRow("Select", sublayout) layout.addRow(self._tbl_detector) layout.addRow(tlb_detector) # Signals btn_detector_add.released.connect(self._onDetectorAdd) act_remove.triggered.connect(self._onDetectorRemove) act_clear.triggered.connect(self._onDetectorClear) self._tbl_detector.doubleClicked.connect(self._onDetectorDoubleClicked) tbl_model.dataChanged.connect(self.valueChanged) tbl_model.rowsInserted.connect(self.valueChanged) tbl_model.rowsRemoved.connect(self.valueChanged) return layout def _onDetectorAdd(self): index = self._tbl_detector.selectionModel().currentIndex() tbl_model = self._tbl_detector.model() cb_model = self._cb_detector.model() widget_class = cb_model.widget_class(self._cb_detector.currentIndex()) wdg_detector = widget_class() dialog = _DetectorDialog(wdg_detector) if not dialog.exec_(): return tbl_model.insert(index.row() + 1, dialog.key(), dialog.detector()) def _onDetectorRemove(self): selection = self._tbl_detector.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Detector", "Select a row") return tbl_model = self._tbl_detector.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): tbl_model.removeRow(row) def _onDetectorClear(self): model = self._tbl_detector.model() for row in reversed(range(model.rowCount())): model.removeRow(row) def _onDetectorDoubleClicked(self, index): if index.column() != 1: return tbl_model = self._tbl_detector.model() key = tbl_model.key(index) detector = tbl_model.detector(index) widget_class = self._widgets[detector.__class__] wdg_detector = widget_class() wdg_detector.setValue(detector) dialog = _DetectorDialog(wdg_detector, key) if not dialog.exec_(): return tbl_model.modify(index.row(), dialog.key(), dialog.detector()) def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) tbl_model = self._tbl_detector.model() cb_model = self._cb_detector.model() # Clear self._widgets.clear() tbl_model.clear() cb_model.clear() # Populate combo box it = self._iter_widgets('pymontecarlo.ui.gui.options.detector', 'DETECTORS') for clasz, widget_class, programs in it: widget = widget_class() self._widgets[clasz] = widget_class program_text = ', '.join(map(attrgetter('name'), programs)) text = '{0} ({1})'.format(widget.accessibleName(), program_text) cb_model.append(text, clasz, widget_class) del widget self._cb_detector.setCurrentIndex(0) # Add detector(s) for key, detectors in self.options().detectors.items(): detectors = np.array(detectors, ndmin=1) for detector in detectors: if not detector.__class__ in self._widgets: continue tbl_model.append(key, detector) def validatePage(self): tbl_model = self._tbl_detector.model() if tbl_model.rowCount() == 0: return False self.options().detectors.clear() self.options().detectors.update(tbl_model.detectors()) return True def expandCount(self): if self._tbl_detector.model().rowCount() == 0: return 0 try: count = 1 for detectors in self._tbl_detector.model().detectors().values(): count *= len(detectors) for detector in detectors: count *= len(expand(detector)) return count except: return 0
class MainWindow(QMainWindow): def __init__(self, datta): QMainWindow.__init__(self) self.setWindowTitle('Project Parser') appIcon = QIcon('search.png') self.setWindowIcon(appIcon) self.viewPortBL = QDesktopWidget().availableGeometry().topLeft() self.viewPortTR = QDesktopWidget().availableGeometry().bottomRight() self.margin = int(QDesktopWidget().availableGeometry().width()*0.1/2) self.shirina = QDesktopWidget().availableGeometry().width() - self.margin*2 self.visota = QDesktopWidget().availableGeometry().height() - self.margin*2 self.setGeometry(self.viewPortBL.x() + self.margin, self.viewPortBL.y() + self.margin, self.shirina, self.visota) # statusbar self.myStatusBar = QStatusBar() self.setStatusBar(self.myStatusBar) #lower long layout self.lowerLong = QFrame() self.detailsLabel = QLabel() self.skillsLabel = QLabel() self.urlLabel = QLabel() self.locationLabel = QLabel() self.skillsLabel.setText('skills') self.detailsLabel.setWordWrap(True) self.la = QVBoxLayout() self.la.addWidget(self.detailsLabel) self.la.addWidget(self.skillsLabel) self.la.addWidget(self.urlLabel) self.la.addWidget(self.locationLabel) self.lowerLong.setLayout(self.la) # table self.source_model = MyTableModel(self, datta, ['Id', 'Date', 'Title']) self.proxy_model = myTableProxy(self) self.proxy_model.setSourceModel(self.source_model) self.proxy_model.setDynamicSortFilter(True) self.table_view = QTableView() self.table_view.setModel(self.proxy_model) self.table_view.setAlternatingRowColors(True) self.table_view.resizeColumnsToContents() self.table_view.resizeRowsToContents() self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.setSortingEnabled(True) self.table_view.sortByColumn(2, Qt.AscendingOrder) # events self.selection = self.table_view.selectionModel() self.selection.selectionChanged.connect(self.handleSelectionChanged) #DO NOT use CreateIndex() method, use index() index = self.proxy_model.index(0,0) self.selection.select(index, QItemSelectionModel.Select) self.upperLong = self.table_view # right side widgets self.right = QFrame() self.la1 = QVBoxLayout() self.btnDownload = QPushButton('Download data') self.btnDownload.clicked.connect(self.download) self.myButton = QPushButton('Show Skillls') self.myButton.clicked.connect(self.showAllSkills) self.btnSearchByWord = QPushButton('Search by word(s)') self.btnSearchByWord.clicked.connect(self.onSearchByWord) self.btnResetFilter= QPushButton('Discard Filter') self.btnResetFilter.clicked.connect(self.discardFilter) self.btnCopyURL = QPushButton('URL to Clipboard') self.btnCopyURL.clicked.connect(self.copyToClipboard) self.btnExit = QPushButton('Exit') self.btnExit.clicked.connect(lambda: sys.exit()) self.dateTimeStamp = QLabel() self.la1.addWidget(self.btnDownload) self.la1.addSpacing(10) self.la1.addWidget(self.myButton) self.la1.addSpacing(10) self.la1.addWidget(self.btnSearchByWord) self.la1.addSpacing(10) self.la1.addWidget(self.btnResetFilter) self.la1.addSpacing(10) self.la1.addWidget(self.btnCopyURL) self.la1.addSpacing(70) self.la1.addWidget(self.btnExit) self.la1.addStretch(stretch=0) self.la1.addWidget(self.dateTimeStamp) self.right.setLayout(self.la1) self.right.setFrameShape(QFrame.StyledPanel) # splitters self.horiSplit = QSplitter(Qt.Vertical) self.horiSplit.addWidget(self.upperLong) self.horiSplit.addWidget(self.lowerLong) self.horiSplit.setSizes([self.visota/2, self.visota/2]) self.vertiSplit = QSplitter(Qt.Horizontal) self.vertiSplit.addWidget(self.horiSplit) self.vertiSplit.addWidget(self.right) self.vertiSplit.setSizes([self.shirina*3/4, self.shirina*1/4]) self.setCentralWidget(self.vertiSplit) self.settings = QSettings('elance.ini', QSettings.IniFormat) self.settings.beginGroup('DATE_STAMP') self.dateTimeStamp.setText('Data actuality: %s' % self.settings.value('date/time')) self.settings.endGroup() self.statusText = '' def handleSelectionChanged(self, selected, deselected): for index in selected.first().indexes(): #print('Row %d is selected' % index.row()) ind = index.model().mapToSource(index) desc = ind.model().mylist[ind.row()]['Description'] self.detailsLabel.setText(desc) skills = ', '.join(ind.model().mylist[ind.row()]['Skills']).strip() self.skillsLabel.setText(skills) url = ind.model().mylist[ind.row()]['URL'] self.urlLabel.setText(url) location = ind.model().mylist[ind.row()]['Location'] self.locationLabel.setText(location) def showAllSkills(self): listSkills = [] for elem in self.source_model.mylist: listSkills += elem['Skills'] allSkills = Counter(listSkills) tbl = MyTableModel(self, allSkills.items(), ['Skill', 'Freq']) win = skillsWindow(tbl, self.table_view) win.exec_() def discardFilter(self): self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.table_view.model().criteria = {} self.table_view.model().emit(SIGNAL("modelReset()")) self.table_view.resizeRowsToContents() def download(self): self.btnDownload.setDisabled(True) self.statusLabel = QLabel('Connecting') self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.myStatusBar.addWidget(self.statusLabel, 2) self.myStatusBar.addWidget(self.progressBar, 1) self.progressBar.setValue(1) self.settings.beginGroup('URLS') initialLink = self.settings.value('CategoriesDetailed/VahaSelected/InitialLink') pagingLink = self.settings.value('CategoriesDetailed/VahaSelected/PagingLink') self.settings.endGroup() downloader = Downloader(initialLink, pagingLink, 25, 5) downloader.messenger.downloadProgressChanged.connect(self.onDownloadProgressChanged) downloader.messenger.downloadComplete.connect(self.onDownloadComplete) downloader.download() def onDownloadComplete(self): #QMessageBox.information(self, 'Download complete', 'Download complete!', QMessageBox.Ok) self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.settings.beginGroup('DATE_STAMP') self.settings.setValue('date/time', time.strftime('%d-%b-%Y, %H:%M:%S')) self.dateTimeStamp.setText('Data actuality: %s' % self.settings.value('date/time')) self.settings.endGroup() with open("elance.json") as json_file: jobDB = json.load(json_file) for elem in jobDB: words = nltk.tokenize.regexp_tokenize(elem['Title'].lower(), r'\w+') elem['Tokens'] = words elem['Skills'] = [t.strip() for t in elem['Skills'].split(',')] self.source_model.mylist = jobDB self.table_view.model().emit(SIGNAL("modelReset()")) self.btnDownload.setEnabled(True) self.myStatusBar.removeWidget(self.statusLabel) self.myStatusBar.removeWidget(self.progressBar) self.myStatusBar.showMessage(self.statusText, timeout = 5000) def onDownloadProgressChanged(self, stata): self.progressBar.setValue(stata[2]) #text = 'Processed records{:5d} of{:5d}'.format(percentage[0], percentage[1]) bajtikov = '{:,}'.format(stata[5]) self.statusText = 'Processed page{:4d} of{:4d}. \ Job entries{:5d} of{:5d}. \ Downloaded{:>12s} Bytes'.format(stata[3], stata[4], stata[0], stata[1], bajtikov) self.statusLabel.setText(self.statusText) def copyToClipboard(self): clipboard = QApplication.clipboard() clipboard.setText(self.urlLabel.text()) self.myStatusBar.showMessage(self.urlLabel.text(), timeout = 3000) def onSearchByWord(self): text, ok = QInputDialog.getText(self, 'Search the base by word(s)', 'Enter your keyword/phrase to search for:') if ok: words = [t.strip() for t in nltk.tokenize.regexp_tokenize(text.lower(), r'\w+')] self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.table_view.model().criteria = {'Description' : words} self.table_view.model().emit(SIGNAL("modelReset()"))
class PenelopeMaterialDialog(_MaterialDialog): def __init__(self, parent=None): _MaterialDialog.__init__(self, parent) self.setWindowTitle('Material') self.setMinimumWidth(1000) def _initUI(self): # Variables model_forcing = _InteractionForcingTableModel() # Actions act_add_forcing = QAction(getIcon("list-add"), "Add interaction forcing", self) act_remove_forcing = QAction(getIcon("list-remove"), "Remove interaction forcing", self) # Widgets self._lbl_elastic_scattering_c1 = QLabel('C1') self._lbl_elastic_scattering_c1.setStyleSheet("color: blue") self._txt_elastic_scattering_c1 = MultiNumericalLineEdit() self._txt_elastic_scattering_c1.setValidator(_ElasticScatteringValidator()) self._txt_elastic_scattering_c1.setValues([0.0]) self._lbl_elastic_scattering_c2 = QLabel('C2') self._lbl_elastic_scattering_c2.setStyleSheet("color: blue") self._txt_elastic_scattering_c2 = MultiNumericalLineEdit() self._txt_elastic_scattering_c2.setValidator(_ElasticScatteringValidator()) self._txt_elastic_scattering_c2.setValues([0.0]) self._lbl_cutoff_energy_inelastic = QLabel('Inelastic collisions') self._lbl_cutoff_energy_inelastic.setStyleSheet("color: blue") self._txt_cutoff_energy_inelastic = MultiNumericalLineEdit() self._txt_cutoff_energy_inelastic.setValidator(_CutoffEnergyValidator()) self._txt_cutoff_energy_inelastic.setValues([50.0]) self._cb_cutoff_energy_inelastic = UnitComboBox('eV') self._lbl_cutoff_energy_bremsstrahlung = QLabel('Bremsstrahlung emission') self._lbl_cutoff_energy_bremsstrahlung.setStyleSheet("color: blue") self._txt_cutoff_energy_bremsstrahlung = MultiNumericalLineEdit() self._txt_cutoff_energy_bremsstrahlung.setValidator(_CutoffEnergyValidator()) self._txt_cutoff_energy_bremsstrahlung.setValues([50.0]) self._cb_cutoff_energy_bremsstrahlung = UnitComboBox('eV') self._lbl_maximum_step_length = QLabel('Maximum step length') self._lbl_maximum_step_length.setStyleSheet("color: blue") self._txt_maximum_step_length = MultiNumericalLineEdit() self._txt_maximum_step_length.setValidator(_MaximumStepLengthValidator()) self._txt_maximum_step_length.setValues([1e15]) self._cb_maximum_step_length_unit = UnitComboBox('m') self._tbl_forcing = QTableView() self._tbl_forcing.setModel(model_forcing) self._tbl_forcing.setItemDelegate(_InteractionForcingDelegate()) header = self._tbl_forcing.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) self._tlb_forcing = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._tlb_forcing.addWidget(spacer) self._tlb_forcing.addAction(act_add_forcing) self._tlb_forcing.addAction(act_remove_forcing) # Layouts layout = QHBoxLayout() layout.addLayout(_MaterialDialog._initUI(self), 1) frame = QFrame() frame.setFrameShape(QFrame.VLine) frame.setFrameShadow(QFrame.Sunken) layout.addWidget(frame) sublayout = QVBoxLayout() box_elastic_scattering = QGroupBox("Elastic scattering") boxlayout = QFormLayout() boxlayout.addRow(self._lbl_elastic_scattering_c1, self._txt_elastic_scattering_c1) boxlayout.addRow(self._lbl_elastic_scattering_c2, self._txt_elastic_scattering_c2) box_elastic_scattering.setLayout(boxlayout) sublayout.addWidget(box_elastic_scattering) box_cutoff_energy = QGroupBox("Cutoff energy") boxlayout = QFormLayout() boxsublayout = QHBoxLayout() boxsublayout.addWidget(self._txt_cutoff_energy_inelastic, 1) boxsublayout.addWidget(self._cb_cutoff_energy_inelastic) boxlayout.addRow(self._lbl_cutoff_energy_inelastic, boxsublayout) boxsublayout = QHBoxLayout() boxsublayout.addWidget(self._txt_cutoff_energy_bremsstrahlung, 1) boxsublayout.addWidget(self._cb_cutoff_energy_bremsstrahlung) boxlayout.addRow(self._lbl_cutoff_energy_bremsstrahlung, boxsublayout) box_cutoff_energy.setLayout(boxlayout) sublayout.addWidget(box_cutoff_energy) subsublayout = QFormLayout() subsubsublayout = QHBoxLayout() subsubsublayout.addWidget(self._txt_maximum_step_length, 1) subsubsublayout.addWidget(self._cb_maximum_step_length_unit) subsublayout.addRow(self._lbl_maximum_step_length, subsubsublayout) sublayout.addLayout(subsublayout) box_forcing = QGroupBox('Interaction forcing') boxlayout = QVBoxLayout() boxlayout.addWidget(self._tbl_forcing) boxlayout.addWidget(self._tlb_forcing) box_forcing.setLayout(boxlayout) sublayout.addWidget(box_forcing) sublayout.addStretch() layout.addLayout(sublayout, 1) # Signals self._txt_elastic_scattering_c1.textChanged.connect(self._onElasticScatteringC1Changed) self._txt_elastic_scattering_c2.textChanged.connect(self._onElasticScatteringC2Changed) self._txt_cutoff_energy_inelastic.textChanged.connect(self._onCutoffEnergyInelasticChanged) self._txt_cutoff_energy_bremsstrahlung.textChanged.connect(self._onCutoffEnergyBremsstrahlungChanged) self._txt_maximum_step_length.textChanged.connect(self._onMaximumStepLengthChanged) act_add_forcing.triggered.connect(self._onForcingAdd) act_remove_forcing.triggered.connect(self._onForcingRemove) return layout def _onElasticScatteringC1Changed(self): if self._txt_elastic_scattering_c1.hasAcceptableInput(): self._txt_elastic_scattering_c1.setStyleSheet('background: none') else: self._txt_elastic_scattering_c1.setStyleSheet('background: pink') def _onElasticScatteringC2Changed(self): if self._txt_elastic_scattering_c2.hasAcceptableInput(): self._txt_elastic_scattering_c2.setStyleSheet('background: none') else: self._txt_elastic_scattering_c2.setStyleSheet('background: pink') def _onCutoffEnergyInelasticChanged(self): if self._txt_cutoff_energy_inelastic.hasAcceptableInput(): self._txt_cutoff_energy_inelastic.setStyleSheet('background: none') else: self._txt_cutoff_energy_inelastic.setStyleSheet('background: pink') def _onCutoffEnergyBremsstrahlungChanged(self): if self._txt_cutoff_energy_bremsstrahlung.hasAcceptableInput(): self._txt_cutoff_energy_bremsstrahlung.setStyleSheet('background: none') else: self._txt_cutoff_energy_bremsstrahlung.setStyleSheet('background: pink') def _onMaximumStepLengthChanged(self): if self._txt_maximum_step_length.hasAcceptableInput(): self._txt_maximum_step_length.setStyleSheet('background: none') else: self._txt_maximum_step_length.setStyleSheet('background: pink') def _onForcingAdd(self): index = self._tbl_forcing.selectionModel().currentIndex() model = self._tbl_forcing.model() model.insertRows(index.row() + 1) def _onForcingRemove(self): selection = self._tbl_forcing.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Interaction forcing", "Select a row") return model = self._tbl_forcing.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def _getParametersDict(self): params = _MaterialDialog._getParametersDict(self) params['c1'] = self._txt_elastic_scattering_c1.values().tolist() params['c2'] = self._txt_elastic_scattering_c2.values().tolist() wcc = self._txt_cutoff_energy_inelastic.values() * self._cb_cutoff_energy_inelastic.factor() params['wcc'] = wcc.tolist() wcr = self._txt_cutoff_energy_bremsstrahlung.values() * self._cb_cutoff_energy_bremsstrahlung.factor() params['wcr'] = wcr.tolist() dsmax = self._txt_maximum_step_length.values() * self._cb_maximum_step_length_unit.factor() params['dsmax'] = dsmax.tolist() params['forcings'] = \ self._tbl_forcing.model().interaction_forcings() return params def _generateName(self, parameters, varied): name = parameters.pop('name') if name is None: name = PenelopeMaterial.generate_name(parameters['composition']) parts = [name] for key in varied: if key == 'composition': continue elif key == 'forcings': forcing = parameters[key][0] forcer = forcing.forcer wlow = forcing.weight[0] whigh = forcing.weight[1] parts.append('forcings={0:n}_{1:n}_{2:n}'.format(forcer, wlow, whigh)) else: parts.append('{0:s}={1:n}'.format(key, parameters[key])) return '+'.join(parts) def _createMaterial(self, parameters, varied): mat = _MaterialDialog._createMaterial(self, parameters, varied) c1 = parameters['c1'] c2 = parameters['c2'] wcc = parameters['wcc'] wcr = parameters['wcr'] dsmax = parameters['dsmax'] forcings = parameters['forcings'] return PenelopeMaterial(mat.composition, mat.name, mat.density_kg_m3, mat.absorption_energy_eV, elastic_scattering=(c1, c2), cutoff_energy_inelastic_eV=wcc, cutoff_energy_bremsstrahlung_eV=wcr, interaction_forcings=forcings, maximum_step_length_m=dsmax) def setValue(self, material): _MaterialDialog.setValue(self, material) # Elastic scattering c1, c2 = material.elastic_scattering self._txt_elastic_scattering_c1.setValues(c1) self._txt_elastic_scattering_c2.setValues(c2) # Cutoff energy self._txt_cutoff_energy_inelastic.setValues(material.cutoff_energy_inelastic_eV) self._cb_cutoff_energy_inelastic.setUnit('eV') self._txt_cutoff_energy_bremsstrahlung.setValues(material.cutoff_energy_bremsstrahlung_eV) self._cb_cutoff_energy_bremsstrahlung.setUnit('eV') # Maximum step length self._txt_maximum_step_length.setValues(material.maximum_step_length_m) self._cb_maximum_step_length_unit.setUnit('m') # Interaction forcings forcings = material.interaction_forcings model = self._tbl_forcing.model() model.removeRows(0, model.rowCount()) model.insertRows(1, len(forcings)) for row, forcing in enumerate(forcings): model.setData(model.index(row, 0), forcing.particle) model.setData(model.index(row, 1), forcing.collision) model.setData(model.index(row, 2), forcing.forcer) model.setData(model.index(row, 3), forcing.weight[0]) model.setData(model.index(row, 4), forcing.weight[1]) def setReadOnly(self, state): _MaterialDialog.setReadOnly(self, state) style = 'color: none' if state else 'color: blue' self._lbl_elastic_scattering_c1.setStyleSheet(style) self._txt_elastic_scattering_c1.setReadOnly(state) self._lbl_elastic_scattering_c2.setStyleSheet(style) self._txt_elastic_scattering_c2.setReadOnly(state) self._lbl_cutoff_energy_inelastic.setStyleSheet(style) self._txt_cutoff_energy_inelastic.setReadOnly(state) self._cb_cutoff_energy_inelastic.setEnabled(not state) self._lbl_cutoff_energy_bremsstrahlung.setStyleSheet(style) self._txt_cutoff_energy_bremsstrahlung.setReadOnly(state) self._cb_cutoff_energy_bremsstrahlung.setEnabled(not state) self._lbl_maximum_step_length.setStyleSheet(style) self._txt_maximum_step_length.setReadOnly(state) self._cb_maximum_step_length_unit.setEnabled(not state) self._tbl_forcing.setEnabled(not state) self._tlb_forcing.setVisible(not state) def isReadOnly(self): return _MaterialDialog.isReadOnly(self) and \ self._txt_elastic_scattering_c1.isReadOnly() and \ self._txt_elastic_scattering_c2.isReadOnly() and \ self._txt_cutoff_energy_inelastic.isReadOnly() and \ self._txt_cutoff_energy_bremsstrahlung.isReadOnly() and \ self._txt_maximum_step_length.isReadOnly() and \ not self._tbl_forcing.isEnabled() and \ not self._tlb_forcing.isVisible()
class TemplateSelectDialog(QDialog): def refresh_templates_list(self): documents = documents_service.all_templates() self.model.removeRows(0, self.model.rowCount()) if documents: for doc in sorted(list(documents), key=lambda d: d.filename): self._add_one_document(doc.filename, doc.template_document_id, doc.file_size, doc.description) def __init__(self, parent=None): super(TemplateSelectDialog, self).__init__(parent) self.setWindowTitle(_("Select a template")) self.template_id = [] self.model = QStandardItemModel() self.view = QTableView() self.view.setModel(self.model) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setVisible(False) self.view.setMinimumSize(500, 200) self.view.setShowGrid(True) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) l = QVBoxLayout() l.addWidget(QLabel(_("Please select one or more template."))) self.view.doubleClicked.connect(self._doubleClicked) l.addWidget(QLabel(u"<h3>{}</h3>".format(_("Documents Templates")))) l.addWidget(self.view) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.StandardButton.Cancel) self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) l.addWidget(self.buttons) self.setLayout(l) def _add_one_document(self, file_name, doc_id, file_size, description): """ Adds a document to the list """ # file_name is either an absolute path or just a file name # If it is an absolute path, then the file is expected # to exist locally (at it absolute path location of course). # If not, then the file is expected to be a remote file # and shall be downloaded before opening. mainlog.debug(u"{} {} {} {}".format(file_name, doc_id, file_size, description)) short_name = file_name if os.path.isabs(file_name): short_name = os.path.basename(file_name) if not os.path.isfile(file_name): raise Exception(u"The file {} doesn't exist".format(file_name)) items = [QStandardItem(short_name)] items.append(QStandardItem(description)) self.model.appendRow(items) self.model.setData(self.model.index(self.model.rowCount() - 1, 0), doc_id, Qt.UserRole + 1) self.view.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) self.view.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) self.view.resizeRowsToContents() @Slot() def accept(self): selected_rows = self.view.selectionModel().selectedRows() if selected_rows: self.template_id = [] for row in selected_rows: ndx_row = row.row() self.template_id.append( self.model.data(self.model.index(ndx_row, 0), Qt.UserRole + 1)) else: self.template_id = [] return super(TemplateSelectDialog, self).accept() @Slot() def reject(self): return super(TemplateSelectDialog, self).reject() @Slot(QModelIndex) def _doubleClicked(self, ndx): self.accept()
class FindOrderDialog(QDialog): def __init__(self,parent): global dao super(FindOrderDialog,self).__init__(parent) title = _("Find order") self.setWindowTitle(title) top_layout = QVBoxLayout() self.title_widget = TitleWidget(title,self) top_layout.addWidget(self.title_widget) hlayout = QHBoxLayout() hlayout.addWidget(QLabel(_("Search"))) self.search_criteria = QLineEdit() self.search_criteria.setObjectName("search_criteria") hlayout.addWidget(self.search_criteria) top_layout.addLayout(hlayout) self.search_results_view = QTableView() self.headers_view = QHeaderView(Qt.Orientation.Horizontal) self.header_model = make_header_model([_("Preorder Nr"),_("Order Nr"),_("Customer Nr"),_("Customer"),_("Order Part")]) self.headers_view.setModel(self.header_model) # qt's doc : The view does *not* take ownership (bt there's something with the selecion mode self.search_results_model = QStandardItemModel() self.search_results_view.setModel(self.search_results_model) self.search_results_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.search_results_view.setHorizontalHeader(self.headers_view) self.search_results_view.verticalHeader().hide() # self.search_results_view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) self.search_results_view.horizontalHeader().setResizeMode(3, QHeaderView.Stretch) self.search_results_view.horizontalHeader().setResizeMode(4, QHeaderView.Stretch) self.search_results_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.buttons = QDialogButtonBox() self.buttons.addButton( QDialogButtonBox.StandardButton.Cancel) self.buttons.addButton( QDialogButtonBox.Ok) self.buttons.button(QDialogButtonBox.Ok).setObjectName("go_search") top_layout.addWidget(self.search_results_view) top_layout.setStretch(2,1000) top_layout.addWidget(self.buttons) self.setLayout(top_layout) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.search_results_view.activated.connect(self.row_activated) self.search_criteria.returnPressed.connect(self.search_criteria_submitted) self.setMinimumSize(800,640) def find_by_text(self,text): text = text.strip() try: too_many_results, res = dao.order_part_dao.find_ids_by_text(text.strip()) if too_many_results: showWarningBox(_("Too many results"),_("The query you've given brought back too many results. Only a part of them is displayed. Consider refining your query"),object_name="too_many_results") return dao.order_part_dao.find_by_ids(res) except DataException as de: if de.code == DataException.CRITERIA_IS_EMPTY: showErrorBox(_("Error in the filter !"), _("The filter can't be empty"),object_name="filter_is_empty") elif de.code == DataException.CRITERIA_IS_TOO_SHORT: showErrorBox(_("Error in the filter !"), _("The filter is too short"),object_name="filter_is_too_short") elif de.code == DataException.CRITERIA_IS_TOO_LONG: showErrorBox(_("Error in the filter !"), _("The filter is too long"),object_name="filter_is_too_long") return [] # order_part_match = [] # matches = [] # super_matches = [] # import re # re_order_part_identifier = re.compile("^([0-9]+)([A-Z]+)$") # re_label_identifier = re.compile("^[0-9]+$") # if re_order_part_identifier.match(text.upper()): # # Look for an exact (and unique) match on the order part full identifier # p = dao.order_part_dao.find_by_full_id(text.upper()) # if p: # # Mimick SQLAlchemy's KeyTuples # # FIXME It seems that I use something that's internal to SQLA # # Search SQLA's doc for KeyedTuple to find about collections.namedtuple() # from sqlalchemy.util._collections import KeyedTuple # kt = KeyedTuple([p.order_id, p.order.preorder_label, p.order.accounting_label, p.order.customer_order_name, p.order.customer.fullname, p.order.creation_date, p.description, p.order_part_id, p.label], # labels=['order_id','preorder_label','accounting_label','customer_order_name','fullname','creation_date','description','order_part_id','label']) # order_part_match = [ kt ] # if re_label_identifier.match(text): # for r in dao.order_dao.find_by_labels(int(text)): # super_matches.append(r) # for r in dao.order_dao.find_by_customer_name(text): # # mainlog.debug('customer name match on {}'.format(text)) # matches.append(r) # for r in dao.order_dao.find_by_customer_order_name(text): # # mainlog.debug('customer name match on {}'.format(text)) # matches.append(r) # for r in dao.order_part_dao.find_by_description(text): # matches.append(r) # # Finally we order the matches to bring the most relevant # # first. The "most relevant" is really a business order. # return order_part_match + super_matches + \ # sorted(matches, lambda a,b: - cmp(a.order_id,b.order_id)) def _search_results_to_array(self,search_results): array = [] for res in search_results: # mainlog.debug("_search_results_to_array {}".format(res.creation_date)) i = QStandardItem(res.preorder_part_label) row = [i, QStandardItem(res.accounting_part_label), QStandardItem(res.customer_order_name), QStandardItem(res.fullname)] if 'order_part_id' in res.__dict__: # It's an order part i.setData( res.order_part_id, Qt.UserRole) i.setData( 'order_part', Qt.UserRole+1) row.append( QStandardItem(res.description)) else: # It's an order i.setData( res.order_id, Qt.UserRole) i.setData( 'order', Qt.UserRole+1) row.append( QStandardItem()) array.append(row) return array def load_search_results(self,text=None): global dao if text is None: text = self.search_criteria.text() db_results = self.find_by_text(text) array = self._search_results_to_array(db_results) self.search_results_model.removeRows(0,self.search_results_model.rowCount()) for row in array: self.search_results_model.appendRow(row) mainlog.debug("Loaded model : {}".format(self.search_results_model.rowCount())) self.search_results_view.resizeColumnsToContents() if self.search_results_model.rowCount() > 0: self.search_results_view.setCurrentIndex(self.search_results_model.index(0,0)) self.search_results_view.setFocus(Qt.OtherFocusReason) if self.search_results_model.rowCount() == 1: self.accept() def selected_item(self): mainlog.debug("FindOrder.selected_item") ndx = self.search_results_view.currentIndex() if ndx.isValid(): ndx = self.search_results_view.model().index( ndx.row(), 0) item = ndx.data(Qt.UserRole) item_type = ndx.data(Qt.UserRole+1) if item_type == 'order': mainlog.debug("FindOrder.selected_item order_id={}".format(item)) return dao.order_dao.find_by_id(item) elif item_type == 'order_part': mainlog.debug("FindOrder.selected_item order_part_id={}".format(item)) return dao.order_part_dao.find_by_id(item) else: mainlog.error("Unsupported item type {}".format(item_type)) else: mainlog.error("Invalid index") return None @Slot() def accept(self): # mainlog.debug("accept") # self.load_search_results() # mainlog.debug("accept - done") return super(FindOrderDialog,self).accept() @Slot() def reject(self): return super(FindOrderDialog,self).reject() @Slot() def search_criteria_submitted(self): mainlog.debug("search_criteria_submitted") self.load_search_results() @Slot(QModelIndex) def row_activated(self,ndx): mainlog.debug("row_activated") self.accept() def keyPressEvent(self,event): # The goal here is to make sure the accept signal is called only # if the user clicks on the "OK" button /with the mouse/ and, # not with the keyboard if event.key() in (Qt.Key_Enter, Qt.Key_Return): return else: super(FindOrderDialog,self).keyPressEvent(event)