class WeekViewWidget(SubFrame): def __init__(self, parent): super(WeekViewWidget, self).__init__("Week", None, parent) self._table_model = QStandardItemModel(1, 2, None) self._table_model.setHorizontalHeaderLabels( [_("# Ord."), _("Customer")]) self.table_view = LeftRightTableView(self) self.table_view.setModel(self._table_model) self.table_view.verticalHeader().hide() self.table_view.setAlternatingRowColors(True) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_view.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents) self.table_view.horizontalHeader().setStretchLastSection(True) self.preorder_brush = QBrush(QColor(255, 255, 128)) self.completed_order_brush = QBrush(QColor(128, 255, 128)) self.layout().addWidget(self.table_view) def _set_last_row(self, data, role): for i in range(self._table_model.columnCount()): self._table_model.setData( self._table_model.index(self._table_model.rowCount() - 1, i), data, role) def set_data(self, data, base_date): self.set_title(date_to_dm(base_date)) self._table_model.removeRows(0, self._table_model.rowCount()) current_ndx = 0 for d in data: order, customer_name = d number = order.preorder_label if order.accounting_label: number = order.accounting_label self._table_model.appendRow( [QStandardItem(str(number)), QStandardItem(customer_name)]) self._table_model.setData(self._table_model.index(current_ndx, 0), order.order_id, Qt.UserRole) if order.state == OrderStatusType.order_completed: self._set_last_row(self.completed_order_brush, Qt.BackgroundRole) elif order.state == OrderStatusType.preorder_definition: self._set_last_row(self.preorder_brush, Qt.BackgroundRole) current_ndx += 1 # Not the slightest idea why this works # and not resizeColumnsToContents for i in range(self._table_model.columnCount()): self.table_view.resizeColumnToContents(i)
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)])
class MainWindow(QMainWindow): COLUMNS = ['Name', 'Comment'] ITEMS = [ ('Ford', "Don't panic"), ('Marvin', "I'm feeling so depressed") ] def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setupUi() # setup a sample model self.model = QStandardItemModel(self) self.model.setHorizontalHeaderLabels(self.COLUMNS) for row, item in enumerate(self.ITEMS): for column, cell in enumerate(item): self.model.setItem(row, column, QStandardItem(cell)) self.proxy = QSortFilterProxyModel(self) self.proxy.setSourceModel(self.model) # filter all columns (use 0, 1, etc. to only filter by a specific column) self.proxy.setFilterKeyColumn(-1) # filter text case insensitively self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self.view.setModel(self.proxy) for column, _ in enumerate(self.COLUMNS): self.view.resizeColumnToContents(column) self.searchBox.textChanged.connect(self.updateFilter) def updateFilter(self, text): self.proxy.setFilterFixedString(text) def setupUi(self): centralWidget = QWidget(self) centralWidget.setLayout(QVBoxLayout(centralWidget)) self.view = QTableView(centralWidget) self.view.setSortingEnabled(True) centralWidget.layout().addWidget(self.view) self.searchBox = QLineEdit(centralWidget) centralWidget.layout().addWidget(self.searchBox) self.setCentralWidget(centralWidget) self.searchBox.setFocus()
class MainWindow(QMainWindow): COLUMNS = ['Name', 'Comment'] ITEMS = [('Ford', "Don't panic"), ('Marvin', "I'm feeling so depressed")] def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setupUi() # setup a sample model self.model = QStandardItemModel(self) self.model.setHorizontalHeaderLabels(self.COLUMNS) for row, item in enumerate(self.ITEMS): for column, cell in enumerate(item): self.model.setItem(row, column, QStandardItem(cell)) self.proxy = QSortFilterProxyModel(self) self.proxy.setSourceModel(self.model) # filter all columns (use 0, 1, etc. to only filter by a specific column) self.proxy.setFilterKeyColumn(-1) # filter text case insensitively self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self.view.setModel(self.proxy) for column, _ in enumerate(self.COLUMNS): self.view.resizeColumnToContents(column) self.searchBox.textChanged.connect(self.updateFilter) def updateFilter(self, text): self.proxy.setFilterFixedString(text) def setupUi(self): centralWidget = QWidget(self) centralWidget.setLayout(QVBoxLayout(centralWidget)) self.view = QTableView(centralWidget) self.view.setSortingEnabled(True) centralWidget.layout().addWidget(self.view) self.searchBox = QLineEdit(centralWidget) centralWidget.layout().addWidget(self.searchBox) self.setCentralWidget(centralWidget) self.searchBox.setFocus()
class List(QDialog): """All Notes dialog""" def __init__(self, app, *args, **kwargs): QDialog.__init__(self, *args, **kwargs) self.app = app self.closed = False self.sort_order = None self.ui = Ui_List() self.ui.setupUi(self) self.setWindowIcon(get_icon()) self.notebooksModel = QStandardItemModel() self.ui.notebooksList.setModel(self.notebooksModel) self.ui.notebooksList.selection.connect(self.selection_changed) self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notebooksList.customContextMenuRequested.connect(self.notebook_context_menu) self.notesModel = QStandardItemModel() self.notesModel.setHorizontalHeaderLabels( [self.tr('Title'), self.tr('Last Updated')]) self.ui.notesList.setModel(self.notesModel) self.ui.notesList.doubleClicked.connect(self.note_dblclicked) self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notesList.customContextMenuRequested.connect(self.note_context_menu) self.ui.notesList.header().sortIndicatorChanged.connect(self.sort_order_updated) self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new')) self.ui.newNotebookBtn.clicked.connect(self.new_notebook) self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new')) self.ui.newNoteBtn.clicked.connect(self.new_note) self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n'))) self.ui.newNotebookBtn.setShortcut(QKeySequence(self.tr('Ctrl+Shift+n'))) QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close) @Slot(QItemSelection, QItemSelection) def selection_changed(self, selected, deselected): if len(selected.indexes()): self.notebook_selected(selected.indexes()[-1]) def showEvent(self, *args, **kwargs): QDialog.showEvent(self, *args, **kwargs) self._reload_notebooks_list() self.readSettings() def writeSettings(self): self.app.settings.setValue('list-geometry', self.saveGeometry()) for key, widget in self._getRestorableItems(): self.app.settings.setValue(key, widget.saveState()) def _getRestorableItems(self): return ( ('list-splitter-state', self.ui.splitter), ('list-header-state', self.ui.notesList.header()), ) def readSettings(self): geometry = self.app.settings.value('list-geometry') if geometry: self.restoreGeometry(geometry) for key, widget in self._getRestorableItems(): state = self.app.settings.value(key) if state: widget.restoreState(state) def closeEvent(self, event): self.writeSettings() event.ignore() self.closed = True self.hide() @Slot(int, Qt.SortOrder) def sort_order_updated(self, logicalIndex, order): self.sort_order = (logicalIndex, order.name) self.app.settings.setValue('list-notes-sort-order', self.sort_order) def notebook_selected(self, index): self.notesModel.setRowCount(0) item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): notebook_id = item.notebook.id else: notebook_id = 0 notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array([], signature='i') notes = self.app.provider.find_notes( '', notebook_filter, dbus.Array([], signature='i'), 0, 2 ** 31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) @Slot() def note_dblclicked(self, index): item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def new_notebook(self): name, status = self._notebook_new_name(self.tr('Create new notebook')) if status: notebook_struct = self.app.provider.create_notebook(name) notebook = Notebook.from_tuple(notebook_struct) self.app.send_notify(self.tr('Notebook "%s" created!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def rename_notebook(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) notebook = item.notebook name, status = self._notebook_new_name( self.tr('Rename notebook'), notebook.name, ) if status: notebook.name = name self.app.provider.update_notebook(notebook.struct) self.app.send_notify(self.tr('Notebook "%s" renamed!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def remove_notebook(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a notebook"), self.tr("Are you sure want to delete this notebook and its notes?"), QMessageBox.Yes | QMessageBox.No ) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) self.app.provider.delete_notebook(item.notebook.id) self.app.send_notify(self.tr('Notebook "%s" deleted!') % item.notebook.name) self._reload_notebooks_list() @Slot() def new_note(self): index = self.ui.notebooksList.currentIndex() notebook_id = NONE_ID if index.row(): item = self.notebooksModel.itemFromIndex(index) notebook_id = item.notebook.id self.app.indicator.create(notebook_id=notebook_id) @Slot() def edit_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def remove_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) msgBox = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a note"), self.tr('Are you sure want to delete note "%s"?') % item.note.title, QMessageBox.Yes | QMessageBox.No ) if msgBox.exec_() == QMessageBox.Yes: self.app.provider.delete_note(item.note.id) self.app.send_notify(self.tr('Note "%s" deleted!') % item.note.title) self.notebook_selected(self.ui.notebooksList.currentIndex()) @Slot(QPoint) def notebook_context_menu(self, pos): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) @Slot(QPoint) def note_context_menu(self, pos): menu = QMenu(self.ui.notesList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note) menu.exec_(self.ui.notesList.mapToGlobal(pos)) def _reload_notebooks_list(self, select_notebook_id=None): self.notebooksModel.clear() root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes')) self.notebooksModel.appendRow(root) selected_item = root for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) count = self.app.provider.get_notebook_notes_count(notebook.id) item = QNotebookItem(notebook, count) root.appendRow(item) if select_notebook_id and notebook.id == select_notebook_id: selected_item = item self.ui.notebooksList.expandAll() if selected_item: index = self.notebooksModel.indexFromItem(selected_item) self.ui.notebooksList.setCurrentIndex(index) self.notebook_selected(index) def _notebook_new_name(self, title, exclude=''): names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks()) try: names.remove(exclude) except ValueError: pass name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude) while name in names and status: message = self.tr('Notebook with this name already exist. Enter notebook name') name, status = QInputDialog.getText(self, title, message) return name, status
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 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 ReprintDeliverySlipDialog(QDialog): def __init__(self, parent, dao): super(ReprintDeliverySlipDialog, self).__init__(parent) self.dao = dao title = _("Print a delivery slip") self.setWindowTitle(title) top_layout = QVBoxLayout() self.title_widget = TitleWidget(title, None) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.StandardButton.Cancel) self.buttons.addButton(QDialogButtonBox.Ok) hlayout = QHBoxLayout() hlayout.addWidget(QLabel(_("Slip number"))) self.slip_number = QLineEdit() hlayout.addWidget(self.slip_number) hlayout.addStretch() self.search_results_view = QTableView() self.search_results_model = QStandardItemModel() self.search_results_model.setHorizontalHeaderLabels( [_("Slip Nr"), _("Date"), _("Customer"), _("Order")]) self.search_results_view.setModel(self.search_results_model) # self.search_results_view.setHorizontalHeader(self.headers_view) self.search_results_view.setEditTriggers( QAbstractItemView.NoEditTriggers) self.search_results_view.horizontalHeader().setResizeMode( 1, QHeaderView.ResizeToContents) self.search_results_view.horizontalHeader().setResizeMode( 2, QHeaderView.Stretch) self.search_results_view.verticalHeader().hide() self.slip_part_view = DeliverySlipViewWidget(self) hlayout_results = QHBoxLayout() hlayout_results.addWidget(self.search_results_view) hlayout_results.addWidget(self.slip_part_view) self.search_results_model.removeRows( 0, self.search_results_model.rowCount()) delivery_slips = self.dao.delivery_slip_part_dao.find_recent() for slip in delivery_slips: self.search_results_model.appendRow([ QStandardItem(str(slip[0])), QStandardItem(date_to_dmy(slip[1])), QStandardItem(slip[2]), QStandardItem(slip[3]) ]) top_layout.addWidget(self.title_widget) top_layout.addLayout(hlayout) top_layout.addLayout(hlayout_results) top_layout.addWidget(self.buttons) top_layout.setStretch(2, 100) self.setLayout(top_layout) self.search_results_view.setSelectionBehavior( QAbstractItemView.SelectRows) self.search_results_view.setSelectionMode( QAbstractItemView.SingleSelection) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.search_results_view.activated.connect(self.row_activated) self.search_results_view.selectionModel().currentRowChanged.connect( self.row_selected) @Slot(QModelIndex, QModelIndex) def row_selected(self, cur_ndx, prev_ndx): slip_id = cur_ndx.model().index(cur_ndx.row(), 0).data() self.slip_number.setText(str(slip_id)) self.slip_part_view.set_delivery_slip_parts( dao.delivery_slip_part_dao.load_slip_parts_frozen(slip_id)) @Slot(QModelIndex) def row_activated(self, ndx): slip_id = ndx.model().index(ndx.row(), 0).data() self.slip_number.setText(str(slip_id)) self.accept() @Slot() def accept(self): try: try: slip_id = int(self.slip_number.text()) except ValueError as e: makeErrorBox( _("The delivery slip number {} is not valid").format( self.slip_number.text())).exec_() return if self.dao.delivery_slip_part_dao.id_exists(slip_id): print_delivery_slip(self.dao, slip_id) else: makeErrorBox( _("The delivery slip {} doesn't exist").format( slip_id)).exec_() return except Exception as e: mainlog.exception(e) msgBox = makeErrorBox(_("Something wrong happened while printing")) msgBox.exec_() return super(ReprintDeliverySlipDialog, self).accept() @Slot() def reject(self): return super(ReprintDeliverySlipDialog, self).reject()
class List(QMainWindow): """All Notes dialog""" def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.app = QApplication.instance() self.closed = False self.sort_order = None self._init_interface() self.app.data_changed.connect(self._reload_data) self._init_notebooks() self._init_tags() self._init_notes() def _init_interface(self): self.ui = Ui_List() self.ui.setupUi(self) self.setWindowIcon(get_icon()) self.setWindowTitle(self.tr("Everpad / All Notes")) self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new')) self.ui.newNotebookBtn.clicked.connect(self.new_notebook) self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new')) self.ui.newNoteBtn.clicked.connect(self.new_note) self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n'))) self.ui.newNotebookBtn.setShortcut( QKeySequence(self.tr('Ctrl+Shift+n'))) QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close) def _init_notebooks(self): self._current_notebook = None self.notebooksModel = QStandardItemModel() self.ui.notebooksList.setModel(self.notebooksModel) self.ui.notebooksList.selection.connect(self.selection_changed) self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notebooksList.customContextMenuRequested.connect( self.notebook_context_menu) def _init_tags(self): self._current_tag = None self.tagsModel = QStandardItemModel() self.ui.tagsList.setModel(self.tagsModel) self.ui.tagsList.selection.connect(self.tag_selection_changed) self.ui.tagsList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tagsList.customContextMenuRequested.connect( self.tag_context_menu) def _init_notes(self): self._current_note = None self.notesModel = QStandardItemModel() self.notesModel.setHorizontalHeaderLabels( [self.tr('Title'), self.tr('Last Updated')]) self.ui.notesList.setModel(self.notesModel) self.ui.notesList.selection.connect(self.note_selection_changed) self.ui.notesList.doubleClicked.connect(self.note_dblclicked) self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notesList.customContextMenuRequested.connect( self.note_context_menu) self.ui.notesList.header().sortIndicatorChanged.connect( self.sort_order_updated) @Slot(QItemSelection, QItemSelection) def selection_changed(self, selected, deselected): if len(selected.indexes()): self.ui.tagsList.clearSelection() self.notebook_selected(selected.indexes()[-1]) @Slot(QItemSelection, QItemSelection) def tag_selection_changed(self, selected, deselected): if len(selected.indexes()): self.ui.notebooksList.clearSelection() self.tag_selected(selected.indexes()[-1]) @Slot(QItemSelection, QItemSelection) def note_selection_changed(self, selected, deselected): if len(selected.indexes()): self.note_selected(selected.indexes()[-1]) def showEvent(self, *args, **kwargs): super(List, self).showEvent(*args, **kwargs) self._reload_data() self.readSettings() def writeSettings(self): self.app.settings.setValue('list-geometry', self.saveGeometry()) for key, widget in self._getRestorableItems(): self.app.settings.setValue(key, widget.saveState()) def _getRestorableItems(self): return ( ('list-splitter-state', self.ui.splitter), ('list-header-state', self.ui.notesList.header()), ) def readSettings(self): geometry = self.app.settings.value('list-geometry') if geometry: self.restoreGeometry(geometry) for key, widget in self._getRestorableItems(): state = self.app.settings.value(key) if state: widget.restoreState(state) def closeEvent(self, event): self.writeSettings() event.ignore() self.closed = True self.hide() @Slot(int, Qt.SortOrder) def sort_order_updated(self, logicalIndex, order): self.sort_order = (logicalIndex, order.name) self.app.settings.setValue('list-notes-sort-order', self.sort_order) def note_selected(self, index): self._current_note = index def notebook_selected(self, index): self.notesModel.setRowCount(0) item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): notebook_id = item.notebook.id else: notebook_id = 0 self._current_notebook = notebook_id self._current_tag = SELECT_NONE notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array( [], signature='i') if hasattr( item, 'stack'): # stack selected, retrieve all underlying notebooks notebook_filter = [] for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if (notebook.stack == item.stack): notebook_filter.append(notebook.id) notes = self.app.provider.find_notes( '', notebook_filter, dbus.Array([], signature='i'), 0, 2**31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) def tag_selected(self, index): self.notesModel.setRowCount(0) item = self.tagsModel.itemFromIndex(index) if hasattr(item, 'tag'): tag_id = item.tag.id else: tag_id = 0 self._current_notebook = SELECT_NONE self._current_tag = tag_id tag_filter = [tag_id] if tag_id > 0 else dbus.Array([], signature='i') notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), tag_filter, 0, 2**31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) @Slot() def note_dblclicked(self, index): item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def new_notebook(self, oldStack=''): name, status, stack = self._notebook_new_name( self.tr('Create new notebook'), '', oldStack) if status: notebook_struct = self.app.provider.create_notebook(name, stack) notebook = Notebook.from_tuple(notebook_struct) self.app.send_notify( self.tr('Notebook "%s" created!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def rename_notebook(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) notebook = item.notebook name, status, stack = self._notebook_new_name( self.tr('Rename notebook'), notebook.name, notebook.stack) if status: notebook.name = name notebook.stack = stack self.app.provider.update_notebook(notebook.struct) self.app.send_notify( self.tr('Notebook "%s" renamed!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def remove_notebook(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a notebook"), self.tr( "Are you sure want to delete this notebook and its notes?"), QMessageBox.Yes | QMessageBox.No) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) self.app.provider.delete_notebook(item.notebook.id) self.app.send_notify( self.tr('Notebook "%s" deleted!') % item.notebook.name) self._reload_notebooks_list() @Slot() def rename_stack(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) stack = item.stack name, status = self._stack_new_name( self.tr('Rename stack'), stack, ) if status: # loop notebooks and update stack str for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if (notebook.stack == item.stack): notebook.stack = name self.app.provider.update_notebook(notebook.struct) self.app.send_notify(self.tr('Stack "%s" renamed!') % name) self._reload_notebooks_list(notebook.id) @Slot() def remove_stack(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a stack"), self. tr("Are you sure want to delete this stack? Notebooks and notes are preserved." ), QMessageBox.Yes | QMessageBox.No) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) # loop notebooks and remove stack str for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if (notebook.stack == item.stack): print "Clearing one notebook from its stack." notebook.stack = '' self.app.provider.update_notebook(notebook.struct) self._reload_notebooks_list() @Slot() def remove_tag(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a tag"), self. tr("Are you sure want to delete this tag and untag all notes tagged with it?" ), QMessageBox.Yes | QMessageBox.No) if msg.exec_() == QMessageBox.Yes: index = self.ui.tagsList.currentIndex() item = self.tagsModel.itemFromIndex(index) self.app.provider.delete_tag(item.tag.id) self.app.send_notify(self.tr('Tag "%s" deleted!') % item.tag.name) self._reload_tags_list() @Slot() def new_note(self): index = self.ui.notebooksList.currentIndex() notebook_id = NONE_ID if index.row(): item = self.notebooksModel.itemFromIndex(index) notebook_id = item.notebook.id self.app.indicator.create(notebook_id=notebook_id) @Slot() def edit_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def remove_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) msgBox = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a note"), self.tr('Are you sure want to delete note "%s"?') % item.note.title, QMessageBox.Yes | QMessageBox.No) if msgBox.exec_() == QMessageBox.Yes: self.app.provider.delete_note(item.note.id) self.app.send_notify( self.tr('Note "%s" deleted!') % item.note.title) self.notebook_selected(self.ui.notebooksList.currentIndex()) @Slot(QPoint) def notebook_context_menu(self, pos): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) if hasattr(item, 'stack'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_stack) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_stack) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) @Slot(QPoint) def tag_context_menu(self, pos): index = self.ui.tagsList.currentIndex() item = self.tagsModel.itemFromIndex(index) if hasattr(item, 'tag'): menu = QMenu(self.ui.tagsList) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_tag) menu.exec_(self.ui.tagsList.mapToGlobal(pos)) @Slot(QPoint) def note_context_menu(self, pos): menu = QMenu(self.ui.notesList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note) menu.exec_(self.ui.notesList.mapToGlobal(pos)) def _reload_data(self): self._reload_notebooks_list(self._current_notebook) self._reload_tags_list(self._current_tag) self._mark_note_selected(self._current_note) def _reload_notebooks_list(self, select_notebook_id=None): # TODO could enable selecting an already selected stack self.notebooksModel.clear() root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes')) self.notebooksModel.appendRow(root) selected_item = root stacks = {} for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) count = self.app.provider.get_notebook_notes_count(notebook.id) item = QNotebookItem(notebook, count) if (notebook.stack == ''): root.appendRow(item) else: if (notebook.stack not in stacks.keys()): stack = QStandardItem(QIcon.fromTheme('user-home'), notebook.stack) stack.stack = notebook.stack root.appendRow(stack) stacks[notebook.stack] = stack stacks[notebook.stack].appendRow(item) if select_notebook_id and notebook.id == select_notebook_id: selected_item = item self.ui.notebooksList.expandAll() if selected_item and not select_notebook_id == SELECT_NONE: index = self.notebooksModel.indexFromItem(selected_item) self.ui.notebooksList.setCurrentIndex(index) self.notebook_selected(index) def _notebook_new_name(self, title, exclude='', oldStack=''): names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks()) try: names.remove(exclude) except ValueError: pass name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude) while name in names and status: message = self.tr( 'Notebook with this name already exist. Enter notebook name') name, status = QInputDialog.getText(self, title, message) if status: stack, status = QInputDialog.getText( self, title, self.tr('Enter stack name (empty for no stack):'), text=oldStack) else: stack = oldStack return name, status, stack def _stack_new_name(self, title, value=''): name, status = QInputDialog.getText(self, title, self.tr('Enter stack name:'), text=value) return name, status def _reload_tags_list(self, select_tag_id=None): # TODO nested tags self.tagsModel.clear() tagRoot = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Tags')) self.tagsModel.appendRow(tagRoot) selected_item = tagRoot for tag_struct in self.app.provider.list_tags(): tag = Tag.from_tuple(tag_struct) count = self.app.provider.get_tag_notes_count(tag.id) item = QTagItem(tag, count) tagRoot.appendRow(item) if select_tag_id and tag.id == select_tag_id: selected_item = item self.ui.tagsList.expandAll() if selected_item and not select_tag_id == SELECT_NONE: index = self.tagsModel.indexFromItem(selected_item) self.ui.tagsList.setCurrentIndex(index) self.tag_selected(index) def _mark_note_selected(self, index): if index: self.ui.notesList.setCurrentIndex(index)
class List(QDialog): """All Notes dialog""" def __init__(self, app, *args, **kwargs): QDialog.__init__(self, *args, **kwargs) self.app = app self.closed = False self.sort_order = None self.ui = Ui_List() self.ui.setupUi(self) self.setWindowIcon(get_icon()) self.notebooksModel = QStandardItemModel() self.ui.notebooksList.setModel(self.notebooksModel) self.ui.notebooksList.selection.connect(self.selection_changed) self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notebooksList.customContextMenuRequested.connect( self.notebook_context_menu) self.notesModel = QStandardItemModel() self.notesModel.setHorizontalHeaderLabels( [self.tr('Title'), self.tr('Last Updated')]) self.ui.notesList.setModel(self.notesModel) self.ui.notesList.doubleClicked.connect(self.note_dblclicked) self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notesList.customContextMenuRequested.connect( self.note_context_menu) self.ui.notesList.header().sortIndicatorChanged.connect( self.sort_order_updated) self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new')) self.ui.newNotebookBtn.clicked.connect(self.new_notebook) self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new')) self.ui.newNoteBtn.clicked.connect(self.new_note) @Slot(QItemSelection, QItemSelection) def selection_changed(self, selected, deselected): if len(selected.indexes()): self.notebook_selected(selected.indexes()[-1]) def showEvent(self, *args, **kwargs): QDialog.showEvent(self, *args, **kwargs) self._reload_notebooks_list() self.readSettings() def writeSettings(self): self.app.settings.setValue('list-geometry', self.saveGeometry()) for key, widget in self._getRestorableItems(): self.app.settings.setValue(key, widget.saveState()) def _getRestorableItems(self): return ( ('list-splitter-state', self.ui.splitter), ('list-header-state', self.ui.notesList.header()), ) def readSettings(self): geometry = self.app.settings.value('list-geometry') if geometry: self.restoreGeometry(geometry) for key, widget in self._getRestorableItems(): state = self.app.settings.value(key) if state: widget.restoreState(state) def closeEvent(self, event): self.writeSettings() event.ignore() self.closed = True self.hide() @Slot(int, Qt.SortOrder) def sort_order_updated(self, logicalIndex, order): self.sort_order = (logicalIndex, order.name) self.app.settings.setValue('list-notes-sort-order', self.sort_order) def notebook_selected(self, index): self.notesModel.setRowCount(0) item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): notebook_id = item.notebook.id else: notebook_id = 0 notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array( [], signature='i') notes = self.app.provider.find_notes( '', notebook_filter, dbus.Array([], signature='i'), 0, 2**31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) @Slot() def note_dblclicked(self, index): item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def new_notebook(self): name, status = self._notebook_new_name(self.tr('Create new notebook')) if status: notebook_struct = self.app.provider.create_notebook(name) notebook = Notebook.from_tuple(notebook_struct) self.app.send_notify( self.tr('Notebook "%s" created!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def rename_notebook(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) notebook = item.notebook name, status = self._notebook_new_name( self.tr('Rename notebook'), notebook.name, ) if status: notebook.name = name self.app.provider.update_notebook(notebook.struct) self.app.send_notify( self.tr('Notebook "%s" renamed!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def remove_notebook(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a notebook"), self.tr( "Are you sure want to delete this notebook and its notes?"), QMessageBox.Yes | QMessageBox.No) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) self.app.provider.delete_notebook(item.notebook.id) self.app.send_notify( self.tr('Notebook "%s" deleted!') % item.notebook.name) self._reload_notebooks_list() @Slot() def new_note(self): index = self.ui.notebooksList.currentIndex() notebook_id = NONE_ID if index.row(): item = self.notebooksModel.itemFromIndex(index) notebook_id = item.notebook.id self.app.indicator.create(notebook_id=notebook_id) @Slot() def edit_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def remove_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) msgBox = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a note"), self.tr('Are you sure want to delete note "%s"?') % item.note.title, QMessageBox.Yes | QMessageBox.No) if msgBox.exec_() == QMessageBox.Yes: self.app.provider.delete_note(item.note.id) self.app.send_notify( self.tr('Note "%s" deleted!') % item.note.title) self.notebook_selected(self.ui.notebooksList.currentIndex()) @Slot(QPoint) def notebook_context_menu(self, pos): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) @Slot(QPoint) def note_context_menu(self, pos): menu = QMenu(self.ui.notesList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note) menu.exec_(self.ui.notesList.mapToGlobal(pos)) def _reload_notebooks_list(self, select_notebook_id=None): self.notebooksModel.clear() root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes')) self.notebooksModel.appendRow(root) selected_item = root for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) count = self.app.provider.get_notebook_notes_count(notebook.id) item = QNotebookItem(notebook, count) root.appendRow(item) if select_notebook_id and notebook.id == select_notebook_id: selected_item = item self.ui.notebooksList.expandAll() if selected_item: index = self.notebooksModel.indexFromItem(selected_item) self.ui.notebooksList.setCurrentIndex(index) self.notebook_selected(index) def _notebook_new_name(self, title, exclude=''): names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks()) try: names.remove(exclude) except ValueError: pass name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude) while name in names and status: message = self.tr( 'Notebook with this name already exist. Enter notebook name') name, status = QInputDialog.getText(self, title, message) return name, status
class List(QMainWindow): """All Notes dialog""" def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.app = QApplication.instance() self.closed = False self.sort_order = None self._init_interface() self.app.data_changed.connect(self._reload_data) self._init_notebooks() self._init_tags() self._init_notes() def _init_interface(self): self.ui = Ui_List() self.ui.setupUi(self) self.setWindowIcon(get_icon()) self.setWindowTitle(self.tr("Everpad / All Notes")) self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new')) self.ui.newNotebookBtn.clicked.connect(self.new_notebook) self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new')) self.ui.newNoteBtn.clicked.connect(self.new_note) self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n'))) self.ui.newNotebookBtn.setShortcut(QKeySequence(self.tr('Ctrl+Shift+n'))) QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close) def _init_notebooks(self): self._current_notebook = None self.notebooksModel = QStandardItemModel() self.ui.notebooksList.setModel(self.notebooksModel) self.ui.notebooksList.selection.connect(self.selection_changed) self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notebooksList.customContextMenuRequested.connect(self.notebook_context_menu) def _init_tags(self): self._current_tag = None self.tagsModel = QStandardItemModel() self.ui.tagsList.setModel(self.tagsModel) self.ui.tagsList.selection.connect(self.tag_selection_changed) self.ui.tagsList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tagsList.customContextMenuRequested.connect(self.tag_context_menu) def _init_notes(self): self._current_note = None self.notesModel = QStandardItemModel() self.notesModel.setHorizontalHeaderLabels( [self.tr('Title'), self.tr('Last Updated')]) self.ui.notesList.setModel(self.notesModel) self.ui.notesList.selection.connect(self.note_selection_changed) self.ui.notesList.doubleClicked.connect(self.note_dblclicked) self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.notesList.customContextMenuRequested.connect(self.note_context_menu) self.ui.notesList.header().sortIndicatorChanged.connect(self.sort_order_updated) @Slot(QItemSelection, QItemSelection) def selection_changed(self, selected, deselected): if len(selected.indexes()): self.ui.tagsList.clearSelection() self.notebook_selected(selected.indexes()[-1]) @Slot(QItemSelection, QItemSelection) def tag_selection_changed(self, selected, deselected): if len(selected.indexes()): self.ui.notebooksList.clearSelection() self.tag_selected(selected.indexes()[-1]) @Slot(QItemSelection, QItemSelection) def note_selection_changed(self, selected, deselected): if len(selected.indexes()): self.note_selected(selected.indexes()[-1]) def showEvent(self, *args, **kwargs): super(List, self).showEvent(*args, **kwargs) self._reload_data() self.readSettings() def writeSettings(self): self.app.settings.setValue('list-geometry', self.saveGeometry()) for key, widget in self._getRestorableItems(): self.app.settings.setValue(key, widget.saveState()) def _getRestorableItems(self): return ( ('list-splitter-state', self.ui.splitter), ('list-header-state', self.ui.notesList.header()), ) def readSettings(self): geometry = self.app.settings.value('list-geometry') if geometry: self.restoreGeometry(geometry) for key, widget in self._getRestorableItems(): state = self.app.settings.value(key) if state: widget.restoreState(state) def closeEvent(self, event): self.writeSettings() event.ignore() self.closed = True self.hide() @Slot(int, Qt.SortOrder) def sort_order_updated(self, logicalIndex, order): self.sort_order = (logicalIndex, order.name) self.app.settings.setValue('list-notes-sort-order', self.sort_order) def note_selected(self, index): self._current_note = index def notebook_selected(self, index): self.notesModel.setRowCount(0) item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): notebook_id = item.notebook.id else: notebook_id = 0 self._current_notebook = notebook_id self._current_tag = SELECT_NONE notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array([], signature='i') if hasattr(item, 'stack'): # stack selected, retrieve all underlying notebooks notebook_filter = [] for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if(notebook.stack == item.stack): notebook_filter.append(notebook.id) notes = self.app.provider.find_notes( '', notebook_filter, dbus.Array([], signature='i'), 0, 2 ** 31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) def tag_selected(self, index): self.notesModel.setRowCount(0) item = self.tagsModel.itemFromIndex(index) if hasattr(item, 'tag'): tag_id = item.tag.id else: tag_id = 0 self._current_notebook = SELECT_NONE self._current_tag = tag_id tag_filter = [tag_id] if tag_id > 0 else dbus.Array([], signature='i') notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), tag_filter, 0, 2 ** 31 - 1, Note.ORDER_TITLE, -1, ) # fails with sys.maxint in 64 for note_struct in notes: note = Note.from_tuple(note_struct) self.notesModel.appendRow(QNoteItemFactory(note).make_items()) sort_order = self.sort_order if sort_order is None: sort_order = self.app.settings.value('list-notes-sort-order') if sort_order: logicalIndex, order = sort_order order = Qt.SortOrder.values[order] self.ui.notesList.sortByColumn(int(logicalIndex), order) @Slot() def note_dblclicked(self, index): item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def new_notebook(self, oldStack=''): name, status, stack = self._notebook_new_name(self.tr('Create new notebook'), '', oldStack) if status: notebook_struct = self.app.provider.create_notebook(name, stack) notebook = Notebook.from_tuple(notebook_struct) self.app.send_notify(self.tr('Notebook "%s" created!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def rename_notebook(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) notebook = item.notebook name, status, stack = self._notebook_new_name( self.tr('Rename notebook'), notebook.name, notebook.stack ) if status: notebook.name = name notebook.stack = stack self.app.provider.update_notebook(notebook.struct) self.app.send_notify(self.tr('Notebook "%s" renamed!') % notebook.name) self._reload_notebooks_list(notebook.id) @Slot() def remove_notebook(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a notebook"), self.tr("Are you sure want to delete this notebook and its notes?"), QMessageBox.Yes | QMessageBox.No ) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) self.app.provider.delete_notebook(item.notebook.id) self.app.send_notify(self.tr('Notebook "%s" deleted!') % item.notebook.name) self._reload_notebooks_list() @Slot() def rename_stack(self): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) stack = item.stack name, status = self._stack_new_name( self.tr('Rename stack'), stack, ) if status: # loop notebooks and update stack str for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if(notebook.stack == item.stack): notebook.stack = name self.app.provider.update_notebook(notebook.struct) self.app.send_notify(self.tr('Stack "%s" renamed!') % name) self._reload_notebooks_list(notebook.id) @Slot() def remove_stack(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a stack"), self.tr("Are you sure want to delete this stack? Notebooks and notes are preserved."), QMessageBox.Yes | QMessageBox.No ) if msg.exec_() == QMessageBox.Yes: index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) # loop notebooks and remove stack str for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) if(notebook.stack == item.stack): print "Clearing one notebook from its stack." notebook.stack = '' self.app.provider.update_notebook(notebook.struct) self._reload_notebooks_list() @Slot() def remove_tag(self): msg = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a tag"), self.tr("Are you sure want to delete this tag and untag all notes tagged with it?"), QMessageBox.Yes | QMessageBox.No ) if msg.exec_() == QMessageBox.Yes: index = self.ui.tagsList.currentIndex() item = self.tagsModel.itemFromIndex(index) self.app.provider.delete_tag(item.tag.id) self.app.send_notify(self.tr('Tag "%s" deleted!') % item.tag.name) self._reload_tags_list() @Slot() def new_note(self): index = self.ui.notebooksList.currentIndex() notebook_id = NONE_ID if index.row(): item = self.notebooksModel.itemFromIndex(index) notebook_id = item.notebook.id self.app.indicator.create(notebook_id=notebook_id) @Slot() def edit_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) self.app.indicator.open(item.note) @Slot() def remove_note(self): index = self.ui.notesList.currentIndex() item = self.notesModel.itemFromIndex(index) msgBox = QMessageBox( QMessageBox.Critical, self.tr("You are trying to delete a note"), self.tr('Are you sure want to delete note "%s"?') % item.note.title, QMessageBox.Yes | QMessageBox.No ) if msgBox.exec_() == QMessageBox.Yes: self.app.provider.delete_note(item.note.id) self.app.send_notify(self.tr('Note "%s" deleted!') % item.note.title) self.notebook_selected(self.ui.notebooksList.currentIndex()) @Slot(QPoint) def notebook_context_menu(self, pos): index = self.ui.notebooksList.currentIndex() item = self.notebooksModel.itemFromIndex(index) if hasattr(item, 'notebook'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) if hasattr(item, 'stack'): menu = QMenu(self.ui.notebooksList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_stack) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_stack) menu.exec_(self.ui.notebooksList.mapToGlobal(pos)) @Slot(QPoint) def tag_context_menu(self, pos): index = self.ui.tagsList.currentIndex() item = self.tagsModel.itemFromIndex(index) if hasattr(item, 'tag'): menu = QMenu(self.ui.tagsList) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_tag) menu.exec_(self.ui.tagsList.mapToGlobal(pos)) @Slot(QPoint) def note_context_menu(self, pos): menu = QMenu(self.ui.notesList) menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note) menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note) menu.exec_(self.ui.notesList.mapToGlobal(pos)) def _reload_data(self): self._reload_notebooks_list(self._current_notebook) self._reload_tags_list(self._current_tag) self._mark_note_selected(self._current_note) def _reload_notebooks_list(self, select_notebook_id=None): # TODO could enable selecting an already selected stack self.notebooksModel.clear() root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes')) self.notebooksModel.appendRow(root) selected_item = root stacks = {} for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) count = self.app.provider.get_notebook_notes_count(notebook.id) item = QNotebookItem(notebook, count) if(notebook.stack == ''): root.appendRow(item) else: if(notebook.stack not in stacks.keys()): stack = QStandardItem(QIcon.fromTheme('user-home'), notebook.stack) stack.stack = notebook.stack root.appendRow(stack) stacks[notebook.stack] = stack stacks[notebook.stack].appendRow(item) if select_notebook_id and notebook.id == select_notebook_id: selected_item = item self.ui.notebooksList.expandAll() if selected_item and not select_notebook_id == SELECT_NONE: index = self.notebooksModel.indexFromItem(selected_item) self.ui.notebooksList.setCurrentIndex(index) self.notebook_selected(index) def _notebook_new_name(self, title, exclude='', oldStack=''): names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks()) try: names.remove(exclude) except ValueError: pass name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude) while name in names and status: message = self.tr('Notebook with this name already exist. Enter notebook name') name, status = QInputDialog.getText(self, title, message) if status: stack, status = QInputDialog.getText(self, title, self.tr('Enter stack name (empty for no stack):'), text=oldStack) else: stack = oldStack return name, status, stack def _stack_new_name(self, title, value=''): name, status = QInputDialog.getText(self, title, self.tr('Enter stack name:'), text=value) return name, status def _reload_tags_list(self, select_tag_id=None): # TODO nested tags self.tagsModel.clear() tagRoot = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Tags')) self.tagsModel.appendRow(tagRoot) selected_item = tagRoot for tag_struct in self.app.provider.list_tags(): tag = Tag.from_tuple(tag_struct) count = self.app.provider.get_tag_notes_count(tag.id) item = QTagItem(tag, count) tagRoot.appendRow(item) if select_tag_id and tag.id == select_tag_id: selected_item = item self.ui.tagsList.expandAll() if selected_item and not select_tag_id == SELECT_NONE: index = self.tagsModel.indexFromItem(selected_item) self.ui.tagsList.setCurrentIndex(index) self.tag_selected(index) def _mark_note_selected(self, index): if index: self.ui.notesList.setCurrentIndex(index)
class MainWindow(QMainWindow): def __init__(self, parent = None): super().__init__(parent) self.Logger = private_logger.create('MainWindow.py') self.Logger.debug('Create MainWindow.') self.setAcceptDrops(True) self.doLayout() self.Logger.debug('Created MainWindow.') def dropEvent(self, event): self.Logger.debug('Drop down files') self.FileListModel.clear() source = event.mimeData() filepaths = source.urls() self.Logger.debug('Drop on %s', filepaths) self.FileListModel.appendRow( [QStandardItem(path.toLocalFile()) for path in filepaths] ) def dragEnterEvent(self, event): self.Logger.debug('Came to drop down.') mime = event.mimeData() if mime.hasUrls(): self.Logger.debug('Accepted drop down items.') event.accept() else: self.Logger.debug('Rejected drop down items.') event.ignore() pass @Slot() def onConvert(self): error_files = list() index = 0 while(True): item = self.FileListModel.item(index) if type(item) is QStandardItem: filepath = item.text() print(filepath) if(filepath == ''): continue try: self.convert(filepath) except Exception as e: error_files.append(filepath) break elif item is None or item == 0: break index += 1 if(len(error_files) == 0): QMessageBox.information(self, self.tr('完了'), self.tr('変換が完了しました')) else: QMessageBox.critical(self, self.tr('エラー'), self.tr('下記のファイルでエラーが発生しました。Safeモードをお試しください。\n' + '\n'.join(error_files))) def convert(self, filepath): self.Logger.info('Processing {0}.'.format(filepath)) ppt = None if self.SafeModeCheckbox.checkState(): ppt = gpt.SafePresentation.SafePresentation(filepath) else: ppt = gpt.Presentation.Presentation(filepath) notes = ppt.getNoteTexts() filename, ext = os.path.splitext(filepath) with open(filename + '.txt', 'w+') as fp: for note in notes: fp.write('P.{0}\n'.format(note.Page)) fp.write(note.Text) fp.write('\n') def doLayout(self): central_window = QWidget(self) vlayout = QVBoxLayout(central_window) self.FileListView = QListView(self) self.SafeModeCheckbox = QCheckBox(self.tr('Safeモードで読み込み(エラーが出る場合に使用ください。改行やページ番号等崩れる場合があります。)'), self) convert_button = QPushButton(self.tr('変換'), central_window) vlayout.addWidget(QLabel(self.tr('ドラッグ&ドロップでファイルを読み込みます'), self)) vlayout.addWidget(self.FileListView) vlayout.addWidget(self.SafeModeCheckbox) vlayout.addWidget(convert_button) convert_button.clicked.connect(self.onConvert) self.FileListModel = QStandardItemModel(self) self.FileListModel.setHorizontalHeaderLabels([self.tr('ファイル名')]) self.FileListView.setModel(self.FileListModel) self.FileListView.setAcceptDrops(True) self.setCentralWidget(central_window)