class HydroprintGUI(myqt.DialogWindow): ConsoleSignal = QSignal(str) def __init__(self, datamanager, parent=None): super(HydroprintGUI, self).__init__(parent, maximize=True) self.__updateUI = True # Child widgets: self.dmngr = datamanager self.dmngr.wldsetChanged.connect(self.wldset_changed) self.dmngr.wxdsetChanged.connect(self.wxdset_changed) self.page_setup_win = PageSetupWin(self) self.page_setup_win.newPageSetupSent.connect(self.layout_changed) self.color_palette_win = ColorsSetupWin(self) self.color_palette_win.newColorSetupSent.connect(self.update_colors) # Memory path variable: self.save_fig_dir = self.workdir # Generate UI: self.__initUI__() def __initUI__(self): # ---- Toolbar self.btn_save = btn_save = QToolButtonNormal(icons.get_icon('save')) btn_save.setToolTip('Save the well hydrograph') # btn_draw is usefull for debugging purposes btn_draw = QToolButtonNormal(icons.get_icon('refresh')) btn_draw.setToolTip('Force a refresh of the well hydrograph') btn_draw.hide() self.btn_load_layout = QToolButtonNormal( icons.get_icon('load_graph_config')) self.btn_load_layout.setToolTip( "<p>Load graph layout for the current water level " " datafile if it exists</p>") self.btn_load_layout.clicked.connect(self.load_layout_isClicked) self.btn_save_layout = QToolButtonNormal( icons.get_icon('save_graph_config')) self.btn_save_layout.setToolTip('Save current graph layout') self.btn_save_layout.clicked.connect(self.save_layout_isClicked) btn_bestfit_waterlvl = QToolButtonNormal(icons.get_icon('fit_y')) btn_bestfit_waterlvl.setToolTip('Best fit the water level scale') btn_bestfit_time = QToolButtonNormal(icons.get_icon('fit_x')) btn_bestfit_time.setToolTip('Best fit the time scale') self.btn_page_setup = QToolButtonNormal(icons.get_icon('page_setup')) self.btn_page_setup.setToolTip('Show the page setup window') self.btn_page_setup.clicked.connect(self.page_setup_win.show) btn_color_pick = QToolButtonNormal(icons.get_icon('color_picker')) btn_color_pick.setToolTip('<p>Show a window to setup the color palette' ' used to draw the hydrograph</p.') btn_color_pick.clicked.connect(self.color_palette_win.show) self.btn_language = LangToolButton() self.btn_language.setToolTip( "Set the language of the text shown in the graph.") self.btn_language.sig_lang_changed.connect(self.layout_changed) self.btn_language.setIconSize(icons.get_iconsize('normal')) # ---- Zoom Panel btn_zoom_out = QToolButtonSmall(icons.get_icon('zoom_out')) btn_zoom_out.setToolTip('Zoom out (ctrl + mouse-wheel-down)') btn_zoom_out.clicked.connect(self.zoom_out) btn_zoom_in = QToolButtonSmall(icons.get_icon('zoom_in')) btn_zoom_in.setToolTip('Zoom in (ctrl + mouse-wheel-up)') btn_zoom_in.clicked.connect(self.zoom_in) self.zoom_disp = QSpinBox() self.zoom_disp.setAlignment(Qt.AlignCenter) self.zoom_disp.setButtonSymbols(QAbstractSpinBox.NoButtons) self.zoom_disp.setReadOnly(True) self.zoom_disp.setSuffix(' %') self.zoom_disp.setRange(0, 9999) self.zoom_disp.setValue(100) zoom_pan = myqt.QFrameLayout() zoom_pan.setSpacing(3) zoom_pan.addWidget(btn_zoom_out, 0, 0) zoom_pan.addWidget(btn_zoom_in, 0, 1) zoom_pan.addWidget(self.zoom_disp, 0, 2) # LAYOUT : btn_list = [btn_save, btn_draw, self.btn_load_layout, self.btn_save_layout, VSep(), btn_bestfit_waterlvl, btn_bestfit_time, VSep(), self.btn_page_setup, btn_color_pick, self.btn_language, VSep(), zoom_pan] subgrid_toolbar = QGridLayout() toolbar_widget = QWidget() row = 0 for col, btn in enumerate(btn_list): subgrid_toolbar.addWidget(btn, row, col) subgrid_toolbar.setSpacing(5) subgrid_toolbar.setContentsMargins(0, 0, 0, 0) subgrid_toolbar.setColumnStretch(col + 1, 100) toolbar_widget.setLayout(subgrid_toolbar) # ---- LEFT PANEL # SubGrid Hydrograph Frame : self.hydrograph = hydrograph.Hydrograph() self.hydrograph_scrollarea = mplFigViewer.ImageViewer() self.hydrograph_scrollarea.zoomChanged.connect(self.zoom_disp.setValue) grid_hydrograph = QGridLayout() grid_hydrograph.addWidget(self.hydrograph_scrollarea, 0, 0) grid_hydrograph.setRowStretch(0, 500) grid_hydrograph.setColumnStretch(0, 500) grid_hydrograph.setContentsMargins(0, 0, 0, 0) # (L, T, R, B) # ASSEMBLING SubGrids : grid_layout = QGridLayout() self.grid_layout_widget = QFrame() row = 0 grid_layout.addWidget(toolbar_widget, row, 0) row += 1 grid_layout.addLayout(grid_hydrograph, row, 0) grid_layout.setContentsMargins(0, 0, 0, 0) # (L, T, R, B) grid_layout.setSpacing(5) grid_layout.setColumnStretch(0, 500) grid_layout.setRowStretch(1, 500) self.grid_layout_widget.setLayout(grid_layout) # ---- Right Panel self.tabscales = self.__init_scalesTabWidget__() self.right_panel = myqt.QFrameLayout() self.right_panel.addWidget(self.dmngr, 0, 0) self.right_panel.addWidget(self.tabscales, 1, 0) self.right_panel.setRowStretch(2, 100) self.right_panel.setSpacing(15) # ---- MAIN GRID mainGrid = QGridLayout() mainGrid.addWidget(self.grid_layout_widget, 0, 0) mainGrid.addWidget(VSep(), 0, 1) mainGrid.addWidget(self.right_panel, 0, 2) mainGrid.setContentsMargins(10, 10, 10, 10) # (L, T, R, B) mainGrid.setSpacing(15) mainGrid.setColumnStretch(0, 500) mainGrid.setColumnMinimumWidth(2, 250) self.setLayout(mainGrid) # ---- EVENTS # Toolbox Layout : btn_bestfit_waterlvl.clicked.connect(self.best_fit_waterlvl) btn_bestfit_time.clicked.connect(self.best_fit_time) btn_draw.clicked.connect(self.draw_hydrograph) btn_save.clicked.connect(self.select_save_path) # Hydrograph Layout : self.Ptot_scale.valueChanged.connect(self.layout_changed) self.qweather_bin.currentIndexChanged.connect(self.layout_changed) # ---- Init Image self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) def __init_scalesTabWidget__(self): class QRowLayout(QGridLayout): def __init__(self, items, parent=None): super(QRowLayout, self).__init__(parent) for col, item in enumerate(items): self.addWidget(item, 0, col) self.setContentsMargins(0, 0, 0, 0) self.setColumnStretch(0, 100) # ---- Time axis properties # Generate the widgets : self.date_start_widget = QDateEdit() self.date_start_widget.setDisplayFormat('01 / MM / yyyy') self.date_start_widget.setAlignment(Qt.AlignCenter) self.date_start_widget.dateChanged.connect(self.layout_changed) self.date_end_widget = QDateEdit() self.date_end_widget.setDisplayFormat('01 / MM / yyyy') self.date_end_widget.setAlignment(Qt.AlignCenter) self.date_end_widget.dateChanged.connect(self.layout_changed) self.time_scale_label = QComboBox() self.time_scale_label.setEditable(False) self.time_scale_label.setInsertPolicy(QComboBox.NoInsert) self.time_scale_label.addItems(['Month', 'Year']) self.time_scale_label.setCurrentIndex(0) self.time_scale_label.currentIndexChanged.connect(self.layout_changed) self.dateDispFreq_spinBox = QSpinBox() self.dateDispFreq_spinBox.setSingleStep(1) self.dateDispFreq_spinBox.setMinimum(1) self.dateDispFreq_spinBox.setMaximum(100) self.dateDispFreq_spinBox.setValue( self.hydrograph.date_labels_pattern) self.dateDispFreq_spinBox.setAlignment(Qt.AlignCenter) self.dateDispFreq_spinBox.setKeyboardTracking(False) self.dateDispFreq_spinBox.valueChanged.connect(self.layout_changed) # Setting up the layout : widget_time_scale = QFrame() widget_time_scale.setFrameStyle(0) grid_time_scale = QGridLayout() GRID = [[QLabel('From :'), self.date_start_widget], [QLabel('To :'), self.date_end_widget], [QLabel('Scale :'), self.time_scale_label], [QLabel('Date Disp. Pattern:'), self.dateDispFreq_spinBox]] for i, ROW in enumerate(GRID): grid_time_scale.addLayout(QRowLayout(ROW), i, 1) grid_time_scale.setVerticalSpacing(5) grid_time_scale.setContentsMargins(10, 10, 10, 10) widget_time_scale.setLayout(grid_time_scale) # ----- Water level axis properties # Widget : self.waterlvl_scale = QDoubleSpinBox() self.waterlvl_scale.setSingleStep(0.05) self.waterlvl_scale.setMinimum(0.05) self.waterlvl_scale.setSuffix(' m') self.waterlvl_scale.setAlignment(Qt.AlignCenter) self.waterlvl_scale.setKeyboardTracking(False) self.waterlvl_scale.valueChanged.connect(self.layout_changed) self.waterlvl_scale.setFixedWidth(100) self.waterlvl_max = QDoubleSpinBox() self.waterlvl_max.setSingleStep(0.1) self.waterlvl_max.setSuffix(' m') self.waterlvl_max.setAlignment(Qt.AlignCenter) self.waterlvl_max.setMinimum(-1000) self.waterlvl_max.setMaximum(1000) self.waterlvl_max.setKeyboardTracking(False) self.waterlvl_max.valueChanged.connect(self.layout_changed) self.waterlvl_max.setFixedWidth(100) self.NZGridWL_spinBox = QSpinBox() self.NZGridWL_spinBox.setSingleStep(1) self.NZGridWL_spinBox.setMinimum(1) self.NZGridWL_spinBox.setMaximum(50) self.NZGridWL_spinBox.setValue(self.hydrograph.NZGrid) self.NZGridWL_spinBox.setAlignment(Qt.AlignCenter) self.NZGridWL_spinBox.setKeyboardTracking(False) self.NZGridWL_spinBox.valueChanged.connect(self.layout_changed) self.NZGridWL_spinBox.setFixedWidth(100) self.datum_widget = QComboBox() self.datum_widget.addItems(['Ground Surface', 'Sea Level']) self.datum_widget.currentIndexChanged.connect(self.layout_changed) # Layout : subgrid_WLScale = QGridLayout() GRID = [[QLabel('Minimum :'), self.waterlvl_max], [QLabel('Scale :'), self.waterlvl_scale], [QLabel('Grid Divisions :'), self.NZGridWL_spinBox], [QLabel('Datum :'), self.datum_widget]] for i, ROW in enumerate(GRID): subgrid_WLScale.addLayout(QRowLayout(ROW), i, 1) subgrid_WLScale.setVerticalSpacing(5) subgrid_WLScale.setContentsMargins(10, 10, 10, 10) # (L, T, R, B) WLScale_widget = QFrame() WLScale_widget.setFrameStyle(0) WLScale_widget.setLayout(subgrid_WLScale) # ---- Weather Axis # Widgets : self.Ptot_scale = QSpinBox() self.Ptot_scale.setSingleStep(5) self.Ptot_scale.setMinimum(5) self.Ptot_scale.setMaximum(500) self.Ptot_scale.setValue(20) self.Ptot_scale.setSuffix(' mm') self.Ptot_scale.setAlignment(Qt.AlignCenter) self.qweather_bin = QComboBox() self.qweather_bin.setEditable(False) self.qweather_bin.setInsertPolicy(QComboBox.NoInsert) self.qweather_bin.addItems(['day', 'week', 'month']) self.qweather_bin.setCurrentIndex(1) # Layout : layout = QGridLayout() GRID = [[QLabel('Precip. Scale :'), self.Ptot_scale], [QLabel('Resampling :'), self.qweather_bin]] for i, row in enumerate(GRID): layout.addLayout(QRowLayout(row), i, 1) layout.setVerticalSpacing(5) layout.setContentsMargins(10, 10, 10, 10) # (L,T,R,B) layout.setRowStretch(i+1, 100) widget_weather_scale = QFrame() widget_weather_scale.setFrameStyle(0) widget_weather_scale.setLayout(layout) # ---- ASSEMBLING TABS tabscales = QTabWidget() tabscales.addTab(widget_time_scale, 'Time') tabscales.addTab(WLScale_widget, 'Water Level') tabscales.addTab(widget_weather_scale, 'Weather') return tabscales @property def workdir(self): return self.dmngr.workdir # ---- Utilities def zoom_in(self): self.hydrograph_scrollarea.zoomIn() def zoom_out(self): self.hydrograph_scrollarea.zoomOut() def update_colors(self): self.hydrograph.update_colors() self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) # ---- Datasets Handlers @property def wldset(self): return self.dmngr.get_current_wldset() @property def wxdset(self): return self.dmngr.get_current_wxdset() def wldset_changed(self): """Handle when the water level dataset of the datamanager changes.""" if self.wldset is None: self.clear_hydrograph() return else: self.hydrograph.set_wldset(self.wldset) self.hydrograph.gluedf = self.wldset.get_glue_at(-1) # Load the manual measurements. fname = os.path.join( self.workdir, "Water Levels", 'waterlvl_manual_measurements') tmeas, wlmeas = load_waterlvl_measures(fname, self.wldset['Well']) self.wldset.set_wlmeas(tmeas, wlmeas) # Setup the layout of the hydrograph. layout = self.wldset.get_layout() if layout is not None: msg = ("Loading existing graph layout for well %s." % self.wldset['Well']) print(msg) self.ConsoleSignal.emit('<font color=black>%s</font>' % msg) self.load_graph_layout(layout) else: print('No graph layout exists for well %s.' % self.wldset['Well']) # Fit Water Level in Layout : self.__updateUI = False self.best_fit_waterlvl() self.best_fit_time() if self.dmngr.set_closest_wxdset() is None: self.draw_hydrograph() self.__updateUI = True def wxdset_changed(self): """Handle when the weather dataset of the datamanager changes.""" if self.wldset is None: self.clear_hydrograph() else: self.hydrograph.set_wxdset(self.wxdset) QCoreApplication.processEvents() self.draw_hydrograph() # ---- Draw Hydrograph Handlers def best_fit_waterlvl(self): wldset = self.dmngr.get_current_wldset() if wldset is not None: WLscale, WLmin = self.hydrograph.best_fit_waterlvl() self.waterlvl_scale.setValue(WLscale) self.waterlvl_max.setValue(WLmin) def best_fit_time(self): wldset = self.dmngr.get_current_wldset() if wldset is not None: date0, date1 = self.hydrograph.best_fit_time(wldset.xldates) self.date_start_widget.setDate(QDate(date0[0], date0[1], date0[2])) self.date_end_widget.setDate(QDate(date1[0], date1[1], date1[2])) @QSlot() def mrc_wl_changed(self): """ Force a redraw of the MRC water levels after the results have changed for the dataset. """ self.hydrograph.draw_mrc_wl() self.hydrograph.setup_legend() self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) @QSlot(GLUEDataFrameBase) def glue_wl_changed(self, gluedf): """ Force a redraw of the GLUE water levels after the results have changed for the dataset. """ self.hydrograph.set_gluedf(gluedf) self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) def layout_changed(self): """ When an element of the graph layout is changed in the UI. """ if self.__updateUI is False: return self.update_graph_layout_parameter() if self.hydrograph.isHydrographExists is False: return sender = self.sender() if sender == self.btn_language: self.hydrograph.draw_ylabels() self.hydrograph.setup_xticklabels() self.hydrograph.setup_legend() elif sender in [self.waterlvl_max, self.waterlvl_scale]: self.hydrograph.setup_waterlvl_scale() self.hydrograph.draw_ylabels() elif sender == self.NZGridWL_spinBox: self.hydrograph.setup_waterlvl_scale() self.hydrograph.update_precip_scale() self.hydrograph.draw_ylabels() elif sender == self.Ptot_scale: self.hydrograph.update_precip_scale() self.hydrograph.draw_ylabels() elif sender == self.datum_widget: yoffset = int(self.wldset['Elevation']/self.hydrograph.WLscale) yoffset *= self.hydrograph.WLscale self.hydrograph.WLmin = (yoffset - self.hydrograph.WLmin) self.waterlvl_max.blockSignals(True) self.waterlvl_max.setValue(self.hydrograph.WLmin) self.waterlvl_max.blockSignals(False) # This is calculated so that trailing zeros in the altitude of the # well is not carried to the y axis labels, so that they remain a # int multiple of *WLscale*. self.hydrograph.setup_waterlvl_scale() self.hydrograph.draw_waterlvl() self.hydrograph.draw_ylabels() elif sender in [self.date_start_widget, self.date_end_widget]: self.hydrograph.set_time_scale() self.hydrograph.draw_weather() self.hydrograph.draw_figure_title() elif sender == self.dateDispFreq_spinBox: self.hydrograph.set_time_scale() self.hydrograph.setup_xticklabels() elif sender == self.page_setup_win: self.hydrograph.update_fig_size() # Implicitly call : set_margins() # draw_ylabels() # set_time_scale() # draw_figure_title self.hydrograph.draw_waterlvl() self.hydrograph.setup_legend() elif sender == self.qweather_bin: self.hydrograph.resample_bin() self.hydrograph.draw_weather() self.hydrograph.draw_ylabels() elif sender == self.time_scale_label: self.hydrograph.set_time_scale() self.hydrograph.draw_weather() else: print('No action for this widget yet.') # !!!! temporary fix until I can find a better solution !!!! # sender.blockSignals(True) if type(sender) in [QDoubleSpinBox, QSpinBox]: sender.setReadOnly(True) for i in range(10): QCoreApplication.processEvents() self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) for i in range(10): QCoreApplication.processEvents() if type(sender) in [QDoubleSpinBox, QSpinBox]: sender.setReadOnly(False) # sender.blockSignals(False) def update_graph_layout_parameter(self): # language : self.hydrograph.language = self.btn_language.language # Scales : self.hydrograph.WLmin = self.waterlvl_max.value() self.hydrograph.WLscale = self.waterlvl_scale.value() self.hydrograph.RAINscale = self.Ptot_scale.value() self.hydrograph.NZGrid = self.NZGridWL_spinBox.value() # WL Datum : self.hydrograph.WLdatum = self.datum_widget.currentIndex() # Dates : self.hydrograph.datemode = self.time_scale_label.currentText() year = self.date_start_widget.date().year() month = self.date_start_widget.date().month() self.hydrograph.TIMEmin = xldate_from_date_tuple((year, month, 1), 0) year = self.date_end_widget.date().year() month = self.date_end_widget.date().month() self.hydrograph.TIMEmax = xldate_from_date_tuple((year, month, 1), 0) self.hydrograph.date_labels_pattern = self.dateDispFreq_spinBox.value() # Page Setup : self.hydrograph.fwidth = self.page_setup_win.pageSize[0] self.hydrograph.fheight = self.page_setup_win.pageSize[1] self.hydrograph.va_ratio = self.page_setup_win.va_ratio self.hydrograph.trend_line = self.page_setup_win.isTrendLine self.hydrograph.isLegend = self.page_setup_win.isLegend self.hydrograph.isGraphTitle = self.page_setup_win.isGraphTitle self.hydrograph.set_meteo_on(self.page_setup_win.is_meteo_on) self.hydrograph.set_glue_wl_on(self.page_setup_win.is_glue_wl_on) self.hydrograph.set_mrc_wl_on(self.page_setup_win.is_mrc_wl_on) self.hydrograph.set_figframe_lw(self.page_setup_win.figframe_lw) # Weather bins : self.hydrograph.bwidth_indx = self.qweather_bin.currentIndex() def clear_hydrograph(self): """Clear the hydrograph figure to show only a blank canvas.""" self.hydrograph.clf() self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) def draw_hydrograph(self): if self.dmngr.wldataset_count() == 0: msg = 'Please import a valid water level data file first.' self.ConsoleSignal.emit('<font color=red>%s</font>' % msg) self.emit_warning(msg) return self.update_graph_layout_parameter() # Generate and Display Graph : for i in range(5): QCoreApplication.processEvents() QApplication.setOverrideCursor(Qt.WaitCursor) self.hydrograph.set_wldset(self.dmngr.get_current_wldset()) self.hydrograph.set_wxdset(self.dmngr.get_current_wxdset()) self.hydrograph.generate_hydrograph() self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph) QApplication.restoreOverrideCursor() def select_save_path(self): """ Open a dialog where the user can select a file name to save the hydrograph. """ if self.wldset is None: return ffmat = "*.pdf;;*.svg;;*.png" fname = find_unique_filename(osp.join( self.save_fig_dir, 'hydrograph_%s.pdf' % self.wldset['Well'])) fname, ftype = QFileDialog.getSaveFileName( self, "Save Figure", fname, ffmat) if fname: ftype = ftype.replace('*', '') fname = fname if fname.endswith(ftype) else fname + ftype self.save_fig_dir = os.path.dirname(fname) try: self.save_figure(fname) except PermissionError: msg = "The file is in use by another application or user." QMessageBox.warning(self, 'Warning', msg, QMessageBox.Ok) self.select_save_path() def save_figure(self, fname): """Save the hydrograph figure in a file.""" self.hydrograph.generate_hydrograph() self.hydrograph.savefig(fname) # ---- Graph Layout Handlers def load_layout_isClicked(self): """Handle when the button to load the graph layout is clicked.""" if self.wldset is None: self.emit_warning( "Please import a valid water level data file first.") return layout = self.wldset.get_layout() if layout is None: self.emit_warning( "No graph layout exists for well %s." % self.wldset['Well']) else: self.load_graph_layout(layout) def load_graph_layout(self, layout): """Load the graph layout into the GUI.""" self.__updateUI = False # Scales : date = layout['TIMEmin'] date = xldate_as_tuple(date, 0) self.date_start_widget.setDate(QDate(date[0], date[1], date[2])) date = layout['TIMEmax'] date = xldate_as_tuple(date, 0) self.date_end_widget.setDate(QDate(date[0], date[1], date[2])) self.dateDispFreq_spinBox.setValue(layout['date_labels_pattern']) self.waterlvl_scale.setValue(layout['WLscale']) self.waterlvl_max.setValue(layout['WLmin']) self.NZGridWL_spinBox.setValue(layout['NZGrid']) self.Ptot_scale.setValue(layout['RAINscale']) x = ['mbgs', 'masl'].index(layout['WLdatum']) self.datum_widget.setCurrentIndex(x) self.qweather_bin.setCurrentIndex(layout['bwidth_indx']) self.time_scale_label.setCurrentIndex( self.time_scale_label.findText(layout['datemode'])) # ---- Language and colors self.btn_language.set_language(layout['language']) self.color_palette_win.load_colors() # ---- Page Setup self.page_setup_win.pageSize = (layout['fwidth'], layout['fheight']) self.page_setup_win.va_ratio = layout['va_ratio'] self.page_setup_win.isLegend = layout['legend_on'] self.page_setup_win.isGraphTitle = layout['title_on'] self.page_setup_win.isTrendLine = layout['trend_line'] self.page_setup_win.is_meteo_on = layout['meteo_on'] self.page_setup_win.is_glue_wl_on = layout['glue_wl_on'] self.page_setup_win.is_mrc_wl_on = layout['mrc_wl_on'] self.page_setup_win.figframe_lw = layout['figframe_lw'] self.page_setup_win.legend_on.set_value(layout['legend_on']) self.page_setup_win.title_on.set_value(layout['title_on']) self.page_setup_win.wltrend_on.set_value(layout['trend_line']) self.page_setup_win.meteo_on.set_value(layout['meteo_on']) self.page_setup_win.glue_wl_on.set_value(layout['glue_wl_on']) self.page_setup_win.mrc_wl_on.set_value(layout['mrc_wl_on']) self.page_setup_win.fframe_lw_widg.setValue(layout['figframe_lw']) self.page_setup_win.fwidth.setValue(layout['fwidth']) self.page_setup_win.fheight.setValue(layout['fheight']) self.page_setup_win.va_ratio_spinBox.setValue(layout['va_ratio']) # Check if Weather Dataset : if layout['wxdset'] in self.dmngr.wxdsets: self.dmngr.set_current_wxdset(layout['wxdset']) else: if self.dmngr.set_closest_wxdset() is None: self.draw_hydrograph() self.__updateUI = True def save_layout_isClicked(self): wldset = self.wldset if wldset is None: self.emit_warning( "Please import a valid water level data file first.") return layout = wldset.get_layout() if layout is not None: msg = ('A graph layout already exists for well %s.Do you want to' ' you want to replace it?') % wldset['Well'] reply = QMessageBox.question(self, 'Save Graph Layout', msg, QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.save_graph_layout() elif reply == QMessageBox.No: msg = "Graph layout not saved for well %s." % wldset['Well'] self.ConsoleSignal.emit('<font color=black>%s' % msg) else: self.save_graph_layout() def save_graph_layout(self): """Save the graph layout in the project hdf5 file.""" print("Saving the graph layout for well %s..." % self.wldset['Well'], end=" ") layout = {'WLmin': self.waterlvl_max.value(), 'WLscale': self.waterlvl_scale.value(), 'RAINscale': self.Ptot_scale.value(), 'fwidth': self.page_setup_win.pageSize[0], 'fheight': self.page_setup_win.pageSize[1], 'va_ratio': self.page_setup_win.va_ratio, 'NZGrid': self.NZGridWL_spinBox.value(), 'bwidth_indx': self.qweather_bin.currentIndex(), 'date_labels_pattern': self.dateDispFreq_spinBox.value(), 'datemode': self.time_scale_label.currentText()} layout['wxdset'] = None if self.wxdset is None else self.wxdset.name year = self.date_start_widget.date().year() month = self.date_start_widget.date().month() layout['TIMEmin'] = xldate_from_date_tuple((year, month, 1), 0) year = self.date_end_widget.date().year() month = self.date_end_widget.date().month() layout['TIMEmax'] = xldate_from_date_tuple((year, month, 1), 0) if self.datum_widget.currentIndex() == 0: layout['WLdatum'] = 'mbgs' else: layout['WLdatum'] = 'masl' # ---- Page Setup layout['title_on'] = bool(self.page_setup_win.isGraphTitle) layout['legend_on'] = bool(self.page_setup_win.isLegend) layout['language'] = self.btn_language.language layout['trend_line'] = bool(self.page_setup_win.isTrendLine) layout['meteo_on'] = bool(self.page_setup_win.is_meteo_on) layout['glue_wl_on'] = bool(self.page_setup_win.is_glue_wl_on) layout['mrc_wl_on'] = bool(self.page_setup_win.is_mrc_wl_on) layout['figframe_lw'] = self.page_setup_win.figframe_lw # Save the colors : cdb = ColorsReader() cdb.load_colors_db() layout['colors'] = cdb.RGB # Save the layout : self.wldset.save_layout(layout) msg = 'Layout saved successfully for well %s.' % self.wldset['Well'] self.ConsoleSignal.emit('<font color=black>%s</font>' % msg) print("done")
def createEditor(self, parent, option, index): editor = QDateEdit(parent) editor.setDisplayFormat('dd.MM.yyyy') editor.date().toString('dd.MM.yyyy') editor.setCalendarPopup(True) return editor
class NetHoldingWindow(AncestorWindow): def __init__(self, *args, **kwargs): super(NetHoldingWindow, self).__init__(*args, **kwargs) # """连接数据库""" # self.__connect_database() """绑定公用属性""" self.products = None self.db_worker = None # 线程是否结束标志位 self.exchange_lib_thread_over = True self.selected_variety_thread_over = True self.selected_contract_thread_over = True """总布局""" vertical_layout = QVBoxLayout() # 总纵向布局 top_select = QHBoxLayout() # 顶部横向条件选择栏 """交易所选择下拉框""" exchange_label = QLabel('选择交易所:') self.exchange_lib = QComboBox() self.exchange_lib.activated[str].connect( self.exchange_lib_selected) # 选择交易所调用的方法 """品种选择下拉框""" variety_label = QLabel('选择品种:') self.variety_lib = QComboBox() self.variety_lib.setMinimumSize(80, 20) self.variety_lib.activated[str].connect( self.variety_lib_selected) # 选择品种所调用的方法 """合约代码下拉框""" contract_label = QLabel('合约代码:') self.contract_lib = QComboBox() self.contract_lib.activated[str].connect(self.contract_lib_selected) """时间选择""" begin_time_label = QLabel("起始日期:") self.begin_time = QDateEdit( QDate.currentDate().addDays(-2)) # 参数为设置的日期 self.begin_time.setDisplayFormat('yyyy-MM-dd') # 时间显示格式 self.begin_time.setCalendarPopup(True) # 使用日历选择时间 end_time_label = QLabel("终止日期:") self.end_time = QDateEdit(QDate.currentDate().addDays(-1)) # 参数为设置的日期 self.end_time.setDisplayFormat('yyyy-MM-dd') # 时间显示格式 self.end_time.setCalendarPopup(True) # 使用日历选择时间 """确定按钮""" self.confirm_button = QPushButton("确定") self.confirm_button.clicked.connect(self.confirm) """各控件加入水平布局""" top_select.addWidget(exchange_label, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.exchange_lib, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(variety_label, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.variety_lib, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(contract_label, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.contract_lib, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(begin_time_label, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.begin_time, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(end_time_label, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.end_time, alignment=Qt.AlignTop | Qt.AlignLeft) top_select.addWidget(self.confirm_button, alignment=Qt.AlignTop | Qt.AlignLeft) """自定义部件""" # 画布 self.map_widget = MapWidget() # 表格,参数为列数 self.table_widget = TableWidget(7) # 设置列名称 self.table_widget.set_style( header_labels=['日期', '价格', '总持仓', '净多', '净空', '净持仓', '净持率']) """创建QSplitter""" show_splitter = QSplitter() show_splitter.setOrientation(Qt.Vertical) # 垂直拉伸 # 加入自定义控件 show_splitter.addWidget(self.map_widget) show_splitter.addWidget(self.table_widget) """垂直布局添加布局和部件""" # vertical_layout.addLayout(self.title_hlayout) # 窗口标题水平布局 vertical_layout.addLayout(top_select) vertical_layout.addWidget(show_splitter) """设置总布局""" self.setLayout(vertical_layout) # 总布局,只能设置一次 def fill_init_data(self): """填充交易所""" self.exchange_lib.clear() for lib in RESEARCH_LIB: self.exchange_lib.addItem(lib) # 获取当前交易所的品种并填充 self.exchange_lib_selected() def fill_contract(self, data): """根据品种选择线程执行返回的信号信息填充相应的品种""" data.reverse() # print("信号槽函数返回的信息", data) self.contract_lib.clear() for contract in data: self.contract_lib.addItem(contract) # 当合约填充完毕就解开交易所的选择锁 self.exchange_lib_thread_over = True self.selected_variety_thread_over = True # 确定合约,显示时间 self.contract_lib_selected() def change_time_interval(self, data): """根据合约选择线程返回的信号改变当前显示的时间""" current_date = datetime.datetime.today() min_time, max_time = data[0], data[1] if not min_time or not max_time: return # 转为时间类 min_date = datetime.datetime.strptime(min_time, '%Y%m%d') max_date = datetime.datetime.strptime(max_time, '%Y%m%d') # 计算天数间隔 big_time_interval = -(current_date - min_date).days # 要往前取数,调整为负值 small_time_interval = -(current_date - max_date).days # 设置时间 self.begin_time.setDate(QDate.currentDate().addDays(big_time_interval)) self.end_time.setDate(QDate.currentDate().addDays(small_time_interval)) # 设置完时间就所有锁打开 self.exchange_lib_thread_over = True self.selected_variety_thread_over = True self.selected_contract_thread_over = True # 设置完时间就设置确认按钮可用 self.confirm_button.setEnabled(True) def exchange_lib_selected(self): """ 选择交易所调用的方法,线程执行: 查询品种并填充 """ # 设置确认按钮不可用 self.confirm_button.setEnabled(False) if not self.exchange_lib_thread_over: QMessageBox.warning(self, "错误", "您手速太快啦...", QMessageBox.Ok) return lib = self.exchange_lib.currentText() self.variety_lib.clear() if lib == "中国金融期货交易所": key = 'cffex' self.db_worker = self.cffex_getter self.products = CFFEX_PRODUCT_NAMES elif lib == "上海期货交易所": key = 'shfe' self.db_worker = self.shfe_getter self.products = SHFE_PRODUCT_NAMES elif lib == "大连商品交易所": key = 'dce' self.db_worker = self.dce_getter self.products = DCE_PRODUCT_NAMES elif lib == "郑州商品交易所": key = 'czce' self.db_worker = self.czce_getter self.products = CZCE_PRODUCT_NAMES else: self.db_worker = None self.products = None QMessageBox.information(self, '错误', "没有此项交易所..", QMessageBox.Ok) return for variety in GOODS_LIB.get(key): self.variety_lib.addItem(variety) self.exchange_lib_thread_over = False # 选择完交易所就锁上不能再选择 # 交易所确定,确定品种 self.variety_lib_selected() def get_variety_en(self, variety): """获取相应的品种英文代号""" # 获取品种英文名称 if not self.products: # print("没有品种库:", self.products) QMessageBox.warning(self, "错误", "内部发生未知错误") return '' return self.products.get(variety) def variety_lib_selected(self): """ 选择品种所调用的方法,线程执行: 查询合约并填充合约 """ # 设置确认按钮不可用 self.confirm_button.setEnabled(False) if not self.selected_variety_thread_over: QMessageBox.warning(self, "错误", "您手速太快啦...", QMessageBox.Ok) return variety = self.variety_lib.currentText() en_variety = self.get_variety_en(variety) # !!!(务必将线程绑在self对象上,否则无法实现多线程) self.selected_variety_thread_over = False if self.db_worker: self.variety_selected_thread = VarietySelectedThread( db_worker=self.db_worker, variety=en_variety) self.variety_selected_thread.result_signal.connect( self.fill_contract) self.variety_selected_thread.start() def contract_lib_selected(self): """ 选择了合约代码,查询本合约代码的时间周期,线程执行: 信号返回结果,设置时间 """ # 设置确认按钮不可用 self.confirm_button.setEnabled(False) if not self.selected_contract_thread_over: QMessageBox.warning(self, "错误", "您手速太快啦...", QMessageBox.Ok) return variety = self.variety_lib.currentText() variety_en = self.get_variety_en(variety) contract = self.contract_lib.currentText() # print("选择了合约代码:\n当前交易所:{}\n当前品种:{}\n当前合约:{}".format(lib, variety_en, contract)) if self.db_worker: self.contract_selected_thread = ContractSelectedThread( db_worker=self.db_worker, variety=variety_en, contract=contract) self.contract_selected_thread.result_signal.connect( self.change_time_interval) self.contract_selected_thread.start() def confirm(self): """ 条件选择好了确认查询相应的数据进行图表生成,线程执行: 信号返回结果设置状态 """ # 定时器计时 self.timer.start(1000) # 设置确认按钮不可用,提示消息改为提交成功 self.confirm_button.setText("提交成功") self.confirm_button.setEnabled(False) # 清除原图表并设置样式 self.map_widget.delete() self.table_widget.clear() self.table_widget.set_style( header_labels=['日期', '价格', '总持仓', '净多', '净空', '净持仓', '净持率']) # 获取各数据 lib = self.exchange_lib.currentText() variety = self.variety_lib.currentText() variety_en = self.get_variety_en(variety) contract = self.contract_lib.currentText() begin_time = re.sub('-', '', str(self.begin_time.date().toPyDate())) end_time = re.sub('-', '', str(self.end_time.date().toPyDate())) # print("确认提交:\n当前交易所:{}\n当前品种:{}:{}\n当前合约:{}\n起始时间:{}\n终止时间:{}".format(lib, variety, variety_en, contract, begin_time, end_time)) # 转成可计算的datetime.datetime时间对象 begin_time_datetime_cls = datetime.datetime.strptime( begin_time, "%Y%m%d") end_time_datetime_cls = datetime.datetime.strptime(end_time, "%Y%m%d") # 起止时间应早于终止时间 if begin_time_datetime_cls >= end_time_datetime_cls: QMessageBox.warning(self, "错误", "起止时间应早于终止时间", QMessageBox.Ok) self.message_signal.emit("时间选择有误!") self.timer.stop() self.cost_time = 0 # 设置确认按钮可用 self.confirm_button.setText("确定") self.confirm_button.setEnabled(True) return # 生成时间可迭代对象 iterator = GenerateTime(begin=begin_time_datetime_cls, end=end_time_datetime_cls) # 线程执行查询目标数据 self.confirm_thread = ConfirmQueryThread(db_worker=self.db_worker, variety=variety_en, contract=contract, iterator=iterator, lib=lib) self.confirm_thread.result_signal.connect(self.draw_map_table) self.confirm_thread.process_signal.connect(self.show_process) self.confirm_thread.start() def show_process(self, data): self.process_signal.emit(data) def draw_map_table(self, data): prices = data.get("prices") rates = data.get("rates") times = data.get("times") items = data.get("items") message = data.get("message") # 设置样式 self.map_widget.set_style() # 重新设置标题 self.map_widget.axes1.set_title(message + "合约价格-净持率趋势图") # 进行画图 self.map_widget.net_holding_map(y_left=prices, y_right=rates, x=times) # 填充表格 self.table_widget.net_holding_table(items) # 结果执行完程序 self.message_signal.emit("处理完毕,生成图表成功!") self.timer.stop() self.cost_time = 0 # 设置确认按钮可用 self.confirm_button.setText("确定") self.confirm_button.setEnabled(True)
class InputDialog(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setWindowTitle('Ввод данных') self.typePageLabel = QLabel("Тип листка:") self.typePageLineEdit = UpperSimplifiedLineEdit() self.surnameLabel = QLabel("Фамилия:") self.surnameLineEdit = UpperSimplifiedLineEdit() self.nameLabel = QLabel("Имя:") self.nameLineEdit = UpperSimplifiedLineEdit() self.patronymicLabel = QLabel("Отчество:") self.patronymicLineEdit = UpperSimplifiedLineEdit() self.personLayout = QGridLayout() self.personLayout.addWidget(self.typePageLabel, 0, 0) self.personLayout.addWidget(self.typePageLineEdit, 0, 1) self.personLayout.addWidget(self.surnameLabel, 0, 2) self.personLayout.addWidget(self.surnameLineEdit, 0, 3) self.personLayout.addWidget(self.nameLabel, 0, 4) self.personLayout.addWidget(self.nameLineEdit, 0, 5) self.personLayout.addWidget(self.patronymicLabel, 0, 6) self.personLayout.addWidget(self.patronymicLineEdit, 0, 7) self.personGroupBox = QGroupBox("ФИО") self.personGroupBox.setLayout(self.personLayout) self.birthdayLabel = QLabel("Дата:") self.birthdayLineEdit = UpperSimplifiedLineEdit() self.countryLabel = QLabel("Государство:") self.countryLineEdit = UpperSimplifiedLineEdit() self.regionLabel = QLabel("Область:") self.regionLineEdit = UpperSimplifiedLineEdit() self.cityLabel = QLabel("Город:") self.cityLineEdit = UpperSimplifiedLineEdit() self.districtLabel = QLabel("Район:") self.districtLineEdit = UpperSimplifiedLineEdit() self.localityLabel = QLabel("Населенный пункт:") self.localityLineEdit = UpperSimplifiedLineEdit() self.birthLayout = QGridLayout() self.birthLayout.addWidget(self.birthdayLabel, 0, 0) self.birthLayout.addWidget(self.birthdayLineEdit, 0, 1) self.birthLayout.addWidget(self.countryLabel, 1, 0) self.birthLayout.addWidget(self.countryLineEdit, 1, 1) self.birthLayout.addWidget(self.regionLabel, 1, 2) self.birthLayout.addWidget(self.regionLineEdit, 1, 3) self.birthLayout.addWidget(self.cityLabel, 1, 4) self.birthLayout.addWidget(self.cityLineEdit, 1, 5) self.birthLayout.addWidget(self.districtLabel, 4, 0) self.birthLayout.addWidget(self.districtLineEdit, 4, 1) self.birthLayout.addWidget(self.localityLabel, 4, 2) self.birthLayout.addWidget(self.localityLineEdit, 4, 3) self.birthGroupBox = QGroupBox("Рождение") self.birthGroupBox.setLayout(self.birthLayout) self.countryResidenceLabel = QLabel("Государство:") self.countryResidenceLineEdit = UpperSimplifiedLineEdit() self.regionResidenceLabel = QLabel("Область:") self.regionResidenceLineEdit = UpperSimplifiedLineEdit() self.cityResidenceLabel = QLabel("Город:") self.cityResidenceLineEdit = UpperSimplifiedLineEdit() self.districtResidenceLabel = QLabel("Район:") self.districtResidenceLineEdit = UpperSimplifiedLineEdit() self.localityResidenceLabel = QLabel("Населенный пункт:") self.localityResidenceLineEdit = UpperSimplifiedLineEdit() self.streetResidenceLabel = QLabel("Улица:") self.streetResidenceLineEdit = UpperSimplifiedLineEdit() self.houseResidenceLabel = QLabel("Дом:") self.houseResidenceLineEdit = UpperSimplifiedLineEdit() self.caseResidenceLabel = QLabel("Корпус:") self.caseResidenceLineEdit = UpperSimplifiedLineEdit() self.apartmentResidenceLabel = QLabel("Квартира:") self.apartmentResidenceLineEdit = UpperSimplifiedLineEdit() self.residenceLayout = QGridLayout() self.residenceLayout.addWidget(self.countryResidenceLabel, 0, 0) self.residenceLayout.addWidget(self.countryResidenceLineEdit, 0, 1) self.residenceLayout.addWidget(self.regionResidenceLabel, 0, 2) self.residenceLayout.addWidget(self.regionResidenceLineEdit, 0, 3) self.residenceLayout.addWidget(self.cityResidenceLabel, 0, 4) self.residenceLayout.addWidget(self.cityResidenceLineEdit, 0, 5) self.residenceLayout.addWidget(self.districtResidenceLabel, 1, 0) self.residenceLayout.addWidget(self.districtResidenceLineEdit, 1, 1) self.residenceLayout.addWidget(self.localityResidenceLabel, 1, 2) self.residenceLayout.addWidget(self.localityResidenceLineEdit, 1, 3) self.residenceLayout.addWidget(self.streetResidenceLabel, 2, 0) self.residenceLayout.addWidget(self.streetResidenceLineEdit, 2, 1) self.residenceLayout.addWidget(self.houseResidenceLabel, 2, 2) self.residenceLayout.addWidget(self.houseResidenceLineEdit, 2, 3) self.residenceLayout.addWidget(self.caseResidenceLabel, 2, 4) self.residenceLayout.addWidget(self.caseResidenceLineEdit, 2, 5) self.residenceLayout.addWidget(self.apartmentResidenceLabel, 2, 6) self.residenceLayout.addWidget(self.apartmentResidenceLineEdit, 2, 7) self.residenceGroupBox = QGroupBox("Адрес места жительства") self.residenceGroupBox.setLayout(self.residenceLayout) self.documentLabel = QLabel("Вид документа:") self.documentLineEdit = UpperSimplifiedLineEdit() self.seriesLabel = QLabel("Серия:") self.seriesLineEdit = UpperSimplifiedLineEdit() self.numberLabel = QLabel("Номер:") self.numberLineEdit = UpperSimplifiedLineEdit() self.dateIssueLabel = QLabel("Дата выдачи:") self.dateIssueLineEdit = UpperSimplifiedLineEdit() self.issuedLabel = QLabel("Кем выдан:") self.issuedLineEdit = UpperSimplifiedLineEdit() self.documentLayout = QGridLayout() self.documentLayout.addWidget(self.documentLabel, 0, 0) self.documentLayout.addWidget(self.documentLineEdit, 0, 1) self.documentLayout.addWidget(self.seriesLabel, 1, 0) self.documentLayout.addWidget(self.seriesLineEdit, 1, 1) self.documentLayout.addWidget(self.numberLabel, 1, 2) self.documentLayout.addWidget(self.numberLineEdit, 1, 3) self.documentLayout.addWidget(self.dateIssueLabel, 1, 4) self.documentLayout.addWidget(self.dateIssueLineEdit, 1, 5) self.documentLayout.addWidget(self.issuedLabel, 2, 0) self.documentLayout.addWidget(self.issuedLineEdit, 2, 1, 1, 5) self.documentGroupBox = QGroupBox("Документ") self.documentGroupBox.setLayout(self.documentLayout) self.dateArrivalLabel = QLabel("Дата прибытия:") self.dateArrivalDateEdit = QDateEdit() self.dateRetirementLabel = QLabel("Дата выбытия:") self.dateRetirementDateEdit = QDateEdit() self.nameHotelLabel = QLabel("Название гостиницы:") self.nameHotelLineEdit = UpperSimplifiedLineEdit() self.structureLabel = QLabel("Строение/Корпус/Сектор:") self.structureLineEdit = UpperSimplifiedLineEdit() self.roomLabel = QLabel("Комната:") self.roomLineEdit = UpperSimplifiedLineEdit() self.checkLayout = QGridLayout() self.checkLayout.addWidget(self.dateArrivalLabel, 0, 0) self.checkLayout.addWidget(self.dateArrivalDateEdit, 0, 1) self.checkLayout.addWidget(self.dateRetirementLabel, 0, 2) self.checkLayout.addWidget(self.dateRetirementDateEdit, 0, 3) self.checkLayout.addWidget(self.nameHotelLabel, 2, 0) self.checkLayout.addWidget(self.nameHotelLineEdit, 2, 1) self.checkLayout.addWidget(self.structureLabel, 2, 2) self.checkLayout.addWidget(self.structureLineEdit, 2, 3) self.checkLayout.addWidget(self.roomLabel, 2, 4) self.checkLayout.addWidget(self.roomLineEdit, 2, 5) self.checkGroupBox = QGroupBox("Регистрация") self.checkGroupBox.setLayout(self.checkLayout) self.saveButton = QPushButton("Сохранить") self.cancelButton = QPushButton("Отмена") self.bottomLayout = QHBoxLayout() self.bottomLayout.addStretch() self.bottomLayout.addWidget(self.saveButton) self.bottomLayout.addWidget(self.cancelButton) self.layout = QVBoxLayout() self.layout.addWidget(self.personGroupBox) self.layout.addWidget(self.birthGroupBox) self.layout.addWidget(self.residenceGroupBox) self.layout.addWidget(self.documentGroupBox) self.layout.addWidget(self.checkGroupBox) self.layout.addLayout(self.bottomLayout) self.setLayout(self.layout) self.saveButton.clicked.connect(self.save_data) self.cancelButton.clicked.connect(self.cancel) self.connect = self.connect_database() def connect_database(self): return sqlite3.connect('fms.db') def save_data(self): cursor = self.connect.cursor() cursor.execute( "INSERT INTO profile(type_page, surname, name, patronymic, birthday, country, region, city, district, " "locality, country_residence, region_residence, city_residence, district_residence, locality_residence, " "street_residence, house_residence, case_residence, apartment_residence, document, series, number, date_issue, " "issued, date_arrival_str, date_retirement_str, name_hotel, structure, room, date_arrival, date_retirement) " "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (self.typePageLineEdit.text(), self.surnameLineEdit.text(), self.nameLineEdit.text(), self.patronymicLineEdit.text(), self.birthdayLineEdit.text(), self.countryLineEdit.text(), self.regionLineEdit.text(), self.cityLineEdit.text(), self.districtLineEdit.text(), self.localityLineEdit.text(), self.countryResidenceLineEdit.text(), self.regionResidenceLineEdit.text(), self.cityResidenceLineEdit.text(), self.districtResidenceLineEdit.text(), self.localityResidenceLineEdit.text(), self.streetResidenceLineEdit.text(), self.houseResidenceLineEdit.text(), self.caseResidenceLineEdit.text(), self.apartmentResidenceLineEdit.text(), self.documentLineEdit.text(), self.seriesLineEdit.text(), self.numberLineEdit.text(), self.dateIssueLineEdit.text(), self.issuedLineEdit.text(), self.dateArrivalDateEdit.date().toString("dd.MM.yyyy"), self.dateRetirementDateEdit.date().toString("dd.MM.yyyy"), self.nameHotelLineEdit.text(), self.structureLineEdit.text(), self.roomLineEdit.text(), self.dateArrivalDateEdit.date().toString("yyyy-MM-dd"), self.dateRetirementDateEdit.date().toString("yyyy-MM-dd"))) self.connect.commit() cursor.close() self.connect.close() self.accept() def cancel(self): self.connect.close() self.reject()
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = MySortFilterProxyModel(self) self.proxyModel.setDynamicSortFilter(True) self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) sourceGroupBox = QGroupBox("Original Model") sourceGroupBox.setLayout(sourceLayout) self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterCaseSensitivityCheckBox.setChecked(True) self.filterPatternLineEdit = QLineEdit() self.filterPatternLineEdit.setText("Grace|Sports") filterPatternLabel = QLabel("&Filter pattern:") filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp) self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard) self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString) self.fromDateEdit = QDateEdit() self.fromDateEdit.setDate(QDate(2006, 12, 22)) self.fromDateEdit.setCalendarPopup(True) fromLabel = QLabel("F&rom:") fromLabel.setBuddy(self.fromDateEdit) self.toDateEdit = QDateEdit() self.toDateEdit.setDate(QDate(2007, 1, 5)) self.toDateEdit.setCalendarPopup(True) toLabel = QLabel("&To:") toLabel.setBuddy(self.toDateEdit) self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged) self.filterSyntaxComboBox.currentIndexChanged.connect(self.textFilterChanged) self.filterCaseSensitivityCheckBox.toggled.connect(self.textFilterChanged) self.fromDateEdit.dateChanged.connect(self.dateFilterChanged) self.toDateEdit.dateChanged.connect(self.dateFilterChanged) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.proxyView.sortByColumn(1, Qt.AscendingOrder) self.textFilterChanged() self.dateFilterChanged() proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1) proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3) proxyLayout.addWidget(fromLabel, 3, 0) proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2) proxyLayout.addWidget(toLabel, 4, 0) proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2) proxyGroupBox = QGroupBox("Sorted/Filtered Model") proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(sourceGroupBox) mainLayout.addWidget(proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Custom Sort/Filter Model") self.resize(500, 450) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def textFilterChanged(self): syntax = QRegExp.PatternSyntax( self.filterSyntaxComboBox.itemData( self.filterSyntaxComboBox.currentIndex())) caseSensitivity = ( self.filterCaseSensitivityCheckBox.isChecked() and Qt.CaseSensitive or Qt.CaseInsensitive) regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax) self.proxyModel.setFilterRegExp(regExp) def dateFilterChanged(self): self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date()) self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
class SeriesDialog(QDialog): "A window for adding/modifying series" series_queue = queue.Queue() SERIES = pyqtSignal(list) SERIES_EDIT = pyqtSignal(list, int) #series_list = [] # might want to extend this to allow mass series adding def _init__(self, parent=None): super().__init__() #TODO: Implement a way to mass add series' #IDEA: Extend dialog in a ScrollArea with more forms... def initUI(self): main_layout = QVBoxLayout() f_web = QGroupBox("From the Web") f_web.setCheckable(False) main_layout.addWidget(f_web) web_layout = QHBoxLayout() f_web.setLayout(web_layout) f_local = QGroupBox("From local folder") f_local.setCheckable(False) main_layout.addWidget(f_local) local_layout = QHBoxLayout() f_local.setLayout(local_layout) f_series = QGroupBox("Series Info") f_series.setCheckable(False) main_layout.addWidget(f_series) series_layout = QFormLayout() f_series.setLayout(series_layout) def basic_web(name): return QLabel(name), QLineEdit(), QPushButton("Fetch") exh_lbl, exh_edit, exh_btn = basic_web("ExHentai:") web_layout.addWidget(exh_lbl, 0, Qt.AlignLeft) web_layout.addWidget(exh_edit, 0) web_layout.addWidget(exh_btn, 0, Qt.AlignRight) choose_folder = QPushButton("Choose Folder") choose_folder.clicked.connect(self.choose_dir) local_layout.addWidget(choose_folder, Qt.AlignLeft) self.title_edit = QLineEdit() self.title_edit.setFocus() self.author_edit = QLineEdit() self.descr_edit = QTextEdit() self.descr_edit.setFixedHeight(70) self.descr_edit.setPlaceholderText("HTML 4 tags are supported") self.lang_box = QComboBox() self.lang_box.addItems(["English", "Japanese", "Other"]) self.lang_box.setCurrentIndex(0) self.tags_edit = QLineEdit() self.tags_edit.setPlaceholderText("namespace1:tag1, tag2, namespace3:tag3, etc..") self.type_box = QComboBox() self.type_box.addItems(["Manga", "Doujinshi", "Other"]) self.type_box.setCurrentIndex(0) #self.type_box.currentIndexChanged[int].connect(self.doujin_show) #self.doujin_parent = QLineEdit() #self.doujin_parent.setVisible(False) self.status_box = QComboBox() self.status_box.addItems(["Unknown", "Ongoing", "Completed"]) self.status_box.setCurrentIndex(0) self.pub_edit = QDateEdit() self.pub_edit.setCalendarPopup(True) self.pub_edit.setDate(QDate.currentDate()) self.path_lbl = QLabel("unspecified...") self.path_lbl.setWordWrap(True) series_layout.addRow("Title:", self.title_edit) series_layout.addRow("Author:", self.author_edit) series_layout.addRow("Description:", self.descr_edit) series_layout.addRow("Language:", self.lang_box) series_layout.addRow("Tags:", self.tags_edit) series_layout.addRow("Type:", self.type_box) series_layout.addRow("Publication Date:", self.pub_edit) series_layout.addRow("Path:", self.path_lbl) final_buttons = QHBoxLayout() final_buttons.setAlignment(Qt.AlignRight) main_layout.addLayout(final_buttons) done = QPushButton("Done") done.setDefault(True) done.clicked.connect(self.accept) cancel = QPushButton("Cancel") cancel.clicked.connect(self.reject) final_buttons.addWidget(cancel) final_buttons.addWidget(done) self.setLayout(main_layout) # TODO: complete this... maybe another time.. #def doujin_show(self, index): # if index is 1: # self.doujin_parent.setVisible(True) # else: # self.doujin_parent.setVisible(False) def choose_dir(self): dir_name = QFileDialog.getExistingDirectory(self, 'Choose a folder') head, tail = os.path.split(dir_name) self.title_edit.setText(tail) self.path_lbl.setText(dir_name) def check(self): if len(self.title_edit.text()) is 0: self.title_edit.setFocus() self.title_edit.setStyleSheet("border-style:outset;border-width:2px;border-color:red;") return False elif len(self.author_edit.text()) is 0: self.author_edit.setText("Anon") if len(self.descr_edit.toPlainText()) is 0: self.descr_edit.setText("<i>No description..</i>") #if self.path_lbl.text() == "unspecified...": # return False return True def accept(self): from ..database import seriesdb def do_chapters(series): thread = threading.Thread(target=self.set_chapters, args=(series,)) thread.start() thread.join() return self.series_queue.get() if self.check(): new_series = seriesdb.Series() new_series.title = self.title_edit.text() new_series.artist = self.author_edit.text() new_series.path = self.path_lbl.text() new_series.info = self.descr_edit.toPlainText() new_series.type = self.type_box.currentText() new_series.language = self.lang_box.currentText() new_series.status = self.status_box.currentText() qpub_d = self.pub_edit.date().toString("ddMMyyyy") dpub_d = datetime.strptime(qpub_d, "%d%m%Y").date() new_series.pub_date = dpub_d updated_series = do_chapters(new_series) #for ser in self.series: self.SERIES.emit([updated_series]) super().accept() def set_chapters(self, series_object): path = series_object.path con = os.listdir(path) # list all folders in series dir chapters = sorted([os.path.join(path,sub) for sub in con if os.path.isdir(os.path.join(path, sub))]) #subfolders # if series has chapters divided into sub folders if len(chapters) != 0: for numb, ch in enumerate(chapters): chap_path = os.path.join(path, ch) series_object.chapters[numb] = chap_path else: #else assume that all images are in series folder series_object.chapters[0] = path #find last edited file times = set() for root, dirs, files in os.walk(path, topdown=False): for img in files: fp = os.path.join(root, img) times.add( os.path.getmtime(fp) ) series_object.last_update = time.asctime(time.gmtime(max(times))) self.series_queue.put(series_object) return True def reject(self): if self.check(): msgbox = QMessageBox() msgbox.setText("<font color='red'><b>Noo oniichan! You were about to add a new series.</b></font>") msgbox.setInformativeText("Do you really want to discard?") msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msgbox.setDefaultButton(QMessageBox.No) if msgbox.exec() == QMessageBox.Yes: super().reject() else: super().reject() def trigger(self, list_of_index=None): if not list_of_index: self.initUI() else: assert isinstance(list_of_index, list) self.position = list_of_index[0].row() for index in list_of_index: series = index.data(Qt.UserRole+1) self.setSeries(series) from ..constants import WINDOW as parent self.resize(500,200) frect = self.frameGeometry() frect.moveCenter(QDesktopWidget().availableGeometry().center()) self.move(frect.topLeft()-QPoint(0,120)) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.FramelessWindowHint) self.exec() def setSeries(self, series): "To be used for when editing a series" self.series = series main_layout = QVBoxLayout() f_web = QGroupBox("Fetch metadata from Web") f_web.setCheckable(False) main_layout.addWidget(f_web) web_layout = QHBoxLayout() f_web.setLayout(web_layout) f_series = QGroupBox("Series Info") f_series.setCheckable(False) main_layout.addWidget(f_series) series_layout = QFormLayout() f_series.setLayout(series_layout) def basic_web(name): return QLabel(name), QLineEdit(), QPushButton("Fetch") exh_lbl, exh_edit, exh_btn = basic_web("ExHentai:") web_layout.addWidget(exh_lbl, 0, Qt.AlignLeft) web_layout.addWidget(exh_edit, 0) web_layout.addWidget(exh_btn, 0, Qt.AlignRight) self.title_edit = QLineEdit() self.title_edit.setText(series.title) self.author_edit = QLineEdit() self.author_edit.setText(series.artist) self.descr_edit = QTextEdit() self.descr_edit.setText(series.info) self.descr_edit.setAcceptRichText(True) self.descr_edit.setFixedHeight(70) self.lang_box = QComboBox() self.lang_box.addItems(["English", "Japanese", "Other"]) if series.language is "English": self.lang_box.setCurrentIndex(0) elif series.language is "Japanese": self.lang_box.setCurrentIndex(1) else: self.lang_box.setCurrentIndex(2) self.tags_edit = QLineEdit() #TODO Finish this.. self.tags_edit.setPlaceholderText("namespace1:tag1, tag2, namespace3:tag3, etc..") self.type_box = QComboBox() self.type_box.addItems(["Manga", "Doujinshi", "Other"]) if series.type is "Manga": self.type_box.setCurrentIndex(0) elif series.type is "Doujinshi": self.type_box.setCurrentIndex(1) else: self.type_box.setCurrentIndex(2) #self.type_box.currentIndexChanged[int].connect(self.doujin_show) #self.doujin_parent = QLineEdit() #self.doujin_parent.setVisible(False) self.status_box = QComboBox() self.status_box.addItems(["Unknown", "Ongoing", "Completed"]) if series.status is "Ongoing": self.status_box.setCurrentIndex(1) elif series.status is "Completed": self.status_box.setCurrentIndex(2) else: self.status_box.setCurrentIndex(0) self.pub_edit = QDateEdit() # TODO: Finish this.. self.pub_edit.setCalendarPopup(True) series_pub_date = "{}".format(series.pub_date) qdate_pub_date = QDate.fromString(series_pub_date, "yyyy-MM-dd") self.pub_edit.setDate(qdate_pub_date) self.path_lbl = QLabel("unspecified...") self.path_lbl.setWordWrap(True) self.path_lbl.setText(series.path) series_layout.addRow("Title:", self.title_edit) series_layout.addRow("Author:", self.author_edit) series_layout.addRow("Description:", self.descr_edit) series_layout.addRow("Language:", self.lang_box) series_layout.addRow("Tags:", self.tags_edit) series_layout.addRow("Type:", self.type_box) series_layout.addRow("Publication Date:", self.pub_edit) series_layout.addRow("Path:", self.path_lbl) final_buttons = QHBoxLayout() final_buttons.setAlignment(Qt.AlignRight) main_layout.addLayout(final_buttons) done = QPushButton("Done") done.setDefault(True) done.clicked.connect(self.accept_edit) cancel = QPushButton("Cancel") cancel.clicked.connect(self.reject_edit) final_buttons.addWidget(cancel) final_buttons.addWidget(done) self.setLayout(main_layout) def accept_edit(self): if self.check(): new_series = self.series new_series.title = self.title_edit.text() new_series.artist = self.author_edit.text() new_series.path = self.path_lbl.text() new_series.info = self.descr_edit.toPlainText() new_series.type = self.type_box.currentText() new_series.language = self.lang_box.currentText() new_series.status = self.status_box.currentText() qpub_d = self.pub_edit.date().toString("ddMMyyyy") dpub_d = datetime.strptime(qpub_d, "%d%m%Y").date() new_series.pub_date = dpub_d #for ser in self.series: self.SERIES_EDIT.emit([new_series], self.position) super().accept() def reject_edit(self): super().reject()
class QTransactionsAdd(QWidget): Session: sessionmaker = sessionmaker(bind=engine) entities: Session = Session() label_date: QLabel label_currency: QLabel label_currency: QLabel label_type: QLabel label_amount: QLabel label_describe: QLabel date: QDateEdit describe: QTextEdit closed: pyqtSignal = pyqtSignal() def __init__(self): super().__init__() self.setWindowTitle("Add a transaction") self.resize(950, 450) # QLabels self.label_date = QLabel("Date of transaction :") self.label_currency = QLabel("Currency :") self.label_type = QLabel("Type of transaction :") self.label_amount = QLabel("Amount :") self.label_describe = QLabel("Describe :") # Date self.date = QDateEdit(QDate.currentDate()) self.date.setDisplayFormat("dd/MM/yyyy") # Currencies combobox self.currency = QComboBox() self.currency.addItem("BTC") self.currency.addItem("ETH") self.currency.addItem("LTC") self.currency.addItem("WXT") self.currency.addItem("XRP") self.currency.addItem("XLM") self.currency.addItem("WAVES") self.currency.addItem("WLO") self.currency.addItem("DAI") self.currency.addItem("NANO") self.currency.addItem("EUR") self.currency.addItem("USD") self.type_transaction = QComboBox() self.type_transaction.addItem("Credits") self.type_transaction.addItem("Debits") self.amount = QDoubleSpinBox() self.amount.setValue(0) self.amount.setDecimals(8) self.amount.setSingleStep(0.00000001) self.amount.setSuffix(" " + self.currency.currentText()) spacer_describe = QSpacerItem(1, 15) self.describe = QTextEdit() validate_button = QPushButton("Validate") cancel_button = QPushButton("Cancel") # Vertical layout for date vlayout_date = QVBoxLayout() vlayout_date.addWidget(self.label_date) vlayout_date.addWidget(self.date) # Vertical layout for currencies vlayout_currency = QVBoxLayout() vlayout_currency.addWidget(self.label_currency) vlayout_currency.addWidget(self.currency) # Vertical layout for type of transaction vlayout_type = QVBoxLayout() vlayout_type.addWidget(self.label_type) vlayout_type.addWidget(self.type_transaction) # Vertical layout for amount vlayout_amount = QVBoxLayout() vlayout_amount.addWidget(self.label_amount) vlayout_amount.addWidget(self.amount) # Horizontal layout for transaction form hlayout_transaction = QHBoxLayout() hlayout_transaction.addLayout(vlayout_date) hlayout_transaction.addLayout(vlayout_currency) hlayout_transaction.addLayout(vlayout_type) hlayout_transaction.addLayout(vlayout_amount) # Vertical layout for describe vlayout_describe = QVBoxLayout() vlayout_describe.addSpacerItem(spacer_describe) vlayout_describe.addWidget(self.label_describe) vlayout_describe.addWidget(self.describe) spacer_desc_buttons = QSpacerItem(1, 180) # Vertical layout for buttons vlayout_buttons = QVBoxLayout() vlayout_buttons.addWidget(validate_button) vlayout_buttons.addWidget(cancel_button) vlayout_buttons.addSpacerItem(spacer_desc_buttons) spacer_desc_buttons = QSpacerItem(30, 1) # Horizontal layout for describe & buttons hlayout_desc_buttons = QHBoxLayout() hlayout_desc_buttons.addLayout(vlayout_describe) hlayout_desc_buttons.addSpacerItem(spacer_desc_buttons) hlayout_desc_buttons.addLayout(vlayout_buttons) hlayout_desc_buttons.addSpacerItem(spacer_desc_buttons) # Vertical layout for transaction layout & describe & buttons vertical_layout = QVBoxLayout(self) vertical_layout.addLayout(hlayout_transaction) vertical_layout.addLayout(hlayout_desc_buttons) # Add signal event in clicked cancel button cancel_button.clicked.connect(self.on_clicked_cancel_button) # Add signal event in clicked validate button validate_button.clicked.connect(self.on_clicked_validate_button) # Add signal event in change currency ComboBox self.currency.currentTextChanged.connect( self.on_currenttextchanged_currency_combobox) def on_clicked_cancel_button(self): self.close() def on_currenttextchanged_currency_combobox(self): self.amount.setSuffix(" " + self.currency.currentText()) def on_clicked_validate_button(self): transaction = TransactionsEntity() transaction.date = self.date.date().toPyDate() transaction.currency = self.currency.currentText() transaction.type = self.type_transaction.currentIndex() transaction.amount = self.amount.value() transaction.describe = self.describe.toPlainText() if not self.currency.currentText() == 'BTC': transaction.btc = self.calc_price(self.currency.currentText(), 'BTC') else: transaction.btc = 0 transaction.eur = self.calc_price(self.currency.currentText(), 'EUR') transaction.usd = self.calc_price(self.currency.currentText(), 'USD') self.entities.add(transaction) self.entities.commit() self.close() def calc_price(self, crypto, currency_dest='EUR') -> float: price: float = 0 value: float = 0 url = 'https://api.coinbase.com/v2/prices/' if crypto == "ETH" or crypto == "LTC" or crypto == "XRP" or crypto == "XLM" or crypto == "DAI": if self.type_transaction.currentIndex() == 0: if currency_dest == 'BTC': price = float( json.loads( requests.get(url + crypto + '-' + 'USD/sell').content)['data'] ['amount']) value = self.amount.value() * price price = float( json.loads(requests.get(url + 'BTC-USD/sell').content) ['data']['amount']) value = value / price else: price = float( json.loads( requests.get(url + crypto + '-' + currency_dest + '/sell').content)['data']['amount']) value = self.amount.value() * price else: if currency_dest == 'BTC': price = float( json.loads( requests.get(url + crypto + '-' + 'USD/buy').content)['data']['amount']) value = self.amount.value() * price price = float( json.loads(requests.get(url + 'BTC-USD/buy').content) ['data']['amount']) value = value / price else: price = float( json.loads( requests.get(url + crypto + '-' + currency_dest + '/buy').content)['data']['amount']) value = self.amount.value() * price elif crypto == 'BTC': if self.type_transaction.currentIndex() == 0: price = float( json.loads( requests.get(url + crypto + '-' + currency_dest + '/sell').content)['data']['amount']) value = self.amount.value() * price else: price = float( json.loads( requests.get(url + crypto + '-' + currency_dest + '/buy').content)['data']['amount']) value = self.amount.value() * price return value def closeEvent(self, a0: QCloseEvent) -> None: self.closed.emit()
class MySpinBox(QGroupBox): def __init__(self): super().__init__() self.setTitle('QSpinBox') self.init_ui() def init_ui(self): layout_group1 = self.group_int_spinbox() layout_group2 = self.group_double_spinbox() layout_group3 = self.group_date_spinbox() layout_group4 = self.group_time_spinbox() layout_group5 = self.group_datetime_spinbox() layout = QVBoxLayout() layout.addLayout(layout_group1) layout.addLayout(layout_group2) layout.addLayout(layout_group3) layout.addLayout(layout_group4) layout.addLayout(layout_group5) self.setLayout(layout) def group_datetime_spinbox(self): group2 = QGroupBox('QDateTimeEdit') lbl = QLabel('QDateTimeEdit') date_time_edit = QDateTimeEdit(self) date_time_edit.setDateTime(QDateTime.currentDateTime()) date_time_edit.setDateTimeRange(QDateTime(1900, 1, 1, 00, 00, 00), QDateTime(2100, 1, 1, 00, 00, 00)) date_time_edit.setDisplayFormat('yyyy-MM-dd hh:mm:ss') date_time_edit.dateTimeChanged.connect(self.datetime_value_change) self.lbl5 = QLabel( date_time_edit.dateTime().toString('yyyy-MM-dd hh:mm:ss')) layout_group5 = QHBoxLayout() layout_group5.addWidget(lbl) layout_group5.addWidget(date_time_edit) layout_group5.addWidget(self.lbl5) return layout_group5 def group_time_spinbox(self): group2 = QGroupBox('QTimeEdit') lbl = QLabel('QTimeEdit') timeedit = QTimeEdit(self) timeedit.setTime(QTime.currentTime()) timeedit.setTimeRange(QTime(3, 00, 00), QTime(23, 30, 00)) timeedit.setDisplayFormat('hh:mm:ss') timeedit.timeChanged.connect(self.time_value_change) # dateedit.setDateRange(QDate(1900, 1, 1), QDate(2100, 12, 31)) # self.lbl4 = QLabel(QTime.toString(timeedit.dateTime, 'hh:mm:ss')) self.lbl4 = QLabel(timeedit.time().toString('hh:mm:ss')) layout_group4 = QHBoxLayout() layout_group4.addWidget(lbl) layout_group4.addWidget(timeedit) layout_group4.addWidget(self.lbl4) return layout_group4 def group_date_spinbox(self): lbl = QLabel('QDateEdit') group2 = QGroupBox('QDateEdit') self.dateedit = QDateEdit(self) self.dateedit.setDate(QDate.currentDate()) self.dateedit.setMinimumDate(QDate(1900, 1, 1)) self.dateedit.setMaximumDate(QDate(2100, 12, 31)) self.dateedit.setDisplayFormat('yyyy-MM-dd') self.dateedit.dateChanged.connect(self.date_value_change) # dateedit.setDateRange(QDate(1900, 1, 1), QDate(2100, 12, 31)) self.lbl3 = QLabel(QDate.toString(self.dateedit.date(), 'yyyy-MM-dd')) layout_group3 = QHBoxLayout() layout_group3.addWidget(lbl) layout_group3.addWidget(self.dateedit) layout_group3.addWidget(self.lbl3) return layout_group3 def group_double_spinbox(self): group2 = QGroupBox('Double QSpinbox') lbl = QLabel('QDoubleSpinBox') self.dspinbox = QDoubleSpinBox() self.dspinbox.setRange(0, 100) self.dspinbox.setSingleStep(0.5) self.dspinbox.setPrefix('$ ') self.dspinbox.setDecimals(1) self.lbl2 = QLabel('$ 0.0') self.dspinbox.valueChanged.connect(self.double_value_changed) layout_group2 = QHBoxLayout() layout_group2.addWidget(lbl) layout_group2.addWidget(self.dspinbox) layout_group2.addWidget(self.lbl2) return layout_group2 def group_int_spinbox(self): group1 = QGroupBox('Integer QSpinBox') lbl = QLabel('QSpinBox') self.spinbox = QSpinBox() self.spinbox.setMinimum(-10) self.spinbox.setMaximum(30) # self.spinbox.setRange(-10, 30) self.spinbox.setSingleStep(2) self.lbl1 = QLabel(str(self.spinbox.value())) self.spinbox.valueChanged.connect(self.int_value_changed) layout_group1 = QHBoxLayout() layout_group1.addWidget(lbl) layout_group1.addWidget(self.spinbox) layout_group1.addWidget(self.lbl1) return layout_group1 def int_value_changed(self): self.lbl1.setText(str(self.spinbox.value())) def double_value_changed(self): self.lbl2.setText(str(self.dspinbox.value())) def date_value_change(self, t): self.lbl3.setText(QDate.toString(t, 'yyyy-MM-dd')) def time_value_change(self, t): self.lbl4.setText(t.toString('hh:mm:ss')) def datetime_value_change(self, dt): self.lbl5.setText(dt.toString('yyyy-MM-dd hh:mm:ss'))
class ReportGen(PageWindow): def __init__(self): super().__init__() self.initUI() self.createWidgets() def initUI(self): self.setWindowTitle("Report Generation") self.setFixedSize(400, 590) # Set the central widget and the general layout self.generalLayout = QVBoxLayout() self._centralWidget = QWidget(self) self.setCentralWidget(self._centralWidget) self._centralWidget.setLayout(self.generalLayout) def createWidgets(self): self.logSelector=QComboBox(self) self.logSelector.resize(110,40) self.logSelector.move(140,40) self.logSelector.addItem("Resident Log") self.logSelector.addItem("Visitor Log") self.datepicker=QDateEdit(self) self.datepicker.setCalendarPopup(True) self.datepicker.move(145,120) self.datepicker.setDate(dt.today()) self.startTime=QTimeEdit(self) self.startTime.move(65,200) self.endTime=QTimeEdit(self) self.endTime.move(225,200) self.genButton=QPushButton("Generate",self) self.genButton.move(145,280) self.backButton=QPushButton("Back",self) self.backButton.move(145,340) self.backButton.clicked.connect(self.goBack) self.genButton.clicked.connect(self.repGen) def goBack(self): self.goto("welcome") def repGen(self): #print("Hey") log=self.logSelector.currentText() date=str(self.datepicker.date().toPyDate()) #print(date) start=str(self.startTime.time().toPyTime()) end=str(self.endTime.time().toPyTime()) vbox=QVBoxLayout() table=QTableWidget() #print("Heyo") #print(log+" "+date+" "+start+" "+end) repList=getRepData(log,date,start,end) rows=len(repList) print(repList) table.setRowCount(rows+1) if(log=="Resident Log"): #print("hey") table.setColumnCount(4) table.setItem(0,0,QTableWidgetItem("Vehicle No.")) table.setItem(0,1,QTableWidgetItem("Flat No.")) table.setItem(0,2,QTableWidgetItem("Entry Time")) table.setItem(0,3,QTableWidgetItem("Exit Time")) table.resize(650,400) else: table.setColumnCount(5) table.setItem(0,0,QTableWidgetItem("Vehicle No.")) table.setItem(0,1,QTableWidgetItem("Visitor Name")) table.setItem(0,2,QTableWidgetItem("Flat No.")) table.setItem(0,3,QTableWidgetItem("Entry Time")) table.setItem(0,4,QTableWidgetItem("Exit Time")) table.resize(800,400) try: i=1 j=0 for row in repList: for j in range(len(row)): #print(j+" "+row[j]) table.setItem(i,j,QTableWidgetItem(row[j])) i+=1 except Exception as e: print(e) table.horizontalHeader().setDefaultSectionSize(155) table.show() vbox.addWidget(table) self.setLayout(vbox)
class HistWindowSelect(QGroupBox): """An extension of the QGroupBox widget. It allows the user to select a start date and end date for the historical window in the Monte Carlo VaR calculation application. The end date of the historical window is set to 22 days from the start date as a minimum. This restriction is implemented through the QDateEdit's set minimum date function. """ end_signal = QtCore.pyqtSignal(dt.datetime) def __init__(self, parent=None): super(HistWindowSelect, self).__init__(parent) self.setTitle('Historical Window Select') grid = QGridLayout() lbl_start = QLabel('Start Date: ') lbl_end = QLabel('End Date: ') self.deStart = QDateEdit() self.deStart.setCalendarPopup(True) self.deStart.setMinimumDateTime(dt.datetime(2015, 1, 1)) self.deStart.setMaximumDateTime( self.getWeekdaysBack(dt.datetime.now(), 22)) self.deStart.dateChanged.connect(self.onStartDateChange) self.deEnd = QDateEdit() self.deEnd.setCalendarPopup(True) self.deEnd.setMaximumDateTime(dt.datetime.now()) self.deEnd.dateChanged.connect(self.onEndDateChange) self.setEndDateMinimum() lbl_disc = QLabel( 'Note: A minimum historical window length <br>of 22 business days has been imposed.' ) grid.addWidget(lbl_start, 0, 0) grid.addWidget(lbl_end, 1, 0) grid.addWidget(self.deStart, 0, 1) grid.addWidget(self.deEnd, 1, 1) grid.addWidget(lbl_disc, 2, 1) self.setLayout(grid) def onStartDateChange(self): """Checks if new start date is a weekend. If so, it adds the appropriate number of days to bring the start date to the following Monday. After, it updates the minimum value of the end date, which must be 22 business days after the start date. """ deStart = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) if deStart.weekday() == 5: deStart = deStart + timedelta(days=2) elif deStart.weekday() == 6: deStart = deStart + timedelta(days=1) self.deStart.setDate(deStart) self.setEndDateMinimum() def setEndDateMinimum(self): """Updates the minimum value of the end date, which must be 22 business days after the start date. """ deStart = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) end_min = self.getWeekdaysOut(deStart, 22) self.deEnd.setMinimumDateTime(end_min) if self.deEnd.date() < end_min: self.deEnd.setDate(end_min) self.test_signal.emit(end_min) def onEndDateChange(self): """Checks if new end date is a weekend. If so, it adds the appropriate number of days to bring the end date to the following Monday. After, it emits a signal containing the new end date to be picked up by a slot in the PredWindowSelect widget. """ deEnd = dt.datetime.combine(self.deEnd.date().toPyDate(), dt.datetime.min.time()) if deEnd.weekday() == 5: deEnd = deEnd + timedelta(days=2) elif deEnd.weekday() == 6: deEnd = deEnd + timedelta(days=1) self.deEnd.setDate(deEnd) self.end_signal.emit(deEnd) def getWeekdaysOut(self, start_date, days_out): """Finds the date that is x weekdays later than the start date (where x = days_out). Args: start_date (datetime.datetime) - Start date from which to count weekdays out. days_out (int) - Number of days out. Returns: curr_date (datetime.datetime) - the date that is days_out weekdays later than start_date. Raises Error: None """ if start_date.weekday() == 5: start_date += timedelta(days=2) if start_date.weekday() == 6: start_date += timedelta(days=1) for i in range(days_out): if i == 0: curr_date = start_date next_date = curr_date + timedelta(days=1) if next_date.weekday() == 5: next_date = curr_date + timedelta(days=3) curr_date = next_date return (curr_date) def getWeekdaysBack(self, start_date, days_back): """Finds the date that is x weekdays earlier than the start date (where x = days_back). Args: start_date (datetime.datetime) - Start date from which to count weekdays out. days_back (int) - Number of days back. Returns: curr_date (datetime.datetime) - the date that is days_back weekdays earlier than start_date. Raises Error: None """ if start_date.weekday() == 5: start_date -= timedelta(days=1) if start_date.weekday() == 6: start_date -= timedelta(days=2) for i in range(days_back): if i == 0: curr_date = start_date next_date = curr_date - timedelta(days=1) if next_date.weekday() == 6: next_date = curr_date - timedelta(days=3) curr_date = next_date return (curr_date) def getDateWindow(self): """Getter function for the date window selected by the user. """ start_date = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) end_date = dt.datetime.combine(self.deEnd.date().toPyDate(), dt.datetime.min.time()) return start_date, end_date
class PredWindowSelect(QGroupBox): """An extension of the QGroupBox widget. It allows the user to select a start date and end date for the prediction window in the Monte Carlo VaR calculation application. The start date of the prediction window is set to the end date of the historical window. The end date of the prediction window is five days later than the start date of the prediction window as a minimum. These restrictions are implemented through the QDateEdit widget's maximum and minimum date functions. """ def __init__(self, parent=None): super(PredWindowSelect, self).__init__(parent) self.setTitle('Prediction Window Select') grid = QGridLayout() lbl_start = QLabel('Start Date: ') lbl_end = QLabel('End Date: ') self.deStart = QDateEdit() self.deStart.setEnabled(False) self.deStart.setCalendarPopup(True) self.deStart.dateChanged.connect(self.setEndDateMinimum) self.deEnd = QDateEdit() self.deEnd.setCalendarPopup(True) self.deEnd.dateChanged.connect(self.onEndDateChange) self.setEndDateMinimum() lbl_disc = QLabel( 'Note: A minimum prediction window length <br>of 5 business days has been imposed.' ) grid.addWidget(lbl_start, 0, 0) grid.addWidget(lbl_end, 1, 0) grid.addWidget(self.deStart, 0, 1) grid.addWidget(self.deEnd, 1, 1) grid.addWidget(lbl_disc, 2, 1) self.setLayout(grid) @QtCore.pyqtSlot(dt.datetime) def setStart(self, date): """Slotted function to set the start date based on end date of the historical window. """ self.deStart.setDate(date) def onEndDateChange(self): """Checks if new end date is a weekend. If so, it adds the appropriate number of days to bring the end date to the following Monday. """ deEnd = dt.datetime.combine(self.deEnd.date().toPyDate(), dt.datetime.min.time()) if deEnd.weekday() == 5: deEnd = deEnd + timedelta(days=2) elif deEnd.weekday() == 6: deEnd = deEnd + timedelta(days=1) self.deEnd.setDate(deEnd) def setEndDateMinimum(self): """Updates the minimum value of the end date, which must be 5 business days after the start date. """ deStart = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) end_min = self.getWeekdaysOut(deStart, 5) self.deEnd.setMinimumDateTime(end_min) if self.deEnd.date() < end_min: self.deEnd.setDate(end_min) def getWeekdaysOut(self, start_date, days_out): """Finds the date that is x weekdays later than the start date (where x = days_out). Args: start_date (datetime.datetime) - Start date from which to count weekdays out. days_out (int) - Number of days out. Returns: curr_date (datetime.datetime) - the date that is days_out weekdays later than start_date. Raises Error: None """ if start_date.weekday() == 5: start_date += timedelta(days=2) if start_date.weekday() == 6: start_date += timedelta(days=1) for i in range(days_out): if i == 0: curr_date = start_date next_date = curr_date + timedelta(days=1) if next_date.weekday() == 5: next_date = curr_date + timedelta(days=3) curr_date = next_date return (curr_date) def getDateWindow(self): """Getter function for the date window selected by the user. """ start_date = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) end_date = dt.datetime.combine(self.deEnd.date().toPyDate(), dt.datetime.min.time()) return start_date, end_date def getNumDays(self): """Getter function for the number of weekdays between the start and end dates.""" start_date = dt.datetime.combine(self.deStart.date().toPyDate(), dt.datetime.min.time()) end_date = dt.datetime.combine(self.deEnd.date().toPyDate(), dt.datetime.min.time()) if start_date.weekday() == 5: start_date += timedelta(days=2) if start_date.weekday() == 6: start_date += timedelta(days=1) days = 0 curr_date = start_date while curr_date != end_date: curr_date += timedelta(days=1) if curr_date.weekday() == 5: continue elif curr_date.weekday() == 6: continue else: days += 1 return days
class HomeScreen(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Home Screen") self.resize(550, 461) self.setStyleSheet(Hariku_Style.get_window_stylesheet()) self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.setupUI() def setupUI(self): self.setWindowIcon(Hariku_Style.getIcon()) self.centralwidget = QWidget(self) self.setFocus() self.gridLayout = QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") self.topcenter_layout = QHBoxLayout() spacerItem = QSpacerItem(25, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.topcenter_layout.addItem(spacerItem) self.monthFilter = QDateEdit(self.centralwidget) self.monthFilter.setCurrentSection(QDateTimeEdit.MonthSection) self.monthFilter.setButtonSymbols(QSpinBox.NoButtons) self.monthFilter.setDate(datetime.now()) self.monthFilter.setDisplayFormat("MMMM/yyyy") self.monthFilter.setFont(Hariku_Style.get_font(10)) self.monthFilter.setStyleSheet(Hariku_Style.get_dateedit_stylesheet()) self.monthFilter.dateChanged.connect(self.filterDiaryByMonth) self.topcenter_layout.addWidget(self.monthFilter) self.showAllBtn = QPushButton("Remove Filter", self.centralwidget) self.showAllBtn.setFont(Hariku_Style.get_font(8)) self.showAllBtn.clicked.connect(self.getDiaries) self.showAllBtn.setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(38, 160, 173)", "rgb(8, 102, 112)")) self.topcenter_layout.addWidget(self.showAllBtn) spacerItem1 = QSpacerItem(25, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.topcenter_layout.addItem(spacerItem1) self.gridLayout.addLayout(self.topcenter_layout, 1, 0, 1, 1) self.bottomLayout = QHBoxLayout() spacerItem2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.bottomLayout.addItem(spacerItem2) self.createBtn = QPushButton("+ Add New Diary", self.centralwidget) self.createBtn.setFont(Hariku_Style.get_font(10)) self.createBtn.clicked.connect(self.addNewDiary) self.createBtn.setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(40, 186, 130)", "rgb(207, 207, 188)")) self.bottomLayout.addWidget(self.createBtn) self.gridLayout.addLayout(self.bottomLayout, 4, 0, 1, 1) self.contentScrollArea = QScrollArea(self.centralwidget) self.contentScrollArea.setEnabled(True) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.contentScrollArea.sizePolicy().hasHeightForWidth()) self.contentScrollArea.setSizePolicy(sizePolicy) self.contentScrollArea.setStyleSheet( Hariku_Style.get_scrollarea_stylesheet()) self.contentScrollArea.setWidgetResizable(True) self.contentScrollArea.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 487, 321)) self.scrollAreaLayout = QVBoxLayout(self.scrollAreaWidgetContents) self.getDiaries() self.contentScrollArea.setWidget(self.scrollAreaWidgetContents) self.gridLayout.addWidget(self.contentScrollArea, 2, 0, 1, 1) self.judul = QLabel("\nWelcome Home:)\n", self) self.judul.setFont(Hariku_Style.get_font(18)) self.judul.setAlignment(QtCore.Qt.AlignCenter) self.gridLayout.addWidget(self.judul, 0, 0, 1, 1) self.setCentralWidget(self.centralwidget) def addNewDiary(self): dialog = DiaryScreen(self) dialog.show() self.hide() def clearScrollAreaLayout(self): # delete the diaries currently showind for i in reversed(range(self.scrollAreaLayout.count())): try: self.scrollAreaLayout.itemAt(i).widget().setParent(None) except AttributeError: self.scrollAreaLayout.removeItem( self.scrollAreaLayout.itemAt(i)) def filterDiaryByMonth(self): self.clearScrollAreaLayout() year = self.monthFilter.date().year() month = self.monthFilter.date().month() diaries = getDiaryByMonth(year, month) for i, diary in enumerate(diaries): self.diaryItem = QWidget(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHeightForWidth( self.diaryItem.sizePolicy().hasHeightForWidth()) self.diaryItem.setSizePolicy(sizePolicy) self.diaryItem.setMinimumSize(QtCore.QSize(0, 50)) self.diaryItem.setStyleSheet( "background-color: rgba(227, 217, 163, 100%);\nborder-radius : 10px;" ) self.diaryItem.setObjectName("diaryItem") self.itemLayout = QGridLayout(self.diaryItem) self.contentDateLayout = QVBoxLayout() self.contentDateLayout.setObjectName("contentDateLayout") self.content = QLabel(self.truncateString(diary.content), self.diaryItem) self.content.setFont(Hariku_Style.get_font(10)) self.contentDateLayout.addWidget(self.content) self.date = QLabel( diary.date.strftime("%d %B %Y") + diary.time.strftime(" %I:%M %p"), self.diaryItem) self.date.setFont(Hariku_Style.get_font(8)) self.contentDateLayout.addWidget(self.date) self.itemLayout.addLayout(self.contentDateLayout, 0, 0, 1, 1) self.buttons.append(i) self.buttons[i] = [ QPushButton("⋗", self.diaryItem), QPushButton("×", self.diaryItem) ] self.buttons[i][0].clicked.connect( lambda checked, i=diary.diary_id: self.viewDiaryById(i)) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.buttons[i][0].sizePolicy().hasHeightForWidth()) self.buttons[i][0].setSizePolicy(sizePolicy) self.buttons[i][0].setFont(Hariku_Style.get_font(9)) self.buttons[i][0].setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(145, 133, 63)", "rgb(235, 224, 157)")) self.itemLayout.addWidget(self.buttons[i][0], 0, 1, 1, 1) # self.deleteBtn = QPushButton("×", self.diaryItem) self.buttons[i][1].clicked.connect( lambda checked, i=diary.diary_id: self.deleteDiaryById(i)) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.buttons[i][1].sizePolicy().hasHeightForWidth()) self.buttons[i][1].setSizePolicy(sizePolicy) self.buttons[i][1].setFont(Hariku_Style.get_font(9)) self.buttons[i][1].setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(145, 63, 63)", "rgb(235, 157, 157)")) self.itemLayout.addWidget(self.buttons[i][1], 0, 2, 1, 1) self.scrollAreaLayout.addWidget(self.diaryItem, alignment=QtCore.Qt.AlignTop) spacerItem2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.MinimumExpanding) self.scrollAreaLayout.addItem(spacerItem2) def getDiaries(self): diaries = getAllDiaries() self.monthFilter.setDate(datetime.now()) self.clearScrollAreaLayout() self.buttons = [] for i, diary in enumerate(diaries): self.diaryItem = QWidget(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHeightForWidth( self.diaryItem.sizePolicy().hasHeightForWidth()) self.diaryItem.setSizePolicy(sizePolicy) self.diaryItem.setMinimumSize(QtCore.QSize(0, 50)) self.diaryItem.setStyleSheet( "background-color: rgba(227, 217, 163, 100%);\nborder-radius : 10px;" ) self.diaryItem.setObjectName("diaryItem") self.itemLayout = QGridLayout(self.diaryItem) self.contentDateLayout = QVBoxLayout() self.contentDateLayout.setObjectName("contentDateLayout") self.content = QLabel(self.truncateString(diary.content), self.diaryItem) self.content.setFont(Hariku_Style.get_font(10)) self.contentDateLayout.addWidget(self.content) self.date = QLabel( diary.date.strftime("%d %B %Y") + diary.time.strftime(" %I:%M %p"), self.diaryItem) self.date.setFont(Hariku_Style.get_font(8)) self.contentDateLayout.addWidget(self.date) self.itemLayout.addLayout(self.contentDateLayout, 0, 0, 1, 1) self.buttons.append(i) self.buttons[i] = [ QPushButton("⋗", self.diaryItem), QPushButton("×", self.diaryItem) ] self.buttons[i][0].clicked.connect( lambda checked, i=diary.diary_id: self.viewDiaryById(i)) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.buttons[i][0].sizePolicy().hasHeightForWidth()) self.buttons[i][0].setSizePolicy(sizePolicy) self.buttons[i][0].setFont(Hariku_Style.get_font(9)) self.buttons[i][0].setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(145, 133, 63)", "rgb(235, 224, 157)")) self.itemLayout.addWidget(self.buttons[i][0], 0, 1, 1, 1) # self.deleteBtn = QPushButton("×", self.diaryItem) self.buttons[i][1].clicked.connect( lambda checked, i=diary.diary_id: self.deleteDiaryById(i)) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.buttons[i][1].sizePolicy().hasHeightForWidth()) self.buttons[i][1].setSizePolicy(sizePolicy) self.buttons[i][1].setFont(Hariku_Style.get_font(9)) self.buttons[i][1].setStyleSheet( Hariku_Style.get_moodBtn_stylesheet("rgb(145, 63, 63)", "rgb(235, 157, 157)")) self.itemLayout.addWidget(self.buttons[i][1], 0, 2, 1, 1) self.scrollAreaLayout.addWidget(self.diaryItem, alignment=QtCore.Qt.AlignTop) spacerItem2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.MinimumExpanding) self.scrollAreaLayout.addItem(spacerItem2) def truncateString(self, string): try: string = string.replace('\n', ' ') return string[:45] + '...' except IndexError: return string def viewDiaryById(self, id): dialog = DiaryScreen(self, edit=False, id=id) dialog.show() self.hide() def deleteDiaryById(self, id): deleteDiaryById(id) self.clearScrollAreaLayout() self.getDiaries()
class NewEntryDialog(QDialog): def __init__(self, parent): QDialog.__init__(self, parent) self.submitted = False self.cover_bytes = b'' self.setWindowTitle('Add new entry') self.container = QWidget() self.horizontal_layout = QHBoxLayout() self.container_left_layout = QVBoxLayout() self.container_right_layout = QVBoxLayout() self.horizontal_layout.addLayout(self.container_left_layout) self.horizontal_layout.addLayout(self.container_right_layout) label_type = QLabel(_('Entry type:')) self.combo_type = QComboBox() label_type.setBuddy(self.combo_type) for entry in EntryType: self.combo_type.addItem(entry.value, entry) label_status = QLabel(_('Entry status:')) self.combo_status = QComboBox() label_status.setBuddy(self.combo_status) for entry in EntryStatus: self.combo_status.addItem(entry.value, entry) label_progress = QLabel(_('Progress status:')) self.combo_progress = QComboBox() label_progress.setBuddy(self.combo_progress) for entry in ProgressStatus: self.combo_progress.addItem(entry.value, entry) label_org_name = QLabel(_('Entry original name:')) self.line_org_name = QLineEdit() label_org_name.setBuddy(self.line_org_name) label_eng_name = QLabel(_('Entry english name:')) self.line_eng_name = QLineEdit() label_eng_name.setBuddy(self.line_eng_name) label_synonyms = QLabel(_('Synonym names:')) self.line_synonyms = QLineEdit() label_synonyms.setBuddy(self.line_synonyms) label_description = QLabel(_('Description:')) self.description_textbox = QPlainTextEdit() label_description.setBuddy(self.description_textbox) self.nsfw = QCheckBox(_('NSFW')) only_int = QIntValidator(bottom=0) label_current_progress = QLabel(_('Current progress:')) self.current_progress_textbox = QLineEdit() label_current_progress.setBuddy(self.current_progress_textbox) self.current_progress_textbox.setValidator(only_int) label_current_progress.setBuddy(self.current_progress_textbox) label_max_progress = QLabel(_('Max progress:')) self.max_progress_textbox = QLineEdit() label_max_progress.setBuddy(self.max_progress_textbox) self.max_progress_textbox.setValidator(only_int) label_max_progress.setBuddy(self.max_progress_textbox) label_release_date = QLabel(_('Release date:')) self.release_date1 = QDateEdit() self.release_date2 = QDateEdit() label_release_date.setBuddy(self.release_date1) label_release_date.setBuddy(self.release_date2) self.cover = QLabel("Cover:") self.load_cover_button = QPushButton(_('Load cover')) self.load_cover_button.clicked.connect(self.load_image) self.accept_button = QPushButton(_('Add')) self.container_left_layout.addWidget(self.cover) self.container_left_layout.addWidget(self.load_cover_button) self.container_right_layout.addWidget(label_type) self.container_right_layout.addWidget(self.combo_type) self.container_right_layout.addWidget(label_status) self.container_right_layout.addWidget(self.combo_status) self.container_right_layout.addWidget(label_progress) self.container_right_layout.addWidget(self.combo_progress) self.container_right_layout.addWidget(label_org_name) self.container_right_layout.addWidget(self.line_org_name) self.container_right_layout.addWidget(label_eng_name) self.container_right_layout.addWidget(self.line_eng_name) self.container_right_layout.addWidget(label_synonyms) self.container_right_layout.addWidget(self.line_synonyms) self.container_right_layout.addWidget(label_description) self.container_right_layout.addWidget(self.description_textbox) self.container_right_layout.addWidget(self.nsfw) self.container_right_layout.addWidget(label_current_progress) self.container_right_layout.addWidget(self.current_progress_textbox) self.container_right_layout.addWidget(label_max_progress) self.container_right_layout.addWidget(self.max_progress_textbox) self.container_right_layout.addWidget(label_release_date) date_layout = QHBoxLayout() date_layout.addWidget(self.release_date1) date_layout.addWidget(self.release_date2) self.container_right_layout.addLayout(date_layout) self.container_right_layout.addWidget(self.accept_button) self.root_layout = QVBoxLayout(self) self.main_layout = QVBoxLayout(self) self.root_layout.addLayout(self.main_layout) self.container.setLayout(self.horizontal_layout) self.main_layout.addWidget(self.container) self.setLayout(self.root_layout) self.accept_button.clicked.connect(self.submit) def submit(self): self.submitted = True self.close() def load_image(self): fname = QFileDialog.getOpenFileName(self, 'Select entry cover') image_path = fname[0] self.cover_bytes = open(image_path, 'rb').read() qp = QPixmap() qp.loadFromData(self.cover_bytes) self.cover.setPixmap(qp) self.cover.setMaximumHeight(500) self.cover.setMaximumWidth(500) def get_values(self): entry_status = self.combo_status.currentData() entry_type = self.combo_type.currentData() entry_progress = self.combo_progress.currentData() date1 = self.release_date1.date().toPyDate() date2 = self.release_date2.date().toPyDate() try: current_progress = int(self.current_progress_textbox.text()) except: current_progress = 0 try: max_progress = int(self.max_progress_textbox.text()) except: max_progress = -1 entry = GenericEntry(self.cover_bytes, self.line_eng_name.text(), self.line_org_name.text(), self.line_synonyms.text(), date1, date2, entry_status, self.description_textbox.toPlainText(), self.nsfw.isChecked(), entry_type, current_progress, max_progress, entry_progress) return entry
class MainWindow(QWidget): articleInfoUpdate = pyqtSignal() entityUpdate = pyqtSignal() crawlerUpdate = pyqtSignal() def __init__(self): super(MainWindow, self).__init__() self.initUI() self.articleInfoUpdate.connect(self.updateArticleInfo) self.entityUpdate.connect(self.updateEntityList) def initUI(self): self.setGeometry(0, 0, 500, 700) self.center() self.setWindowTitle('PView') mainLayout = QVBoxLayout() self.createArticleInfoBox() self.createViewArticleBox() self.createEntityBox() self.createReportBox() self.createDatabaseBox() mainLayout.addWidget(self.infoBox) mainLayout.addWidget(self.viewArticleBox) mainLayout.addWidget(self.entityBox) mainLayout.addWidget(self.raportBox) mainLayout.addWidget(self.databaseBox) self.setLayout(mainLayout) self.show() def createArticleInfoBox(self): self.articleCount = self.selectCountArticles() entityCount = self.selectCountEntities() associationsCount = self.selectCountAssociations() classifiedCount = self.selectCountClassifiedAssociations() label = "Number of articles: " + str(self.articleCount) self.articleCountLabel = QLabel(label) label = "Number of entities: " + str(entityCount) self.entitiesCountLabel = QLabel(label) label = "Number of associations: " + str(associationsCount) self.associationsCountLabel = QLabel(label) label = "Number of classified associations: " + str(classifiedCount) self.classifiedCountLabel = QLabel(label) layout = QVBoxLayout() layout.addWidget(self.articleCountLabel) layout.addWidget(self.entitiesCountLabel) layout.addWidget(self.associationsCountLabel) layout.addWidget(self.classifiedCountLabel) self.infoBox = QGroupBox("Statistics") self.infoBox.setLayout(layout) def createCrawlerBox(self): self.crawlButton = QPushButton("Crawl") self.crawlButton.setFocusPolicy(Qt.NoFocus) self.websiteList = QComboBox() self.websiteList.addItem("HotNews") layout = QGridLayout() layout.addWidget(self.websiteList, 0, 0, 1, 1) layout.addWidget(self.crawlButton, 0, 1, 1, 1) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 1) self.crawlerBox = QGroupBox("Crawler") self.crawlerBox.setLayout(layout) def createViewArticleBox(self): self.articleNumberLineEdit = QLineEdit("") self.articleNumberLineEdit.setAlignment(Qt.AlignHCenter) self.viewArticleButton = QPushButton("Open") self.viewArticleButton.clicked.connect(self.viewArticle) layout = QGridLayout() layout.addWidget(self.articleNumberLineEdit, 0, 0, 1, 1) layout.addWidget(self.viewArticleButton, 0, 1, 1, 1) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 1) self.viewArticleBox = QGroupBox("View Article") self.viewArticleBox.setLayout(layout) def createReportBox(self): minDate, maxDate = self.selectMinAndMaxDate() minDate = QDate(minDate.year, minDate.month, minDate.day) maxDate = QDate(maxDate.year, maxDate.month, maxDate.day) self.fromDateEdit = QDateEdit() self.fromDateEdit.setDate(minDate) self.fromDateEdit.setDisplayFormat('d MMM yyyy') self.fromDateEdit.setAlignment(Qt.AlignHCenter) self.toDateEdit = QDateEdit() self.toDateEdit.setDate(maxDate) self.toDateEdit.setDisplayFormat('d MMM yyyy') self.toDateEdit.setAlignment(Qt.AlignHCenter) self.reportTypeComboBox = QComboBox() for item in reportTypes: self.reportTypeComboBox.addItem(item) monthlyButton = QPushButton("View") monthlyButton.clicked.connect(self.createReport) layout = QGridLayout() layout.addWidget(self.fromDateEdit, 0, 0, 1, 1) layout.addWidget(self.toDateEdit, 0, 1, 1, 1) layout.addWidget(self.reportTypeComboBox, 1, 0, 1, 1) layout.addWidget(monthlyButton, 1, 1, 1, 1) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 1) self.raportBox = QGroupBox("Charts") self.raportBox.setLayout(layout) def createEntityBox(self): rows = self.selectCountEntities() self.entityList = QListWidget() entities = self.selectAllEntities() for entity in entities: self.doAssociationsForEntity(entity[1]) self.entityList.addItem(entity[1]) addButton = QPushButton("Add") addButton.clicked.connect(self.addEntity) removeButton = QPushButton("Delete") removeButton.clicked.connect(self.removeEntity) self.addEntityLineEdit = QLineEdit("") viewArticlesButton = QPushButton("View articles") viewArticlesButton.clicked.connect(self.viewArticleByEntity) self.algorithmComboBox = QComboBox() for key in classifiers.keys(): self.algorithmComboBox.addItem(key) classifyButton = QPushButton("Classify") classifyButton.clicked.connect(self.classifyAllAssociations) layout = QGridLayout() layout.addWidget(self.entityList, 0, 0, 1, 4) layout.addWidget(self.addEntityLineEdit, 1, 0, 1, 2) layout.addWidget(addButton, 1, 2, 1, 1) layout.addWidget(removeButton, 1, 3, 1, 1) layout.addWidget(viewArticlesButton, 2, 0, 1, 4) layout.addWidget(self.algorithmComboBox, 3, 0, 1, 2) layout.addWidget(classifyButton, 3, 2, 1, 2) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 1) layout.setColumnStretch(2, 1) layout.setColumnStretch(3, 1) self.entityBox = QGroupBox("Entities") self.entityBox.setLayout(layout) def createDatabaseBox(self): deleteClassificationsButton = QPushButton("Remove all classifications") deleteClassificationsButton.clicked.connect(self.clearAllCalculatedPolarities) deleteEntitiesButton = QPushButton("Remove all entities") deleteEntitiesButton.clicked.connect(self.deleteAllEntities) deleteAssociationsButton = QPushButton("Remove all associations") deleteAssociationsButton.clicked.connect(self.deleteAllAssociations) layout = QVBoxLayout() layout.addWidget(deleteClassificationsButton) layout.addWidget(deleteAssociationsButton) layout.addWidget(deleteEntitiesButton) self.databaseBox = QGroupBox("Database") self.databaseBox.setLayout(layout) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def monthsBetweenDates(self, fromDate, toDate): curDate = QDate(fromDate) months =[] while curDate < toDate: months.append(curDate) curDate = curDate.addMonths(1) return months def makeMonthlyPolarityChart(self, entities, fromDate, toDate): cursor = mysql_conn.cursor() chartData = [] for (entityId, entity) in entities: monthlyPol = self.selectAllPolaritiesForEntity(entityId, fromDate, toDate) trace0=Bar( x = [self.monthYearLabel(month) for (month, _, _) in monthlyPol], y = [(0.0 + rows.count(1L)) / (l+1) * 100 for (_, l, rows) in monthlyPol], name = entity, marker = Marker( color = 'rgb(204,204,204)', opacity = 0.5, ), ) chartData.append(trace0) chartData = Data(chartData) layout = Layout( xaxis=XAxis( #set x-axis' labels direction at 45 degree angle tickangle=-45, ), barmode='group', ) fig = Figure(data = chartData, layout = layout) py.image.save_as({'data': chartData}, "polarities.png") img = Image.open("polarities.png") img.show() def makeMonthlyAppearanceChart(self, entities, fromDate, toDate): cursor = mysql_conn.cursor() chartData = [] for (entityId, entity) in entities: monthlyApp = self.selectCountAssociationsForEntityBetweenDates(entityId, fromDate, toDate) trace0=Bar( x = [self.monthYearLabel(month) for (month, _) in monthlyApp], y = [count for (_, count) in monthlyApp], name = entity, marker = Marker( color = 'rgb(204,204,204)', opacity = 0.5, ), ) chartData.append(trace0) chartData = Data(chartData) layout = Layout( xaxis=XAxis( #set x-axis' labels direction at 45 degree angle tickangle=-45, ), barmode='group', ) fig = Figure(data = chartData, layout = layout) py.image.save_as({'data': chartData}, "appearances.png") img = Image.open("appearances.png") img.show() def getStringDate(self, date): sDate = str(date.year()) sDate += '-'+str(date.month()) sDate += '-'+'01' time = '00:00:00' return sDate + ' ' + time def monthYearLabel(self, date): label = monthIntToString[date.month()] + ' ' label += str(date.year()) return label def createReport(self): reportType = self.reportTypeComboBox.currentText() fromDate = self.fromDateEdit.date() toDate = self.toDateEdit.date() entities = [] if "All entities" in reportType: entities = self.selectAllEntities() else: selected = self.entityList.selectedItems() if len(selected) == 1: entity = selected[0].text() entities = [(self.selectEntityId(entity), entity)] if len(entities) > 0: if "Opinions" in reportType: self.makeMonthlyPolarityChart(entities, fromDate, toDate) else: print entities self.makeMonthlyAppearanceChart(entities, fromDate, toDate) def viewArticle(self): try: articleId = int(self.articleNumberLineEdit.text()) if articleId > 0 and articleId < self.articleCount: self.viewArticleButton.setEnabled(False) self.articleNumberLineEdit.setDisabled(True) articleList = [i+1 for i in xrange(self.articleCount)] articleView = Article(articleId-1, articleList, parentW=self) articleView.exec_() self.viewArticleButton.setEnabled(True) self.articleNumberLineEdit.setDisabled(False) except ValueError: print "Invalid article id" def viewArticleByEntity(self): selected = self.entityList.selectedItems() if len(selected) == 1: articles = self.selectAllArticlesByEntity(selected[0].text()) articleList = [a[0] for a in articles] articleView = Article(0, articleList, shuffle_=True, parentW=self) articleView.exec_() def addEntity(self): newEntity = self.addEntityLineEdit.text().strip() newEntity = re.sub(r' +', ' ', newEntity) cursor = mysql_conn.cursor() if len(newEntity) != 0: selectStmt = """SELECT * FROM entities WHERE entity=%s""" data = (newEntity,) cursor.execute(selectStmt, data) rows = cursor.fetchall() if len(rows) == 0: insertStmt = """INSERT INTO entities (entity) VALUES (%s)""" data = (newEntity,) cursor.execute(insertStmt, data) cursor.execute("""COMMIT""") self.entityUpdate.emit() self.doAssociationsForEntity(newEntity) self.addEntityLineEdit.setText("") def removeEntity(self): selected = self.entityList.selectedItems() cursor = mysql_conn.cursor() for item in selected: self.deleteAssciationsForEntity(item.text()) selectStmt = """SELECT entity_id FROM entities WHERE entity=%s""" data = (item.text(),) cursor.execute(selectStmt, data) entityId = cursor.fetchone() deleteStmt = """DELETE FROM entities WHERE entity_id=%s""" data = (entityId[0],) cursor.execute(deleteStmt, data) cursor.execute("""COMMIT""") self.entityUpdate.emit() def updateEntityList(self): self.entityList.clear() entities = self.selectAllEntities() for entity in entities: self.entityList.addItem(entity[1]) label = "Number of entities: " + str(len(entities)) self.entitiesCountLabel.setText(label) def updateArticleInfo(self): self.articleCount = self.selectCountArticles() entityCount = self.selectCountEntities() associationsCount = self.selectCountAssociations() classifiedCount = self.selectCountClassifiedAssociations() label = "Number of articles: " + str(self.articleCount) self.articleCountLabel.setText(label) label = "Number of entities: " + str(entityCount) self.entitiesCountLabel.setText(label) label = "Number of classified associations: " + str(classifiedCount) self.classifiedCountLabel.setText(label) label = "Number of associations: " + str(associationsCount) self.associationsCountLabel.setText(label) def classifyAllAssociations(self): cursor = mysql_conn.cursor() entities = self.selectAllEntities() for (entityId, entity) in entities: manualPol = self.selectManualPolaritiesForEntity(entityId) trainingData = [self.selectArticle(id_)[4] for (id_, _) in manualPol] trainingTarget = [polarity for (_, polarity) in manualPol] algorithm = self.algorithmComboBox.currentText() textClf = Pipeline([('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', classifiers[algorithm]), ]) textClf.fit(trainingData, trainingTarget) # select all articles associated with entity that need to be classified selectStmt = """SELECT article_id FROM assocEntityArticle WHERE polarity_manual IS NULL AND polarity_calculated IS NULL AND entity_id=%s""" data = (entityId,) cursor.execute(selectStmt, data) ids = cursor.fetchall() if len(ids) > 0: ids = [a[0] for a in ids] testData = [self.selectArticle(id_)[4] for id_ in ids] predicted = textClf.predict(testData) print [x for x in predicted].count(1) updateData = zip(predicted, ids) updateData = [(polarity, entityId, id_) for (polarity, id_) in updateData] updateStmt = """UPDATE assocEntityArticle SET polarity_calculated=%s WHERE entity_id=%s AND article_id=%s""" cursor.executemany(updateStmt, updateData) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() def selectArticle(self, articleId): cursor = mysql_conn.cursor() selectStmt = """SELECT * FROM articles WHERE article_id=%s""" data = (articleId,) cursor.execute(selectStmt, data) row = cursor.fetchone() return row def selectEntityId(self, entity): cursor = mysql_conn.cursor() selectStmt = """SELECT entity_id FROM entities WHERE entity=%s""" data = (entity,) cursor.execute(selectStmt, data) entityId = cursor.fetchone()[0] return entityId def selectAllArticlesByEntity(self, entity): cursor = mysql_conn.cursor() selectStmt = """SELECT * FROM articles WHERE content LIKE %s""" data = ("%" + entity + "%",) cursor.execute(selectStmt, data) rows = cursor.fetchall() return rows def selectAllEntities(self): cursor = mysql_conn.cursor() selectStmt = """SELECT * FROM entities""" cursor.execute(selectStmt) rows = cursor.fetchall() return rows def selectMinAndMaxDate(self): cursor = mysql_conn.cursor() selectStmt = """SELECT MIN(date), MAX(date) FROM articles""" cursor.execute(selectStmt) row = cursor.fetchone() return row def selectCountArticles(self): cursor = mysql_conn.cursor() selectStmt = """SELECT count(*) FROM articles""" cursor.execute(selectStmt) row = cursor.fetchone() return row[0] def selectCountAuthors(self): cursor = mysql_conn.cursor() selectStmt = """SELECT count(*) FROM authors""" cursor.execute(selectStmt) row = cursor.fetchone() return row[0] def selectCountEntities(self): cursor = mysql_conn.cursor() selectStmt = """SELECT count(*) FROM entities""" cursor.execute(selectStmt) row = cursor.fetchone() return row[0] def selectCountAssociations(self): cursor = mysql_conn.cursor() selectStmt = """SELECT count(*) FROM assocEntityArticle""" cursor.execute(selectStmt) row = cursor.fetchone() return row[0] def selectCountAssociationsForEntityBetweenDates(self, entityId, fromDate, toDate): cursor = mysql_conn.cursor() months = self.monthsBetweenDates(fromDate, toDate) selectStmt = """SELECT count(*) FROM assocEntityArticle a, articles b WHERE a.article_id = b.article_id AND b.date BETWEEN %s AND %s AND a.entity_id=%s""" associations = [] if len(months) != 0: for month in months: fromDateString = self.getStringDate(month) toDateString = self.getStringDate(month.addMonths(1)) data = (fromDateString, toDateString, entityId) cursor.execute(selectStmt, data) count = cursor.fetchone()[0] associations.append((month, count)) return associations def selectCountClassifiedAssociations(self): cursor = mysql_conn.cursor() selectStmt = """SELECT count(*) FROM assocEntityArticle WHERE polarity_calculated IS NOT NULL OR polarity_manual IS NOT NULL""" cursor.execute(selectStmt) row = cursor.fetchone() return row[0] def selectManualPolaritiesForEntity(self, entityId): cursor = mysql_conn.cursor() selectStmt = """SELECT article_id, polarity_manual FROM assocEntityArticle WHERE polarity_manual IS NOT NULL AND entity_id=%s""" data = (entityId,) cursor.execute(selectStmt, data) rows = cursor.fetchall() return rows def selectAllPolaritiesForEntity(self, entityId, fromDate, toDate): cursor = mysql_conn.cursor() months = self.monthsBetweenDates(fromDate, toDate) selectStmt = """SELECT a.polarity_manual, a.polarity_calculated FROM assocEntityArticle a, articles b WHERE (a.polarity_manual IS NOT NULL OR a.polarity_calculated IS NOT NULL) AND a.article_id = b.article_id AND b.date BETWEEN %s AND %s AND a.entity_id=%s""" polarities = [] if len(months) != 0: for month in months: fromDateString = self.getStringDate(month) toDateString = self.getStringDate(month.addMonths(1)) data = (fromDateString, toDateString, entityId) cursor.execute(selectStmt, data) rows = cursor.fetchall() rows = [a or b for a, b in rows] polarities.append((month, len(rows), rows)) return polarities def doAssociationsForEntity(self, entity): cursor = mysql_conn.cursor() # select entity_id for entity given as parameter entityId = self.selectEntityId(entity) # select list of article_id for which associations exist # in database for entity given as param selectStmt = """SELECT article_id FROM assocEntityArticle WHERE entity_id=%s""" data = (entityId,) cursor.execute(selectStmt, data) articleIdsInDB = cursor.fetchall() articleIdsInDB = [pair[0] for pair in articleIdsInDB] # select all articles that contain entity in content selectStmt = """SELECT article_id FROM articles WHERE content LIKE %s""" data = ("%" + entity + "%",) cursor.execute(selectStmt, data) rows = cursor.fetchall() rows = [pair[0] for pair in rows] # find articles for which associations don't exist in the database diff = list(set(rows) - set(articleIdsInDB)) if len(diff) != 0: insertStmt = """INSERT INTO assocEntityArticle (article_id, entity_id) VALUES (%s, %s)""" data = [(articleId, entityId) for articleId in diff] cursor.executemany(insertStmt, data) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() def deleteAssciationsForEntity(self, entity): cursor = mysql_conn.cursor() selectStmt = """SELECT entity_id FROM entities WHERE entity=%s""" data = (entity,) cursor.execute(selectStmt, data) entityId = cursor.fetchone()[0] deleteStmt = """DELETE FROM assocEntityArticle WHERE entity_id=%s""" data = (entityId,) cursor.execute(deleteStmt, data) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() def doAllAssociations(self): cursor = mysql_conn.cursor() entities = self.selectAllEntities() for entity in entities: self.doAssociationsForEntity(entity) self.articleInfoUpdate.emit() def deleteAllAssociations(self): cursor = mysql_conn.cursor() deleteStmt = """DELETE FROM assocEntityArticle WHERE article_id > 0""" cursor.execute(deleteStmt) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() def clearAllCalculatedPolarities(self): cursor = mysql_conn.cursor() updateStmt = """UPDATE assocEntityArticle SET polarity_calculated=%s WHERE polarity_calculated IS NOT NULL""" data = (None,) cursor.execute(updateStmt, data) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() def deleteAllArticles(self): try: cursor = mysql_conn.cursor() deleteStmt = """DELETE FROM articles WHERE article_id > 0""" cursor.execute(deleteStmt) alterTableStmt = """ALTER TABLE articles AUTO_INCREMENT = 1""" cursor.execute(alterTableStmt) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() except IntegrityError: pass def deleteAllAuthors(self): cursor = mysql_conn.cursor() deleteStmt = """DELETE FROM authors WHERE author_id > 0""" cursor.execute(deleteStmt) alterTableStmt = """ALTER TABLE authors AUTO_INCREMENT = 1""" cursor.execute(alterTableStmt) cursor.execute("""COMMIT""") def deleteAllArticlesAndAuthors(self): self.deleteAllArticles() self.deleteAllAuthors() def deleteAllEntities(self): cursor = mysql_conn.cursor() deleteStmt = """DELETE FROM entities WHERE entity_id > 0""" cursor.execute(deleteStmt) alterTableStmt = """ALTER TABLE entities AUTO_INCREMENT = 1""" cursor.execute(alterTableStmt) cursor.execute("""COMMIT""") self.articleInfoUpdate.emit() self.entityUpdate.emit()
class Filters(QWidget): def __init__(self, db, display={}): QWidget.__init__(self, None) self.db = db self.cursor = db.cursor self.sql = db.sql self.conf = db.config self.display = display self.gameName = { "27_1draw": _("Single Draw 2-7 Lowball"), "27_3draw": _("Triple Draw 2-7 Lowball"), "a5_3draw": _("Triple Draw A-5 Lowball"), "5_studhi": _("5 Card Stud"), "badugi": _("Badugi"), "fivedraw": _("5 Card Draw"), "holdem": _("Hold'em"), "omahahi": _("Omaha"), "omahahilo": _("Omaha Hi/Lo"), "razz": _("Razz"), "studhi": _("7 Card Stud"), "studhilo": _("7 Card Stud Hi/Lo"), "5_omahahi": _("5 Card Omaha"), "5_omaha8": _("5 Card Omaha Hi/Lo"), "cour_hi": _("Courchevel"), "cour_hilo": _("Courchevel Hi/Lo"), "2_holdem": _("Double hold'em"), "irish": _("Irish"), "6_omahahi": _("6 Card Omaha") } self.currencyName = { "USD": _("US Dollar"), "EUR": _("Euro"), "T$": _("Tournament Dollar"), "play": _("Play Money") } # text used on screen stored here so that it can be configured self.filterText = { 'limitsall': _('All'), 'limitsnone': _('None'), 'limitsshow': _('Show Limits'), 'gamesall': _('All'), 'gamesnone': _('None'), 'positionsall': _('All'), 'positionsnone': _('None'), 'currenciesall': _('All'), 'currenciesnone': _('None'), 'seatsbetween': _('Between:'), 'seatsand': _('And:'), 'seatsshow': _('Show Number of Players'), 'playerstitle': _('Hero:'), 'sitestitle': (_('Sites') + ':'), 'gamestitle': (_('Games') + ':'), 'limitstitle': _('Limits:'), 'positionstitle': _('Positions:'), 'seatstitle': _('Number of Players:'), 'groupstitle': _('Grouping:'), 'posnshow': _('Show Position Stats'), 'datestitle': _('Date:'), 'currenciestitle': (_('Currencies') + ':'), 'groupsall': _('All Players'), 'cardstitle': (_('Hole Cards') + ':'), 'limitsFL': 'FL', 'limitsNL': 'NL', 'limitsPL': 'PL', 'limitsCN': 'CAP', 'ring': _('Ring'), 'tour': _('Tourney'), 'limitsHP': 'HP' } gen = self.conf.get_general_params() self.day_start = 0 if 'day_start' in gen: self.day_start = float(gen['day_start']) self.setLayout(QVBoxLayout()) self.callback = {} self.setStyleSheet( "QPushButton {padding-left:5;padding-right:5;padding-top:2;padding-bottom:2;}" ) self.make_filter() def make_filter(self): self.siteid = {} self.cards = {} for site in self.conf.get_supported_sites(): # Get db site id for filtering later self.cursor.execute(self.sql.query['getSiteId'], (site, )) result = self.db.cursor.fetchall() if len(result) == 1: self.siteid[site] = result[0][0] else: log.debug(_("Either 0 or more than one site matched for %s"), site) # For use in date ranges. self.start_date = QDateEdit(QDate(1970, 1, 1)) self.end_date = QDateEdit(QDate(2100, 1, 1)) # For use in groups etc self.cbGroups = {} self.phands = None playerFrame = QGroupBox(self.filterText['playerstitle']) self.leHeroes = {} self.fillPlayerFrame(playerFrame, self.display) self.layout().addWidget(playerFrame) # Sites sitesFrame = QGroupBox(self.filterText['sitestitle']) self.cbSites = {} self.fillSitesFrame(sitesFrame) self.layout().addWidget(sitesFrame) # Game types gamesFrame = QGroupBox(self.filterText['gamestitle']) self.layout().addWidget(gamesFrame) self.cbGames = {} self.fillGamesFrame(gamesFrame) # Currencies currenciesFrame = QGroupBox(self.filterText['currenciestitle']) self.layout().addWidget(currenciesFrame) self.cbCurrencies = {} self.fillCurrenciesFrame(currenciesFrame) # Limits limitsFrame = QGroupBox(self.filterText['limitstitle']) self.layout().addWidget(limitsFrame) self.cbLimits = {} self.rb = {} # radio buttons for ring/tour self.type = None # ring/tour self.fillLimitsFrame(limitsFrame, self.display) # Positions positionsFrame = QGroupBox(self.filterText['positionstitle']) self.layout().addWidget(positionsFrame) self.cbPositions = {} self.fillPositionsFrame(positionsFrame, self.display) # GraphOps graphopsFrame = QGroupBox(_("Graphing Options:")) self.layout().addWidget(graphopsFrame) self.cbGraphops = {} self.fillGraphOpsFrame(graphopsFrame) # Seats seatsFrame = QGroupBox(self.filterText['seatstitle']) self.layout().addWidget(seatsFrame) self.sbSeats = {} self.fillSeatsFrame(seatsFrame) # Groups groupsFrame = QGroupBox(self.filterText['groupstitle']) self.layout().addWidget(groupsFrame) self.fillGroupsFrame(groupsFrame, self.display) # Date dateFrame = QGroupBox(self.filterText['datestitle']) self.layout().addWidget(dateFrame) self.fillDateFrame(dateFrame) # Hole cards cardsFrame = QGroupBox(self.filterText['cardstitle']) self.layout().addWidget(cardsFrame) self.fillHoleCardsFrame(cardsFrame) # Buttons self.Button1 = QPushButton("Unnamed 1") self.Button2 = QPushButton("Unnamed 2") self.layout().addWidget(self.Button1) self.layout().addWidget(self.Button2) # Should do this cleaner if "Heroes" not in self.display or self.display["Heroes"] is False: playerFrame.hide() if "Sites" not in self.display or self.display["Sites"] is False: sitesFrame.hide() if "Games" not in self.display or self.display["Games"] is False: gamesFrame.hide() if "Currencies" not in self.display or self.display[ "Currencies"] is False: currenciesFrame.hide() if "Limits" not in self.display or self.display["Limits"] is False: limitsFrame.hide() if "Positions" not in self.display or self.display[ "Positions"] is False: positionsFrame.hide() if "Seats" not in self.display or self.display["Seats"] is False: seatsFrame.hide() if "Groups" not in self.display or self.display["Groups"] is False: groupsFrame.hide() if "Dates" not in self.display or self.display["Dates"] is False: dateFrame.hide() if "GraphOps" not in self.display or self.display["GraphOps"] is False: graphopsFrame.hide() if "Cards" not in self.display or self.display["Cards"] is False: cardsFrame.hide() if "Button1" not in self.display or self.display["Button1"] is False: self.Button1.hide() if "Button2" not in self.display or self.display["Button2"] is False: self.Button2.hide() # make sure any locks on db are released: self.db.rollback() def getNumHands(self): if self.phands: return self.phands.value() return 0 def getNumTourneys(self): return 0 def getSites(self): return [s for s in self.cbSites if self.cbSites[s].isChecked()] def getPositions(self): return [p for p in self.cbPositions if self.cbPositions[p].isChecked()] def getTourneyTypes(self): return [] def getGames(self): return [g for g in self.cbGames if self.cbGames[g].isChecked()] def getCards(self): return self.cards def getCurrencies(self): return [ c for c in self.cbCurrencies if self.cbCurrencies[c].isChecked() ] def getSiteIds(self): return self.siteid def getHeroes(self): return dict([(site, unicode(self.leHeroes[site].text())) for site in self.leHeroes]) def getGraphOps(self): return [g for g in self.cbGraphops if self.cbGraphops[g].isChecked()] def getLimits(self): return [l for l in self.cbLimits if self.cbLimits[l].isChecked()] def getType(self): return (self.type) def getSeats(self): result = {} if 'from' in self.sbSeats: result['from'] = self.sbSeats['from'].value() if 'to' in self.sbSeats: result['to'] = self.sbSeats['to'].value() return result def getGroups(self): return [g for g in self.cbGroups if self.cbGroups[g].isChecked()] def getDates(self): # self.day_start gives user's start of day in hours offset = int(self.day_start * 3600) # calc day_start in seconds t1 = self.start_date.date() t2 = self.end_date.date() adj_t1 = QDateTime(t1).addSecs(offset) adj_t2 = QDateTime(t2).addSecs(offset + 24 * 3600 - 1) return (adj_t1.toUTC().toString("yyyy-MM-dd HH:mm:ss"), adj_t2.toUTC().toString("yyyy-MM-dd HH:mm:ss")) def registerButton1Name(self, title): self.Button1.setText(title) def registerButton1Callback(self, callback): self.Button1.clicked.connect(callback) self.Button1.setEnabled(True) self.callback['button1'] = callback def registerButton2Name(self, title): self.Button2.setText(title) def registerButton2Callback(self, callback): self.Button2.clicked.connect(callback) self.Button2.setEnabled(True) self.callback['button2'] = callback def registerCardsCallback(self, callback): self.callback['cards'] = callback def __set_tourney_type_select(self, w, tourneyType): self.tourneyTypes[tourneyType] = w.get_active() log.debug("self.tourney_types[%s] set to %s", tourneyType, self.tourneyTypes[tourneyType]) def createTourneyTypeLine(self, hbox, tourneyType): cb = QCheckBox(str(tourneyType)) cb.clicked.connect( partial(self.__set_tourney_type_select, tourneyType=tourneyType)) hbox.addWidget(cb) cb.setChecked(True) def createCardsWidget(self, grid): grid.setSpacing(0) for i in range(0, 13): for j in range(0, 13): abbr = Card.card_map_abbr[j][i] b = QPushButton("") import platform if platform.system == "Darwin": b.setStyleSheet( "QPushButton {border-width:0;margin:6;padding:0;}") else: b.setStyleSheet( "QPushButton {border-width:0;margin:0;padding:0;}") b.clicked.connect( partial(self.__toggle_card_select, widget=b, card=abbr)) self.cards[ abbr] = False # NOTE: This is flippped in __toggle_card_select below self.__toggle_card_select(False, widget=b, card=abbr) grid.addWidget(b, j, i) def createCardsControls(self, hbox): selections = ["All", "Suited", "Off Suit"] for s in selections: cb = QCheckBox(s) cb.clicked.connect(self.__set_cards) hbox.addWidget(cb) def __card_select_bgcolor(self, card, selected): s_on = "red" s_off = "orange" o_on = "white" o_off = "lightgrey" p_on = "blue" p_off = "lightblue" if len(card) == 2: return p_on if selected else p_off if card[2] == 's': return s_on if selected else s_off if card[2] == 'o': return o_on if selected else o_off def __toggle_card_select(self, checkState, widget, card): font = widget.font() font.setPointSize(10) widget.setFont(font) widget.setText(card) self.cards[card] = not self.cards[card] # bg_color = self.__card_select_bgcolor(card, self.cards[card]) # style = w.get_style().copy() # style.bg[gtk.STATE_NORMAL] = w.get_colormap().alloc(bg_color) # w.set_style(style) if 'cards' in self.callback: self.callback['cards'](card) def __set_cards(self, checkState): pass def __set_checkboxes(self, checkState, checkBoxes, setState): for checkbox in checkBoxes.values(): checkbox.setChecked(setState) def __select_limit(self, checkState, limit): for l, checkbox in self.cbLimits.items(): if l.endswith(limit): checkbox.setChecked(True) def fillPlayerFrame(self, frame, display): vbox = QVBoxLayout() frame.setLayout(vbox) for site in self.conf.get_supported_sites(): player = self.conf.supported_sites[site].screen_name _pname = Charset.to_gui(player) vbox.addWidget(QLabel(site + " id:")) self.leHeroes[site] = QLineEdit(_pname) vbox.addWidget(self.leHeroes[site]) names = self.db.get_player_names(self.conf, self.siteid[site]) completer = QCompleter([Charset.to_gui(n[0]) for n in names]) self.leHeroes[site].setCompleter(completer) if "GroupsAll" in display and display["GroupsAll"]: hbox = QHBoxLayout() vbox.addLayout(hbox) self.cbGroups['allplayers'] = QCheckBox( self.filterText['groupsall']) hbox.addWidget(self.cbGroups['allplayers']) lbl = QLabel(_('Min # Hands:')) hbox.addWidget(lbl) self.phands = QSpinBox() self.phands.setMaximum(1e5) hbox.addWidget(self.phands) def fillSitesFrame(self, frame): vbox = QVBoxLayout() frame.setLayout(vbox) for site in self.conf.get_supported_sites(): self.cbSites[site] = QCheckBox(site) self.cbSites[site].setChecked(True) vbox.addWidget(self.cbSites[site]) def fillTourneyTypesFrame(self, vbox): vbox1 = QVBoxLayout() vbox.setLayout(vbox1) self.boxes['tourneyTypes'] = vbox1 result = self.db.getTourneyTypesIds() if len(result) >= 1: for line in result: hbox = QHBoxLayout() vbox1.addLayout(hbox) self.createTourneyTypeLine(hbox, line[0]) else: print _("INFO: No tourney types returned from database") log.info(_("No tourney types returned from database")) def fillGamesFrame(self, frame): vbox1 = QVBoxLayout() frame.setLayout(vbox1) self.cursor.execute(self.sql.query['getGames']) result = self.db.cursor.fetchall() if len(result) >= 1: for line in sorted(result, key=lambda game: self.gameName[game[0]]): self.cbGames[line[0]] = QCheckBox(self.gameName[line[0]]) self.cbGames[line[0]].setChecked(True) vbox1.addWidget(self.cbGames[line[0]]) if len(result) >= 2: hbox = QHBoxLayout() vbox1.addLayout(hbox) hbox.addStretch() btnAll = QPushButton(self.filterText['gamesall']) btnAll.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbGames, setState=True)) hbox.addWidget(btnAll) btnNone = QPushButton(self.filterText['gamesnone']) btnNone.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbGames, setState=False)) hbox.addWidget(btnNone) hbox.addStretch() else: print _("INFO: No games returned from database") log.info(_("No games returned from database")) def fillPositionsFrame(self, frame, display): vbox1 = QVBoxLayout() frame.setLayout(vbox1) # the following is not the fastest query (as it querys a table with potentialy a lot of data), so dont execute it if not necessary if "Positions" not in display or display["Positions"] is False: return #This takes too long if there are a couple of 100k hands in the DB #self.cursor.execute(self.sql.query['getPositions']) #result = self.db.cursor.fetchall() result = [[0], [1], [2], [3], [4], [5], [6], [7], ['S'], ['B']] res_count = len(result) if res_count > 0: v_count = 0 COL_COUNT = 4 #Number of columns hbox = None for line in result: if v_count == 0: #start a new line when the vertical count is 0 hbox = QHBoxLayout() vbox1.addLayout(hbox) line_str = str(line[0]) self.cbPositions[line_str] = QCheckBox(line_str) self.cbPositions[line_str].setChecked(True) hbox.addWidget(self.cbPositions[line_str]) v_count += 1 if v_count == COL_COUNT: #set the counter to 0 if the line is full v_count = 0 dif = res_count % COL_COUNT while dif > 0: #fill the rest of the line with empy boxes, so that every line contains COL_COUNT elements fillbox = QVBoxLayout() hbox.addLayout(fillbox) dif -= 1 if res_count > 1: hbox = QHBoxLayout() vbox1.addLayout(hbox) hbox.addStretch() btnAll = QPushButton(self.filterText['positionsall']) btnAll.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbPositions, setState=True)) hbox.addWidget(btnAll) btnNone = QPushButton(self.filterText['positionsnone']) btnNone.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbPositions, setState=False)) hbox.addWidget(btnNone) hbox.addStretch() else: print(_("INFO") + ": " + _("No positions returned from database")) log.info(_("No positions returned from database")) def fillHoleCardsFrame(self, frame): vbox1 = QVBoxLayout() frame.setLayout(vbox1) grid = QGridLayout() vbox1.addLayout(grid) self.createCardsWidget(grid) # Additional controls for bulk changing card selection hbox = QHBoxLayout() vbox1.addLayout(hbox) self.createCardsControls(hbox) def fillCurrenciesFrame(self, frame): vbox1 = QVBoxLayout() frame.setLayout(vbox1) self.cursor.execute(self.sql.query['getCurrencies']) result = self.db.cursor.fetchall() if len(result) >= 1: for line in result: if line[0] in self.currencyName: cname = self.currencyName[line[0]] else: cname = line[0] self.cbCurrencies[line[0]] = QCheckBox(cname) self.cbCurrencies[line[0]].setChecked(True) vbox1.addWidget(self.cbCurrencies[line[0]]) if len(result) >= 2: hbox = QHBoxLayout() vbox1.addLayout(hbox) hbox.addStretch() btnAll = QPushButton(self.filterText['currenciesall']) btnAll.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbCurrencies, setState=True)) hbox.addWidget(btnAll) btnNone = QPushButton(self.filterText['currenciesnone']) btnNone.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbCurrencies, setState=False)) hbox.addWidget(btnNone) hbox.addStretch() else: # There is only one currency. Select it, even if it's Play Money. self.cbCurrencies[line[0]].setChecked(True) else: log.info(_("No currencies returned from database")) def fillLimitsFrame(self, frame, display): vbox1 = QVBoxLayout() frame.setLayout(vbox1) self.cursor.execute(self.sql.query['getCashLimits']) # selects limitType, bigBlind result = self.db.cursor.fetchall() limits_found = set() types_found = set() if len(result) >= 1: hbox = QHBoxLayout() vbox1.addLayout(hbox) vbox2 = QVBoxLayout() hbox.addLayout(vbox2) vbox3 = QVBoxLayout() hbox.addLayout(vbox3) for i, line in enumerate(result): if "UseType" in self.display: if line[0] != self.display["UseType"]: continue hbox = QHBoxLayout() if i < (len(result) + 1) / 2: vbox2.addLayout(hbox) else: vbox3.addLayout(hbox) if True: #line[0] == 'ring': name = str(line[2]) + line[1] limits_found.add(line[1]) self.cbLimits[name] = QCheckBox(name) self.cbLimits[name].setChecked(True) hbox.addWidget(self.cbLimits[name]) types_found.add(line[0]) # type is ring/tour self.type = line[0] # if only one type, set it now if "LimitSep" in display and display["LimitSep"] and len( result) >= 2: hbox = QHBoxLayout() vbox1.addLayout(hbox) hbox.addStretch() btnAll = QPushButton(self.filterText['limitsall']) btnAll.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbLimits, setState=True)) hbox.addWidget(btnAll) btnNone = QPushButton(self.filterText['limitsnone']) btnNone.clicked.connect( partial(self.__set_checkboxes, checkBoxes=self.cbLimits, setState=False)) hbox.addWidget(btnNone) if "LimitType" in display and display["LimitType"] and len( limits_found) > 1: for limit in limits_found: btn = QPushButton(self.filterText['limits' + limit.upper()]) btn.clicked.connect( partial(self.__select_limit, limit=limit)) hbox.addWidget(btn) hbox.addStretch() else: print _("INFO: No games returned from database") log.info(_("No games returned from database")) if "Type" in display and display[ "Type"] and 'ring' in types_found and 'tour' in types_found: # rb1 = QRadioButton(frame, self.filterText['ring']) # rb1.clicked.connect(self.__set_limit_select) # rb2 = QRadioButton(frame, self.filterText['tour']) # rb2.clicked.connect(self.__set_limit_select) # top_hbox.addWidget(rb1) # top_hbox.addWidget(rb2) # # self.rb['ring'] = rb1 # self.rb['tour'] = rb2 # #print "about to set ring to true" # rb1.setChecked(True) # # set_active doesn't seem to call this for some reason so call manually: # self.__set_limit_select(rb1) self.type = 'ring' def fillGraphOpsFrame(self, frame): vbox1 = QVBoxLayout() frame.setLayout(vbox1) hbox1 = QHBoxLayout() vbox1.addLayout(hbox1) label = QLabel(_("Show Graph In:")) hbox1.addWidget(label) self.cbGraphops['$'] = QRadioButton("$$", frame) hbox1.addWidget(self.cbGraphops['$']) self.cbGraphops['$'].setChecked(True) self.cbGraphops['BB'] = QRadioButton("BB", frame) hbox1.addWidget(self.cbGraphops['BB']) self.cbGraphops['showdown'] = QCheckBox(_("Showdown Winnings")) vbox1.addWidget(self.cbGraphops['showdown']) self.cbGraphops['nonshowdown'] = QCheckBox(_("Non-Showdown Winnings")) vbox1.addWidget(self.cbGraphops['nonshowdown']) self.cbGraphops['ev'] = QCheckBox(_("EV")) vbox1.addWidget(self.cbGraphops['ev']) def fillSeatsFrame(self, frame): hbox = QHBoxLayout() frame.setLayout(hbox) lbl_from = QLabel(self.filterText['seatsbetween']) lbl_to = QLabel(self.filterText['seatsand']) adj1 = QSpinBox() adj1.setRange(2, 10) adj1.setValue(2) adj1.valueChanged.connect(partial(self.__seats_changed, 'from')) adj2 = QSpinBox() adj2.setRange(2, 10) adj2.setValue(10) adj2.valueChanged.connect(partial(self.__seats_changed, 'to')) hbox.addStretch() hbox.addWidget(lbl_from) hbox.addWidget(adj1) hbox.addWidget(lbl_to) hbox.addWidget(adj2) hbox.addStretch() self.sbSeats['from'] = adj1 self.sbSeats['to'] = adj2 def fillGroupsFrame(self, frame, display): vbox = QVBoxLayout() frame.setLayout(vbox) self.cbGroups['limits'] = QCheckBox(self.filterText['limitsshow']) vbox.addWidget(self.cbGroups['limits']) self.cbGroups['posn'] = QCheckBox(self.filterText['posnshow']) vbox.addWidget(self.cbGroups['posn']) if "SeatSep" in display and display["SeatSep"]: self.cbGroups['seats'] = QCheckBox(self.filterText['seatsshow']) vbox.addWidget(self.cbGroups['seats']) def fillDateFrame(self, frame): table = QGridLayout() frame.setLayout(table) lbl_start = QLabel(_('From:')) btn_start = QPushButton("Cal") btn_start.clicked.connect( partial(self.__calendar_dialog, dateEdit=self.start_date)) clr_start = QPushButton("Reset") clr_start.clicked.connect(self.__clear_start_date) lbl_end = QLabel(_('To:')) btn_end = QPushButton("Cal") btn_end.clicked.connect( partial(self.__calendar_dialog, dateEdit=self.end_date)) clr_end = QPushButton("Reset") clr_end.clicked.connect(self.__clear_end_date) table.addWidget(lbl_start, 0, 0) table.addWidget(btn_start, 0, 1) table.addWidget(self.start_date, 0, 2) table.addWidget(clr_start, 0, 3) table.addWidget(lbl_end, 1, 0) table.addWidget(btn_end, 1, 1) table.addWidget(self.end_date, 1, 2) table.addWidget(clr_end, 1, 3) table.setColumnStretch(0, 1) def get_limits_where_clause(self, limits): """Accepts a list of limits and returns a formatted SQL where clause starting with AND. Sql statement MUST link to gameType table and use the alias gt for that table.""" where = "" lims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'fl'] potlims = [ int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl' ] nolims = [ int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl' ] capnolims = [ int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'cn' ] hpnolims = [ int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'hp' ] where = "AND ( " if lims: clause = "(gt.limitType = 'fl' and gt.bigBlind in (%s))" % ( ','.join(map(str, lims))) else: clause = "(gt.limitType = 'fl' and gt.bigBlind in (-1))" where = where + clause if potlims: clause = "or (gt.limitType = 'pl' and gt.bigBlind in (%s))" % ( ','.join(map(str, potlims))) else: clause = "or (gt.limitType = 'pl' and gt.bigBlind in (-1))" where = where + clause if nolims: clause = "or (gt.limitType = 'nl' and gt.bigBlind in (%s))" % ( ','.join(map(str, nolims))) else: clause = "or (gt.limitType = 'nl' and gt.bigBlind in (-1))" where = where + clause if hpnolims: clause = "or (gt.limitType = 'hp' and gt.bigBlind in (%s))" % ( ','.join(map(str, hpnolims))) else: clause = "or (gt.limitType = 'hp' and gt.bigBlind in (-1))" where = where + clause if capnolims: clause = "or (gt.limitType = 'cp' and gt.bigBlind in (%s))" % ( ','.join(map(str, capnolims))) else: clause = "or (gt.limitType = 'cp' and gt.bigBlind in (-1))" where = where + clause + ' )' return where def replace_placeholders_with_filter_values(self, query): """ Returnes given query with replaced placeholders by the filter values from self. List of Placeholders that are replaced and some infos how the statement has to look like: (whole clause means it starts with AND and contains the whole clause) Placeholders table & alias or field SQL usage coresponding filter Name <player_test> Players.Id in <player_test> Heroes <game_test> GameType gt whole clause Game <limit_test> GameType gt whole clause Limits, LimitSep, LimitType <position_test> HandsPlayers hp whole clause Positions """ if '<game_test>' in query: games = self.getGames() if len(games) > 0: gametest = str(tuple(games)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)", ")") gametest = gametest.replace("u'", "'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" query = query.replace('<game_test>', gametest) if '<limit_test>' in query: #copyed from GuiGraphView limits = self.getLimits() limittest = self.get_limits_where_clause(limits) query = query.replace('<limit_test>', limittest) if '<player_test>' in query: #copyed from GuiGraphView sites = self.getSites() heroes = self.getHeroes() siteids = self.getSiteIds() sitenos = [] playerids = [] for site in sites: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(str(result)) query = query.replace('<player_test>', '(' + ','.join(playerids) + ')') if '<position_test>' in query: positions = self.getPositions() positiontest = "AND hp.position in ('" + "','".join( positions ) + "')" #values must be set in '' because they can be strings as well as numbers query = query.replace('<position_test>', positiontest) return query def __calendar_dialog(self, checkState, dateEdit): d = QDialog() d.setWindowTitle(_('Pick a date')) vb = QVBoxLayout() d.setLayout(vb) cal = QCalendarWidget() vb.addWidget(cal) btn = QPushButton(_('Done')) btn.clicked.connect( partial(self.__get_date, dlg=d, calendar=cal, dateEdit=dateEdit)) vb.addWidget(btn) d.exec_() def __clear_start_date(self, checkState): self.start_date.setDate(QDate(1970, 1, 1)) def __clear_end_date(self, checkState): self.end_date.setDate(QDate(2100, 1, 1)) def __get_date(self, checkState, dlg, calendar, dateEdit): newDate = calendar.selectedDate() dateEdit.setDate(newDate) # if the opposite date is set, and now the start date is later # than the end date, modify the one we didn't just set to be # the same as the one we did just set if dateEdit == self.start_date: end = self.end_date.date() if newDate > end: self.end_date.setDate(newDate) else: start = self.start_date.date() if newDate < start: self.start_date.setDate(newDate) dlg.accept() def __seats_changed(self, value, which): seats_from = self.sbSeats['from'].value() seats_to = self.sbSeats['to'].value() if seats_from > seats_to: if which == 'from': self.sbSeats['to'].setValue(seats_from) else: self.sbSeats['from'].setValue(seats_to)
class PaymentDlg(QDialog): def __init__(self, parent=None): super(PaymentDlg, self).__init__(parent) forenameLabel = QLabel("&Forename:") self.forenameLineEdit = QLineEdit() forenameLabel.setBuddy(self.forenameLineEdit) surnameLabel = QLabel("&Surname:") self.surnameLineEdit = QLineEdit() surnameLabel.setBuddy(self.surnameLineEdit) invoiceLabel = QLabel("&Invoice No.:") self.invoiceSpinBox = QSpinBox() invoiceLabel.setBuddy(self.invoiceSpinBox) self.invoiceSpinBox.setRange(1, 10000000) self.invoiceSpinBox.setValue(100000) self.invoiceSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter) amountLabel = QLabel("&Amount:") self.amountSpinBox = QDoubleSpinBox() amountLabel.setBuddy(self.amountSpinBox) self.amountSpinBox.setRange(0, 5000.0) self.amountSpinBox.setPrefix("$ ") self.amountSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.paidCheckBox = QCheckBox("&Paid") checkNumLabel = QLabel("Check &No.:") self.checkNumLineEdit = QLineEdit() checkNumLabel.setBuddy(self.checkNumLineEdit) bankLabel = QLabel("&Bank:") self.bankLineEdit = QLineEdit() bankLabel.setBuddy(self.bankLineEdit) accountNumLabel = QLabel("Acco&unt No.:") self.accountNumLineEdit = QLineEdit() accountNumLabel.setBuddy(self.accountNumLineEdit) sortCodeLabel = QLabel("Sort &Code:") self.sortCodeLineEdit = QLineEdit() sortCodeLabel.setBuddy(self.sortCodeLineEdit) creditCardLabel = QLabel("&Number:") self.creditCardLineEdit = QLineEdit() creditCardLabel.setBuddy(self.creditCardLineEdit) validFromLabel = QLabel("&Valid From:") self.validFromDateEdit = QDateEdit() validFromLabel.setBuddy(self.validFromDateEdit) self.validFromDateEdit.setDisplayFormat("MMM yyyy") self.validFromDateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter) expiryLabel = QLabel("E&xpiry Date:") self.expiryDateEdit = QDateEdit() expiryLabel.setBuddy(self.expiryDateEdit) self.expiryDateEdit.setDisplayFormat("MMM yyyy") self.expiryDateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| QDialogButtonBox.Cancel) tabWidget = QTabWidget() cashWidget = QWidget() cashLayout = QHBoxLayout() cashLayout.addWidget(self.paidCheckBox) cashWidget.setLayout(cashLayout) tabWidget.addTab(cashWidget, "Cas&h") checkWidget = QWidget() checkLayout = QGridLayout() checkLayout.addWidget(checkNumLabel, 0, 0) checkLayout.addWidget(self.checkNumLineEdit, 0, 1) checkLayout.addWidget(bankLabel, 0, 2) checkLayout.addWidget(self.bankLineEdit, 0, 3) checkLayout.addWidget(accountNumLabel, 1, 0) checkLayout.addWidget(self.accountNumLineEdit, 1, 1) checkLayout.addWidget(sortCodeLabel, 1, 2) checkLayout.addWidget(self.sortCodeLineEdit, 1, 3) checkWidget.setLayout(checkLayout) tabWidget.addTab(checkWidget, "Chec&k") creditWidget = QWidget() creditLayout = QGridLayout() creditLayout.addWidget(creditCardLabel, 0, 0) creditLayout.addWidget(self.creditCardLineEdit, 0, 1, 1, 3) creditLayout.addWidget(validFromLabel, 1, 0) creditLayout.addWidget(self.validFromDateEdit, 1, 1) creditLayout.addWidget(expiryLabel, 1, 2) creditLayout.addWidget(self.expiryDateEdit, 1, 3) creditWidget.setLayout(creditLayout) tabWidget.addTab(creditWidget, "Credit Car&d") gridLayout = QGridLayout() gridLayout.addWidget(forenameLabel, 0, 0) gridLayout.addWidget(self.forenameLineEdit, 0, 1) gridLayout.addWidget(surnameLabel, 0, 2) gridLayout.addWidget(self.surnameLineEdit, 0, 3) gridLayout.addWidget(invoiceLabel, 1, 0) gridLayout.addWidget(self.invoiceSpinBox, 1, 1) gridLayout.addWidget(amountLabel, 1, 2) gridLayout.addWidget(self.amountSpinBox, 1, 3) layout = QVBoxLayout() layout.addLayout(gridLayout) layout.addWidget(tabWidget) layout.addWidget(self.buttonBox) self.setLayout(layout) for lineEdit in (self.forenameLineEdit, self.surnameLineEdit, self.checkNumLineEdit, self.accountNumLineEdit, self.bankLineEdit, self.sortCodeLineEdit, self.creditCardLineEdit): lineEdit.textEdited.connect(self.updateUi) for dateEdit in (self.validFromDateEdit, self.expiryDateEdit): dateEdit.dateChanged.connect(self.updateUi) self.paidCheckBox.clicked.connect(self.updateUi) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.updateUi() self.setWindowTitle("Payment Form") def updateUi(self): today = QDate.currentDate() enable = (self.forenameLineEdit.text() and self.surnameLineEdit.text()) if enable: enable = (self.paidCheckBox.isChecked() or (self.checkNumLineEdit.text() and self.accountNumLineEdit.text() and self.bankLineEdit.text() and self.sortCodeLineEdit.text()) or (self.creditCardLineEdit.text() and self.validFromDateEdit.date() <= today and self.expiryDateEdit.date() >= today)) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(bool(enable))
class ExportWindow(QDialog): """ Class describing a dialog for exports of schedules. """ def __init__(self, schedule: Schedule, parent: QWidget = None): super().__init__(parent) self._schedule_ref = schedule self._date_start_cache = None # window settings self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) self.setWindowTitle(self.tr("Export window")) self.setMinimumWidth(800) # title, add_date, work mode self.layout_title_date_mode = QHBoxLayout() self.label_title = QLabel(self.tr("Title")) self.layout_title_date_mode.addWidget(self.label_title) self.line_edit_title = QLineEdit() self.layout_title_date_mode.addWidget(self.line_edit_title) self.check_box_add_date = QCheckBox(self.tr("Add date")) self.layout_title_date_mode.addWidget(self.check_box_add_date) self.combo_box_work_mode = QComboBox() self.layout_title_date_mode.addWidget(self.combo_box_work_mode) self.line_edit_title.setPlaceholderText( self.tr("My Group. A subgroup - green color, " "B subgroup - yellow color. ")) self.check_box_add_date.setChecked(True) self.combo_box_work_mode.addItem(self.tr("Weekly")) self.combo_box_work_mode.addItem(self.tr("Full")) # list widget with files self.layout_list_widget = QVBoxLayout() self.check_box_use_current = QCheckBox(self.tr("Use current schedule")) self.layout_list_widget.addWidget(self.check_box_use_current) self.check_box_use_current.setChecked(True) self.list_widget = QListWidget() self.layout_list_widget.addWidget(self.list_widget) self.list_widget.setSortingEnabled(True) self.list_widget.setEnabled(False) self.layout_open_folder = QHBoxLayout() self.layout_list_widget.addLayout(self.layout_open_folder) self.label_find = QLabel(self.tr("Schedules: ") + "0") self.layout_open_folder.addWidget(self.label_find) self.layout_open_folder.addStretch(1) self.push_button_open_folder = QToolButton() self.layout_open_folder.addWidget(self.push_button_open_folder) self.push_button_open_folder.setEnabled(False) self.push_button_open_folder.setText(self.tr("Open folder")) self.push_button_open_folder.setPopupMode(QToolButton.MenuButtonPopup) self.action_open_files = QAction(self.tr("Open files")) self.push_button_open_folder.addAction(self.action_open_files) # font edit self.group_box_font = QGroupBox(self.tr("Font settings")) self.form_layout_font = QFormLayout(self.group_box_font) self.label_font = QLabel(self.tr("Font")) self.form_layout_font.setWidget(0, QFormLayout.LabelRole, self.label_font) self.combo_box_font = QComboBox() self.form_layout_font.setWidget(0, QFormLayout.FieldRole, self.combo_box_font) self.label_encoding = QLabel(self.tr("Encoding")) self.form_layout_font.setWidget(1, QFormLayout.LabelRole, self.label_encoding) self.combo_box_encoding = QComboBox() self.form_layout_font.setWidget(1, QFormLayout.FieldRole, self.combo_box_encoding) for font_name, font_path in util.get_fonts(): self.combo_box_font.addItem(font_name, font_path) self.combo_box_font.setCurrentText(qApp.font().family()) self.combo_box_font.setEditable(True) self.combo_box_encoding.addItem("UTF-8") self.combo_box_encoding.addItem("Latin-1") self.combo_box_encoding.addItem("Windows-1252") # date edit self.group_box_date = QGroupBox(self.tr("Date settings")) self.form_layout_date = QFormLayout(self.group_box_date) self.label_date_start = QLabel(self.tr("Start")) self.form_layout_date.setWidget(0, QFormLayout.LabelRole, self.label_date_start) self.date_edit_start = QDateEdit() self.form_layout_date.setWidget(0, QFormLayout.FieldRole, self.date_edit_start) self.label_date_end = QLabel(self.tr("End")) self.form_layout_date.setWidget(1, QFormLayout.LabelRole, self.label_date_end) self.date_edit_end = QDateEdit() self.form_layout_date.setWidget(1, QFormLayout.FieldRole, self.date_edit_end) self.date_edit_start.setCalendarPopup(True) self.date_edit_end.setCalendarPopup(True) if QDate.currentDate().day() < (QDate.currentDate().dayOfYear() / 2): date = QDate(QDate.currentDate().year(), 2, 1) else: date = QDate(QDate.currentDate().year(), 9, 1) self._date_start_cache = date.addDays(8 - date.dayOfWeek()) self.date_edit_start.setDate(self._date_start_cache) self.date_edit_end.setMinimumDate(self._date_start_cache.addDays(7)) self.date_edit_end.setDate(self._date_start_cache.addDays(16 * 7)) # subgroup edit self.group_box_subgroup = QGroupBox(self.tr("Subgroup settings")) self.form_layout_subgroup = QFormLayout(self.group_box_subgroup) self.label_color_a = QLabel(self.tr("Color A")) self.form_layout_subgroup.setWidget(0, QFormLayout.LabelRole, self.label_color_a) self.combo_box_color_a = QComboBox() self.form_layout_subgroup.setWidget(0, QFormLayout.FieldRole, self.combo_box_color_a) self.label_color_b = QLabel(self.tr("Color B")) self.form_layout_subgroup.setWidget(1, QFormLayout.LabelRole, self.label_color_b) self.combo_box_color_b = QComboBox() self.form_layout_subgroup.setWidget(1, QFormLayout.FieldRole, self.combo_box_color_b) self.label_pattern_a_b = QLabel(self.tr("Pattern A and B")) self.form_layout_subgroup.setWidget(2, QFormLayout.LabelRole, self.label_pattern_a_b) self.combo_box_pattern_a_b = QComboBox() self.form_layout_subgroup.setWidget(2, QFormLayout.FieldRole, self.combo_box_pattern_a_b) self.add_standard_colors(self.combo_box_color_a) self.add_standard_colors(self.combo_box_color_b) self.combo_box_color_a.setCurrentIndex(9) # lime self.combo_box_color_b.setCurrentIndex(15) # yellow self.combo_box_pattern_a_b.addItem(self.tr("Chess order")) self.combo_box_pattern_a_b.setEnabled(False) # navigate buttons self.layout_navigate = QHBoxLayout() self.layout_navigate.addStretch(1) self.push_button_export = QPushButton(self.tr("Export")) self.layout_navigate.addWidget(self.push_button_export) self.push_button_cancel = QPushButton(self.tr("Cancel")) self.layout_navigate.addWidget(self.push_button_cancel) # layout setup self.layout_right_setting = QVBoxLayout() self.layout_right_setting.addWidget(self.group_box_font) self.layout_right_setting.addWidget(self.group_box_date) self.layout_right_setting.addWidget(self.group_box_subgroup) self.layout_right_setting.addStretch(1) self.layout_center = QHBoxLayout() self.layout_center.addLayout(self.layout_list_widget) self.layout_center.addLayout(self.layout_right_setting) self.layout_main = QVBoxLayout() self.layout_main.addLayout(self.layout_title_date_mode) self.layout_main.addLayout(self.layout_center) self.layout_main.addLayout(self.layout_navigate) self.setLayout(self.layout_main) # connection self.check_box_use_current.clicked.connect( self.check_box_use_current_clicked) self.push_button_open_folder.clicked.connect(self.open_folder_clicked) self.action_open_files.triggered.connect(self.open_files_clicked) self.date_edit_start.dateChanged.connect(self.date_edit_start_changed) self.combo_box_color_a.activated.connect( self.combo_box_color_a_clicked) self.combo_box_color_b.activated.connect( self.combo_box_color_b_clicked) self.push_button_export.clicked.connect(self.export_to_pdf) self.push_button_cancel.clicked.connect(self.close) def add_standard_colors(self, combo_box: QComboBox) -> None: """ Adds colors to the color selection menu. :param combo_box: Color selection menu """ color_items = [(self.tr("Custom color"), QColor()), (self.tr("Aqua"), QColor(0, 255, 255)), (self.tr("Grey"), QColor(128, 128, 128)), (self.tr("Navy"), QColor(0, 0, 192)), (self.tr("Silver"), QColor(192, 192, 192)), (self.tr("Black"), QColor(0, 0, 0)), (self.tr("Green"), QColor(0, 128, 0)), (self.tr("Olive"), QColor(192, 192, 0)), (self.tr("Blue"), QColor(0, 0, 255)), (self.tr("Lime"), QColor(0, 255, 0)), (self.tr("Purple"), QColor(128, 0, 128)), (self.tr("White"), QColor(255, 255, 255)), (self.tr("Fuchsia"), QColor(255, 0, 255)), (self.tr("Maroon"), QColor(128, 0, 0)), (self.tr("Red"), QColor(255, 0, 0)), (self.tr("Yellow"), QColor(255, 255, 0))] for name, data in color_items: combo_box.addItem(util.create_color_icon(data), name, data) def export_to_pdf(self) -> None: # select path paths = [] if self.check_box_use_current.isChecked(): path = QFileDialog.getSaveFileName(self, self.tr("Export to pdf"), ".", "PDF file (*.pdf)")[0] if path == "": return if not path.endswith(".pdf"): path += ".pdf" paths.append(path) else: for index in range(self.list_widget.count()): path = self.list_widget.item(index).data(Qt.UserRole) paths.append(path) # progress dialog progress = QProgressDialog(self.tr("Export to pdf"), self.tr("Abort exports"), 0, 100, self) progress.setWindowModality(Qt.WindowModal) progress.setMinimumDuration(2000) try: for index, path in enumerate(paths): if self.check_box_use_current.isChecked(): title_text = self.line_edit_title.text() schedule = self._schedule_ref else: title_text = QFileInfo(path).baseName() schedule = Schedule() schedule.load(path) path = path[0:-5] + ".pdf" print(path) mode = self.combo_box_work_mode.currentIndex() if mode == 0: export_weeks_to_pdf( schedule, title_text, self.check_box_add_date.isChecked(), path, self.combo_box_font.currentText(), self.combo_box_font.currentData(Qt.UserRole), self.combo_box_encoding.currentText(), self.date_edit_start.date().toPyDate(), self.date_edit_end.date().toPyDate(), self.combo_box_color_a.currentData(Qt.UserRole), self.combo_box_color_b.currentData(Qt.UserRole), progress if self.check_box_use_current.isChecked() else None) else: export_full_to_pdf( schedule, title_text, path, self.combo_box_font.currentText(), self.combo_box_font.currentData(Qt.UserRole), self.combo_box_encoding.currentText(), progress if self.check_box_use_current.isChecked() else None) progress.setValue(int(index * 100 / len(paths))) # finish dialog progress.setValue(100) finish_msg_box = QMessageBox(QMessageBox.Information, self.tr("Export to pdf"), self.tr("Gone!")) open_folder_button = finish_msg_box.addButton( self.tr("Open folder"), QMessageBox.ActionRole) finish_msg_box.addButton(QMessageBox.Ok) finish_msg_box.exec_() if finish_msg_box.clickedButton() == open_folder_button: QDesktopServices.openUrl( QUrl( QFileInfo(paths[0] if len(paths) != 0 else "."). absolutePath())) except UnicodeEncodeError as ex: QMessageBox.critical(self, self.tr("Encoding error"), str(ex)) except Exception as ex: QMessageBox.critical(self, self.tr("Unknown error"), str(ex)) progress.setValue(100) def check_box_use_current_clicked(self, checked: bool): if checked: self.list_widget.setEnabled(False) self.push_button_open_folder.setEnabled(False) self.line_edit_title.setEnabled(False) else: self.list_widget.setEnabled(True) self.push_button_open_folder.setEnabled(True) self.line_edit_title.setEnabled(True) def open_folder_clicked(self): path = QFileDialog.getExistingDirectory(self, self.tr("Select folder")) provider = QFileIconProvider() self.list_widget.clear() for dir_path, dir_names, file_names in os.walk(path): for file_name in file_names: if file_name.endswith(".json"): item = QListWidgetItem() item.setText(file_name[0:-5]) item.setData(Qt.UserRole, dir_path + os.sep + file_name) item.setIcon( provider.icon(QFileInfo(dir_path + os.sep + file_name))) self.list_widget.addItem(item) self.label_find.setText( self.tr("Schedules: ") + str(self.list_widget.count())) def open_files_clicked(self): files = QFileDialog.getOpenFileNames( self, self.tr("Select files"), "", "JSON file (*.json) ;; All files (*.*)")[0] provider = QFileIconProvider() self.list_widget.clear() for file_path in files: file = QFileInfo(file_path) item = QListWidgetItem() item.setText(file.baseName()) item.setData(Qt.UserRole, file_path) item.setIcon(provider.icon(file)) self.list_widget.addItem(item) self.label_find.setText( self.tr("Schedules: ") + str(self.list_widget.count())) def combo_box_color_a_clicked(self) -> None: """ Slot for color selection of A subgroup. """ if self.combo_box_color_a.currentIndex() == 0: self.custom_color_selected(self.combo_box_color_a) def combo_box_color_b_clicked(self) -> None: """ Slot for color selection of B subgroup. """ if self.combo_box_color_b.currentIndex() == 0: self.custom_color_selected(self.combo_box_color_b) def custom_color_selected(self, combo_box: QComboBox) -> None: """ Slot to select the color for the desired menu. :param combo_box: Menu """ color = QColorDialog.getColor(combo_box.currentData(), self) if color.isValid(): combo_box.setItemIcon(0, util.create_color_icon(color)) combo_box.setItemData(0, color) def date_edit_start_changed(self, date: QDate): """ Slot for changing the end of a range of dates. :param date: Start of the date range """ end_date = self.date_edit_end.date().addDays( self._date_start_cache.daysTo(date)) self.date_edit_end.setMinimumDate(date.addDays(7)) self.date_edit_end.setDate(end_date) self._date_start_cache = QDate(date)
class ImportWindow(QDialog): def __init__(self, parent: QWidget = None): super().__init__(parent) self._size: int = os.cpu_count() if self._size is None: self._size = 2 self._processes_pool = [] self._manager = ImportManager(self._size) self._max_progress: int = 0 self._timer: QTimer = QTimer() self._confuser = ConfuseWindow(self) self._date_start_cache = None self._tesseract_path_cache = None self._poppler_path_cache = None # window settings self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) self.setWindowTitle(self.tr("Import window")) # list widget with files self.list_widget = QListWidget() self.list_widget.setSortingEnabled(True) self.layout_open_folder = QHBoxLayout() self.label_find = QLabel(self.tr("Schedules: ") + "0") self.layout_open_folder.addWidget(self.label_find) self.layout_open_folder.addStretch(1) self.push_button_open_folder = QToolButton() self.layout_open_folder.addWidget(self.push_button_open_folder) self.push_button_open_folder.setText(self.tr("Open folder")) self.push_button_open_folder.setPopupMode(QToolButton.MenuButtonPopup) self.action_open_files = QAction(self.tr("Open files")) self.push_button_open_folder.addAction(self.action_open_files) # main progress self.group_box_main_progress = QGroupBox(self.tr("Main progress")) self.layout_main_progress = QVBoxLayout(self.group_box_main_progress) self.process_bar_main = QProgressBar() self.layout_main_progress.addWidget(self.process_bar_main) self.layout_start_process = QHBoxLayout() self.layout_start_process.addStretch(1) self.push_button_import = QPushButton(self.tr("Import")) self.layout_start_process.addWidget(self.push_button_import) self.push_button_stop = QPushButton(self.tr("Stop")) self.layout_start_process.addWidget(self.push_button_stop) self.push_button_stop.setEnabled(False) self.layout_main_progress.addLayout(self.layout_start_process) # threads process self.group_box_threads = QGroupBox(self.tr("Threads")) self.grid_layout_threads = QGridLayout(self.group_box_threads) self._progresses_bars = [] rows = self._size // 2 columns = 2 for i in range(rows): for j in range(columns): progress_bar = QProgressBar() progress_bar.setTextVisible(True) self._progresses_bars.append(progress_bar) self.grid_layout_threads.addWidget(progress_bar, i, j) # options self.group_box_options = QGroupBox(self.tr("Options")) self.form_layout_options = QFormLayout(self.group_box_options) self.check_box_weekly = QCheckBox(self.tr("Create weekly schedule")) self.form_layout_options.addRow(self.check_box_weekly) self.check_box_full = QCheckBox(self.tr("Create full schedule")) self.form_layout_options.addRow(self.check_box_full) self.check_box_debug_img = QCheckBox(self.tr("Create debug image")) self.form_layout_options.addRow(self.check_box_debug_img) self.spin_box_dpi = QSpinBox() self.form_layout_options.addRow(self.tr("DPI"), self.spin_box_dpi) self.combo_box_tesseract_path = QComboBox() self.form_layout_options.addRow(self.tr("Tesseract path"), self.combo_box_tesseract_path) self.combo_box_poppler_path = QComboBox() self.form_layout_options.addRow(self.tr("Poppler path"), self.combo_box_poppler_path) self.check_box_debug_img.setChecked(True) self.check_box_debug_img.setEnabled(False) self.spin_box_dpi.setRange(200, 800) self.spin_box_dpi.setValue(500) self.combo_box_tesseract_path.addItem( self.tr("<select tesseract path>")) self.combo_box_tesseract_path.addItem("Default", "tesseract") self.combo_box_tesseract_path.setCurrentIndex(1) self._tesseract_path_cache = self.combo_box_tesseract_path.currentText( ) self.combo_box_poppler_path.addItem(self.tr("<select poppler path>")) self.combo_box_poppler_path.addItem("Default", None) self.combo_box_poppler_path.setCurrentIndex(1) self._poppler_path_cache = self.combo_box_poppler_path.currentText() # font edit self.group_box_font = QGroupBox(self.tr("Font settings")) self.form_layout_font = QFormLayout(self.group_box_font) self.label_font = QLabel(self.tr("Font")) self.form_layout_font.setWidget(0, QFormLayout.LabelRole, self.label_font) self.combo_box_font = QComboBox() self.form_layout_font.setWidget(0, QFormLayout.FieldRole, self.combo_box_font) self.label_encoding = QLabel(self.tr("Encoding")) self.form_layout_font.setWidget(1, QFormLayout.LabelRole, self.label_encoding) self.combo_box_encoding = QComboBox() self.form_layout_font.setWidget(1, QFormLayout.FieldRole, self.combo_box_encoding) for font_name, font_path in util.get_fonts(): self.combo_box_font.addItem(font_name, font_path) self.combo_box_font.setCurrentText(qApp.font().family()) self.combo_box_font.setEditable(True) self.combo_box_encoding.addItem("UTF-8") self.combo_box_encoding.addItem("Latin-1") self.combo_box_encoding.addItem("Windows-1252") # date edit self.group_box_date = QGroupBox(self.tr("Date settings")) self.form_layout_date = QFormLayout(self.group_box_date) self.label_date_start = QLabel(self.tr("Start")) self.form_layout_date.setWidget(0, QFormLayout.LabelRole, self.label_date_start) self.date_edit_start = QDateEdit() self.form_layout_date.setWidget(0, QFormLayout.FieldRole, self.date_edit_start) self.label_date_end = QLabel(self.tr("End")) self.form_layout_date.setWidget(1, QFormLayout.LabelRole, self.label_date_end) self.date_edit_end = QDateEdit() self.form_layout_date.setWidget(1, QFormLayout.FieldRole, self.date_edit_end) self.date_edit_start.setCalendarPopup(True) self.date_edit_end.setCalendarPopup(True) if QDate.currentDate().day() < QDate.currentDate().dayOfYear() / 2: date = QDate(QDate.currentDate().year(), 2, 1) else: date = QDate(QDate.currentDate().year(), 9, 1) self._date_start_cache = date.addDays(8 - date.dayOfWeek()) self.date_edit_start.setDate(self._date_start_cache) self.date_edit_end.setMinimumDate(self._date_start_cache.addDays(7)) self.date_edit_end.setDate(self._date_start_cache.addDays(16 * 7)) # subgroup edit self.group_box_subgroup = QGroupBox(self.tr("Subgroup settings")) self.form_layout_subgroup = QFormLayout(self.group_box_subgroup) self.label_color_a = QLabel(self.tr("Color A")) self.form_layout_subgroup.setWidget(0, QFormLayout.LabelRole, self.label_color_a) self.combo_box_color_a = QComboBox() self.form_layout_subgroup.setWidget(0, QFormLayout.FieldRole, self.combo_box_color_a) self.label_color_b = QLabel(self.tr("Color B")) self.form_layout_subgroup.setWidget(1, QFormLayout.LabelRole, self.label_color_b) self.combo_box_color_b = QComboBox() self.form_layout_subgroup.setWidget(1, QFormLayout.FieldRole, self.combo_box_color_b) self.label_pattern_a_b = QLabel(self.tr("Pattern A and B")) self.form_layout_subgroup.setWidget(2, QFormLayout.LabelRole, self.label_pattern_a_b) self.combo_box_pattern_a_b = QComboBox() self.form_layout_subgroup.setWidget(2, QFormLayout.FieldRole, self.combo_box_pattern_a_b) self.add_standard_colors(self.combo_box_color_a) self.add_standard_colors(self.combo_box_color_b) self.combo_box_color_a.setCurrentIndex(9) # lime self.combo_box_color_b.setCurrentIndex(15) # yellow self.combo_box_pattern_a_b.addItem(self.tr("Chess order")) self.combo_box_pattern_a_b.setEnabled(False) # navigate self.layout_navigate = QHBoxLayout() self.layout_navigate.addStretch(1) self.push_button_ok = QPushButton(self.tr("OK")) self.layout_navigate.addWidget(self.push_button_ok) self.push_button_cancel = QPushButton(self.tr("Cancel")) self.layout_navigate.addWidget(self.push_button_cancel) # layout setup self.layout_left = QVBoxLayout() self.layout_left.addWidget(self.list_widget) self.layout_left.addLayout(self.layout_open_folder) self.layout_right = QVBoxLayout() self.layout_right.addWidget(self.group_box_main_progress) self.layout_right.addWidget(self.group_box_threads) self.layout_down = QGridLayout() self.layout_down.addWidget(self.group_box_options, 0, 0) self.layout_down.addWidget(self.group_box_font, 1, 0) self.layout_down.addWidget(self.group_box_date, 0, 1) self.layout_down.addWidget(self.group_box_subgroup, 1, 1) self.layout_right.addLayout(self.layout_down) self.layout_right.addStretch(1) self.layout_right.addLayout(self.layout_navigate) self.layout_main = QHBoxLayout() self.layout_main.addLayout(self.layout_left, 1) self.layout_main.addLayout(self.layout_right, 2) self.setLayout(self.layout_main) # connections self._timer.timeout.connect(self.check_processes) self.push_button_open_folder.clicked.connect(self.open_folder_clicked) self.action_open_files.triggered.connect(self.open_files_clicked) self.push_button_import.clicked.connect( self.push_button_import_clicked) self.push_button_stop.clicked.connect(self.push_button_stop_clicked) self.check_box_weekly.clicked.connect(self.check_box_weekly_clicked) self.combo_box_tesseract_path.activated.connect( self.combo_box_tesseract_path_clicked) self.combo_box_poppler_path.activated.connect( self.combo_box_poppler_path_clicked) self.date_edit_start.dateChanged.connect(self.date_edit_start_changed) self.combo_box_color_a.activated.connect( self.combo_box_color_a_clicked) self.combo_box_color_b.activated.connect( self.combo_box_color_b_clicked) self.push_button_ok.clicked.connect(self.close) self.push_button_cancel.clicked.connect(self.close) def check_processes(self): work_processes = 0 for index in range(self._size): if self._processes_pool[index].is_alive(): work_processes += 1 text = self._manager.progress_text_list[index] if text is not None and text != "": self._progresses_bars[index].setFormat(text) self._progresses_bars[index].setValue( self._manager.progress_value_list[index]) if self._manager.confuse_answer_list[ index] == ConfuseWindow.NeededSolution: if self._confuser.status == ConfuseWindow.Solved: answer_index = self._confuser.index self._manager.confuse_list[ answer_index] = self._confuser.answer self._manager.confuse_answer_list[ answer_index] = ConfuseWindow.Solved self._confuser.status = ConfuseWindow.Nothing elif self._confuser.status == ConfuseWindow.Nothing: self._confuser.status = ConfuseWindow.Work self._confuser.index = index self._confuser.text_edit_confuse.setText( self._manager.confuse_info[index]) self._confuser.text_edit_answer.setText( self._manager.confuse_list[index]) self._confuser.set_image( self._manager.confuse_file_path[index]) self._confuser.show() progress = self._max_progress - self._manager.queue.qsize( ) - work_processes self.process_bar_main.setValue(progress) if work_processes == 0: self.push_button_stop_clicked() return if not self._manager.flags["stop"]: self._timer.start(1000) def open_folder_clicked(self): path = QFileDialog.getExistingDirectory(self, self.tr("Select folder")) provider = QFileIconProvider() self.list_widget.clear() for dir_path, dir_names, file_names in os.walk(path): for file_name in file_names: if file_name.endswith(".pdf"): item = QListWidgetItem() item.setText(file_name[0:-4]) item.setData(Qt.UserRole, dir_path + os.sep + file_name) item.setIcon( provider.icon(QFileInfo(dir_path + os.sep + file_name))) self.list_widget.addItem(item) self.label_find.setText( self.tr("Schedules: ") + str(self.list_widget.count())) def open_files_clicked(self): files = QFileDialog.getOpenFileNames( self, self.tr("Select files"), "", "PDF file (*.pdf) ;; All files (*.*)")[0] provider = QFileIconProvider() self.list_widget.clear() for file_path in files: file = QFileInfo(file_path) item = QListWidgetItem() item.setText(file.baseName()) item.setData(Qt.UserRole, file_path) item.setIcon(provider.icon(file)) self.list_widget.addItem(item) self.label_find.setText( self.tr("Schedules: ") + str(self.list_widget.count())) def push_button_import_clicked(self): self.group_box_options.setEnabled(False) self.group_box_font.setEnabled(False) self.group_box_date.setEnabled(False) self.group_box_subgroup.setEnabled(False) self.push_button_import.setEnabled(False) self.push_button_stop.setEnabled(True) self.push_button_ok.setEnabled(False) self.push_button_cancel.setEnabled(False) self.push_button_open_folder.setEnabled(False) for number in range(self.list_widget.count()): path = self.list_widget.item(number).data(Qt.UserRole) self._manager.queue.put(path) self._max_progress = self.list_widget.count() self.process_bar_main.setRange(0, self._max_progress) self.process_bar_main.setValue(0) self._manager.weekly = self.check_box_weekly.isChecked() self._manager.full = self.check_box_full.isChecked() self._manager.debug_image = self.check_box_debug_img.isChecked() self._manager.dpi = self.spin_box_dpi.value() self._manager.tesseract_path = self.combo_box_tesseract_path.currentData( Qt.UserRole) self._manager.poppler_path = self.combo_box_poppler_path.currentData( Qt.UserRole) self._manager.font_name = self.combo_box_font.currentText() self._manager.font_path = self.combo_box_font.currentData(Qt.UserRole) self._manager.encoding = self.combo_box_encoding.currentText() self._manager.start = self.date_edit_start.date().toPyDate() self._manager.end = self.date_edit_end.date().toPyDate() self._manager.color_a = self.combo_box_color_a.currentData(Qt.UserRole) self._manager.color_b = self.combo_box_color_b.currentData(Qt.UserRole) self._manager.flags["stop"] = False self._processes_pool.clear() for index in range(self._size): process = Process(target=import_from_pdf, args=(index, self._manager), daemon=True) process.start() self._processes_pool.append(process) self._timer.start(500) def push_button_stop_clicked(self): self.push_button_stop.setEnabled(False) self._manager.flags["stop"] = True self.group_box_options.setEnabled(True) self.group_box_font.setEnabled(True) self.group_box_date.setEnabled(True) self.group_box_subgroup.setEnabled(True) self.push_button_import.setEnabled(True) self.push_button_ok.setEnabled(True) self.push_button_cancel.setEnabled(True) self.push_button_open_folder.setEnabled(True) def check_box_weekly_clicked(self): if self.check_box_weekly.isChecked(): self.group_box_date.setEnabled(True) self.group_box_subgroup.setEnabled(True) else: self.group_box_date.setEnabled(False) self.group_box_subgroup.setEnabled(False) def combo_box_tesseract_path_clicked(self, index): if index == 0: path = QFileDialog.getOpenFileName(self, self.tr("Select Tesseract"))[0] if path == "": self.combo_box_tesseract_path.setCurrentText( self._tesseract_path_cache) return self.combo_box_tesseract_path.addItem(path, path) self.combo_box_tesseract_path.setCurrentText(path) self._tesseract_path_cache = path def combo_box_poppler_path_clicked(self, index): if index == 0: path = QFileDialog.getOpenFileName(self, self.tr("Select Poppler"))[0] if path == "": self.combo_box_poppler_path.setCurrentText( self._poppler_path_cache) return self.combo_box_poppler_path.addItem(path, path) self.combo_box_poppler_path.setCurrentText(path) self._poppler_path_cache = path def add_standard_colors(self, combo_box: QComboBox) -> None: """ Adds colors to the color selection menu. :param combo_box: Color selection menu """ color_items = [(self.tr("Custom color"), QColor()), (self.tr("Aqua"), QColor(0, 255, 255)), (self.tr("Grey"), QColor(128, 128, 128)), (self.tr("Navy"), QColor(0, 0, 192)), (self.tr("Silver"), QColor(192, 192, 192)), (self.tr("Black"), QColor(0, 0, 0)), (self.tr("Green"), QColor(0, 128, 0)), (self.tr("Olive"), QColor(192, 192, 0)), (self.tr("Blue"), QColor(0, 0, 255)), (self.tr("Lime"), QColor(0, 255, 0)), (self.tr("Purple"), QColor(128, 0, 128)), (self.tr("White"), QColor(255, 255, 255)), (self.tr("Fuchsia"), QColor(255, 0, 255)), (self.tr("Maroon"), QColor(128, 0, 0)), (self.tr("Red"), QColor(255, 0, 0)), (self.tr("Yellow"), QColor(255, 255, 0))] for name, data in color_items: combo_box.addItem(util.create_color_icon(data), name, data) def combo_box_color_a_clicked(self) -> None: """ Slot for color selection of A subgroup. """ if self.combo_box_color_a.currentIndex() == 0: self.custom_color_selected(self.combo_box_color_a) def combo_box_color_b_clicked(self) -> None: """ Slot for color selection of B subgroup. """ if self.combo_box_color_b.currentIndex() == 0: self.custom_color_selected(self.combo_box_color_b) def custom_color_selected(self, combo_box: QComboBox) -> None: """ Slot to select the color for the desired menu. :param combo_box: Menu """ color = QColorDialog.getColor(combo_box.currentData(), self) if color.isValid(): combo_box.setItemIcon(0, util.create_color_icon(color)) combo_box.setItemData(0, color) def date_edit_start_changed(self, date: QDate): """ Slot for changing the end of a range of dates. :param date: Start of the date range """ end_date = self.date_edit_end.date().addDays( self._date_start_cache.daysTo(date)) self.date_edit_end.setMinimumDate(date.addDays(7)) self.date_edit_end.setDate(end_date) self._date_start_cache = QDate(date) def closeEvent(self, event: QCloseEvent) -> None: for process in self._processes_pool: process.terminate() print("Processes terminate")
class QStudyingView(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.time_label = QLabel("Время начала", self) self.time_edit = QTimeEdit() self.date_label = QLabel("Дата", parent=self) self.date_edit = QDateEdit(calendarPopup=True) self.subjects_label = QLabel("Темы", self) self.subjects_list = QListWidget(self) layout = QVBoxLayout() layout.addWidget(self.time_label) layout.addWidget(self.time_edit) layout.addWidget(self.date_label) layout.addWidget(self.date_edit) layout.addWidget(self.subjects_label) layout.addWidget(self.subjects_list) self.ok_btn = QPushButton('OK', parent=self) self.ok_btn.clicked.connect(self.ok) self.ok_cancel = QPushButton('Cancel', parent=self) self.ok_cancel.clicked.connect(self.cancel) btn_layout = QHBoxLayout() btn_layout.addWidget(self.ok_btn) btn_layout.addWidget(self.ok_cancel) layout.addLayout(btn_layout) layout.addStretch(0) self.setLayout(layout) self.presenter = StudyingPresenter(self) def set_subject_items(self, items: str): for i in items: item = QListWidgetItem(i) item.setCheckState(Qt.Unchecked) self.subjects_list.addItem(item) def set_subjects(self, subjects): if not subjects: return for i in subjects: self.subjects_list.findItems(i, Qt.MatchFixedString)[0].setData( Qt.CheckStateRole, Qt.Checked) def get_subjects(self): return [ self.subjects_list.item(i).text() for i in range(self.subjects_list.count()) if self.subjects_list.item(i).checkState() == Qt.Checked ] def get_time(self): return self.time_edit.time().toPyTime() def set_time(self, value): if value is not None: self.time_edit.setTime(value) def get_date(self): return self.date_edit.date().toPyDate() def set_date(self, value): if value is not None: self.date_edit.setTime(value) def ok(self): self.presenter.ok() def cancel(self): self.presenter.cancel() def get_presenter(self): return self.presenter
class TaskAddEditor(QDialog): #initialize everything def __init__(self, dialog_name, button_name, identifier=None): super(TaskAddEditor, self).__init__() self.dialog_name = dialog_name self.button_name = button_name self.identifier = identifier self.setGeometry(50, 50, 300, 250) self.tabs = QTabWidget() self.tabs.tab_1 = QWidget() self.tabs.tab_2 = QWidget() self.tabs.addTab(self.tabs.tab_1, "Basic Info") self.tabs.addTab(self.tabs.tab_2, "Notifications") self.tab_1() self.tab_2() main_layout = QVBoxLayout() main_layout.addWidget(self.tabs) buttons = QHBoxLayout() button_ok = QPushButton(self.button_name) button_close = QPushButton("Cancel") button_ok.clicked.connect(self.dialog_button_click) button_close.clicked.connect(self.dialog_cancel_click) buttons.addWidget(button_ok) buttons.addWidget(button_close) main_layout.addLayout(buttons) self.setLayout(main_layout) self.setWindowTitle(dialog_name) self.exec_() def tab_1(self): # main layout layout = QVBoxLayout() task_name = QLabel('Task Name') due_date = QLabel('Due Date') due_time = QLabel('Due Time') if (self.button_name == "Add"): self.task_name_input = QLineEdit() self.due_date_input = QDateEdit() self.due_date_input.setMinimumDate(QDate.currentDate()) self.due_time_input = QTimeEdit() else: task_info = user_tasks.get_task(self.identifier) self.task_name_input = QLineEdit(task_info[1]) self.due_date_input = QDateEdit(task_info[2].date()) self.due_date_input.setMinimumDate(QDate.currentDate()) self.due_time_input = QTimeEdit(task_info[2].time()) layout.addWidget(task_name) layout.addWidget(self.task_name_input) layout.addWidget(due_date) layout.addWidget(self.due_date_input) layout.addWidget(due_time) layout.addWidget(self.due_time_input) layout.addSpacing(20) self.tabs.tab_1.setLayout(layout) def tab_2(self): layout = QVBoxLayout() page_name = QLabel('Notification Settings') layout.addWidget(page_name) add_notification_area = QHBoxLayout() description = QLabel('Remind Me Everyday At: ') self.time_input = QTimeEdit() add_notification = QPushButton('+') add_notification.setFixedSize(30, 30) add_notification.clicked.connect(self.add_notification) add_notification_area.addWidget(description) add_notification_area.addWidget(self.time_input) add_notification_area.addWidget(add_notification) layout.addLayout(add_notification_area) your_notifications = QLabel('Your Notifications:') layout.addWidget(your_notifications) #create a scroll area to hold notifications notifications_area = QScrollArea(self) notifications_area.setWidgetResizable(True) widget = QWidget() notifications_area.setWidget(widget) self.notifications_layout = QVBoxLayout(widget) notifications_area.setAlignment(Qt.AlignTop) self.notifications_layout.setAlignment(Qt.AlignTop) layout.addWidget(notifications_area) if self.identifier is not None: #get a list of datetime objects notifications = user_tasks.get_notifications(self.identifier) #add notifications to the tab for notification_date in notifications: self.notifications_layout.addWidget( Notification(notification_date)) self.tabs.tab_2.setLayout(layout) def add_notification(self): # adds a notification to the layout of notifications for index in range(self.notifications_layout.count()): if ((self.notifications_layout.itemAt( index).widget().notification_time) > (self.time_input.time().toPyTime())): self.notifications_layout.insertWidget( index, Notification(self.time_input.time().toPyTime())) return elif (self.notifications_layout.itemAt(index).widget(). notification_time) == self.time_input.time().toPyTime(): error = QMessageBox() error.setText("Time Already Set") error.exec_() return self.notifications_layout.addWidget( Notification(self.time_input.time().toPyTime())) def dialog_button_click(self): # when add is pressed if (input_error_box(self.due_time_input, self.due_date_input, self.task_name_input)): notification_dates = [] for notification in range(self.notifications_layout.count()): notification_dates.append( self.notifications_layout.itemAt( notification).widget().notification_time) if (self.button_name == 'Add'): print(notification_dates) user_tasks.add_task( self.task_name_input.text(), datetime.combine(self.due_date_input.date().toPyDate(), self.due_time_input.time().toPyTime()), datetime.today(), str(uuid.uuid4()), notification_dates) else: print(notification_dates) user_tasks.edit_task( self.identifier, self.task_name_input.text(), datetime.combine(self.due_date_input.date().toPyDate(), self.due_time_input.time().toPyTime()), notification_dates) self.reject() gui_window.refresh_tasks() def dialog_cancel_click(self): #used in the input window and closes it self.reject()
class QSyllabusView(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) start_label = QLabel("Начало", self) self.start_edit = QDateEdit(calendarPopup=True) finish_label = QLabel("Конец", self) self.finish_edit = QDateEdit(calendarPopup=True) studyings_label = QLabel("Расписание", self) self.studyings_list = QListWidget(self) layout = QVBoxLayout() layout.addWidget(start_label) layout.addWidget(self.start_edit) layout.addWidget(finish_label) layout.addWidget(self.finish_edit) layout.addWidget(studyings_label) layout.addWidget(self.studyings_list) self.ok_btn = QPushButton('OK', parent=self) self.ok_btn.clicked.connect(self.ok) self.ok_cancel = QPushButton('Cancel', parent=self) self.ok_cancel.clicked.connect(self.cancel) btn_layout = QHBoxLayout() btn_layout.addWidget(self.ok_btn) btn_layout.addWidget(self.ok_cancel) layout.addLayout(btn_layout) layout.addStretch(0) self.setLayout(layout) self.presenter = SyllabusPresenter(self) def set_studying_items(self, items: str): for i in items: item = QListWidgetItem(i) item.setCheckState(Qt.Unchecked) self.studyings_list.addItem(item) def set_studyings(self, studyings): if not studyings: return for i in studyings: self.studyings_list.findItems(i, Qt.MatchFixedString)[0].setData( Qt.CheckStateRole, Qt.Checked) def get_studyings(self): return [ self.studyings_list.item(i).text() for i in range(self.studyings_list.count()) if self.studyings_list.item(i).checkState() == Qt.Checked ] def get_start(self): return self.start_edit.date().toPyDate() def set_start(self, value): if value is not None: self.start_edit.setDate(value) def get_finish(self): return self.finish_edit.date().toPyDate() def set_finish(self, value): if value is not None: self.finish_edit.setDate(value) def ok(self): self.presenter.ok() def cancel(self): self.presenter.cancel() def get_presenter(self): return self.presenter
class YuToolsDailyPlan(QWidget): def __init__(self): super().__init__() ActionDao.auto_update_action() self.txt_content = QPlainTextEdit(self) self.txt_content.setGeometry(10, 10, 350, 50) self.rb_begin = QRadioButton(self) self.rb_begin.setText('Begin') self.rb_begin.setChecked(True) self.rb_begin.setGeometry(10, 70, 50, 22) self.de_begin = QDateEdit(self) self.de_begin.setDate(QDate.currentDate()) self.de_begin.setGeometry(70, 70, 90, 22) self.rb_begin.clicked.connect( lambda: self.rb_select_date(self.de_begin)) self.rb_deadline = QRadioButton(self) self.rb_deadline.setText("Deadline") self.rb_deadline.setGeometry(180, 70, 60, 22) self.de_deadline = QDateEdit(self) self.de_deadline.setDate(QDate.currentDate().addDays(7)) self.de_deadline.setGeometry(250, 70, 90, 22) self.rb_deadline.clicked.connect( lambda: self.rb_select_date(self.de_deadline)) self.calendar_select = QCalendarWidget(self) self.calendar_select.setGeometry(10, 100, 256, 190) self.calendar_select.clicked.connect(self.select_date) self.combo_frequency = QComboBox(self) self.combo_frequency.addItem(PlanFrequency.NoRepeat.name) self.combo_frequency.addItem(PlanFrequency.Day.name) self.combo_frequency.addItem(PlanFrequency.Week.name) self.combo_frequency.addItem(PlanFrequency.Month.name) self.combo_frequency.addItem(PlanFrequency.Quarter.name) self.combo_frequency.addItem(PlanFrequency.Year.name) self.combo_frequency.setGeometry(280, 100, 80, 26) self.sb_repeat = QSpinBox(self) self.sb_repeat.setMinimum(1) self.sb_repeat.setGeometry(280, 130, 80, 26) self.cb_include = QCheckBox(self) self.cb_include.setText("Include Begin") self.cb_include.setChecked(True) self.cb_include.setGeometry(280, 160, 80, 26) self.combo_importance = QComboBox(self) self.combo_importance.addItem('Important') self.combo_importance.addItem('Unimportant') self.combo_importance.setGeometry(280, 190, 80, 26) self.combo_urgency = QComboBox(self) self.combo_urgency.addItem('Urgent') self.combo_urgency.addItem('Non-Urgent') self.combo_urgency.setGeometry(280, 220, 80, 26) self.btn_save = QPushButton(self) self.btn_save.setGeometry(280, 260, 80, 26) self.btn_save.setText("Save") self.btn_save.clicked.connect(self.save_plan) self.tb_plan = QTableView(self) self.tb_plan.horizontalHeader().setStretchLastSection(True) self.tb_plan.horizontalHeader().setSectionResizeMode( QHeaderView.Custom) self.tb_plan.verticalHeader().hide() self.tb_plan.setGeometry(370, 10, 421, 281) self.line_importance = QFrame(self) self.line_importance.setGeometry(390, 295, 20, 435) self.line_importance.setFrameShape(QFrame.VLine) self.line_importance.setFrameShadow(QFrame.Sunken) self.line_urgency = QFrame(self) self.line_urgency.setGeometry(5, 480, 791, 16) self.line_urgency.setFrameShape(QFrame.HLine) self.line_urgency.setFrameShadow(QFrame.Sunken) self.combo_ac_list_filter = QComboBox(self) self.combo_ac_list_filter.addItem('Wait') self.combo_ac_list_filter.addItem('Going') self.combo_ac_list_filter.addItem('Done') self.combo_ac_list_filter.addItem('Cancel') self.combo_ac_list_filter.addItem('Expire') self.combo_ac_list_filter.addItem('Will') self.combo_ac_list_filter.addItem('All') self.combo_ac_list_filter.setCurrentIndex(1) self.combo_ac_list_filter.setGeometry(375, 478, 50, 20) self.combo_ac_list_filter.currentIndexChanged.connect( self.change_tb_ac_list) self.tb_ac_first = QTableView(self) self.tb_ac_first.horizontalHeader().setStretchLastSection(True) self.tb_ac_first.horizontalHeader().setSectionResizeMode( QHeaderView.Custom) self.tb_ac_first.verticalHeader().hide() self.tb_ac_first.setItemDelegateForColumn( 3, ActionStatusDelegate(self.tb_ac_first)) self.tb_ac_first.setGeometry(410, 300, 381, 178) self.tb_ac_second = QTableView(self) self.tb_ac_second.horizontalHeader().setStretchLastSection(True) self.tb_ac_second.horizontalHeader().setSectionResizeMode( QHeaderView.Custom) self.tb_ac_second.verticalHeader().hide() self.tb_ac_second.setItemDelegateForColumn( 3, ActionStatusDelegate(self.tb_ac_second)) self.tb_ac_second.setGeometry(10, 300, 381, 178) self.tb_ac_third = QTableView(self) self.tb_ac_third.horizontalHeader().setStretchLastSection(True) self.tb_ac_third.horizontalHeader().setSectionResizeMode( QHeaderView.Custom) self.tb_ac_third.verticalHeader().hide() self.tb_ac_third.setItemDelegateForColumn( 3, ActionStatusDelegate(self.tb_ac_third)) self.tb_ac_third.setGeometry(10, 498, 381, 178) self.tb_ac_fourth = QTableView(self) self.tb_ac_fourth.horizontalHeader().setStretchLastSection(True) self.tb_ac_fourth.horizontalHeader().setSectionResizeMode( QHeaderView.Custom) self.tb_ac_fourth.verticalHeader().hide() self.tb_ac_fourth.setItemDelegateForColumn( 3, ActionStatusDelegate(self.tb_ac_fourth)) self.tb_ac_fourth.setGeometry(410, 498, 381, 178) self.tb_acs = { 1: self.tb_ac_first, 2: self.tb_ac_second, 3: self.tb_ac_third, 4: self.tb_ac_fourth } self.refresh_tb_plan() for index in range(1, 5): self.refresh_tb_action(index) def rb_select_date(self, de_target): self.calendar_select.setSelectedDate(de_target.date()) def select_date(self): date = self.calendar_select.selectedDate() if self.rb_begin.isChecked(): self.de_begin.setDate(date) elif self.rb_deadline.isChecked(): self.de_deadline.setDate(date) def save_plan(self): code, data = self.valid_check() if code == -1: QMessageBox.critical(self, 'Error', data) return code2, actions = self.generate_actions(data) if code2 == -1: QMessageBox.critical(self, 'Error', actions) return plan_id = PlanDao.add_plan(data) ActionDao.add_actions(actions, plan_id) self.txt_content.clear() self.refresh_tb_plan() self.refresh_tb_action(tb_index=code) def valid_check(self): content = self.txt_content.toPlainText().replace('\n', '.') if content.strip(' ').strip('.').strip(' ') == '': return -1, 'Content must be a normal string!' begin = self.de_begin.date() deadline = self.de_deadline.date() now = QDate.currentDate() diff_begin = now.daysTo(begin) diff_deadline = now.daysTo(deadline) if diff_begin < 0 or diff_deadline < 0 or diff_deadline < diff_begin: return -1, 'Deadline date must be farther than begin date and both of them must be farther than Now' begin_date = datetime.date(begin.year(), begin.month(), begin.day()) deadline = datetime.date(deadline.year(), deadline.month(), deadline.day()) frequency = self.combo_frequency.currentText() repeat = self.sb_repeat.value() importance = self.combo_importance.currentText() degree_importance = False if importance.lower().startswith( 'un') else True urgent = self.combo_urgency.currentText() degree_urgent = False if urgent.lower().startswith('non') else True plan = Plan(content=content, begin_date=begin_date, deadline=deadline, frequency=PlanFrequency[frequency], repeat=repeat, degree_importance=degree_importance, degree_urgency=degree_urgent) if degree_importance and degree_urgent: code = 1 elif degree_importance and not degree_urgent: code = 2 elif not degree_importance and not degree_urgent: code = 3 elif not degree_importance and degree_urgent: code = 4 return code, plan def generate_actions(self, plan): action_list = [] begin = QDate(plan.begin_date) deadline = QDate(plan.deadline) include = self.cb_include.isChecked() if plan.frequency == PlanFrequency.NoRepeat: days = begin.daysTo(deadline) if not include: if days == 0: return -1, 'There is not time to complete the plan' for i in range(plan.repeat): action = Action(content=plan.content, begin_date=plan.begin_date, deadline=plan.deadline, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) elif plan.frequency == PlanFrequency.Day: days = begin.daysTo(deadline) if not include: if days == 0: return -1, 'There is not time to complete the plan' for day in range(days + 1): if not include and day == 0: continue begin_date = begin.addDays(day) begin_date = datetime.date(begin_date.year(), begin_date.month(), begin_date.day()) for i in range(plan.repeat): action = Action(content=plan.content, begin_date=begin_date, deadline=begin_date, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) elif plan.frequency == PlanFrequency.Week: begin_week, begin_year = begin.weekNumber() begin_day_of_week = begin.dayOfWeek() deadline_week, deadline_year = deadline.weekNumber() weeks = deadline_week + (deadline_year - begin_year) * 52 - begin_week if not include: if weeks == 0: return -1, 'There is not time to complete the plan' current_week_deadline = begin.addDays(7 - begin_day_of_week) for week in range(weeks + 1): if not include and week == 0: continue current_week_deadline = current_week_deadline.addDays(7 * week) current_week_begin = current_week_deadline.addDays(-6) if week == 0: begin_date = plan.begin_date if week == weeks: deadline = plan.deadline else: deadline = datetime.date(current_week_deadline.year(), current_week_deadline.month(), current_week_deadline.day()) elif week == weeks: begin_date = datetime.date(current_week_begin.year(), current_week_begin.month(), current_week_begin.day()) deadline = plan.deadline else: begin_date = datetime.date(current_week_begin.year(), current_week_begin.month(), current_week_begin.day()) deadline = datetime.date(current_week_deadline.year(), current_week_deadline.month(), current_week_deadline.day()) for i in range(plan.repeat): action = Action(content=plan.content, begin_date=begin_date, deadline=deadline, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) elif plan.frequency == PlanFrequency.Month: begin_year = begin.year() deadline_year = deadline.year() years = deadline_year - begin_year begin_month = begin.month() deadline_month = deadline.month() months = deadline_month + 12 * years - begin_month if not include: if months == 0: return -1, 'There is not time to complete the plan' current_year = begin_year for month in range(months + 1): if not include and month == 0: continue current_month = begin_month + month if current_month > 12: current_month -= 12 current_year += 1 if month == 0: begin_date = plan.begin_date if month == months: deadline = plan.deadline else: deadline = datetime.date(current_year, current_month, begin.daysInMonth()) elif month == months: begin_date = datetime.date(current_year, current_month, 1) deadline = plan.deadline else: begin_date = datetime.date(current_year, current_month, 1) deadline = datetime.date(current_year, current_month, 1) deadline = datetime.date( current_year, current_month, QDate(deadline.year(), deadline.month(), deadline.day()).daysInMonth()) for i in range(plan.repeat): action = Action(content=plan.content, begin_date=begin_date, deadline=deadline, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) elif plan.frequency == PlanFrequency.Quarter: begin_year = begin.year() deadline_year = deadline.year() years = deadline_year - begin_year begin_month = begin.month() deadline_month = deadline.month() begin_quarter = (begin_month + 2) / 3 deadline_quarter = (deadline_month + 2) / 3 quarters = deadline_quarter + years * 4 - begin_quarter if not include: if quarters == 0: return -1, 'There is not time to complete the plan' current_year = begin_year for quarter in range(quarters + 1): if not include and quarter == 0: continue current_quarter = begin_quarter + quarter if current_quarter > 4: current_quarter -= 4 current_year += 1 begin_month = (current_quarter - 1) * 3 + 1 deadline_month = begin_month + 2 if quarter == 0: begin_date = plan.begin_date if quarter == quarters: deadline = plan.deadline else: deadline = datetime.date( current_year, deadline_month, (30 if deadline_month == 4 else 31)) elif quarter == quarters: begin_date = datetime.date(current_year, begin_month, 1) deadline = plan.deadline else: begin_date = datetime.date(current_year, begin_month, 1) deadline = datetime.date( current_year, deadline_month, (30 if deadline_month == 4 else 31)) for i in range(plan.repeat): action = Action(content=plan.content, begin_date=begin_date, deadline=deadline, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) elif plan.frequency == PlanFrequency.Year: begin_year = begin.year() deadline_year = deadline.year() years = deadline_year - begin_year if not include: if years == 0: return -1, 'There is not time to complete the plan' for year in range(years + 1): if not include and year == 0: continue current_year = begin_year + year if year == 0: begin_date = plan.begin_date if year == years: deadline = plan.deadline else: deadline = datetime.date(current_year, 12, 31) elif year == years: begin_date = datetime.date(current_year, 1, 1) deadline = plan.deadline else: begin_date = datetime.date(current_year, 1, 1) deadline = datetime.date(current_year, 12, 31) for i in range(plan.repeat): action = Action(content=plan.content, begin_date=begin_date, deadline=deadline, degree_importance=plan.degree_importance, degree_urgency=plan.degree_urgency, status=1) action_list.append(action) return 0, action_list def change_tb_ac_list(self): for index in range(1, 5): self.refresh_tb_action(index) def refresh_tb_plan(self): plans = PlanDao.query_plans() model = QStandardItemModel(len(plans), 3) model.setHorizontalHeaderLabels(['Content', 'Frequency', 'Flag']) row_index = 0 for plan in plans: qsi_content = QStandardItem(plan.content) qsi_content.setEditable(False) qsi_frequency = QStandardItem(plan.frequency.name) qsi_frequency.setEditable(False) qsi_flag = QStandardItem('{},{}'.format( 'Important' if plan.degree_importance else 'Unimportant', 'Urgency' if plan.degree_urgency else 'Non-urgency')) qsi_flag.setEditable(False) model.setItem(row_index, 0, qsi_content) model.setItem(row_index, 1, qsi_frequency) model.setItem(row_index, 2, qsi_flag) row_index += 1 self.tb_plan.setModel(model) self.tb_plan.setColumnWidth(0, 220) self.tb_plan.setColumnWidth(1, 70) self.tb_plan.setColumnWidth(2, 100) def refresh_tb_action(self, tb_index): tb_action = self.tb_acs[tb_index] ac_list_filter = self.combo_ac_list_filter.currentIndex() actions = ActionDao.query_actions(tb_index, ac_list_filter) model = QStandardItemModel(len(actions), 3) model.setHorizontalHeaderLabels( ['Content', 'Begin', 'Deadline', 'Status']) row_index = 0 for action in actions: qsi_content = QStandardItem(action.content) qsi_content.setEditable(False) qsi_begin_date = QStandardItem( action.begin_date.strftime("%Y/%m/%d")) qsi_content.setEditable(False) qsi_deadline = QStandardItem(action.deadline.strftime("%Y/%m/%d")) qsi_deadline.setEditable(False) qsi_status = QStandardItem() qsi_status.setData({'id': action.id, 'status': action.status}) qsi_status.setEditable(False) model.setItem(row_index, 0, qsi_content) model.setItem(row_index, 1, qsi_begin_date) model.setItem(row_index, 2, qsi_deadline) model.setItem(row_index, 3, qsi_status) row_index += 1 tb_action.setModel(model) tb_action.setColumnWidth(0, 150) tb_action.setColumnWidth(1, 75) tb_action.setColumnWidth(2, 75) tb_action.setColumnWidth(3, 40) def change_status(self, tb_action, act_id, status): if status == 0: QMessageBox.information(self, 'Tip', 'Please wait for beginning') elif status == 1: menu = QMenu(tb_action) done_act = menu.addAction(QIcon('icons/daily_plan/done.png'), 'Done') cancel_act = menu.addAction(QIcon('icons/daily_plan/cancel.png'), 'Cancel') act = menu.exec_( tb_action.mapToGlobal( QPoint(self.sender().x(), self.sender().y() + 10))) refresh = False if act == done_act: ActionDao.update_action(act_id, 2) refresh = True elif act == cancel_act: ActionDao.update_action(act_id, 3) refresh = True if refresh: self.refresh_tb_action( list(self.tb_acs.keys())[list( self.tb_acs.values()).index(tb_action)]) elif status == 2: QMessageBox.information( self, 'Tip', 'You are good that had completed the task') elif status == 3: QMessageBox.information(self, 'Tip', 'It is sadly you had canceled this task') elif status == 4: QMessageBox.information( self, 'Tip', 'It is sorry that this task had expired and you cannot operate it' )
class Registration(QDialog): def __init__(self): super(Registration, self).__init__() self.createFormGroupBox() buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.clickSearch) #buttonBox.accepted.connect(self.close) buttonBox.rejected.connect(self.reject) mainLayout = QVBoxLayout() mainLayout.addWidget(self.formGroupBox) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("Registration") def createFormGroupBox(self): self.formGroupBox = QGroupBox("Registration Form") layout = QFormLayout(self) self.email = QLineEdit() layout.addRow(QLabel("*Email:"), self.email) self.confirmEmail = QLineEdit() layout.addRow(QLabel("*Confirm Email:"), self.confirmEmail) self.password = QLineEdit() self.password.setEchoMode(QLineEdit.Password) self.confirmPassword = QLineEdit() self.confirmPassword.setEchoMode(QLineEdit.Password) layout.addRow(QLabel("*Password:"******"*Confirm Password:"******"*First Name:"), self.firstName) self.lastName = QLineEdit() layout.addRow(QLabel("*Last Name:"), self.lastName) self.address1 = QLineEdit() layout.addRow(QLabel("*Address 1:"), self.address1) self.address2 = QLineEdit() layout.addRow(QLabel("Address 2:"), self.address2) self.city = QLineEdit() layout.addRow(QLabel("*City:"), self.city) self.state = QLineEdit() layout.addRow(QLabel("*State:"), self.state) self.postalCode = QLineEdit() layout.addRow(QLabel("*Postal Code:"), self.postalCode) self.combo = QComboBox(self) self.combo.addItem("AFG") self.combo.addItem("ALB") self.combo.addItem("DZA") self.combo.addItem("ASM") self.combo.addItem("AND") self.combo.addItem("AGO") self.combo.addItem("AIA") self.combo.addItem("ATA") self.combo.addItem("ATG") self.combo.addItem("ARG") self.combo.addItem("ARM") self.combo.addItem("ABW") self.combo.addItem("AUS") self.combo.addItem("AUT") self.combo.addItem("AZE") self.combo.addItem("BHS") self.combo.addItem("BHR") self.combo.addItem("BGD") self.combo.addItem("BRB") self.combo.addItem("BLR") self.combo.addItem("BEL") self.combo.addItem("BLZ") self.combo.addItem("BEN") self.combo.addItem("BMU") self.combo.addItem("BTN") self.combo.addItem("BOL") self.combo.addItem("BES") self.combo.addItem("BIH") self.combo.addItem("BWA") self.combo.addItem("BVT") self.combo.addItem("BRA") self.combo.addItem("IOT") self.combo.addItem("BRN") self.combo.addItem("BGR") self.combo.addItem("BFA") self.combo.addItem("BDI") self.combo.addItem("KHM") self.combo.addItem("CMR") self.combo.addItem("CAN") self.combo.addItem("CPV") self.combo.addItem("CYM") self.combo.addItem("CAF") self.combo.addItem("TCD") self.combo.addItem("CHL") self.combo.addItem("CHN") self.combo.addItem("CXR") self.combo.addItem("CCK") self.combo.addItem("COL") self.combo.addItem("COM") self.combo.addItem("COG") self.combo.addItem("COD") self.combo.addItem("C*K") self.combo.addItem("CRI") self.combo.addItem("HRV") self.combo.addItem("CUB") self.combo.addItem("CUW") self.combo.addItem("CYP") self.combo.addItem("CZE") self.combo.addItem("CIV") self.combo.addItem("DNK") self.combo.addItem("DJI") self.combo.addItem("DMA") self.combo.addItem("DOM") self.combo.addItem("ECU") self.combo.addItem("EGY") self.combo.addItem("SLV") self.combo.addItem("GNQ") self.combo.addItem("ERI") self.combo.addItem("EST") self.combo.addItem("ETH") self.combo.addItem("FLK") self.combo.addItem("FRO") self.combo.addItem("FJI") self.combo.addItem("FIN") self.combo.addItem("FRA") self.combo.addItem("GUF") self.combo.addItem("PYF") self.combo.addItem("ATF") self.combo.addItem("GAB") self.combo.addItem("GMB") self.combo.addItem("GEO") self.combo.addItem("DEU") self.combo.addItem("GHA") self.combo.addItem("GIB") self.combo.addItem("GRC") self.combo.addItem("GRL") self.combo.addItem("GRD") self.combo.addItem("GLP") self.combo.addItem("GUM") self.combo.addItem("GTM") self.combo.addItem("GGY") self.combo.addItem("GIN") self.combo.addItem("GNB") self.combo.addItem("GUY") self.combo.addItem("HTI") self.combo.addItem("HMD") self.combo.addItem("VAT") self.combo.addItem("HND") self.combo.addItem("HKG") self.combo.addItem("HUN") self.combo.addItem("ISL") self.combo.addItem("IND") self.combo.addItem("IDN") self.combo.addItem("IRN") self.combo.addItem("IRQ") self.combo.addItem("IRL") self.combo.addItem("IMN") self.combo.addItem("ISR") self.combo.addItem("ITA") self.combo.addItem("JAM") self.combo.addItem("JPN") self.combo.addItem("JEY") self.combo.addItem("JOR") self.combo.addItem("KAZ") self.combo.addItem("KEN") self.combo.addItem("KIR") self.combo.addItem("PRK") self.combo.addItem("KOR") self.combo.addItem("KWT") self.combo.addItem("KGZ") self.combo.addItem("LAO") self.combo.addItem("LVA") self.combo.addItem("LBN") self.combo.addItem("LSO") self.combo.addItem("LBR") self.combo.addItem("LBY") self.combo.addItem("LIE") self.combo.addItem("LTU") self.combo.addItem("LUX") self.combo.addItem("MAC") self.combo.addItem("MKD") self.combo.addItem("MDG") self.combo.addItem("MWI") self.combo.addItem("MYS") self.combo.addItem("MDV") self.combo.addItem("MLI") self.combo.addItem("MLT") self.combo.addItem("MHL") self.combo.addItem("MTQ") self.combo.addItem("MRT") self.combo.addItem("MUS") self.combo.addItem("MYT") self.combo.addItem("MEX") self.combo.addItem("FSM") self.combo.addItem("MDA") self.combo.addItem("MCO") self.combo.addItem("MNG") self.combo.addItem("MNE") self.combo.addItem("MSR") self.combo.addItem("MAR") self.combo.addItem("MOZ") self.combo.addItem("MMR") self.combo.addItem("NAM") self.combo.addItem("NRU") self.combo.addItem("NPL") self.combo.addItem("NLD") self.combo.addItem("NCL") self.combo.addItem("NZL") self.combo.addItem("NIC") self.combo.addItem("NER") self.combo.addItem("NGA") self.combo.addItem("NIU") self.combo.addItem("NFK") self.combo.addItem("MNP") self.combo.addItem("NOR") self.combo.addItem("OMN") self.combo.addItem("PAK") self.combo.addItem("PLW") self.combo.addItem("PSE") self.combo.addItem("PAN") self.combo.addItem("PNG") self.combo.addItem("PRY") self.combo.addItem("PER") self.combo.addItem("PHL") self.combo.addItem("PCN") self.combo.addItem("POL") self.combo.addItem("PRT") self.combo.addItem("PRI") self.combo.addItem("QAT") self.combo.addItem("ROU") self.combo.addItem("RUS") self.combo.addItem("RWA") self.combo.addItem("REU") self.combo.addItem("BLM") self.combo.addItem("SHN") self.combo.addItem("KNA") self.combo.addItem("LCA") self.combo.addItem("MAF") self.combo.addItem("SPM") self.combo.addItem("VCT") self.combo.addItem("WSM") self.combo.addItem("SMR") self.combo.addItem("STP") self.combo.addItem("SAU") self.combo.addItem("SEN") self.combo.addItem("SRB") self.combo.addItem("SYC") self.combo.addItem("SLE") self.combo.addItem("SGP") self.combo.addItem("SXM") self.combo.addItem("SVK") self.combo.addItem("SVN") self.combo.addItem("SLB") self.combo.addItem("SOM") self.combo.addItem("ZAF") self.combo.addItem("SGS") self.combo.addItem("SSD") self.combo.addItem("ESP") self.combo.addItem("LKA") self.combo.addItem("SDN") self.combo.addItem("SUR") self.combo.addItem("SJM") self.combo.addItem("SWZ") self.combo.addItem("SWE") self.combo.addItem("CHE") self.combo.addItem("SYR") self.combo.addItem("TWN") self.combo.addItem("TJK") self.combo.addItem("TZA") self.combo.addItem("THA") self.combo.addItem("TLS") self.combo.addItem("TGO") self.combo.addItem("TKL") self.combo.addItem("TON") self.combo.addItem("TTO") self.combo.addItem("TUN") self.combo.addItem("TUR") self.combo.addItem("TKM") self.combo.addItem("TCA") self.combo.addItem("TUV") self.combo.addItem("UGA") self.combo.addItem("UKR") self.combo.addItem("ARE") self.combo.addItem("GBR") self.combo.addItem("USA") self.combo.addItem("UMI") self.combo.addItem("URY") self.combo.addItem("UZB") self.combo.addItem("VUT") self.combo.addItem("VEN") self.combo.addItem("VNM") self.combo.addItem("VGB") self.combo.addItem("VIR") self.combo.addItem("WLF") self.combo.addItem("ESH") self.combo.addItem("YEM") self.combo.addItem("ZMB") self.combo.addItem("ZWE") layout.addRow(QLabel("*Country:"),self.combo) self.ccNum = QLineEdit() layout.addRow(QLabel("*Credit Card#:"), self.ccNum) default = QDate.currentDate().toPyDate() self.ccExpiry = QDateEdit(QDate(default.year, default.month, default.day)) layout.addRow(QLabel("*CC Expiry:"), self.ccExpiry) default = QDate.currentDate().toPyDate() self.birthdate = QDateEdit(QDate(default.year, default.month, default.day)) layout.addRow(QLabel("*Birth Date:"), self.birthdate) self.formGroupBox.setLayout(layout) def clickSearch(self): ccMonth = str(self.ccExpiry.date().month()).zfill(2) ccDay = str(self.ccExpiry.date().day()).zfill(2) ccYear = str(self.ccExpiry.date().year()) ccDate = ccYear + ccMonth + ccDay #print(ccDate) bMonth = str(self.birthdate.date().month()).zfill(2) bDay = str(self.birthdate.date().day()).zfill(2) bYear = str(self.birthdate.date().year()) bDate = bYear + bMonth + bDay if (self.email.text() == '' or self.password.text() == '' or self.firstName.text() == '' or self.lastName.text() == '' or self.address1.text() == '' or self.city.text() == '' or self.state.text() =='' or self.postalCode.text() == '' or self.combo.currentText() == '' or self.ccNum.text() == ''): QMessageBox.warning(self, 'Error', "Please fill out all required lines.") elif (self.email.text() != self.confirmEmail.text()): QMessageBox.warning(self, 'Error', "Emails do not match.") elif (self.password.text() != self.confirmPassword.text()): QMessageBox.warning(self, 'Error', "Passwords do not match.") else: self.hide() self._new_window = Search() self._new_window.show() userSQL = "insert into user(email,password,first_name,last_name) values(%s, %s, %s, %s);" cursor.execute(userSQL, (self.email.text(),self.password.text(), self.firstName.text(), self.lastName.text())) userIDSQL = "select user_id from user where email = %s and password = %s;" cursor.execute(userIDSQL, (self.email.text(), self.password.text())) dict1 = cursor.fetchall()[0] list1 = list(dict1.values()) global userID userID = (list(dict1.values()))[0] addressSQL = "insert into address(line1,line2,city,state,post_code,country) values(%s,%s,%s,%s,%s,%s);" cursor.execute(addressSQL, (self.address1.text(), self.address2.text(), self.city.text(), self.state.text(), self.postalCode.text(), self.combo.currentText())) addressIDSQL = "select address_id from address where line1=%s and line2=%s and city=%s and state=%s and post_code=%s and country=%s;" cursor.execute(addressIDSQL, (self.address1.text(), self.address2.text(), self.city.text(), self.state.text(), self.postalCode.text(), self.combo.currentText())) dict2 = cursor.fetchall()[0] list2 = list(dict2.values()) addressID = (list(dict2.values()))[0] customerSQL = "insert into customer(user_id, address_id, birthdate, credit_card_no, credit_card_expiry) values(%s, %s, %s, %s, %s);" cursor.execute(customerSQL, (str(userID), str(addressID), bDate, self.ccNum.text(),ccDate)) connection.commit()
class AddTransDialog(QDialog): def __init__(self, parent=None, title='', item=None): super(AddTransDialog, self).__init__(parent) self.setFixedWidth(300) self.setWindowTitle(title) self.setWindowIcon(Icon('money.png')) self.showMessage = self.parent().mainWindow.showMessage self.createLayout(item) def createLayout(self, item): layout = QFormLayout() types = QGroupBox("Tipo", self) self.expense = QRadioButton("Despesa") self.expense.setChecked(True) self.expense.toggled.connect(lambda: changePrefix(self)) self.income = QRadioButton("Receita") self.income.toggled.connect(lambda: changePrefix(self)) hbox = QHBoxLayout() hbox.addWidget(self.expense) hbox.addWidget(self.income) types.setLayout(hbox) layout.addRow(types) self.date = QDateEdit() self.date.setDate(QDate.currentDate()) self.date.setCalendarPopup(True) self.date.setFocusPolicy(Qt.StrongFocus) layout.addRow("Data", self.date) self.description = QLineEdit() self.description.setMaxLength(40) completer = QCompleter(DATABASE.getDescriptions()) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.InlineCompletion) self.description.setCompleter(completer) layout.addRow("Descrição", self.description) self.category = QComboBox() self.category.setEditable(True) self.category.completer().setCompletionMode(QCompleter.PopupCompletion) categories = DATABASE.getCategories() if not categories: DATABASE.populateCategories() categories = DATABASE.getCategories() for cat in categories: self.category.addItem(cat) layout.addRow("Categoria", self.category) self.account = QComboBox() self.account.setEditable(False) accounts = DATABASE.getAccounts() for acc in accounts: self.account.addItem(acc) layout.addRow("Conta", self.account) self.value = QDoubleSpinBox() self.value.setDecimals(2) self.value.setSingleStep(100) self.value.setRange(0, 1000000000) layout.addRow("Valor", self.value) divBox = QHBoxLayout() self.initial = QSpinBox() self.initial.setRange(1, 1000) label = QLabel(" de ") label.setAlignment(Qt.AlignCenter) self.subdivision = QSpinBox() self.subdivision.setRange(1, 1000) self.subdivision.setSuffix(" parcelas") self.subdivision.setAlignment(Qt.AlignCenter) divBox.addWidget(self.initial) divBox.addWidget(label) divBox.addWidget(self.subdivision) layout.addRow("Parcelas", divBox) sublayout = QHBoxLayout() self.ok = QPushButton("Adicionar") self.ok.clicked.connect(self.accept) if not accounts: self.ok.setEnabled(False) cancel = QPushButton("Cancelar") cancel.clicked.connect(self.reject) sublayout.addWidget(self.ok) sublayout.addWidget(cancel) layout.addRow(sublayout) if item: info = DATABASE.getTransactions(ident=item.ident) if info[-1] >= 0: self.income.setChecked(True) self.value.setPrefix("R$ ") else: self.expense.setChecked(True) self.value.setPrefix("R$ -") self.date.setDate(QDate(info[3], info[2], info[1])) self.description.setText(info[4]) idx = self.category.findText(info[5], Qt.MatchExactly) self.category.setCurrentIndex(idx) idx = self.account.findText(info[7], Qt.MatchExactly) self.account.setCurrentIndex(idx) self.value.setValue(abs(info[8])) self.ok.setText("Editar") self.initial.setEnabled(False) label.setEnabled(False) self.subdivision.setEnabled(False) self.setLayout(layout) def process(self, item): if DATABASE.getCategories(self.category.currentText()): self.insertTransaction(item) self.parent().mainWindow.loadInfo() else: message = QMessageBox( QMessageBox.Warning, "Categoria Inexistente", "Deseja adicionar a categoria %s" % self.category.currentText(), QMessageBox.Ok | QMessageBox.Cancel) resultMsg = message.exec_() if resultMsg == QMessageBox.Ok: self.insertTransaction(item, True) self.parent().mainWindow.loadInfo() def insertTransaction(self, item, newCategory=False): if self.expense.isChecked(): value = -self.value.value() else: value = self.value.value() if not item: value = value / self.subdivision.value() count = 0 for i in range(self.initial.value(), self.subdivision.value() + 1): date = self.date.date().addMonths(count) transaction = [date.day(), date.month(), date.year()] if self.subdivision.value() > 1: transaction += [ self.description.text() + " (%d/%d)" % (i, self.subdivision.value()) ] else: transaction += [self.description.text()] if newCategory: transaction += [self.category.currentText(), None] else: transaction += [ self.category.currentText(), DATABASE.getCategories(self.category.currentText(), infos=True)[1] ] transaction += [self.account.currentText(), value] DATABASE.addTransaction(transaction) self.showMessage("Transação Adicionada.") count += 1 else: date = self.date.date() transaction = [date.day(), date.month(), date.year()] transaction += [self.description.text()] if newCategory: transaction += [self.category.currentText(), None] else: transaction += [ self.category.currentText(), DATABASE.getCategories(self.category.currentText(), infos=True)[1] ] transaction += [self.account.currentText(), value, item.ident] DATABASE.updateTransaction(transaction) self.showMessage("Transação Editada.")
class UserPopup(QWidget): """Super class for the two popups which allows users to be edited or added. They are so similar that it warrants one super class which creates the labels and entries.""" def __init__(self, parent=None): super().__init__() self.controller = parent self.initUI() self.setWindowModality(Qt.ApplicationModal) self.show() def initUI(self): self.mainLayout = QVBoxLayout() lineEditsGroup = QGroupBox() grid = QGridLayout() #Create line edit labels for each field except user ID (non-editable) for i, key in enumerate(['Name', 'Birthday', 'Gender', 'Nationality']): grid.addWidget(QLabel(key + "*"), i, 0) #Create name line entry self.nameEntry = QLineEdit(self) grid.addWidget(self.nameEntry, 0, 1) #Create birthday entry self.birthdayEntry = QDateEdit(self) grid.addWidget(self.birthdayEntry, 1, 1) #Create gender selection self.genderBox = QComboBox(self) self.genderBox.addItem("Male") self.genderBox.addItem("Female") self.genderBox.addItem("Other") grid.addWidget(self.genderBox, 2, 1) #Add nationality self.nationEntry = QLineEdit(self) grid.addWidget(self.nationEntry, 3, 1) lineEditsGroup.setLayout(grid) self.mainLayout.addWidget(lineEditsGroup) self.setLayout(self.mainLayout) def verifyInputValidity(self): """Triggers when user tries to save changes. Checks all fields for correct data.""" #Reset borders in case of earlier tries for field in [ self.nameEntry, self.birthdayEntry, self.genderBox, self.nationEntry ]: field.setStyleSheet("border: 1px solid black") if self.nameEntry.text().strip() == "": self.nameEntry.setStyleSheet("border: 2px solid red") return 1 if (self.birthdayEntry.date() >= QDate().currentDate()): self.birthdayEntry.setStyleSheet("border: 2px solid red") return 1 if self.genderBox.currentText().strip() not in [ "Male", "Female", "Other" ]: self.genderBox.setStyleSheet("border: 2px solid red") return 1 if self.nationEntry.text().strip() == "": self.nationEntry.setStyleSheet("border: 2px solid red") return 1 return 0
class DateCreatorWindow(QDialog): """ Dialog window for creation / editing date. """ def __init__(self, parent: QWidget = None): super().__init__(parent) self._date = None self._date_delta = 7 self._date_start_temp = QDate.currentDate() # window settings self.setWindowTitle(self.tr("Date creator")) # toggle and status self.check_box = QCheckBox(self.tr("Range date")) # date layout self.layout_date_edit = QFormLayout() # simple date layout self.label_simple_date = QLabel(self.tr("Date")) self.layout_date_edit.setWidget(0, QFormLayout.LabelRole, self.label_simple_date) self.date_edit_simple = QDateEdit() self.layout_date_edit.setWidget(0, QFormLayout.FieldRole, self.date_edit_simple) self.date_edit_simple.setCalendarPopup(True) self.date_edit_simple.setDisplayFormat("dd.MM.yyyy") self.date_edit_simple.setDate(QDate.currentDate()) self.date_edit_simple.setDateRange(QDate.currentDate().addDays(-365), QDate.currentDate().addDays(365)) # range date layout self.label_date_start = QLabel(self.tr("Start")) self.layout_date_edit.setWidget(1, QFormLayout.LabelRole, self.label_date_start) self.date_edit_start = QDateEdit() self.layout_date_edit.setWidget(1, QFormLayout.FieldRole, self.date_edit_start) self.label_date_end = QLabel(self.tr("End")) self.layout_date_edit.setWidget(2, QFormLayout.LabelRole, self.label_date_end) self.date_edit_end = QDateEdit() self.layout_date_edit.setWidget(2, QFormLayout.FieldRole, self.date_edit_end) self.label_date_frequency = QLabel(self.tr("Frequency")) self.layout_date_edit.setWidget(3, QFormLayout.LabelRole, self.label_date_frequency) self.combo_box_frequency = QComboBox() self.layout_date_edit.setWidget(3, QFormLayout.FieldRole, self.combo_box_frequency) self.date_edit_start.setCalendarPopup(True) self.date_edit_start.setDisplayFormat("dd.MM.yyyy") self.date_edit_start.setDate(QDate.currentDate()) self.date_edit_start.setDateRange(QDate.currentDate().addDays(-365), QDate.currentDate().addDays(365)) self.date_edit_end.setCalendarPopup(True) self.date_edit_end.setDisplayFormat("dd.MM.yyyy") self.date_edit_end.setDate(QDate.currentDate().addDays( self._date_delta)) self.date_edit_end.setDateRange( QDate.currentDate().addDays(self._date_delta), QDate.currentDate().addDays(365)) self.combo_box_frequency.addItem(str(FrequencyDate.Every), FrequencyDate.Every) self.combo_box_frequency.addItem(str(FrequencyDate.Throughout), FrequencyDate.Throughout) # navigate self.layout_navigate = QHBoxLayout() self.layout_navigate.addStretch(1) self.push_button_ok = QPushButton(self.tr("OK")) self.layout_navigate.addWidget(self.push_button_ok) self.push_button_apply = QPushButton(self.tr("Apply")) self.layout_navigate.addWidget(self.push_button_apply) self.push_button_cancel = QPushButton(self.tr("Cancel")) self.layout_navigate.addWidget(self.push_button_cancel) # layout setup self.layout_main = QVBoxLayout() self.layout_main.setAlignment(Qt.AlignCenter) self.layout_main.addWidget(self.check_box) self.layout_main.addLayout(self.layout_date_edit) self.layout_main.addStretch(1) self.layout_main.addLayout(self.layout_navigate) self.setLayout(self.layout_main) # connection self.check_box.clicked.connect(self.check_box_clicked) self.date_edit_start.dateChanged.connect(self.date_edit_start_change) self.date_edit_end.dateChanged.connect(self.date_range_validation) self.combo_box_frequency.currentIndexChanged.connect( self.combo_box_frequency_changed) self.push_button_ok.clicked.connect(self.push_button_ok_clicked) self.push_button_apply.clicked.connect(self.push_button_apply_clicked) self.push_button_cancel.clicked.connect( self.push_button_cancel_clicked) self.show_simple_date() def set_date(self, date_item) -> None: """ Sets the date for editing. :param date_item: Set date """ if isinstance(date_item, DateItem): self.show_simple_date() self.date_edit_simple.setDate( QDate.fromString(date_item.date, "yyyy.MM.dd")) self._date = date_item if isinstance(date_item, DateRange): self.check_box.setChecked(True) self.show_range_date() self.date_edit_start.setDate( QDate.fromString(date_item.date_from, "yyyy.MM.dd")) self.date_edit_end.setDate( QDate.fromString(date_item.date_to, "yyyy.MM.dd")) self.combo_box_frequency.setCurrentText(str(date_item.frequency)) self._date = date_item def get_date(self) -> (DateItem, DateRange, None): """ Returns the created date. """ return self._date def show_simple_date(self) -> None: """ Switches the window to simple date editing mode. """ self.label_simple_date.setVisible(True) self.date_edit_simple.setVisible(True) self.label_date_start.setVisible(False) self.date_edit_start.setVisible(False) self.label_date_end.setVisible(False) self.date_edit_end.setVisible(False) self.label_date_frequency.setVisible(False) self.combo_box_frequency.setVisible(False) def show_range_date(self) -> None: """ Switches the window to range date editing mode. """ self.label_date_start.setVisible(True) self.date_edit_start.setVisible(True) self.label_date_end.setVisible(True) self.date_edit_end.setVisible(True) self.label_date_frequency.setVisible(True) self.combo_box_frequency.setVisible(True) self.label_simple_date.setVisible(False) self.date_edit_simple.setVisible(False) def date_edit_start_change(self, date: QDate) -> None: """ Slot for changing the end of a range of dates. :param date: Start of the date range """ end_date = self.date_edit_end.date().addDays( self._date_start_temp.daysTo(date)) self.date_edit_end.setDateRange(date.addDays(self._date_delta), date.addDays(365)) self.date_edit_end.setDate(end_date) self._date_start_temp = QDate(date) def combo_box_frequency_changed(self, index: int) -> None: """ Slot for frequency combo box. :param index: Current index """ if index == 0: self._date_delta = 7 else: self._date_delta = 14 self.date_edit_end.setDateRange( self.date_edit_start.date().addDays(self._date_delta), self.date_edit_start.date().addDays(365)) self.date_range_validation() def date_range_validation(self) -> None: """ Checks the correctness of the entered dates. """ if self.date_edit_start.date().dayOfWeek() == self.date_edit_end.date( ).dayOfWeek(): if self.date_edit_start.date().daysTo( self.date_edit_end.date()) % self._date_delta == 0: msg = "" else: msg = self.tr("The number of days between " "dates is not a multiple of {}").format( self._date_delta) else: msg = self.tr("Different days of the week at dates") if msg == "": self.push_button_ok.setEnabled(True) self.push_button_apply.setEnabled(True) else: self.push_button_ok.setEnabled(False) self.push_button_apply.setEnabled(False) self.date_edit_start.setToolTip(msg) self.date_edit_end.setToolTip(msg) def check_box_clicked(self, value: bool) -> None: """ Slot for check box. :param value: Check box status """ if value is False: # simple date self.show_simple_date() else: # range date self.show_range_date() def push_button_ok_clicked(self) -> None: """ Slot for ok button. """ if self.push_button_apply_clicked(): self.close() def push_button_apply_clicked(self) -> bool: """ Slot for apply button. """ try: if self.check_box.isChecked() is False: # simple date self._date = DateItem( self.date_edit_simple.date().toString("yyyy.MM.dd")) else: # range date self._date = DateRange( self.date_edit_start.date().toString("yyyy.MM.dd"), self.date_edit_end.date().toString("yyyy.MM.dd"), self.combo_box_frequency.currentData(Qt.UserRole)) return True except InvalidDatePair as pair_ex: QMessageBox.warning(self, self.tr("Date error"), str(pair_ex)) except Exception as ex: QMessageBox.critical(self, self.tr("Unknown error"), str(ex)) return False def push_button_cancel_clicked(self) -> None: """ Slot for cancel button. """ self._date = None self.close()
class MainDialog(QWidget): """ Main dialog of the loan calculator. """ def __init__(self): super().__init__() self._vbox = None self._data_grid = None self._hbox_buttons = None self._hbox_close_button = None self._kredit_summe_edit = None self._tilgung_prozent_edit = None self._zins_prozent_edit = None self._start_month_edit = None self._monats_rate_label = None self._laufzeit_label = None self._kosten_label = None self._load_button = None self._save_button = None self._calc_button = None self._soti_button = None self._table_button = None self._table_button = None self._plot_button = None self._close_button = None self._window = None self._table_window = None self._current_project_file_name = None self._settings = None self._kredit_verlauf = [] self._extra_payments = [] self.init_ui() # Load/initialize settings self._credit_dir = os.path.join(pathlib.Path.home(), '.credit') self._credit_settings_file = os.path.join(self._credit_dir, 'settings.yaml') if not os.path.exists(self._credit_dir): os.makedirs(self._credit_dir) if not os.path.exists(self._credit_settings_file): self._settings = CreditSettings() self._save_settings(self._credit_settings_file) self._load_settings(self._credit_settings_file) def init_ui(self): self.setGeometry(300, 300, 300, 300) self.setWindowTitle('Annuity Loan Calculator') self.init_layout() self.init_project_buttons() self.init_input_fields() self.init_calc_buttons() self.init_output_fields() self.init_show_buttons() self.init_close_button() self._load_button.clicked.connect(self.load_button_pressed) self._save_button.clicked.connect(self.save_button_pressed) self._calc_button.clicked.connect(self.calc_button_pressed) self._soti_button.clicked.connect(self.soti_button_pressed) self._plot_button.clicked.connect(self.plot_button_pressed) self._table_button.clicked.connect(self.table_button_pressed) self._close_button.clicked.connect(self.close_button_pressed) self.show() def _load_settings(self, file_name): if os.path.exists(file_name): try: self._settings = yaml.load(open(file_name, 'r')) self._kredit_summe_edit.setText("{:.2f}".format(self._settings.kreditsumme)) self._tilgung_prozent_edit.setText("{:.2f}".format(self._settings.tilgung)) self._zins_prozent_edit.setText("{:.2f}".format(self._settings.zins)) self._start_month_edit.lineEdit().setText(self._settings.start_date) except yaml.YAMLError as ex: log.LOGGER.error(ex) def _save_settings(self, file_name): save_dir = os.path.dirname(file_name) if os.path.exists(save_dir): try: self._settings.kreditsumme = float(self._kredit_summe_edit.text()) except ValueError: self._settings.kreditsumme = 0.0 try: self._settings.zins = float(self._zins_prozent_edit.text()) except ValueError: self._settings.zins = 5.0 try: self._settings.tilgung = float(self._tilgung_prozent_edit.text()) except ValueError: self._settings.tilgung = 1.0 try: self._settings.start_date = self._start_month_edit.lineEdit().text() except ValueError: self._settings.start_date = "2000-01-01" try: with open(file_name, mode='w') as file: file.write(yaml.dump(self._settings)) except yaml.YAMLError as ex: log.LOGGER.error(ex) else: QMessageBox.warning( self, "Project coud not be saved", "Target directory {} does not exist".format(save_dir)) def init_layout(self): self._vbox = QVBoxLayout() self._data_grid = QGridLayout() self._hbox_buttons = QHBoxLayout() self._hbox_close_button = QHBoxLayout() self._vbox.addLayout(self._data_grid) self._vbox.addLayout(self._hbox_buttons) self._vbox.addLayout(self._hbox_close_button) self._vbox.addStretch(1) self.setLayout(self._vbox) def init_project_buttons(self): start_row = 0 self._load_button = QPushButton("Load Project") self._save_button = QPushButton("Save Project") hbox = QHBoxLayout() hbox.addWidget(self._load_button) hbox.addWidget(self._save_button) self._data_grid.addLayout(hbox, start_row + 0, 0, 1, 2) def init_input_fields(self): start_row = 1 self._data_grid.addWidget(QLabel("Loan Amount"), start_row + 0, 0) self._data_grid.addWidget(QLabel("Down Payment [%]"), start_row + 1, 0) self._data_grid.addWidget(QLabel("Nominal Interest [%]"), start_row + 2, 0) self._data_grid.addWidget(QLabel("Start Date"), start_row + 3, 0) self._kredit_summe_edit = QLineEdit() self._tilgung_prozent_edit = QLineEdit() self._zins_prozent_edit = QLineEdit() self._start_month_edit = QDateEdit(QDate.currentDate()) self._start_month_edit.setDisplayFormat(conf.DATE_FORMAT) self._start_month_edit.currentSection = QDateTimeEdit.MonthSection # self._start_month_edit.setCalendarPopup(True) self._data_grid.addWidget(self._kredit_summe_edit, start_row + 0, 1) self._data_grid.addWidget(self._tilgung_prozent_edit, start_row + 1, 1) self._data_grid.addWidget(self._zins_prozent_edit, start_row + 2, 1) self._data_grid.addWidget(self._start_month_edit, start_row + 3, 1) def init_calc_buttons(self): start_row = 5 self._calc_button = QPushButton("Calculate") self._soti_button = QPushButton("Extra Down Payments") hbox = QHBoxLayout() hbox.addWidget(self._calc_button) hbox.addWidget(self._soti_button) self._data_grid.addLayout(hbox, start_row + 0, 0, 1, 2) def init_output_fields(self): start_row = 6 self._monats_rate_label = QLabel("...") self._laufzeit_label = QLabel("...") self._kosten_label = QLabel("...") self._data_grid.addWidget(QLabel("Monthly Rate:"), start_row + 0, 0) self._data_grid.addWidget(QLabel("Period:"), start_row + 1, 0) self._data_grid.addWidget(QLabel("Total Interest:"), start_row + 2, 0) self._data_grid.addWidget(self._monats_rate_label, start_row + 0, 1) self._data_grid.addWidget(self._laufzeit_label, start_row + 1, 1) self._data_grid.addWidget(self._kosten_label, start_row + 2, 1) def init_show_buttons(self): self._table_button = QPushButton("Show Schedue") self._plot_button = QPushButton("Show Chart") self._hbox_buttons.addWidget(self._table_button) self._hbox_buttons.addWidget(self._plot_button) def init_close_button(self): self._close_button = QPushButton("Close") self._hbox_close_button.addStretch(1) self._hbox_close_button.addWidget(self._close_button) @property def summe(self): """ :returns loan amount """ return float(self._kredit_summe_edit.text()) @property def tilgung(self): """ :returns redemption rate in % """ return float(self._tilgung_prozent_edit.text()) @property def zins(self): """ :returns nominal interest in % """ return float(self._zins_prozent_edit.text()) @property def monatsrate(self): raise NotImplementedError("Setting is not allowed") @monatsrate.setter def monatsrate(self, x): self._monats_rate_label.setText("{0:.2f}".format(x)) @property def laufzeit(self): raise NotImplementedError("Setting is not allowed") @laufzeit.setter def laufzeit(self, x): self._laufzeit_label.setText(str(x)) @property def kosten(self): raise NotImplementedError("Setting is not allowed") @kosten.setter def kosten(self, x): """ :param x (float): cost of the credit (sum of interest payments). """ self._kosten_label.setText("{0:.2f}".format(x)) def load_button_pressed(self, e): if self._current_project_file_name: start_file = self._current_project_file_name else: start_file = str(pathlib.Path.home()) file_name = QFileDialog.getOpenFileName( self, "Open Project File", start_file, "Credit Project Files (*.yaml)")[0] self._load_settings(file_name) self._current_project_file_name = file_name def save_button_pressed(self, e): if self._current_project_file_name: start_file = self._current_project_file_name else: start_file = str(pathlib.Path.home()) file_name = QFileDialog.getSaveFileName( self, "Save Project File", start_file, "Credit Project Files (*.yaml)")[0] self._save_settings(file_name) self._current_project_file_name = file_name def calc_button_pressed(self, e): kredit = AnnuitaetenKredit(self.summe, self.tilgung, self.zins) # Convert absolute to relative months extra_payments = {} for payment in self._settings.extra_payments: month = util.month_diff( QDate.fromString(payment[0], conf.DATE_FORMAT), QDate.fromString(self._settings.start_date, conf.DATE_FORMAT)) extra_payments[month] = payment[1] self._kredit_verlauf = kredit.berechne_kreditverlauf(extra_payments) self.monatsrate = kredit.Monatsrate self.laufzeit = "{0:d} Years {1:d} Months".format( int(self._kredit_verlauf[-1].Monat) // 12, self._kredit_verlauf[-1].Monat % 12) self.kosten = kredit.GesamtKosten def soti_button_pressed(self, e): soti_dialog = SotiDialog(self, self._settings.extra_payments) ret = soti_dialog.exec() if ret == QDialog.Accepted: self._settings.extra_payments = soti_dialog.payments def table_button_pressed(self, e): if len(self._kredit_verlauf): table_dialog = TableDialog(self) table_dialog.show_table(self._kredit_verlauf, self._start_month_edit.date()) table_dialog.exec() def plot_button_pressed(self, e): if len(self._kredit_verlauf): plot_dialog = PlotWindow(self) plot_dialog.plot(self._kredit_verlauf) plot_dialog.exec() def close_button_pressed(self): self._save_settings(self._credit_settings_file) self.close()
class comic_meta_data_editor(QDialog): configGroup = "ComicsProjectManagementTools" # Translatable genre dictionary that has it's translated entries added to the genrelist and from which the untranslated items are taken. acbfGenreList = { "science_fiction": str(i18n("Science Fiction")), "fantasy": str(i18n("Fantasy")), "adventure": str(i18n("Adventure")), "horror": str(i18n("Horror")), "mystery": str(i18n("Mystery")), "crime": str(i18n("Crime")), "military": str(i18n("Military")), "real_life": str(i18n("Real Life")), "superhero": str(i18n("Superhero")), "humor": str(i18n("Humor")), "western": str(i18n("Western")), "manga": str(i18n("Manga")), "politics": str(i18n("Politics")), "caricature": str(i18n("Caricature")), "sports": str(i18n("Sports")), "history": str(i18n("History")), "biography": str(i18n("Biography")), "education": str(i18n("Education")), "computer": str(i18n("Computer")), "religion": str(i18n("Religion")), "romance": str(i18n("Romance")), "children": str(i18n("Children")), "non-fiction": str(i18n("Non Fiction")), "adult": str(i18n("Adult")), "alternative": str(i18n("Alternative")), "artbook": str(i18n("Artbook")), "other": str(i18n("Other")) } acbfAuthorRolesList = { "Writer": str(i18n("Writer")), "Adapter": str(i18n("Adapter")), "Artist": str(i18n("Artist")), "Penciller": str(i18n("Penciller")), "Inker": str(i18n("Inker")), "Colorist": str(i18n("Colorist")), "Letterer": str(i18n("Letterer")), "Cover Artist": str(i18n("Cover Artist")), "Photographer": str(i18n("Photographer")), "Editor": str(i18n("Editor")), "Assistant Editor": str(i18n("Assistant Editor")), "Designer": str(i18n("Designer")), "Translator": str(i18n("Translator")), "Other": str(i18n("Other")) } def __init__(self): super().__init__() # Get the keys for the autocompletion. self.genreKeysList = [] self.characterKeysList = [] self.ratingKeysList = {} self.formatKeysList = [] self.otherKeysList = [] self.authorRoleList = [] for g in self.acbfGenreList.values(): self.genreKeysList.append(g) for r in self.acbfAuthorRolesList.values(): self.authorRoleList.append(r) mainP = Path(os.path.abspath(__file__)).parent self.get_auto_completion_keys(mainP) extraKeyP = Path(QDir.homePath()) / Application.readSetting( self.configGroup, "extraKeysLocation", str()) self.get_auto_completion_keys(extraKeyP) # Setup the dialog. self.setLayout(QVBoxLayout()) mainWidget = QTabWidget() self.layout().addWidget(mainWidget) self.setWindowTitle(i18n("Comic Metadata")) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.layout().addWidget(buttons) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) # Title, concept, summary, genre, characters, format, rating, language, series, other keywords metadataPage = QWidget() mformLayout = QFormLayout() metadataPage.setLayout(mformLayout) self.lnTitle = QLineEdit() self.lnTitle.setToolTip(i18n("The proper title of the comic.")) self.teSummary = QPlainTextEdit() self.teSummary.setToolTip( i18n("What will you tell others to entice them to read your comic?" )) self.lnGenre = QLineEdit() genreCompletion = multi_entry_completer() genreCompletion.setModel(QStringListModel(self.genreKeysList)) self.lnGenre.setCompleter(genreCompletion) genreCompletion.setCaseSensitivity(False) self.lnGenre.setToolTip( i18n( "The genre of the work. Prefilled values are from the ACBF, but you can fill in your own. Separate genres with commas. Try to limit the amount to about two or three" )) self.lnCharacters = QLineEdit() characterCompletion = multi_entry_completer() characterCompletion.setModel(QStringListModel(self.characterKeysList)) characterCompletion.setCaseSensitivity(False) characterCompletion.setFilterMode( Qt.MatchContains ) # So that if there is a list of names with last names, people can type in a last name. self.lnCharacters.setCompleter(characterCompletion) self.lnCharacters.setToolTip( i18n( "The names of the characters that this comic revolves around. Comma-separated." )) self.lnFormat = QLineEdit() formatCompletion = multi_entry_completer() formatCompletion.setModel(QStringListModel(self.formatKeysList)) formatCompletion.setCaseSensitivity(False) self.lnFormat.setCompleter(formatCompletion) ratingLayout = QHBoxLayout() self.cmbRatingSystem = QComboBox() self.cmbRatingSystem.addItems(self.ratingKeysList.keys()) self.cmbRatingSystem.setEditable(True) self.cmbRating = QComboBox() self.cmbRating.setEditable(True) self.cmbRatingSystem.currentIndexChanged.connect( self.slot_refill_ratings) ratingLayout.addWidget(self.cmbRatingSystem) ratingLayout.addWidget(self.cmbRating) self.lnSeriesName = QLineEdit() self.lnSeriesName.setToolTip( i18n( "If this is part of a series, enter the name of the series and the number." )) self.spnSeriesNumber = QSpinBox() self.spnSeriesNumber.setPrefix("No. ") self.spnSeriesVol = QSpinBox() self.spnSeriesVol.setPrefix("Vol. ") seriesLayout = QHBoxLayout() seriesLayout.addWidget(self.lnSeriesName) seriesLayout.addWidget(self.spnSeriesVol) seriesLayout.addWidget(self.spnSeriesNumber) otherCompletion = multi_entry_completer() otherCompletion.setModel(QStringListModel(self.otherKeysList)) otherCompletion.setCaseSensitivity(False) otherCompletion.setFilterMode(Qt.MatchContains) self.lnOtherKeywords = QLineEdit() self.lnOtherKeywords.setCompleter(otherCompletion) self.lnOtherKeywords.setToolTip( i18n( "Other keywords that don't fit in the previously mentioned sets. As always, comma-separated" )) self.cmbLanguage = language_combo_box() self.cmbReadingMode = QComboBox() self.cmbReadingMode.addItem(i18n("Left to Right")) self.cmbReadingMode.addItem(i18n("Right to Left")) self.cmbCoverPage = QComboBox() self.cmbCoverPage.setToolTip( i18n( "Which page is the cover page? This will be empty if there's no pages." )) mformLayout.addRow(i18n("Title:"), self.lnTitle) mformLayout.addRow(i18n("Cover Page:"), self.cmbCoverPage) mformLayout.addRow(i18n("Summary:"), self.teSummary) mformLayout.addRow(i18n("Language:"), self.cmbLanguage) mformLayout.addRow(i18n("Reading Direction:"), self.cmbReadingMode) mformLayout.addRow(i18n("Genre:"), self.lnGenre) mformLayout.addRow(i18n("Characters:"), self.lnCharacters) mformLayout.addRow(i18n("Format:"), self.lnFormat) mformLayout.addRow(i18n("Rating:"), ratingLayout) mformLayout.addRow(i18n("Series:"), seriesLayout) mformLayout.addRow(i18n("Other:"), self.lnOtherKeywords) mainWidget.addTab(metadataPage, i18n("Work")) # The page for the authors. authorPage = QWidget() authorPage.setLayout(QVBoxLayout()) explanation = QLabel( i18n( "The following is a table of the authors that contributed to this comic. You can set their nickname, proper names (first, middle, last), Role (Penciller, Inker, etc), email and homepage." )) explanation.setWordWrap(True) self.authorModel = QStandardItemModel(0, 8) labels = [ i18n("Nick Name"), i18n("Given Name"), i18n("Middle Name"), i18n("Family Name"), i18n("Role"), i18n("Email"), i18n("Homepage"), i18n("Language") ] self.authorModel.setHorizontalHeaderLabels(labels) self.authorTable = QTableView() self.authorTable.setModel(self.authorModel) self.authorTable.verticalHeader().setDragEnabled(True) self.authorTable.verticalHeader().setDropIndicatorShown(True) self.authorTable.verticalHeader().setSectionsMovable(True) self.authorTable.verticalHeader().sectionMoved.connect( self.slot_reset_author_row_visual) delegate = author_delegate() delegate.setCompleterData(self.authorRoleList, 4) delegate.setLanguageData(len(labels) - 1) self.authorTable.setItemDelegate(delegate) author_button_layout = QWidget() author_button_layout.setLayout(QHBoxLayout()) btn_add_author = QPushButton(i18n("Add Author")) btn_add_author.clicked.connect(self.slot_add_author) btn_remove_author = QPushButton(i18n("Remove Author")) btn_remove_author.clicked.connect(self.slot_remove_author) author_button_layout.layout().addWidget(btn_add_author) author_button_layout.layout().addWidget(btn_remove_author) authorPage.layout().addWidget(explanation) authorPage.layout().addWidget(self.authorTable) authorPage.layout().addWidget(author_button_layout) mainWidget.addTab(authorPage, i18n("Authors")) # The page with publisher information. publisherPage = QWidget() publisherLayout = QFormLayout() publisherPage.setLayout(publisherLayout) self.publisherName = QLineEdit() self.publisherName.setToolTip( i18n( "The name of the company, group or person who is responsible for the final version the reader gets." )) publishDateLayout = QHBoxLayout() self.publishDate = QDateEdit() self.publishDate.setDisplayFormat(QLocale().system().dateFormat()) currentDate = QPushButton(i18n("Set Today")) currentDate.setToolTip( i18n("Sets the publish date to the current date.")) currentDate.clicked.connect(self.slot_set_date) publishDateLayout.addWidget(self.publishDate) publishDateLayout.addWidget(currentDate) self.publishCity = QLineEdit() self.publishCity.setToolTip( i18n( "Traditional publishers are always mentioned in source with the city they are located." )) self.isbn = QLineEdit() self.license = license_combo_box( ) # Maybe ought to make this a QLineEdit... self.license.setEditable(True) self.license.completer().setCompletionMode(QCompleter.PopupCompletion) dataBaseReference = QVBoxLayout() self.ln_database_name = QLineEdit() self.ln_database_name.setToolTip( i18n( "If there's an entry in a comics data base, that should be added here. It is unlikely to be a factor for comics from scratch, but useful when doing a conversion." )) self.cmb_entry_type = QComboBox() self.cmb_entry_type.addItems(["IssueID", "SeriesID", "URL"]) self.cmb_entry_type.setEditable(True) self.ln_database_entry = QLineEdit() dbHorizontal = QHBoxLayout() dbHorizontal.addWidget(self.ln_database_name) dbHorizontal.addWidget(self.cmb_entry_type) dataBaseReference.addLayout(dbHorizontal) dataBaseReference.addWidget(self.ln_database_entry) publisherLayout.addRow(i18n("Name:"), self.publisherName) publisherLayout.addRow(i18n("City:"), self.publishCity) publisherLayout.addRow(i18n("Date:"), publishDateLayout) publisherLayout.addRow(i18n("ISBN:"), self.isbn) publisherLayout.addRow(i18n("License:"), self.license) publisherLayout.addRow(i18n("Database:"), dataBaseReference) mainWidget.addTab(publisherPage, i18n("Publisher")) """ Ensure that the drag and drop of authors doesn't mess up the labels. """ def slot_reset_author_row_visual(self): headerLabelList = [] for i in range(self.authorTable.verticalHeader().count()): headerLabelList.append(str(i)) for i in range(self.authorTable.verticalHeader().count()): logicalI = self.authorTable.verticalHeader().logicalIndex(i) headerLabelList[logicalI] = str(i + 1) self.authorModel.setVerticalHeaderLabels(headerLabelList) """ Set the publish date to the current date. """ def slot_set_date(self): self.publishDate.setDate(QDate().currentDate()) """ Append keys to autocompletion lists from the directory mainP. """ def get_auto_completion_keys(self, mainP=Path()): genre = Path(mainP / "key_genre") characters = Path(mainP / "key_characters") rating = Path(mainP / "key_rating") format = Path(mainP / "key_format") keywords = Path(mainP / "key_other") authorRole = Path(mainP / "key_author_roles") if genre.exists(): for t in list(genre.glob('**/*.txt')): file = open(str(t), "r", errors="replace") for l in file: if str(l).strip("\n") not in self.genreKeysList: self.genreKeysList.append(str(l).strip("\n")) file.close() if characters.exists(): for t in list(characters.glob('**/*.txt')): file = open(str(t), "r", errors="replace") for l in file: if str(l).strip("\n") not in self.characterKeysList: self.characterKeysList.append(str(l).strip("\n")) file.close() if format.exists(): for t in list(format.glob('**/*.txt')): file = open(str(t), "r", errors="replace") for l in file: if str(l).strip("\n") not in self.formatKeysList: self.formatKeysList.append(str(l).strip("\n")) file.close() if rating.exists(): for t in list(rating.glob('**/*.csv')): file = open(str(t), "r", newline="", encoding="utf-8") ratings = csv.reader(file) title = os.path.basename(str(t)) r = 0 for row in ratings: listItem = [] if r is 0: title = row[1] else: listItem = self.ratingKeysList[title] item = [] item.append(row[0]) item.append(row[1]) listItem.append(item) self.ratingKeysList[title] = listItem r += 1 file.close() if keywords.exists(): for t in list(keywords.glob('**/*.txt')): file = open(str(t), "r", errors="replace") for l in file: if str(l).strip("\n") not in self.otherKeysList: self.otherKeysList.append(str(l).strip("\n")) file.close() if authorRole.exists(): for t in list(authorRole.glob('**/*.txt')): file = open(str(t), "r", errors="replace") for l in file: if str(l).strip("\n") not in self.authorRoleList: self.authorRoleList.append(str(l).strip("\n")) file.close() """ Refill the ratings box. This is called whenever the rating system changes. """ def slot_refill_ratings(self): if self.cmbRatingSystem.currentText() in self.ratingKeysList.keys(): self.cmbRating.clear() model = QStandardItemModel() for i in self.ratingKeysList[self.cmbRatingSystem.currentText()]: item = QStandardItem() item.setText(i[0]) item.setToolTip(i[1]) model.appendRow(item) self.cmbRating.setModel(model) """ Add an author with default values initialised. """ def slot_add_author(self): listItems = [] listItems.append(QStandardItem(i18n("Anon"))) # Nick name listItems.append(QStandardItem(i18n("John"))) # First name listItems.append(QStandardItem()) # Middle name listItems.append(QStandardItem(i18n("Doe"))) # Last name listItems.append(QStandardItem()) # role listItems.append(QStandardItem()) # email listItems.append(QStandardItem()) # homepage language = QLocale.system().name().split("_")[0] if language == "C": language = "en" listItems.append(QStandardItem(language)) # Language self.authorModel.appendRow(listItems) """ Remove the selected author from the author list. """ def slot_remove_author(self): self.authorModel.removeRow(self.authorTable.currentIndex().row()) """ Load the UI values from the config dictionary given. """ def setConfig(self, config): if "title" in config.keys(): self.lnTitle.setText(config["title"]) self.teSummary.clear() if "pages" in config.keys(): self.cmbCoverPage.clear() for page in config["pages"]: self.cmbCoverPage.addItem(page) if "cover" in config.keys(): if config["cover"] in config["pages"]: self.cmbCoverPage.setCurrentText(config["cover"]) if "summary" in config.keys(): self.teSummary.appendPlainText(config["summary"]) if "genre" in config.keys(): genreList = [] genreListConf = config["genre"] totalMatch = 100 if isinstance(config["genre"], dict): genreListConf = config["genre"].keys() totalMatch = 0 for genre in genreListConf: genreKey = genre if genre in self.acbfGenreList: genreKey = self.acbfGenreList[genre] if isinstance(config["genre"], dict): genreValue = config["genre"][genre] if genreValue > 0: genreKey = str(genreKey + "(" + str(genreValue) + ")") genreList.append(genreKey) self.lnGenre.setText(", ".join(genreList)) if "characters" in config.keys(): self.lnCharacters.setText(", ".join(config["characters"])) if "format" in config.keys(): self.lnFormat.setText(", ".join(config["format"])) if "rating" in config.keys(): self.cmbRating.setCurrentText(config["rating"]) else: self.cmbRating.setCurrentText("") if "ratingSystem" in config.keys(): self.cmbRatingSystem.setCurrentText(config["ratingSystem"]) else: self.cmbRatingSystem.setCurrentText("") if "otherKeywords" in config.keys(): self.lnOtherKeywords.setText(", ".join(config["otherKeywords"])) if "seriesName" in config.keys(): self.lnSeriesName.setText(config["seriesName"]) if "seriesVolume" in config.keys(): self.spnSeriesVol.setValue(config["seriesVolume"]) if "seriesNumber" in config.keys(): self.spnSeriesNumber.setValue(config["seriesNumber"]) if "language" in config.keys(): code = config["language"] if "_" in code: code = code.split("_")[0] self.cmbLanguage.setEntryToCode(code) if "readingDirection" in config.keys(): if config["readingDirection"] is "leftToRight": self.cmbReadingMode.setCurrentIndex(int(Qt.LeftToRight)) else: self.cmbReadingMode.setCurrentIndex(int(Qt.RightToLeft)) else: self.cmbReadingMode.setCurrentIndex( QLocale( self.cmbLanguage.codeForCurrentEntry()).textDirection()) if "publisherName" in config.keys(): self.publisherName.setText(config["publisherName"]) if "publisherCity" in config.keys(): self.publishCity.setText(config["publisherCity"]) if "publishingDate" in config.keys(): self.publishDate.setDate( QDate.fromString(config["publishingDate"], Qt.ISODate)) if "isbn-number" in config.keys(): self.isbn.setText(config["isbn-number"]) if "license" in config.keys(): self.license.setCurrentText(config["license"]) else: self.license.setCurrentText( "" ) # I would like to keep it ambiguous whether the artist has thought about the license or not. if "authorList" in config.keys(): authorList = config["authorList"] for i in range(len(authorList)): author = authorList[i] if len(author.keys()) > 0: listItems = [] listItems = [] listItems.append(QStandardItem(author.get("nickname", ""))) listItems.append( QStandardItem(author.get("first-name", ""))) listItems.append(QStandardItem(author.get("initials", ""))) listItems.append(QStandardItem(author.get("last-name", ""))) role = author.get("role", "") if role in self.acbfAuthorRolesList.keys(): role = self.acbfAuthorRolesList[role] listItems.append(QStandardItem(role)) listItems.append(QStandardItem(author.get("email", ""))) listItems.append(QStandardItem(author.get("homepage", ""))) listItems.append(QStandardItem(author.get("language", ""))) self.authorModel.appendRow(listItems) else: self.slot_add_author() dbRef = config.get("databaseReference", {}) self.ln_database_name.setText(dbRef.get("name", "")) self.ln_database_entry.setText(dbRef.get("entry", "")) stringCmbEntryType = self.cmb_entry_type.itemText(0) self.cmb_entry_type.setCurrentText( dbRef.get("type", stringCmbEntryType)) """ Store the GUI values into the config dictionary given. @return the config diactionary filled with new values. """ def getConfig(self, config): text = self.lnTitle.text() if len(text) > 0 and text.isspace() is False: config["title"] = text elif "title" in config.keys(): config.pop("title") config["cover"] = self.cmbCoverPage.currentText() listkeys = self.lnGenre.text() if len(listkeys) > 0 and listkeys.isspace() is False: preSplit = self.lnGenre.text().split(",") genreMatcher = re.compile(r'\((\d+)\)') genreList = {} totalValue = 0 for key in preSplit: m = genreMatcher.search(key) if m: genre = str(genreMatcher.sub("", key)).strip() match = int(m.group()[:-1][1:]) else: genre = key.strip() match = 0 if genre in self.acbfGenreList.values(): i = list(self.acbfGenreList.values()).index(genre) genreList[list(self.acbfGenreList.keys())[i]] = match else: genreList[genre] = match totalValue += match # Normalize the values: for key in genreList.keys(): if genreList[key] > 0: genreList[key] = round(genreList[key] / totalValue * 100) config["genre"] = genreList elif "genre" in config.keys(): config.pop("genre") listkeys = self.lnCharacters.text() if len(listkeys) > 0 and listkeys.isspace() is False: config["characters"] = self.lnCharacters.text().split(", ") elif "characters" in config.keys(): config.pop("characters") listkeys = self.lnFormat.text() if len(listkeys) > 0 and listkeys.isspace() is False: config["format"] = self.lnFormat.text().split(", ") elif "format" in config.keys(): config.pop("format") config["ratingSystem"] = self.cmbRatingSystem.currentText() config["rating"] = self.cmbRating.currentText() listkeys = self.lnOtherKeywords.text() if len(listkeys) > 0 and listkeys.isspace() is False: config["otherKeywords"] = self.lnOtherKeywords.text().split(", ") elif "otherKeywords" in config.keys(): config.pop("otherKeywords") text = self.teSummary.toPlainText() if len(text) > 0 and text.isspace() is False: config["summary"] = text elif "summary" in config.keys(): config.pop("summary") if len(self.lnSeriesName.text()) > 0: config["seriesName"] = self.lnSeriesName.text() config["seriesNumber"] = self.spnSeriesNumber.value() if self.spnSeriesVol.value() > 0: config["seriesVolume"] = self.spnSeriesVol.value() config["language"] = str(self.cmbLanguage.codeForCurrentEntry()) if self.cmbReadingMode.currentIndex() is Qt.LeftToRight: config["readingDirection"] = "leftToRight" else: config["readingDirection"] = "rightToLeft" authorList = [] for row in range(self.authorTable.verticalHeader().count()): logicalIndex = self.authorTable.verticalHeader().logicalIndex(row) listEntries = [ "nickname", "first-name", "initials", "last-name", "role", "email", "homepage", "language" ] author = {} for i in range(len(listEntries)): entry = self.authorModel.data( self.authorModel.index(logicalIndex, i)) if entry is None: entry = " " if entry.isspace() is False and len(entry) > 0: if listEntries[i] == "role": if entry in self.acbfAuthorRolesList.values(): entryI = list( self.acbfAuthorRolesList.values()).index(entry) entry = list( self.acbfAuthorRolesList.keys())[entryI] author[listEntries[i]] = entry elif listEntries[i] in author.keys(): author.pop(listEntries[i]) authorList.append(author) config["authorList"] = authorList config["publisherName"] = self.publisherName.text() config["publisherCity"] = self.publishCity.text() config["publishingDate"] = self.publishDate.date().toString(Qt.ISODate) config["isbn-number"] = self.isbn.text() config["license"] = self.license.currentText() if self.ln_database_name.text().isalnum( ) and self.ln_database_entry.text().isalnum(): dbRef = {} dbRef["name"] = self.ln_database_name.text() dbRef["entry"] = self.ln_database_entry.text() dbRef["type"] = self.cmb_entry_type.currentText() config["databaseReference"] = dbRef return config
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.setWindowTitle("Query Cocorahs") self.initUI() def initUI(self): self.createPreviewGroupBox() self.createDatesGroupBox() self.infoPanel() layout = QGridLayout() # layout.addWidget(self.previewGroupBox, 0, 0) layout.addWidget(self.datesGroupBox, 0, 1) layout.addWidget(self.infoPanelBox, 0, 2, 0, 1) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.previewLayout.setRowMinimumHeight(0, self.calendar.sizeHint().height()) self.previewLayout.setColumnMinimumWidth(0, self.calendar.sizeHint().width()) def createPreviewGroupBox(self): self.previewGroupBox = QGroupBox("Calendar") self.calendar = QCalendarWidget() self.calendar.setMaximumDate(QDate(3000, 1, 1)) self.calendar.setGridVisible(True) self.previewLayout = QGridLayout() self.previewLayout.addWidget(self.calendar, 0, 0) self.previewGroupBox.setLayout(self.previewLayout) def createDatesGroupBox(self): self.datesGroupBox = QGroupBox(self.tr("Selections")) # QUERY self.queryButton = QPushButton("Query Cocorahs Database") self.queryButton.clicked.connect(self.query_on_click) # START DATES self.currentDateEdit = QDateEdit() self.currentDateEdit.setDisplayFormat('dd MMM yyyy') self.currentDateEdit.setDate(self.calendar.selectedDate()) self.currentDateEdit.setDateRange(self.calendar.minimumDate(), self.calendar.maximumDate()) self.currentDateLabel = QLabel("&Date:") self.currentDateLabel.setBuddy(self.currentDateEdit) # END DATES self.maximumDateEdit = QDateEdit() self.maximumDateEdit.setDisplayFormat('dd MMM yyyy') self.maximumDateEdit.setDate(self.calendar.selectedDate()) self.maximumDateEdit.setDateRange(self.calendar.minimumDate(), self.calendar.maximumDate()) self.maximumDateLabel = QLabel("&End Date:") self.maximumDateLabel.setBuddy(self.maximumDateEdit) # LONGITUDE self.textboxLong = QLineEdit(self) self.textboxLongLabel = QLabel("Target Longitude:") # LATITUDE self.textboxLat = QLineEdit(self) self.textboxLatLabel = QLabel("Target Latitude:") # STATES self.textboxStates = QLineEdit(self) self.textboxStatesLabel = QLabel("State(s): e.g., 'CO,NE,WY'") dateBoxLayout = QGridLayout() dateBoxLayout.addWidget(self.currentDateLabel, 1, 0) dateBoxLayout.addWidget(self.currentDateEdit, 1, 1, 1, 2) # dateBoxLayout.addWidget(self.maximumDateLabel, 2, 0) # dateBoxLayout.addWidget(self.maximumDateEdit, 2, 1, 1, 2) dateBoxLayout.addWidget(self.textboxLatLabel, 3, 0) dateBoxLayout.addWidget(self.textboxLat, 3, 1, 1, 2) dateBoxLayout.addWidget(self.textboxLongLabel, 4, 0) dateBoxLayout.addWidget(self.textboxLong, 4, 1, 1, 2) dateBoxLayout.addWidget(self.textboxStatesLabel, 5, 0) dateBoxLayout.addWidget(self.textboxStates, 5, 1, 1, 2) dateBoxLayout.addWidget(self.queryButton, 6, 0,1,3) dateBoxLayout.setRowStretch(5, 1) self.datesGroupBox.setLayout(dateBoxLayout) def infoPanel(self): self.infoPanelBox = QGroupBox(self.tr("Info Console")) self.infoConsole = QTextEdit(self) infoPanelBoxLayout = QGridLayout() infoPanelBoxLayout.addWidget(self.infoConsole, 0, 0, 1, 1) self.infoPanelBox.setLayout(infoPanelBoxLayout) def query_on_click(self): long = self.textboxLong.text() lat = self.textboxLat.text() startDate = self.currentDateEdit.date() endDate = self.maximumDateEdit.date() states = self.textboxStates.text() date_range = list(range(startDate.month(), endDate.month() + 1)) dateString = startDate.toString('MM/dd/yyyy') query(long=long, lat=lat, dateString=dateString, states=states)
class GalleryDialog(QWidget): """ A window for adding/modifying gallery. Pass a list of QModelIndexes to edit their data or pass a path to preset path """ gallery_queue = queue.Queue() SERIES = pyqtSignal(list) SERIES_EDIT = pyqtSignal(list, int) #gallery_list = [] # might want to extend this to allow mass gallery adding def __init__(self, parent=None, arg=None): super().__init__(parent, Qt.Dialog) self.setAttribute(Qt.WA_DeleteOnClose) self.parent_widget = parent log_d('Triggered Gallery Edit/Add Dialog') m_l = QVBoxLayout() self.main_layout = QVBoxLayout() dummy = QWidget(self) scroll_area = QScrollArea(self) scroll_area.setWidgetResizable(True) scroll_area.setFrameStyle(scroll_area.StyledPanel) dummy.setLayout(self.main_layout) scroll_area.setWidget(dummy) m_l.addWidget(scroll_area, 3) final_buttons = QHBoxLayout() final_buttons.setAlignment(Qt.AlignRight) m_l.addLayout(final_buttons) self.done = QPushButton("Done") self.done.setDefault(True) cancel = QPushButton("Cancel") final_buttons.addWidget(cancel) final_buttons.addWidget(self.done) def new_gallery(): self.setWindowTitle('Add a new gallery') self.newUI() self.commonUI() self.done.clicked.connect(self.accept) cancel.clicked.connect(self.reject) if arg: if isinstance(arg, list): self.setWindowTitle('Edit gallery') self.position = arg[0].row() for index in arg: gallery = index.data(Qt.UserRole+1) self.commonUI() self.setGallery(gallery) self.done.clicked.connect(self.accept_edit) cancel.clicked.connect(self.reject_edit) elif isinstance(arg, str): new_gallery() self.choose_dir(arg) else: new_gallery() log_d('GalleryDialog: Create UI: successful') #TODO: Implement a way to mass add galleries #IDEA: Extend dialog in a ScrollArea with more forms... self.setLayout(m_l) self.resize(500,560) frect = self.frameGeometry() frect.moveCenter(QDesktopWidget().availableGeometry().center()) self.move(frect.topLeft()) #self.setAttribute(Qt.WA_DeleteOnClose) def commonUI(self): f_web = QGroupBox("Metadata from the Web") f_web.setCheckable(False) self.main_layout.addWidget(f_web) web_main_layout = QVBoxLayout() web_layout = QHBoxLayout() web_main_layout.addLayout(web_layout) f_web.setLayout(web_main_layout) f_gallery = QGroupBox("Gallery Info") f_gallery.setCheckable(False) self.main_layout.addWidget(f_gallery) gallery_layout = QFormLayout() f_gallery.setLayout(gallery_layout) def basic_web(name): return QLabel(name), QLineEdit(), QPushButton("Get metadata"), QProgressBar() url_lbl, self.url_edit, url_btn, url_prog = basic_web("URL:") url_btn.clicked.connect(lambda: self.web_metadata(self.url_edit.text(), url_btn, url_prog)) url_prog.setTextVisible(False) url_prog.setMinimum(0) url_prog.setMaximum(0) web_layout.addWidget(url_lbl, 0, Qt.AlignLeft) web_layout.addWidget(self.url_edit, 0) web_layout.addWidget(url_btn, 0, Qt.AlignRight) web_layout.addWidget(url_prog, 0, Qt.AlignRight) self.url_edit.setPlaceholderText("Paste g.e-hentai/exhentai gallery url or just press the button.") url_prog.hide() self.title_edit = QLineEdit() self.author_edit = QLineEdit() author_completer = misc.GCompleter(self, False, True, False) author_completer.setCaseSensitivity(Qt.CaseInsensitive) self.author_edit.setCompleter(author_completer) self.descr_edit = QTextEdit() self.descr_edit.setFixedHeight(45) self.descr_edit.setAcceptRichText(True) self.lang_box = QComboBox() self.lang_box.addItems(["English", "Japanese", "Other"]) self.lang_box.setCurrentIndex(0) tags_l = QVBoxLayout() tag_info = misc.ClickedLabel("How do i write namespace & tags? (hover)", parent=self) tag_info.setToolTip("Ways to write tags:\n\nNormal tags:\ntag1, tag2, tag3\n\n"+ "Namespaced tags:\nns1:tag1, ns1:tag2\n\nNamespaced tags with one or more"+ " tags under same namespace:\nns1:[tag1, tag2, tag3], ns2:[tag1, tag2]\n\n"+ "Those three ways of writing namespace & tags can be combined freely.\n"+ "Tags are seperated by a comma, NOT whitespace.\nNamespaces will be capitalized while tags"+ " will be lowercased.") tag_info.setToolTipDuration(99999999) tags_l.addWidget(tag_info) self.tags_edit = misc.CompleterTextEdit() self.tags_edit.setCompleter(misc.GCompleter(self, False, False)) tags_l.addWidget(self.tags_edit, 3) self.tags_edit.setFixedHeight(70) self.tags_edit.setPlaceholderText("Press Tab to autocomplete (Ctrl + E to show popup)") self.type_box = QComboBox() self.type_box.addItems(["Manga", "Doujinshi", "Artist CG Sets", "Game CG Sets", "Western", "Image Sets", "Non-H", "Cosplay", "Other"]) self.type_box.setCurrentIndex(0) #self.type_box.currentIndexChanged[int].connect(self.doujin_show) #self.doujin_parent = QLineEdit() #self.doujin_parent.setVisible(False) self.status_box = QComboBox() self.status_box.addItems(["Unknown", "Ongoing", "Completed"]) self.status_box.setCurrentIndex(0) self.pub_edit = QDateEdit() self.pub_edit.setCalendarPopup(True) self.pub_edit.setDate(QDate.currentDate()) self.path_lbl = QLabel("") self.path_lbl.setWordWrap(True) link_layout = QHBoxLayout() self.link_lbl = QLabel("") self.link_lbl.setWordWrap(True) self.link_edit = QLineEdit() link_layout.addWidget(self.link_edit) link_layout.addWidget(self.link_lbl) self.link_edit.hide() self.link_btn = QPushButton("Modify") self.link_btn.setFixedWidth(50) self.link_btn2 = QPushButton("Set") self.link_btn2.setFixedWidth(40) self.link_btn.clicked.connect(self.link_modify) self.link_btn2.clicked.connect(self.link_set) link_layout.addWidget(self.link_btn) link_layout.addWidget(self.link_btn2) self.link_btn2.hide() gallery_layout.addRow("Title:", self.title_edit) gallery_layout.addRow("Author:", self.author_edit) gallery_layout.addRow("Description:", self.descr_edit) gallery_layout.addRow("Language:", self.lang_box) gallery_layout.addRow("Tags:", tags_l) gallery_layout.addRow("Type:", self.type_box) gallery_layout.addRow("Status:", self.status_box) gallery_layout.addRow("Publication Date:", self.pub_edit) gallery_layout.addRow("Path:", self.path_lbl) gallery_layout.addRow("Link:", link_layout) self.title_edit.setFocus() def _find_combobox_match(self, combobox, key, default): f_index = combobox.findText(key, Qt.MatchFixedString) try: combobox.setCurrentIndex(f_index) except: combobox.setCurrentIndex(default) def setGallery(self, gallery): "To be used for when editing a gallery" self.gallery = gallery self.url_edit.setText(gallery.link) self.title_edit.setText(gallery.title) self.author_edit.setText(gallery.artist) self.descr_edit.setText(gallery.info) self.tags_edit.setText(utils.tag_to_string(gallery.tags)) self._find_combobox_match(self.lang_box, gallery.language, 2) self._find_combobox_match(self.type_box, gallery.type, 0) self._find_combobox_match(self.status_box, gallery.status, 0) gallery_pub_date = "{}".format(gallery.pub_date).split(' ') try: self.gallery_time = datetime.strptime(gallery_pub_date[1], '%H:%M:%S').time() except IndexError: pass qdate_pub_date = QDate.fromString(gallery_pub_date[0], "yyyy-MM-dd") self.pub_edit.setDate(qdate_pub_date) self.link_lbl.setText(gallery.link) self.path_lbl.setText(gallery.path) def newUI(self): f_local = QGroupBox("Directory/Archive") f_local.setCheckable(False) self.main_layout.addWidget(f_local) local_layout = QHBoxLayout() f_local.setLayout(local_layout) choose_folder = QPushButton("From Directory") choose_folder.clicked.connect(lambda: self.choose_dir('f')) local_layout.addWidget(choose_folder) choose_archive = QPushButton("From Archive") choose_archive.clicked.connect(lambda: self.choose_dir('a')) local_layout.addWidget(choose_archive) self.file_exists_lbl = QLabel() local_layout.addWidget(self.file_exists_lbl) self.file_exists_lbl.hide() def choose_dir(self, mode): """ Pass which mode to open the folder explorer in: 'f': directory 'a': files Or pass a predefined path """ self.done.show() self.file_exists_lbl.hide() if mode == 'a': name = QFileDialog.getOpenFileName(self, 'Choose archive', filter=utils.FILE_FILTER) name = name[0] elif mode == 'f': name = QFileDialog.getExistingDirectory(self, 'Choose folder') elif mode: if os.path.exists(mode): name = mode else: return None if not name: return head, tail = os.path.split(name) name = os.path.join(head, tail) parsed = utils.title_parser(tail) self.title_edit.setText(parsed['title']) self.author_edit.setText(parsed['artist']) self.path_lbl.setText(name) l_i = self.lang_box.findText(parsed['language']) if l_i != -1: self.lang_box.setCurrentIndex(l_i) if gallerydb.GalleryDB.check_exists(name): self.file_exists_lbl.setText('<font color="red">Gallery already exists.</font>') self.file_exists_lbl.show() # check galleries gs = 1 if name.endswith(utils.ARCHIVE_FILES): gs = len(utils.check_archive(name)) elif os.path.isdir(name): g_dirs, g_archs = utils.recursive_gallery_check(name) gs = len(g_dirs) + len(g_archs) if gs == 0: self.file_exists_lbl.setText('<font color="red">Invalid gallery source.</font>') self.file_exists_lbl.show() self.done.hide() if app_constants.SUBFOLDER_AS_GALLERY: if gs > 1: self.file_exists_lbl.setText('<font color="red">More than one galleries detected in source! Use other methods to add.</font>') self.file_exists_lbl.show() self.done.hide() def check(self): if len(self.title_edit.text()) is 0: self.title_edit.setFocus() self.title_edit.setStyleSheet("border-style:outset;border-width:2px;border-color:red;") return False elif len(self.author_edit.text()) is 0: self.author_edit.setText("Unknown") if len(self.path_lbl.text()) == 0 or self.path_lbl.text() == 'No path specified': self.path_lbl.setStyleSheet("color:red") self.path_lbl.setText('No path specified') return False return True def set_chapters(self, gallery_object, add_to_model=True): path = gallery_object.path chap_container = gallerydb.ChaptersContainer(gallery_object) metafile = utils.GMetafile() try: log_d('Listing dir...') con = scandir.scandir(path) # list all folders in gallery dir log_i('Gallery source is a directory') log_d('Sorting') chapters = sorted([sub.path for sub in con if sub.is_dir() or sub.name.endswith(utils.ARCHIVE_FILES)]) #subfolders # if gallery has chapters divided into sub folders if len(chapters) != 0: log_d('Chapters divided in folders..') for ch in chapters: chap = chap_container.create_chapter() chap.title = utils.title_parser(ch)['title'] chap.path = os.path.join(path, ch) metafile.update(utils.GMetafile(chap.path)) chap.pages = len(list(scandir.scandir(chap.path))) else: #else assume that all images are in gallery folder chap = chap_container.create_chapter() chap.title = utils.title_parser(os.path.split(path)[1])['title'] chap.path = path metafile.update(utils.GMetafile(path)) chap.pages = len(list(scandir.scandir(path))) except NotADirectoryError: if path.endswith(utils.ARCHIVE_FILES): gallery_object.is_archive = 1 log_i("Gallery source is an archive") archive_g = sorted(utils.check_archive(path)) for g in archive_g: chap = chap_container.create_chapter() chap.path = g chap.in_archive = 1 metafile.update(utils.GMetafile(g, path)) arch = utils.ArchiveFile(path) chap.pages = len(arch.dir_contents(g)) arch.close() metafile.apply_gallery(gallery_object) if add_to_model: self.SERIES.emit([gallery_object]) log_d('Sent gallery to model') def reject(self): if self.check(): msgbox = QMessageBox() msgbox.setText("<font color='red'><b>Noo oniichan! You were about to add a new gallery.</b></font>") msgbox.setInformativeText("Do you really want to discard?") msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msgbox.setDefaultButton(QMessageBox.No) if msgbox.exec() == QMessageBox.Yes: self.close() else: self.close() def web_metadata(self, url, btn_widget, pgr_widget): self.link_lbl.setText(url) f = fetch.Fetch() thread = QThread(self) thread.setObjectName('Gallerydialog web metadata') btn_widget.hide() pgr_widget.show() def status(stat): def do_hide(): try: pgr_widget.hide() btn_widget.show() except RuntimeError: pass if stat: do_hide() else: danger = """QProgressBar::chunk { background: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0,stop: 0 #FF0350,stop: 0.4999 #FF0020,stop: 0.5 #FF0019,stop: 1 #FF0000 ); border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; border: .px solid black;}""" pgr_widget.setStyleSheet(danger) QTimer.singleShot(3000, do_hide) f.deleteLater() def gallery_picker(gallery, title_url_list, q): self.parent_widget._web_metadata_picker(gallery, title_url_list, q, self) try: dummy_gallery = self.make_gallery(self.gallery) except AttributeError: dummy_gallery = self.make_gallery(gallerydb.Gallery(), False) if not dummy_gallery: status(False) return None dummy_gallery.link = url f.galleries = [dummy_gallery] f.moveToThread(thread) f.GALLERY_PICKER.connect(gallery_picker) f.GALLERY_EMITTER.connect(self.set_web_metadata) thread.started.connect(f.auto_web_metadata) thread.finished.connect(thread.deleteLater) f.FINISHED.connect(status) thread.start() def set_web_metadata(self, metadata): assert isinstance(metadata, gallerydb.Gallery) self.link_lbl.setText(metadata.link) self.title_edit.setText(metadata.title) self.author_edit.setText(metadata.artist) tags = "" lang = ['English', 'Japanese'] self._find_combobox_match(self.lang_box, metadata.language, 2) self.tags_edit.setText(utils.tag_to_string(metadata.tags)) pub_string = "{}".format(metadata.pub_date) pub_date = QDate.fromString(pub_string.split()[0], "yyyy-MM-dd") self.pub_edit.setDate(pub_date) self._find_combobox_match(self.type_box, metadata.type, 0) def make_gallery(self, new_gallery, add_to_model=True, new=False): if self.check(): new_gallery.title = self.title_edit.text() log_d('Adding gallery title') new_gallery.artist = self.author_edit.text() log_d('Adding gallery artist') log_d('Adding gallery path') if new and app_constants.MOVE_IMPORTED_GALLERIES: app_constants.OVERRIDE_MONITOR = True new_gallery.path = utils.move_files(self.path_lbl.text()) else: new_gallery.path = self.path_lbl.text() new_gallery.info = self.descr_edit.toPlainText() log_d('Adding gallery descr') new_gallery.type = self.type_box.currentText() log_d('Adding gallery type') new_gallery.language = self.lang_box.currentText() log_d('Adding gallery lang') new_gallery.status = self.status_box.currentText() log_d('Adding gallery status') new_gallery.tags = utils.tag_to_dict(self.tags_edit.toPlainText()) log_d('Adding gallery: tagging to dict') qpub_d = self.pub_edit.date().toString("ddMMyyyy") dpub_d = datetime.strptime(qpub_d, "%d%m%Y").date() try: d_t = self.gallery_time except AttributeError: d_t = datetime.now().time().replace(microsecond=0) dpub_d = datetime.combine(dpub_d, d_t) new_gallery.pub_date = dpub_d log_d('Adding gallery pub date') new_gallery.link = self.link_lbl.text() log_d('Adding gallery link') if not new_gallery.chapters: log_d('Starting chapters') thread = threading.Thread(target=self.set_chapters, args=(new_gallery,add_to_model), daemon=True) thread.start() thread.join() log_d('Finished chapters') return new_gallery def link_set(self): t = self.link_edit.text() self.link_edit.hide() self.link_lbl.show() self.link_lbl.setText(t) self.link_btn2.hide() self.link_btn.show() def link_modify(self): t = self.link_lbl.text() self.link_lbl.hide() self.link_edit.show() self.link_edit.setText(t) self.link_btn.hide() self.link_btn2.show() def accept(self): new_gallery = self.make_gallery(gallerydb.Gallery(), new=True) if new_gallery: self.close() def accept_edit(self): new_gallery = self.make_gallery(self.gallery) #for ser in self.gallery: if new_gallery: self.SERIES_EDIT.emit([new_gallery], self.position) self.close() def reject_edit(self): self.close()
class WidgetGallery(QDialog): def __init__(self, parent=None): super(WidgetGallery, self).__init__(parent) self.localization = de self.srcFileString = "" self.targetFileString = "" self.truePath = None self.firstRun = True styleComboBox = QComboBox() styleComboBox.addItems(QStyleFactory.keys()) self.createStartSection() self.createFileSelection() self.createDateSelection() self.createCheckboxArea() self.createInfoOutputSection() mainLayout = QGridLayout() mainLayout.addWidget(self.fileSelectionGroup, 1, 0) mainLayout.addWidget(self.dateSelectionGroupBox, 2, 0) mainLayout.addWidget(self.checkboxGroup, 3, 0) mainLayout.addWidget(self.startSection, 4, 0) mainLayout.addWidget(self.infoOutputSection, 5, 0) self.setLayout(mainLayout) self.setWindowTitle(self.localization.window_title) def createStartSection(self): '''Generate Aread containing the start button''' self.startSection = QGroupBox(self.localization.start_section) self.buttonGo = QPushButton(self.localization.button_go) self.buttonGo.setDisabled(True) self.buttonGo.clicked.connect(self.run) layout = QVBoxLayout() layout.addWidget(self.buttonGo) self.startSection.setLayout(layout) def createInfoOutputSection(self): '''Generate Aread containing progress, error and warning outputs''' self.infoOutputSection = QGroupBox(self.localization.infoOutput) self.infoTextBox = QTextBrowser() layout = QVBoxLayout() layout.addWidget(self.infoTextBox) self.infoOutputSection.setLayout(layout) def createFileSelection(self): '''Generate the area containing the file selectors and go button''' self.fileSelectionGroup = QGroupBox(self.localization.file_selection) # basic object # self.buttonSrcFile = QPushButton(self.localization.button_set_src_file) self.srcFileName = QLabel(self.localization.output_file) self.buttonTargetFile = QPushButton( self.localization.output_file_placeholder) self.boxUseSrcDir = QCheckBox(self.localization.button_use_src_dir) # connectors # self.buttonSrcFile.clicked.connect(self.selectSrcFile) self.buttonTargetFile.clicked.connect(self.selectTargetFile) self.boxUseSrcDir.stateChanged.connect(self.useSrcDir) self.boxUseSrcDir.setChecked(True) # layout # layout = QVBoxLayout() layout.addWidget(self.buttonSrcFile) layout.addWidget(self.srcFileName) layout.addWidget(self.buttonTargetFile) layout.addWidget(self.boxUseSrcDir) layout.addStretch(1) self.fileSelectionGroup.setLayout(layout) def createDateSelection(self): '''Generate the area containing the date selectors''' self.dateSelectionGroupBox = QGroupBox( self.localization.date_selection) layout = QGridLayout() self.startDateEdit = QDateEdit(calendarPopup=True) self.startDateEdit.setDisplayFormat("dd.MM.yyyy") self.startDateEdit.setReadOnly(True) self.startDateEdit.lineEdit().setDisabled(True) self.endDateEdit = QDateEdit(calendarPopup=True) self.endDateEdit.setDisplayFormat("dd.MM.yyyy") self.endDateEdit.setReadOnly(True) self.endDateEdit.lineEdit().setDisabled(True) self.startTimeEdit = QLineEdit("00:00") self.endTimeEdit = QLineEdit("23:59") self.startTimeEdit.setDisabled(True) self.endTimeEdit.setDisabled(True) layout.addWidget(self.startDateEdit, 0, 0) layout.addWidget(self.startTimeEdit, 0, 1) layout.addWidget(self.endDateEdit, 1, 0) layout.addWidget(self.endTimeEdit, 1, 1) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 1) self.dateSelectionGroupBox.setLayout(layout) def createCheckboxArea(self): '''Generate area with configuration options''' self.checkboxGroup = QGroupBox(self.localization.options) self.boxOTemp = QCheckBox(self.localization.button_otemp) self.boxOHumidity = QCheckBox(self.localization.button_ohumidity) layout = QVBoxLayout() layout.addWidget(self.boxOTemp) layout.addWidget(self.boxOHumidity) layout.addStretch(1) self.checkboxGroup.setLayout(layout) def run(self): '''Run generation with selected file and options''' # set save target if nessesary # self.infoTextBox.clear() self.buttonGo.setText(self.localization.button_go_wait) self.buttonGo.setDisabled(True) self.repaint() if self.boxUseSrcDir.isChecked(): target = self.srcFileString forcePath = False else: target = self.targetFileString forcePath = True # workaround for checkboxes changed # outsideDataNeeded = self.boxOTemp.isChecked( ) or self.boxOHumidity.isChecked() self.datapoints = input_backend.read_in_file( self.srcFileString, outsideData=outsideDataNeeded, plotOutsideTemp=self.boxOTemp.isChecked(), plotOutsideHum=self.boxOHumidity.isChecked(), qtTextBrowser=self.infoTextBox) # build dates # try: startTimeHelper = dt.datetime.strptime(self.startTimeEdit.text(), "%H:%M") endTimeHelper = dt.datetime.strptime(self.endTimeEdit.text(), "%H:%M") except ValueError as e: errorBox = QMessageBox(self) errorBox.setAttribute(PyQt5.QtCore.Qt.WA_DeleteOnClose) errorBox.setText(self.localization.bad_time) errorBox.setDetailedText(str(e)) errorBox.show() self.buttonGo.setText(self.localization.button_go) self.buttonGo.setDisabled(False) return startTimeOffset = dt.timedelta(hours=startTimeHelper.hour, minutes=startTimeHelper.minute) endTimeOffset = dt.timedelta(hours=endTimeHelper.hour, minutes=endTimeHelper.minute) zeroTime = dt.time(0, 0) startDateTime = dt.datetime.combine( self.startDateEdit.date().toPyDate(), zeroTime) startDateTime += startTimeOffset endDateTime = dt.datetime.combine(self.endDateEdit.date().toPyDate(), zeroTime) endDateTime += endTimeOffset try: self.truePath = plot.plot(self.datapoints, path=target, date1=startDateTime, date2=endDateTime, forcePath=forcePath, qtTextBrowser=self.infoTextBox) except ValueError as e: self.infoTextBox.append("ERROR: " + str(e)) self.buttonGo.setText(self.localization.button_go) return self.buttonGo.setText(self.localization.button_go) self.buttonGo.setDisabled(False) self.infoTextBox.append(self.localization.success) doneDialog = QMessageBox(self) doneDialog.setAttribute(PyQt5.QtCore.Qt.WA_DeleteOnClose) doneDialog.setText(self.localization.done_text) doneDialog.addButton(self.localization.open_pic, QMessageBox.YesRole) doneDialog.addButton(self.localization.close, QMessageBox.NoRole) doneDialog.buttonClicked.connect(self.openFile) doneDialog.show() def selectSrcFile(self): '''Function to select a src-file''' if not self.firstRun: targetDir = "" # meaning the last one opened else: targetDir = cp.CFG("default_source_dir") self.srcFileString = QFileDialog.getOpenFileName( self, self.localization.src_file_dialog, targetDir, "Data-Files (*.txt *.csv *.dbf *.xls)")[0] self.srcFileName.setText(self.srcFileString) if not self.srcFileString: return self.infoTextBox.append(self.localization.testing_input) self.firstRun = False try: self.datapoints = input_backend.read_in_file( self.srcFileString, outsideData=False, plotOutsideTemp=False, plotOutsideHum=False, qtTextBrowser=self.infoTextBox) except Exception as e: errorBox = QMessageBox(self) errorBox.setStyleSheet("QLabel{min-width: 700px;}") errorBox.setAttribute(PyQt5.QtCore.Qt.WA_DeleteOnClose) errorBox.setText(self.localization.error_read_in) errorBox.setDetailedText(traceback.format_exc()) errorBox.show() return start = self.datapoints[cp.CFG("plot_temperatur_key")].getFirstTime() self.startDateEdit.setDateTime(start) end = self.datapoints[cp.CFG("plot_temperatur_key")].getLastTime() self.endDateEdit.setDateTime(end) self.buttonGo.setDisabled(False) self.endDateEdit.setReadOnly(False) self.startDateEdit.setReadOnly(False) self.startDateEdit.lineEdit().setDisabled(False) self.endDateEdit.lineEdit().setDisabled(False) self.startTimeEdit.setDisabled(False) self.endTimeEdit.setDisabled(False) self.buttonGo.setFocus(PyQt5.QtCore.Qt.OtherFocusReason) self.infoTextBox.append(self.localization.testing_input_suc) def selectTargetFile(self): '''Function to select a target-file''' self.targetFileString = QFileDialog.getSaveFileName( self, self.localization.save_file_dialog)[0] if not self.targetFileString: return self.buttonTargetFile.setText(self.targetFileString) self.buttonGo.setDisabled(False) self.buttonGo.setFocus(PyQt5.QtCore.Qt.OtherFocusReason) def useSrcDir(self): '''Function to handle use src dir checkbox''' if self.boxUseSrcDir.isChecked(): self.buttonTargetFile.setDisabled(True) if self.srcFileString: self.buttonGo.setDisabled(False) self.srcFileName.setText(self.srcFileString) else: self.buttonTargetFile.setDisabled(False) if self.targetFileString: self.buttonTargetFile.setText(self.targetFileString) else: self.buttonGo.setDisabled(True) def openFile(self, button): if button.text() == self.localization.open_pic and self.truePath: PyQt5.QtGui.QDesktopServices.openUrl( QUrl.fromLocalFile(self.truePath)) else: pass
class Invoice(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setFixedSize(600, 650) self.setWindowTitle("Ver Fatura\Extrato: ") self.setWindowIcon(Icon('fatura.png')) layout = QFormLayout() self.account = QComboBox() accounts = DATABASE.getAccounts() for account in accounts: self.account.addItem(account) self.account.activated.connect(self.attPeriod) layout.addRow("Conta", self.account) self.period1 = QDateEdit() self.period1.setCalendarPopup(True) label = QLabel(" a ") label.setAlignment(Qt.AlignCenter) self.period2 = QDateEdit() self.period2.setCalendarPopup(True) dateLayout = QHBoxLayout() dateLayout.addWidget(self.period1, 1) dateLayout.addWidget(label) dateLayout.addWidget(self.period2, 1) layout.addRow("Periodo", dateLayout) infoBox = QHBoxLayout() infos = QFormLayout() self.accountView = QLabel() self.totalValue = QLabel() self.date1 = QLabel() label2 = QLabel(" a ") label2.setAlignment(Qt.AlignCenter) self.date2 = QLabel() dateLayout2 = QHBoxLayout() dateLayout2.addWidget(self.date1, 1) dateLayout2.addWidget(label2) dateLayout2.addWidget(self.date2, 1) infos.addRow("Conta", self.accountView) infos.addRow("Período ", dateLayout2) infos.addRow("Total", self.totalValue) infoBox.addLayout(infos) left = QToolButton() left.setArrowType(Qt.LeftArrow) left.clicked.connect(self.previousMonth) right = QToolButton() right.setArrowType(Qt.RightArrow) right.clicked.connect(self.nextMonth) infoBox.addWidget(left) infoBox.addWidget(right) infoBox.addStretch(1) layout.addRow(infoBox) self.table = Tree(4) self.table.setHeaderLabels(['Data', 'Descrição', 'Categoria', 'Valor']) self.table.setResizeColumn(2) self.table.itemDoubleClicked.connect(self.editItem) self.table.itemClicked.connect(self.alterSum) layout.addRow(self.table) self.attPeriod(0) self.period1.dateChanged.connect(self.showTable) self.period2.dateChanged.connect(self.showTable) self.setLayout(layout) def alterSum(self, item, column): if column == 1: suma = 0 for itemTree in self.table.getChecked(1): if itemTree.checkState(1): suma += float(itemTree.text(4)[3:]) self.totalValue.setText("R$ %.2f" % suma) def attPeriod(self, index): info = DATABASE.getAccounts(name=self.account.itemText(index), info=True) currentDate = QDate.currentDate() if info[3]: currentDate.setDate(currentDate.year(), currentDate.month() - 1, info[3] + 1) self.period1.setDate(currentDate.addDays(-10)) self.period2.setDate(currentDate.addMonths(1).addDays(-11)) self.showTable() else: currentDate.setDate(currentDate.year(), currentDate.month(), 1) self.period1.setDate(currentDate) self.period2.setDate(QDate(currentDate.year(), currentDate.month(), currentDate.daysInMonth())) self.showTable(inverse=True) def editItem(self, item, column): self.parent().mainWindow.transWindow.editItem(item, column, invoice=True) self.showTable() def nextMonth(self): self.period1.setDate(self.period1.date().addMonths(1)) self.period2.setDate(self.period2.date().addMonths(1)) def previousMonth(self): self.period1.setDate(self.period1.date().addMonths(-1)) self.period2.setDate(self.period2.date().addMonths(-1)) def showTable(self, inverse=False): self.table.clear() self.table.setHeaderLabels(["#",'Data', 'Descrição', 'Categoria', 'Valor']) self.accountView.setText(self.account.currentText()) self.date1.setText(self.period1.date().toString("dd/MM/yyyy")) self.date2.setText(self.period2.date().toString("dd/MM/yyyy")) transactions = DATABASE.getTransactionsFrom(self.period1.date(), self.period2.date(), self.account.currentText()) suma = 0 count = 1 for trans in transactions: row = TreeItem(self.table, '') row.setIdent(trans[0]) row.setData(0, Qt.DisplayRole, count) row.setData(1, Qt.DisplayRole, QDate(trans[3], trans[2], trans[1]), ) row.setCheckState(1, Qt.Checked) row.setText(2, trans[4]) row.setText(3, trans[5]) if inverse: row.setText(4, "R$ %.2f" % trans[8]) suma += trans[8] else: row.setText(4, "R$ %.2f" % -trans[8]) suma -= trans[8] if trans[8] > 0: defineColor(row, trans[8], 2) count += 1 self.totalValue.setText("R$ %.2f" % suma) self.table.resizeColumnToContents(0)
class Addstocks(QWidget): def __init__(self, pf): self.counter = 0 self.pf = pf super(Addstocks, self).__init__() self.setMinimumSize(QSize(300, 230)) self.resize(550, 320) self.setWindowTitle("Add stocks") self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.darkCyan) # darkcyan self.setPalette(p) label1 = QLabel("Enter a stock ticker:", self) self.ticker = QLineEdit(self) self.ticker.move(270, 20) self.ticker.resize(200, 32) label1.move(20, 25) label2 = QLabel("How many shares?", self) self.amount = QLineEdit(self) self.amount.move(270, 80) self.amount.resize(200, 32) label2.move(20, 85) label3 = QLabel("When you bought it?", self) self.date = QDateEdit(self) self.date.move(270, 140) self.date.resize(200, 32) self.date.setDate(QDate.currentDate()) label3.move(20, 145) self.date.editingFinished.connect(self.date_method) pybutton = QPushButton('Add a stock', self) pybutton.clicked.connect(self.click) pybutton.resize(200, 32) pybutton.move(270, 200) pybutton2 = QPushButton('Finished', self) pybutton2.clicked.connect(self.click1) pybutton2.resize(200, 32) pybutton2.move(270, 260) def date_method(self): # Change the date received by the user into a correct form self.counter += 1 value = self.date.date() self.day = value.toPyDate() def click(self): # Finds the ticker from Yahoo Finance and checks that the number of stocks owned is legit. # Also checks that the date chosen is a legitimate. # self.counter is used because editingFinished.connect does not work if the user does not touch the # date picker at all. Thus, the program needs to have a date even though the date_method has not been # activated. if self.counter == 0: value = QDate.currentDate() self.day = value.toPyDate() today = date.today() delta = today - self.day if delta.days < 0: QMessageBox.about(self, "Error", "The date you gave is incorrect.") else: self.stock = self.ticker.text() self.am = self.amount.text() tc = yf.Ticker(self.stock) pricedata = tc.history(period='5y')['Close'] if len(pricedata) > 0: try: self.am = float(self.am) if self.am < 0.01: QMessageBox.about(self, "Error", "Incorrect amount!") else: self.pf.add_stock(self.stock, self.am, self.day) QMessageBox.about(self, "Success", "Stock added!") self.amount.clear() self.ticker.clear() except ValueError: QMessageBox.about(self, "Error", "Incorrect amount!") else: QMessageBox.about(self, "Error", "Did not find any data for that stock.") def click1(self): #counts the different ratios for the portfolio and launches the new window self.pf.pf_value() self.pf.pf_returns() self.pf.get_greeks() self.pf.count_sharpe() self.next_window = Infopage(self.pf) self.next_window.show() self.close()
class AddressForm: user_id = None def __init__(self, user=None): self.form_layout = QFormLayout() self.second_name = QLineEdit() self.first_name = QLineEdit() self.birthday = QDateEdit() self.notes = QTextEdit() self.phones = list() if user is not None and user['id'] is not None: self.user_id = user['id'] if user is not None and user['second_name'] is not None: self.second_name.setText(user['second_name']) second_name_label = QLabel('Фамилия:') if user is not None and user['first_name'] is not None: self.first_name.setText(user['first_name']) first_name_label = QLabel('Имя:') self.birthday.setCalendarPopup(True) self.birthday.setDisplayFormat('dd MMMM yyyy') if user is not None and user['birthday'] is not None: self.birthday.setDate( QDate.fromString(user['birthday'], "d.M.yyyy")) birthday_label = QLabel('Дата рождения:') notes_label = QLabel('Примечание:') if user is not None and user['notes'] is not None: self.notes.setText(user['notes']) self.form_layout.addRow(second_name_label, self.second_name) self.form_layout.addRow(first_name_label, self.first_name) self.form_layout.addRow(birthday_label, self.birthday) self.form_layout.addRow(notes_label, self.notes) phone_label = QLabel('Телефон') self.form_layout.addRow(phone_label) if user is not None and user['phone'] is not None: for phone_row in user['phone']: phone = QLineEdit() phone.setText(phone_row['phone']) phone_type = QComboBox() phone_type.addItem('Домашний') phone_type.addItem('Рабочий') if phone_row['type'] == 'home': phone_type.setCurrentIndex(0) if phone_row['type'] == 'work': phone_type.setCurrentIndex(1) self.phones.append({'phone': phone, 'type': phone_type}) self.form_layout.addRow(phone_type, phone) phone = QLineEdit() phone_type = QComboBox() phone_type.addItem('Домашний') phone_type.addItem('Рабочий') self.phones.append({'phone': phone, 'type': phone_type}) self.form_layout.addRow(phone_type, phone) def add_row(self, *args): self.form_layout.addRow(*args) def get_layout(self): return self.form_layout def get_second_name(self): return self.second_name.text() def get_first_name(self): return self.first_name.text() def get_birthday(self): tmp_date = self.birthday.date() tmp_date = tmp_date.toPyDate() return '{0:%d.%m.%Y}'.format(tmp_date) def get_notes(self): return self.notes.toPlainText() def get_phones(self): return self.phones def get_user_id(self): return self.user_id def validate_form(self): errors = list()
class DataWidget(QWidget): def __init__(self): super(DataWidget, self).__init__(None) self.read_data_file(config.HISTORIC_DATA) self.start_date = date(2016, 10, 1) self.end_date = date(2016, 11, 1) self.graph = GraphCanvas(self.data_frame) with open(config.LOGFILE, 'w', newline='') as logfile: logwriter = csv.writer(logfile) logwriter.writerow(config.headers) # options group box self.optionsGroupBox = QGroupBox("Options") options_layout = QVBoxLayout() date_range_layout = QFormLayout() start_date_label = QLabel("Start Date:") self.start_date_box = QDateEdit(self.start_date) self.start_date_box.setCalendarPopup(True) self.start_date_box.setDisplayFormat(config.DATE_DISPLAY_FORMAT) self.start_date_box.dateChanged.connect(self.plot) end_date_label = QLabel("End Date:") self.end_date_box = QDateEdit(self.end_date) self.end_date_box.setCalendarPopup(True) self.end_date_box.setDisplayFormat(config.DATE_DISPLAY_FORMAT) self.end_date_box.dateChanged.connect(self.plot) date_range_layout.addRow(start_date_label, self.start_date_box) date_range_layout.addRow(end_date_label, self.end_date_box) # days of week self.sundayCheckBox = QCheckBox("Sunday") self.mondayCheckBox = QCheckBox("Monday") self.tuesdayCheckBox = QCheckBox("Tuesday") self.wednesdayCheckBox = QCheckBox("Wednesday") self.thursdayCheckBox = QCheckBox("Thursday") self.fridayCheckBox = QCheckBox("Friday") self.saturdayCheckBox = QCheckBox("Saturday") self.sundayCheckBox.toggled.connect(self.plot) self.mondayCheckBox.toggled.connect(self.plot) self.tuesdayCheckBox.toggled.connect(self.plot) self.wednesdayCheckBox.toggled.connect(self.plot) self.thursdayCheckBox.toggled.connect(self.plot) self.fridayCheckBox.toggled.connect(self.plot) self.saturdayCheckBox.toggled.connect(self.plot) options_layout.addLayout(date_range_layout) options_layout.addWidget(self.sundayCheckBox) options_layout.addWidget(self.mondayCheckBox) options_layout.addWidget(self.tuesdayCheckBox) options_layout.addWidget(self.wednesdayCheckBox) options_layout.addWidget(self.thursdayCheckBox) options_layout.addWidget(self.fridayCheckBox) options_layout.addWidget(self.saturdayCheckBox) options_layout.addWidget(QPushButton("Button")) self.optionsGroupBox.setLayout(options_layout) # graph group box self.graphGroupBox = QGroupBox("Graph") graphLayout = QVBoxLayout() graphLayout.addWidget(self.graph) self.graphGroupBox.setLayout(graphLayout) # choice group box self.choiceGroupBox = QGroupBox("Choice") choice_layout = QVBoxLayout() choice_layout_1 = QHBoxLayout() choice_layout_1.addWidget(QLabel("A")) choice_layout_1.addWidget(QLabel("B")) choice_layout_1.addWidget(QLabel("C")) choice_layout_1.addWidget(QLabel("D")) choice_layout_2 = QHBoxLayout() self.decision_value_1 = QSpinBox() self.decision_value_2 = QSpinBox() self.decision_value_3 = QSpinBox() self.decision_value_4 = QSpinBox() choice_layout_2.addWidget(self.decision_value_1) choice_layout_2.addWidget(self.decision_value_2) choice_layout_2.addWidget(self.decision_value_3) choice_layout_2.addWidget(self.decision_value_4) choice_layout.addLayout(choice_layout_1) choice_layout.addLayout(choice_layout_2) self.decision_value_1.setValue(42) self.choiceGroupBox.setLayout(choice_layout) # count-down timer self.timerGroupBox = QGroupBox("Timer") timer_layout = QVBoxLayout() self.timerGroupBox.setLayout(timer_layout) self.count_down_timer = CountDownTimer() timer_layout.addWidget(self.count_down_timer.lcd) # central widget frame = QFrame(self) self.grid = QGridLayout(frame) self.grid.setSpacing(16) self.grid.setContentsMargins(16, 16, 16, 16) self.grid.addWidget(self.optionsGroupBox, 0, 0, 2, 1) self.grid.addWidget(self.graphGroupBox, 0, 1, 2, 1) self.grid.addWidget(self.timerGroupBox, 0, 2) self.grid.addWidget(self.choiceGroupBox, 2, 1) self.grid.setColumnStretch(0, 2) self.grid.setColumnStretch(1, 6) self.grid.setColumnStretch(2, 1) self.grid.setRowStretch(0, 1) self.grid.setRowStretch(1, 2) self.grid.setRowStretch(2, 2) self.setLayout(self.grid) def read_data_file(self, data_file): """read data_file and import it's content as pandas dataframe""" dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d') self.data_frame = pd.read_csv(data_file, index_col=0, parse_dates=True) def plot(self): """ Method to update start / end dates upon selection. Fetch the start / end dates, convert the QTime datatype to DateTime.Date datatype, and plot the graph. """ self.start_date = self.start_date_box.date().toPyDate() self.end_date = self.end_date_box.date().toPyDate() self.graph.plot(self.data_frame.loc[self.start_date : self.end_date]) def add_log_row(self): """ Write data in the log file :return: """ new_row = [config.id, config.age, config.male, config.field, 'test_condition', datetime.now()] with open(config.LOGFILE, 'a', newline='') as logfile: logwriter = csv.writer(logfile) logwriter.writerow(new_row)