class MainPage(QDialog): def __init__(self, parent=None): super(MainPage, self).__init__(parent) self.initUI() def initUI(self): self.setWindowTitle("sa1tFish") self.setGeometry(200, 200, 800, 400) self.selectList = QListWidget() self.Item = QListWidgetItem() self.selectList.setFlow(QListWidget.LeftToRight) self.selectList.addItems(["function1", "function2", "function3"]) self.selectList.setMaximumHeight(40) self.selectList.setMinimumHeight(20) self.resultEdit1 = QTextEdit("function1--result1--111", self) self.resultEdit2 = QTextEdit("function2--result2--222", self) self.resultEdit3 = QTextEdit("function3--result3--333", self) self.stack = QStackedWidget() self.stack.addWidget(self.resultEdit1) self.stack.addWidget(self.resultEdit2) self.stack.addWidget(self.resultEdit3) layout = QVBoxLayout(self) layout.addWidget(self.selectList) layout.addWidget(self.stack) layout.setStretch(0, 1) layout.setStretch(1, 20) self.selectList.currentRowChanged.connect(self.stack.setCurrentIndex) self.setMinimumHeight(200) self.show()
class SpectateBattle(QWidget): def __init__(self): super(SpectateBattle, self).__init__() self.exitButton = QPushButton('Exit to menu') self.exitButton.clicked.connect(self.interruptBattle) self.menu = BotBattleMenu() self.menu.startButton.clicked.connect(self.startBattle) self.battle = None self.stack = QStackedWidget() self.stack.addWidget(self.menu) layout = QVBoxLayout() layout.addWidget(self.stack) layout.addWidget(self.exitButton) self.setLayout(layout) def startBattle(self): bot1 = game.players.ai.select_bot(self.menu.bot1Level) bot2 = game.players.ai.select_bot(self.menu.bot2Level) self.battle = BotBattle(bot1, bot2) self.stack.addWidget(self.battle) self.stack.setCurrentWidget(self.battle) def interruptBattle(self): if self.battle: self.battle.interrupt()
class FormComboWidget(QWidget): def __init__(self, datalist, comment="", parent=None): QWidget.__init__(self, parent) layout = QVBoxLayout() self.setLayout(layout) self.combobox = QComboBox() layout.addWidget(self.combobox) self.stackwidget = QStackedWidget(self) layout.addWidget(self.stackwidget) self.combobox.currentIndexChanged[int].connect(self.stackwidget, setCurrentIndex) self.widgetlist = [] for data, title, comment in datalist: self.combobox.addItem(title) widget = FormWidget(data, comment=comment, parent=self) self.stackwidget.addWidget(widget) self.widgetlist.append(widget) def setup(self): for widget in self.widgetlist: widget.setup() def get(self): return [widget.get() for widget in self.widgetlist]
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setStyleSheet(open('assets/style.qss').read()) self.setWindowTitle("Bombe") self.game = Game(self) ui.initUi(self.game) self.setup = Setup.Setup(self) self.setupSymbole = Setup.SetupSymbole(self) self.bios = QWidget() self.desamorcage = QWidget() self.win = StressMode.winEscape(self) self.stackSetup = QStackedWidget() self.stackSetup.addWidget(self.setup) self.stackSetup.addWidget(self.setupSymbole) self.stackSetup.addWidget(self.game) self.stackSetup.addWidget(self.bios) self.stackSetup.addWidget(self.win) self.stackSetup.addWidget(self.desamorcage) self.bios.setStyleSheet( "border-image: url(assets/Images/biosurgence.png) 0 0 0 0 stretch stretch" ) self.desamorcage.setStyleSheet( "border-image: url(assets/Images/desamorcage.png) 0 0 0 0 stretch stretch" ) self.setGeometry(0, 0, 480, 720) self.setCentralWidget(self.stackSetup)
class LoginWindow(QDialog): def __init__(self, interface): super().__init__() self.interface = interface self.widget_stack = QStackedWidget(self) self.widget_stack.addWidget(ConnectingWidget(self)) self.input = InputWidget( self, 'images/splash_icon.png', 200, 'Username:'******'Login', constants.MAX_NAME_LENGTH, self.__connect) self.widget_stack.addWidget(self.input) hbox = QHBoxLayout() hbox.addWidget(self.widget_stack) self.setLayout(hbox) self.setWindowTitle(settings.APP_NAME) self.setWindowIcon(QIcon('images/icon.png')) utils.resize_window(self, 500, 200) utils.center_window(self) def __connect(self, username): self.widget_stack.setCurrentIndex(0) conn.synchronous_send( command = constants.CMD_AUTH, data = username)
class DynaFrame(QFrame): """ The DynaFrame class wraps one custom widget which can be replaced with another during runtime. """ def __init__(self, parent, widget: QWidget=None): super(__class__, self).__init__(parent) layout = QHBoxLayout() self._stk_widget = QStackedWidget() if widget is not None: self.setWidget(widget) layout.addWidget(self._stk_widget) self.setLayout(layout) def setWidget(self, widget: QWidget): """ Replaces the currently present widget (if any) with the specified one. """ self._clear_stack_widget() self._stk_widget.addWidget(widget) def getWidget(self) -> QWidget: """ Returns the widget wrapped in the frame. """ return self._stk_widget.widget(0) def _clear_stack_widget(self): """ Clears all widgets which were added to the stack. """ count = self._stk_widget.count() assert 0 <= count <= 1 for index in range(0, count): widget = self._stk_widget.widget(index) self._stk_widget.removeWidget(widget)
class App(QMainWindow): results_sig = pyqtSignal(tuple) def __init__(self): super().__init__() self.title = 'Compress4D' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.widget_stacks = QStackedWidget() self.init_ui() def init_ui(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.init_layout() self.show() def init_layout(self): base_widget = QWidget() # Widgets for stacks # matrix_input_widget = MatrixInputWidget() results_widget = ResultsWidget() # Widget stacking # self.widget_stacks.addWidget(matrix_input_widget) self.widget_stacks.addWidget(results_widget) # Layout setup # base_layout = QVBoxLayout() base_layout.addWidget(self.widget_stacks) base_widget.setLayout(base_layout) # Menubar actions # exit_action = QAction(QIcon("exit.png"), "Salir", self) exit_action.setShortcut("Ctrl+Q") exit_action.setStatusTip("Salir de la aplicación") exit_action.triggered.connect(qApp.quit) # Menubar configuration # menubar = self.menuBar() file_menu = menubar.addMenu("&Archivo") file_menu.addAction(exit_action) # Signals connection # matrix_input_widget.wk_sig_done.connect(self.show_results) results_widget.go_input_sig.connect(self.show_input) self.results_sig.connect(results_widget.update_results) self.setCentralWidget(base_widget) @pyqtSlot(tuple) def show_results(self, results: tuple): self.results_sig.emit(results) self.widget_stacks.setCurrentIndex(1) @pyqtSlot() def show_input(self): self.widget_stacks.setCurrentIndex(0)
class GOQT(QWidget): def __init__(self, maingo): super().__init__() self.maingo = maingo self.initUI() def initUI(self): self.move(0, 0) self.setWindowTitle('Go') self.hb = QHBoxLayout() self.hb.setContentsMargins(0, 0, 0, 0) self.oldSizeConstraing = self.hb.sizeConstraint() self.stack = QStackedWidget() self.authorizeWidget = AuthorizeWidget(self.maingo, self) self.changeWidget(self.authorizeWidget) self.hb.addWidget(self.stack) self.setLayout(self.hb) self.show() def changeWidget(self, widget, oldsize=True): if oldsize: self.hb.setSizeConstraint(self.oldSizeConstraing) else: self.hb.setSizeConstraint(QLayout.SetFixedSize) if self.stack.currentWidget() != 0: self.stack.removeWidget(self.stack.currentWidget()) self.stack.addWidget(widget) self.stack.setCurrentWidget(widget)
class Results(QMainWindow): def __init__(self, query): super().__init__() self.title = 'Google - Web Scraper' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.query = query self.initUI() def initUI(self): self.setWindowTitle(self.title) self.statusBar().showMessage("Query - " + self.query) self.setGeometry(self.left, self.top, self.width, self.height) google = Google.Google() self.__data = google.scrape(self.query) self.layout = QVBoxLayout(self) self.Stack = QStackedWidget() for i in self.__data['results']: self.Stack.addWidget(Card(i)) self.layout.addWidget(self.Stack) self.setCentralWidget(self.Stack) self.show()
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.button1 = QPushButton('button1', self) # 1 self.button2 = QPushButton('button2', self) self.button2.setProperty('name', 'btn') self.lineedit1 = QLineEdit(self) # 2 self.lineedit1.setPlaceholderText('direct') self.lineedit2 = SubLineEdit() my_list = ['A', 'B', 'C', 'D'] # 3 self.combo = QComboBox(self) self.combo.addItems(my_list) self.combo.setObjectName('cb') self.gb = QGroupBox() # 4 self.label1 = QLabel('label1') self.label2 = QLabel('label2') self.stack = QStackedWidget() self.stack.addWidget(self.label2) self.gb_layout = QVBoxLayout() self.gb_layout.addWidget(self.label1) self.gb_layout.addWidget(self.stack) self.gb.setLayout(self.gb_layout) self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.button1) self.v_layout.addWidget(self.button2) self.v_layout.addWidget(self.lineedit1) self.v_layout.addWidget(self.lineedit2) self.v_layout.addWidget(self.combo) self.v_layout.addWidget(self.gb) self.setLayout(self.v_layout)
class App(QMainWindow): def __init__(self): '''생성자''' super().__init__() self.title = 'Cyworld Bot' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.cpu_count = multiprocessing.cpu_count() self.worker = None self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.setFixedSize(self.width, self.height) self.mainWidget = MainWidget(self, self.left, self.top, self.width, self.height, self.cpu_count) self.processWidget = ProcessWidget(self, self.left, self.top, self.width, self.height) self.centralWidget = QStackedWidget() self.setCentralWidget(self.centralWidget) self.centralWidget.addWidget(self.mainWidget) self.centralWidget.addWidget(self.processWidget) self.showMainWidget() self.show() def showMainWidget(self): '''메인 위젯으로 화면을 전환합니다.''' self.centralWidget.setCurrentWidget(self.mainWidget) def showProcessWidget(self): '''작업 중 위젯으로 화면을 전환합니다.''' self.centralWidget.setCurrentWidget(self.processWidget) def start(self, user_email, user_password, chromedriver, parser, downloader, wait_timeout, delay): self.processWidget.message.setText('작업 준비 중..') self.processWidget.homeButton.hide() self.processWidget.openButton.hide() self.showProcessWidget() worker = BotWorker(self, user_email, user_password, chromedriver, parser, downloader, wait_timeout, delay) worker.start() self.worker = worker def log_callback(self, message): self.processWidget.message.setText(message) def error(self): self.worker.stop() self.processWidget.homeButton.show() def done(self): self.processWidget.message.setText('작업 완료!') self.processWidget.homeButton.show() self.processWidget.openButton.show()
class Window(QMainWindow): def __init__(self): super().__init__() self.width = 800 self.height = 600 self.initUI() def initUI(self): self.setGeometry(100, 100, self.width, self.height) self.setWindowTitle("TicTacToe") #Creacion del Widget Apilado self.windows = QStackedWidget(self) #Creacion de los items a apilar self.menu = MenuWidget(self) self.game = GameWidget() #Agregar los widgets al Widget apilado self.windows.addWidget(self.menu) self.windows.addWidget(self.game) self.setCentralWidget(self.windows) self.show() def play(self): self.windows.setCurrentIndex(1) print("Play") #server() def join(self): #client() pass
class MainWindow(QMainWindow): def __init__(self): super().__init__() self._widgetList = [SearchWidget, TitleViewer, ImageViewer] self.__createWidgets() def __createWidgets(self): self._stack = QStackedWidget() # self.setWindowTitle("Cute Manga") self._stack.addWidget(SearchWidget(parent=self, backEnd=mangahere)) # self.setCentralWidget(self._stack) def goBack(self) -> NoReturn: ci = self._stack.currentIndex() if ci == 0: sys.exit(0) else: cw = self._stack.currentWidget() self._stack.setCurrentIndex(ci - 1) self._stack.removeWidget(cw) def goTo(self, widget: QWidget) -> NoReturn: ci = self._stack.currentIndex() self._stack.addWidget(widget) self._stack.setCurrentIndex(ci + 1)
class MainWindow(QMainWindow): current_instance = None def __init__(self): super().__init__() self.setAttribute(Qt.WA_DeleteOnClose) self.setFocusPolicy(Qt.TabFocus) scroll = QScrollArea(self) scroll.setWidgetResizable(True) self.central_widget = QStackedWidget() scroll.setWidget(self.central_widget) self.setCentralWidget(scroll) self.toolbar = self.addToolBar('&Main') self.toolbar.setLayoutDirection(Qt.RightToLeft) self.toolbar.toggleViewAction().setEnabled(False) self.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbar.addActions(btn for btn in ToolBar(self).Buttons) self.statusBar().showMessage( "Offline Documentation is your external memory") self.toolbar.installEventFilter(self) self.installEventFilter(self) self.setObjectName("main_window") self.setFixedWidth(1200) self.setFixedHeight(800) self.show() def run(self, which): self.central_widget.addWidget(which) self.central_widget.setCurrentWidget(which) if not self.current_instance: self.current_instance = self
class MainWindow(QMainWindow): def __init__(self, sorting_func_map): super().__init__() self.setWindowTitle('Sorting Algorithms Visualized') self.setStyleSheet("background-color: #181818; color: white") # Setup window tabs self.tabs = QStackedWidget(self) self.setCentralWidget(self.tabs) self.selection_tab = SelectionTab(self.tabs, sorting_func_map, self) self.sorting_tab = SortingTab(self.tabs) self.tabs.addWidget(self.selection_tab) self.tabs.addWidget(self.sorting_tab) self.tabs.setCurrentIndex(0) self.tabs.currentWidget().setFocus() def switchToSorting(self, sorting_algos, element_count, rendering_type, rainbow, sound, lock_type): count = max(1, len(sorting_algos)) # Setup correct window size image_size = 384 if len(sorting_algos) <= 2: image_size = 384 * 2 self.tabs.setCurrentIndex(1) self.sorting_tab.rendering_type = rendering_type self.sorting_tab.rainbow = rainbow self.sorting_tab.sound_enabled = sound special_types.ThreadManagment.lock_type = lock_type self.sorting_tab.updateSortingAlgorithms(sorting_algos, image_size) self.sorting_tab.startRendering()
class SpectateBattle(QWidget): def __init__(self): super(SpectateBattle, self).__init__() self.exitButton = QPushButton('Exit to menu') self.exitButton.clicked.connect(self.interruptBattle) self.menu = BotBattleMenu() self.menu.startButton.clicked.connect(self.startBattle) self.battle = None self.stack = QStackedWidget() self.stack.addWidget(self.menu) layout = QVBoxLayout() layout.addWidget(self.stack) layout.addWidget(self.exitButton) self.setLayout(layout) def startBattle(self): bot1 = game.players.ai.select_bot(self.menu.bot1Level) bot2 = game.players.ai.select_bot(self.menu.bot2Level) self.battle = BotBattle(bot1, bot2) self.stack.addWidget(self.battle) self.stack.setCurrentWidget(self.battle) def interruptBattle(self): if self.battle: self.battle.interrupt()
class AddItemDialog(QDialog): def __init__(self): super(AddItemDialog, self).__init__() self.setupUi(self) def setupUi(self, AddItemDialog): self.item_type_picker = QComboBox() self.item_properties_switch = QStackedWidget() for form in FORMS: self.item_type_picker.addItem(form.__name__.split('_')[0], FORMS.index(form)) self.item_properties_switch.addWidget(form()) self.item_type_picker.currentIndexChanged.connect( self.item_properties_switch.setCurrentIndex) self.add_button = QPushButton("Add") mainLayout = QVBoxLayout(self) mainLayout.addWidget(self.item_type_picker) mainLayout.addWidget(self.item_properties_switch, 1) mainLayout.addWidget(self.add_button) self.add_button.clicked.connect(self.add_item) self.setWindowIcon(QIcon(QPixmap('hotel_icon.jpg'))) self.layout().setSizeConstraint(QLayout.SetFixedSize) self.setWindowTitle("Add Items") def add_item(self): self.item_properties_switch.currentWidget().add_button_click()
class EditableLabel(QWidget): def __init__(self, text): super().__init__() self.label = QLabel(text) self.edit = QLineEdit(text) self.edit.returnPressed.connect(self.returnEvent) self.setLayout(QVBoxLayout()) self.stack = QStackedWidget() self.stack.addWidget(self.label) self.stack.addWidget(self.edit) self.layout().addWidget(self.stack) def mouseDoubleClickEvent(self, event): self.stack.setCurrentIndex(1 - self.stack.currentIndex()) def returnEvent(self): self.label.setText(self.edit.text()) self.stack.setCurrentIndex(1 - self.stack.currentIndex()) def setBold(self, bold): if bold: self.label.setText('<b>' + self.label.text() + '</b>') else: self.label.setText(self.label.text())
class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.m_pages = {} self.register(MainWindow(), "main") self.register(WelcomeUser(), "welcome") self.register(RegisterUser(),"register") self.register(ReportGen(),"report") self.goto("main") def register(self, widget, name): self.m_pages[name] = widget self.stacked_widget.addWidget(widget) if isinstance(widget, PageWindow): widget.gotoSignal.connect(self.goto) @QtCore.pyqtSlot(str) def goto(self, name): if name in self.m_pages: widget = self.m_pages[name] self.stacked_widget.setCurrentWidget(widget) self.setWindowTitle(widget.windowTitle())
class MyStack(QMainWindow): def __init__(self, parent=None): super(MyStack, self).__init__(parent) self.setWindowTitle('A staked Widget') self.setFixedSize(400, 400) self.init_ui() def init_ui(self): self.cbx = QComboBox(self) self.cbx.addItems(['Boton', 'Etiqueta']) self.cbx.setGeometry(20, 20, 360, 24) self.boton = Boton(self) self.et = Etiqueta(self) self.MyStack = QStackedWidget(self) self.MyStack.addWidget(self.boton) self.MyStack.addWidget(self.et) self.MyStack.setGeometry(20, 84, 360, 250) self.cbx.activated.connect(self.do_something) def do_something(self): widget = self.cbx.currentIndex() self.MyStack.setCurrentIndex(widget)
class MainWidget(QWidget): def __init__(self, parent=None): super(MainWidget, self).__init__(parent=parent) self.setWindowTitle("Smart Driving Assistant") self.setMinimumWidth(1024) self.setMinimumHeight(550) self.setWindowIcon(QIcon(Constants.LOGO_PATH)) # header self.header = HeaderWidget() self.header.currentRowChanged.connect(self.display) self.images_widget = ImagesWidget(parent=self) self.videos_widget = VideosWidget(parent=self) self.stack = QStackedWidget(parent=self) self.stack.addWidget(self.images_widget) self.stack.addWidget(self.videos_widget) layout = QVBoxLayout(self) layout.addWidget(self.header) layout.addWidget(self.stack) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setStyleSheet(open(Constants.STYLES_PATH + "MainWidget.css").read()) def display(self, index): self.stack.setCurrentIndex(index)
class MatplotlibWidget(QWidget): def __init__(self, parent=None): super(MatplotlibWidget, self).__init__(parent) self.initUi() def initUi(self): self.layout = QVBoxLayout(self) # s1 = my mpl = MyMplCanvas(self, width=9, height=6, dpi=100) mpl2 = MyMplCanvas(self, width=9, height=6, dpi=100) self.stack = QStackedWidget() self.stack.addWidget(mpl) self.stack.addWidget(mpl2) # self.mpl.start_static_plot() # 如果你想要初始化的时候就呈现静态图,请把这行注释去掉 self.mpl.start_dynamic_draw() # self.mpl.start_dynamic_plot() # 如果你想要初始化的时候就呈现动态图,请把这行注释去掉 # self.mpl_ntb = NavigationToolbar(self.mpl, self) # 添加完整的 toolbar # self.zoom_pic # self.mpl.zoom_pic() self.mpl.draw_all_line('add') # self.layout.addWidget(self.mpl) # self.layout.addWidget(self.mpl_ntb) self.layout.addWidget(self.stack)
def __init__(self): super(Window, self).__init__() widget_selector = QStackedWidget() self.setCentralWidget(widget_selector) self.status_bar = QStatusBar() self.status_bar.showMessage("Please select a task to complete") available_tasks = [ Task(idx, status_bar=self.status_bar) for idx in range(8) ] # replace me with actual loading routine task_selection = TaskSelection(available_tasks) widget_selector.addWidget(task_selection) for task in available_tasks: task.task_solved.connect(self.backButtonClicked) widget_selector.addWidget(task) task.preview.clicked.connect(self.taskSelected) task.back_button.clicked.connect(self.backButtonClicked) widget_selector.setCurrentIndex(0) self.tasks = widget_selector self.setStatusBar(self.status_bar) self.setGeometry(300, 300, 800, 600) self.setWindowTitle("ANIMATAS Waves Scenario 1") self.setWindowIcon(QIcon("wave.png"))
class Widget(QWidget): isFirst = True def __init__(self, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) grid_layout = QGridLayout(self) logo = QLabel() logo_pixmap = QPixmap('logo.jpg') logo.setPixmap(logo_pixmap) grid_layout.addWidget(logo, 0, 0, 2, 6) grid_layout.addItem( QSpacerItem(500, 50, QSizePolicy.Minimum, QSizePolicy.Expanding), 0, 6) graph_label = QLabel('Graph') self.graph_names_combobox = QComboBox() grid_layout.addWidget(graph_label, 0, 7, 1, 1) grid_layout.addWidget(self.graph_names_combobox, 0, 8, 1, 4) self.graph_widget = QStackedWidget() grid_layout.addWidget(self.graph_widget, 2, 1, 10, 12) temp_webpage = QWebEngineView(self) temp_webpage.setHtml(test()) self.graph_widget.addWidget(temp_webpage) temp_webpage.loadFinished.connect(self.onLoadFinished) def onLoadFinished(self, ok): if self.isFirst: # self.sender().page().action(QWebEnginePage.Reload).trigger() self.sender().reload() self.isFirst = False
class FormComboWidget(QWidget): def __init__(self, datalist, comment="", parent=None): QWidget.__init__(self, parent) layout = QVBoxLayout() self.setLayout(layout) self.combobox = QComboBox() layout.addWidget(self.combobox) self.stackwidget = QStackedWidget(self) layout.addWidget(self.stackwidget) self.combobox.currentIndexChanged[int].connect(self.stackwidget,setCurrentIndex) self.widgetlist = [] for data, title, comment in datalist: self.combobox.addItem(title) widget = FormWidget(data, comment=comment, parent=self) self.stackwidget.addWidget(widget) self.widgetlist.append(widget) def setup(self): for widget in self.widgetlist: widget.setup() def get(self): return [ widget.get() for widget in self.widgetlist]
class Window(QWidget): """ This part might be removed or included for main window application """ def __init__(self, algorithms): super(Window, self).__init__() self.stackedWidget = QStackedWidget() self.orientationCombo = QComboBox() for category in categories: pass for algorithm in algorithms: self.orientationCombo.addItem(algorithm.get_name()) self.stackedWidget.addWidget(GroupOfSliders(algorithm)) layout = QBoxLayout(QBoxLayout.TopToBottom) settings_layout = QBoxLayout(QBoxLayout.TopToBottom) settings_layout.addWidget(self.stackedWidget) select_layout = QBoxLayout(QBoxLayout.TopToBottom) select_layout.addWidget(self.orientationCombo) layout.addItem(settings_layout) layout.addItem(select_layout) self.setLayout(layout) self.setWindowTitle(algorithm.get_name() + " Settings")
class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.m_pages = {} self.register(MainPage(), "main_page") self.goto(self, "main_page") def register(self, widget, name): self.m_pages[name] = widget self.stacked_widget.addWidget(widget) if isinstance(widget, PageWindow): widget.gotoSignal.connect(self.goto) @pyqtSlot(object, str) def goto(self, previous, name): if name in self.m_pages: widget = self.m_pages[name] widget.usedSignal.emit(previous) self.stacked_widget.setCurrentWidget(widget) self.setWindowTitle(widget.windowTitle())
class ExperimentView(QDialog): def __init__(self, size, model): super().__init__() self.setFixedSize(size) # TODO: Variable screensize self.stack = QStackedWidget(parent=self) self.stack.size = self.size self.setWindowFlags(Qt.FramelessWindowHint) self.matrices_widget = MatricesGrid(model, parent=self) self.video_widget = VideoPlayer(parent=self) self.text_panel = TextPanel(parent=self, text=Resources.text.introduction) self.fixation_cross = FixationCross(parent=self) self.stack.addWidget(self.text_panel) self.stack.addWidget(self.video_widget) self.stack.addWidget(self.matrices_widget) self.stack.addWidget(self.fixation_cross) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.addWidget(self.stack) self.setLayout(hbox) self.showFullScreen() def changePanelTo(self, widget: QWidget): self.stack.setCurrentWidget(widget) logging.debug('stack set to {}'.format(self.stack.currentWidget())) def addWidget(self, widget: QWidget): logging.debug('adding widget {} to stack'.format(widget)) self.stack.addWidget(widget) def closeEvent(self, QCloseEvent): super().closeEvent(QCloseEvent) logging.debug("Close {}".format(self))
class Window(QMainWindow): # For Managing Windows and transition def __init__(self, parent=None): """ Creates Qstacked Widgets and registers available pages""" super().__init__(parent) self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.setGeometry(250, 250, 600, 389) self.m_pages = {} self.register(UiStartPage(), "main") self.register(UiYoutube(), "Youtube") self.register(UiFacebook(), "fb_setup") self.register(UiInstagram(), "insta") self.register(GeneralManager(), "general") self.goto("main") def register(self, widget, name): self.m_pages[name] = widget self.stacked_widget.addWidget(widget) if isinstance(widget, PageWindow): widget.gotoSignal.connect(self.goto) @pyqtSlot(str) def goto(self, name): """ Window transition""" if name in self.m_pages: widget = self.m_pages[name] self.stacked_widget.setCurrentWidget(widget) self.setWindowTitle(widget.windowTitle())
class StackedWidget(QWidget): def __init__(self): super().__init__() self.title = "PyQt5 StackedWidget" self.top = 200 self.left = 500 self.width = 300 self.height = 200 self.setWindowIcon(QtGui.QIcon("icon.png")) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.stackedWidget() self.show() def stackedWidget(self): vbox = QVBoxLayout() self.stackedWidget = QStackedWidget() vbox.addWidget(self.stackedWidget) for x in range(0, 8): label = QLabel("Stacked Child: " + str(x)) label.setFont(QtGui.QFont("sanserif", 15)) label.setStyleSheet('color:red') self.stackedWidget.addWidget(label) self.button = QPushButton("Stack" + str(x)) self.button.setStyleSheet('background-color:green') self.button.page = x self.button.clicked.connect(self.btn_clicked) vbox.addWidget(self.button) self.setLayout(vbox) def btn_clicked(self): self.button = self.sender() self.stackedWidget.setCurrentIndex(self.button.page - 1)
class Cart_Pay(QWidget): #Initialiaze the first page for class View_Menu_First_Page() def __init__(self,parent=None): super(Cart_Pay,self).__init__(parent) self.resize(800,600) self.stackedwidget = QStackedWidget(self) self.stackedwidget.setGeometry(QtCore.QRect(0,0 , 800, 600)) self.stackedwidget.setObjectName("stackwidget") self.add_first_page() #function to show first page def add_first_page(self): self.first_page = Pay_First_Page() self.stackedwidget.addWidget(self.first_page) self.stackedwidget.setCurrentIndex(0) print(self.first_page.tableWidget.rowCount()) self.first_page.BlueButton.clicked.connect(self.add_second_page) #only can use lambda: to make use of return value form functions #function to show second page def add_second_page(self): self.second_page = See_Waiting_Time() self.stackedwidget.addWidget(self.second_page) self.stackedwidget.setCurrentIndex(1)
class MoodMainActivity(QMainWindow): def __init__(self, widgets): super().__init__(None, Qt.MSWindowsFixedSizeDialogHint) self.stacker = QStackedWidget(self) self.stacker.setProperty('class', 'ctm-widget') self.widgets = [MoodActivity(self), MoodStackedActivity(widgets, self)] for w in self.widgets: self.stacker.addWidget(w) self.c_layout = QHBoxLayout() self.c_layout.addWidget(self.stacker) self.setLayout(self.c_layout) self.set_page(0) def show(self): super().show() self.move(screen_center(self)) def set_page(self, i): self.setWindowTitle(self.widgets[i].windowTitle()) self.stacker.setGeometry(self.widgets[i].geometry()) self.setGeometry(self.widgets[i].geometry()) self.stacker.setCurrentIndex(i) self.move(screen_center(self))
class SettingCategories(QWidget): def __init__(self, parent=None, footer_ref=None): super().__init__(parent) # main layout of the widget self.mainLayout = QVBoxLayout(self) # self.setStyleSheet('border: 1px solid white;') # container for all the other widgets which are to be stacked self.widgetStackContainer = QStackedWidget(self) # instantiating all the required widgets self.schoolInfoForm = SchoolInfoForm(self, footer_ref) # stacking widgets self.widgetStackContainer.addWidget(self.schoolInfoForm) self.widgetStackContainer.setCurrentIndex(0) # font family, size and color self.navTextFont = QFont() self.navTextFont.setBold(False) self.navTextFont.setPointSize(13) # in pixels self.setFont(self.navTextFont) # adding both widget into main layout self.mainLayout.addWidget(self.widgetStackContainer) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.mainLayout)
class Controller(QMainWindow): def __init__(self, parent=None): super(Controller, self).__init__(parent) self.central_widget = QStackedWidget() self.setCentralWidget(self.central_widget) self.createWidget = CreateWidget(self) self.gradeWidget = GradeWidget(self) self.createWidget.switchModeButton.clicked.connect(self.switch) self.gradeWidget.switchModeButton.clicked.connect(self.switch) self.central_widget.addWidget(self.createWidget) self.central_widget.addWidget(self.gradeWidget) self.central_widget.setCurrentWidget(self.createWidget) self.gradeMode = False self.setWindowTitle("Grading Automation") def switch(self): if (self.gradeMode): self.central_widget.setCurrentWidget(self.createWidget) self.gradeMode = False else: self.central_widget.setCurrentWidget(self.gradeWidget) self.gradeMode = True
class Window(QWidget): def __init__(self, all_algorithms): super(Window, self).__init__() self.stackedWidget = QStackedWidget() for item in algorithms: widget = GuiElement(item) self.stackedWidget.addWidget(widget) layout = QHBoxLayout() layout.addWidget(self.stackedWidget) self.setLayout(layout) self.setWindowTitle("Algorithm Settings")
class Window(QWidget): def __init__(self, title): super(Window, self).__init__() self.horizontalSliders = SlidersGroup(title) self.stackedWidget = QStackedWidget() self.stackedWidget.addWidget(self.horizontalSliders) layout = QHBoxLayout() layout.addWidget(self.stackedWidget) self.setLayout(layout) self.setWindowTitle("Algorithm Settings: ")
def initUI(self): # create menus menu.GuiMenu() menu.WidgetMenu() menu.SettingsMenu() menu.LoggingMenu() # main layout mainVLayout = QVBoxLayout() elementHLayouts = [] # top element: toolbar, status t = QLabel("Toolbar") s = QLabel("Status") toolbarStatusHLayout = QHBoxLayout() toolbarStatusHLayout.addWidget(t) toolbarStatusHLayout.addWidget(s) elementHLayouts.append(toolbarStatusHLayout) # mid element: menu buttons menubuttonsHLayout = QHBoxLayout() stackedButtonsWidget = QStackedWidget() buttonGroup = QButtonGroup(self) buttonGroup.setExclusive(True) for m in menu.Menu.menus: btn = QPushButton(m.name) btn.setCheckable(True) btn.pressed.connect(lambda m=m: stackedButtonsWidget.setCurrentWidget(m)) menubuttonsHLayout.addWidget(btn) buttonGroup.addButton(btn) stackedButtonsWidget.addWidget(m) elementHLayouts.append(menubuttonsHLayout) # bot element: menu specific widgets menuwidgetsHLayout = QHBoxLayout() menuwidgetsHLayout.addWidget(stackedButtonsWidget) elementHLayouts.append(menuwidgetsHLayout) # click first button for defined initial state if len(buttonGroup.buttons()) > 0: buttonGroup.buttons()[0].click() for l in elementHLayouts: mainVLayout.addLayout(l) self.setLayout(mainVLayout)
class MultiPlayer(QWidget): def __init__(self): super(MultiPlayer, self).__init__() self.exitButton = QPushButton('Exit to menu') self.exitButton.clicked.connect(self.exit) self.menu = MultiPlayerMenu() self.menu.connectButton.clicked.connect(self.connect) self.menu.hostButton.clicked.connect(self.host) self.menu.hotseatButton.clicked.connect(self.hotseat) self.stack = QStackedWidget() self.stack.addWidget(self.menu) layout = QVBoxLayout() layout.addWidget(self.stack) layout.addWidget(self.exitButton) self.setLayout(layout) self.clientGame = None self.serverGame = None self.hotseatGame = None def host(self): name = self.menu.nameField.text() port = self.menu.portSpinBox.value() self.serverGame = ServerGame(name, port) self.showGame(self.serverGame) def connect(self): name = self.menu.nameField.text() ip = self.menu.ipField.text() port = self.menu.portSpinBox.value() self.clientGame = ClientGame(name, ip, port) self.showGame(self.clientGame) def hotseat(self): self.hotseatGame = HotSeatGame() self.showGame(self.hotseatGame) def showGame(self, game): self.stack.addWidget(game) self.stack.setCurrentWidget(game) def exit(self): self.stack.setCurrentWidget(self.menu) if self.serverGame: self.serverGame.end() if self.clientGame: self.clientGame.end()
def __init__(self, ref_templates, parent=None): super().__init__(parent) # Variables sections. # NB: Sorted. self._types = ["book", "bookSection", "conferencePaper", "document", "forumPost", "journalArticle", "patent", "report", "thesis", "webpage"] templates_subset = OrderedDict([(k, v) for k, v in ref_templates.items() if k in self._types]) # Own configuration section. # TODO Better layout configuration. self.setMinimumSize(QSize(800, 1000)) # Widgets section. # TODO Prevent edition of the reference type for an existing reference? self._types_edit = QComboBox() self._types_edit.addItems(self._types) self._types_widgets = self._templates_widgets(templates_subset) types_forms = self._templates_forms(self._types_widgets) forms_stack = QStackedWidget() for x in types_forms: forms_stack.addWidget(x) # NB: The first button with the accept role is made the default button. ok_cancel = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # Layouts section. layout = QFormLayout() layout.addRow(self.ITEM_TYPE_FIELD + ":", self._types_edit) layout.addRow(forms_stack) layout.addRow(ok_cancel) # NB: Don't use AllNonFixedFieldsGrow because it expands the QComboBox. layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) utils.configure_form_layout(layout) self.setLayout(layout) # Signals section. self._types_edit.currentIndexChanged.connect(forms_stack.setCurrentIndex) ok_cancel.accepted.connect(self.accept) ok_cancel.rejected.connect(self.reject)
def addApplet(self, appletId: int, applet) -> None: """ Adds applet to toolbox if it is available in interactive mode """ if applet.interactive: # We need new id before we added item, to handle currentChangedSignal newToolbarId = self._toolbox.count() self._toolbarIdByAppletId[appletId] = newToolbarId self._appletIdByToolbarId[newToolbarId] = appletId widget = applet.getMultiLaneGui().appletDrawer() assert isinstance(widget, QWidget), f"Not a widget: {widget}" stackedWidget = QStackedWidget() stackedWidget.addWidget(widget) self._toolbox.addItem(stackedWidget, applet.name) else: self._toolbarIdByAppletId[appletId] = None
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.centralWidget = QStackedWidget() self.setCentralWidget(self.centralWidget) startWidget = StartWidget(self) self.centralWidget.addWidget(startWidget) self.setWindowTitle("Mountain King") self.resize(210, 50) def host(self): hostWidget = HostWidget(self) self.centralWidget.addWidget(hostWidget) self.centralWidget.setCurrentWidget(hostWidget) self.setWindowTitle("Hosting Mountain King") self.resize(255, 50) def join(self): joinWidget = JoinWidget(self) self.centralWidget.addWidget(joinWidget) self.centralWidget.setCurrentWidget(joinWidget) self.setWindowTitle("Joining Mountain King") self.resize(255, 50)
class MainGame(QWidget): def __init__(self): super(MainGame, self).__init__() self.menu = MainGameMenu() self.menu.singlePlayerButton.clicked.connect(self.startSinglePlayer) self.menu.multiPlayerButton.clicked.connect(self.startMultiPlayer) self.menu.spectateButton.clicked.connect(self.startSpectateBattle) self.stack = QStackedWidget() self.stack.addWidget(self.menu) layout = QVBoxLayout() layout.addWidget(self.stack) self.setLayout(layout) self.setWindowTitle('Ultimate tic-tac-toe') self.resize(400, 200) self.singlePlayer = None self.multiPlayer = None self.spectate = None self.showMenu() def showMenu(self): self.stack.setCurrentWidget(self.menu) def startSinglePlayer(self): self.singlePlayer = SinglePlayer() self.singlePlayer.exitButton.clicked.connect(self.showMenu) self.stack.addWidget(self.singlePlayer) self.stack.setCurrentWidget(self.singlePlayer) def startMultiPlayer(self): if not self.multiPlayer: self.multiPlayer = MultiPlayer() self.multiPlayer.exitButton.clicked.connect(self.showMenu) self.stack.addWidget(self.multiPlayer) self.stack.setCurrentWidget(self.multiPlayer) def startSpectateBattle(self): self.spectate = SpectateBattle() self.spectate.exitButton.clicked.connect(self.showMenu) self.stack.addWidget(self.spectate) self.stack.setCurrentWidget(self.spectate)
class QChatTab(QWidget): def __init__(self, chat_window, nick=None, just_accepted=False): QWidget.__init__(self) self.chat_window = chat_window self.nick = nick self.just_accepted = just_accepted self.unread_count = 0 self.widget_stack = QStackedWidget(self) self.widget_stack.addWidget(QNickInputWidget('new_chat.png', 150, self.connectClicked, parent=self)) self.widget_stack.addWidget(QConnectingWidget(parent=self)) self.widget_stack.addWidget(QChatWidget(self.chat_window, nick=nick, parent=self)) if self.nick is not None: self.widget_stack.setCurrentIndex(2) else: self.widget_stack.setCurrentIndex(0) layout = QHBoxLayout() layout.addWidget(self.widget_stack) self.setLayout(layout) def connectClicked(self, nick): if self.chat_window.isNickInTabs(nick): QMessageBox.warning(self, TITLE_ALREADY_CONNECTED, ALREADY_CONNECTED % (nick)) return self.nick = nick self.widget_stack.widget(1).setConnectingToNick(self.nick) self.widget_stack.setCurrentIndex(1) self.chat_window.client.openSession(self.nick) self.widget_stack.widget(2).setRemoteNick(self.nick) def appendMessage(self, message, source): self.widget_stack.widget(2).appendMessage(message, source) def showNowChattingMessage(self): self.widget_stack.setCurrentIndex(2) self.widget_stack.widget(2).showNowChattingMessage(self.nick) def enable(self): self.widget_stack.setCurrentIndex(2) self.widget_stack.widget(2).enable() def resetOrDisable(self): cur_widget_index = self.widget_stack.currentIndex() if cur_widget_index == 1: self.widget_stack.setCurrentIndex(0) elif cur_widget_index == 2: self.widget_stack.widget(2).disable()
class SubTabWidget(QWidget): _tabChanged = pyqtSignal(int, name = "tabChanged") def __init__(self, subtitleData, videoWidget, parent = None): super(SubTabWidget, self).__init__(parent) self._subtitleData = subtitleData self.__initTabWidget(videoWidget) def __initTabWidget(self, videoWidget): settings = SubSettings() mainLayout = QVBoxLayout(self) mainLayout.setContentsMargins(0, 0, 0, 0) mainLayout.setSpacing(0) #TabBar self.tabBar = QTabBar(self) # Splitter (bookmarks + pages) self.splitter = QSplitter(self) self.splitter.setObjectName("sidebar_splitter") self._toolbox = ToolBox(self._subtitleData, self) self._toolbox.setObjectName("sidebar") self._toolbox.setMinimumWidth(100) self._toolbox.addTool(Details(self._subtitleData, self)) self._toolbox.addTool(Synchronizer(videoWidget, self._subtitleData, self)) self._toolbox.addTool(History(self)) self.rightWidget = QWidget() rightLayout = QGridLayout() rightLayout.setContentsMargins(0, 0, 0, 0) self.rightWidget.setLayout(rightLayout) self._mainTab = FileList(_("Subtitles"), self._subtitleData, self) self.pages = QStackedWidget(self) rightLayout.addWidget(self.pages, 0, 0) self.tabBar.addTab(self._mainTab.name) self.pages.addWidget(self._mainTab) self.splitter.addWidget(self._toolbox) self.splitter.addWidget(self.rightWidget) self.__drawSplitterHandle(1) # Setting widgets mainLayout.addWidget(self.tabBar) mainLayout.addWidget(self.splitter) # Widgets settings self.tabBar.setMovable(True) self.tabBar.setTabsClosable(True) self.tabBar.setExpanding(False) # Don't resize left panel if it's not needed leftWidgetIndex = self.splitter.indexOf(self._toolbox) rightWidgetIndex = self.splitter.indexOf(self.rightWidget) self.splitter.setStretchFactor(leftWidgetIndex, 0) self.splitter.setStretchFactor(rightWidgetIndex, 1) self.splitter.setCollapsible(leftWidgetIndex, False) self.splitter.setSizes([250]) # Some signals self.tabBar.currentChanged.connect(self.showTab) self.tabBar.tabCloseRequested.connect(self.closeTab) self.tabBar.tabMoved.connect(self.moveTab) self._mainTab.requestOpen.connect(self.openTab) self._mainTab.requestRemove.connect(self.removeFile) self.tabChanged.connect(lambda i: self._toolbox.setContentFor(self.tab(i))) self.setLayout(mainLayout) def __addTab(self, filePath): """Returns existing tab index. Creates a new one if it isn't opened and returns its index otherwise.""" for i in range(self.tabBar.count()): widget = self.pages.widget(i) if not widget.isStatic and filePath == widget.filePath: return i tab = SubtitleEditor(filePath, self._subtitleData, self) newIndex = self.tabBar.addTab(self._createTabName(tab.name, tab.history.isClean())) tab.history.cleanChanged.connect( lambda clean: self._cleanStateForFileChanged(filePath, clean)) self.pages.addWidget(tab) return newIndex def __drawSplitterHandle(self, index): splitterHandle = self.splitter.handle(index) splitterLayout = QVBoxLayout(splitterHandle) splitterLayout.setSpacing(0) splitterLayout.setContentsMargins(0, 0, 0, 0) line = QFrame(splitterHandle) line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) splitterLayout.addWidget(line) splitterHandle.setLayout(splitterLayout) def _createTabName(self, name, cleanState): if cleanState is True: return name else: return "%s +" % name def _cleanStateForFileChanged(self, filePath, cleanState): page = self.tabByPath(filePath) if page is not None: for i in range(self.tabBar.count()): if self.tabBar.tabText(i)[:len(page.name)] == page.name: self.tabBar.setTabText(i, self._createTabName(page.name, cleanState)) return def saveWidgetState(self, settings): settings.setState(self.splitter, self.splitter.saveState()) settings.setHidden(self._toolbox, self._toolbox.isHidden()) def restoreWidgetState(self, settings): self.showPanel(not settings.getHidden(self._toolbox)) splitterState = settings.getState(self.splitter) if not splitterState.isEmpty(): self.splitter.restoreState(settings.getState(self.splitter)) @pyqtSlot(str, bool) def openTab(self, filePath, background=False): if self._subtitleData.fileExists(filePath): tabIndex = self.__addTab(filePath) if background is False: self.showTab(tabIndex) else: log.error(_("SubtitleEditor not created for %s!" % filePath)) @pyqtSlot(str) def removeFile(self, filePath): tab = self.tabByPath(filePath) command = RemoveFile(filePath) if tab is not None: index = self.pages.indexOf(tab) if self.closeTab(index): self._subtitleData.execute(command) else: self._subtitleData.execute(command) @pyqtSlot(int) def closeTab(self, index): tab = self.tab(index) if tab.canClose(): widgetToRemove = self.pages.widget(index) self.tabBar.removeTab(index) self.pages.removeWidget(widgetToRemove) widgetToRemove.deleteLater() return True return False def count(self): return self.tabBar.count() def currentIndex(self): return self.tabBar.currentIndex() def currentPage(self): return self.pages.currentWidget() @pyqtSlot(int, int) def moveTab(self, fromIndex, toIndex): fromWidget = self.pages.widget(fromIndex) toWidget = self.pages.widget(toIndex) if fromWidget.isStatic or toWidget.isStatic: self.tabBar.blockSignals(True) # signals would cause infinite recursion self.tabBar.moveTab(toIndex, fromIndex) self.tabBar.blockSignals(False) return else: self.pages.removeWidget(fromWidget) self.pages.removeWidget(toWidget) if fromIndex < toIndex: self.pages.insertWidget(fromIndex, toWidget) self.pages.insertWidget(toIndex, fromWidget) else: self.pages.insertWidget(toIndex, fromWidget) self.pages.insertWidget(fromIndex, toWidget) # Hack # Qt changes tabs during mouse drag and dropping. The next line is added # to prevent it. self.showTab(self.tabBar.currentIndex()) @pyqtSlot(int) def showTab(self, index): showWidget = self.pages.widget(index) if showWidget: self.pages.setCurrentWidget(showWidget) self.tabBar.blockSignals(True) self.tabBar.setCurrentIndex(index) self.tabBar.blockSignals(False) # Try to update current tab. showWidget.updateTab() self._tabChanged.emit(index) def showPanel(self, val): if val is True: self._toolbox.show() else: self._toolbox.hide() def togglePanel(self): if self._toolbox.isHidden(): self._toolbox.show() else: self._toolbox.hide() def tab(self, index): return self.pages.widget(index) def tabByPath(self, path): for i in range(self.pages.count()): page = self.tab(i) if not page.isStatic and page.filePath == path: return page return None @property def fileList(self): return self._mainTab
class MainView(base, form): def __init__(self, pipeline, parent=None): super(base, self).__init__(parent) self.setupUi(self) self.pipeline = pipeline self.pip_widgets = [] self.default_pips = [] self.draw_ui() self.connect_ui() def register_observers(self): pass def connect_ui(self): """ This function connects the ui using signals from the ui elements and its method counterparts. """ self.input_btn.clicked.connect(self.set_input_url) self.output_btn.clicked.connect(self.set_output_url) self.save_btn.clicked.connect(self.save_pipeline) self.load_favorite_pipelines() self.fav_pips_combo_box.activated.connect(self.select_default_pip) self.run_btn.clicked.connect(self.run) self.delete_btn.clicked.connect(self.trash_pipeline) self.add_btn.clicked.connect(lambda: self.add_pipe_entry_new()) def draw_ui(self): """ This function draws all additional UI elements. If you want the application to display any additional things like a button you can either add it in the QtDesigner or declare it here. """ # *TODO* Create these ones with Qt Designer and put them into select_cat_alg_vbox_layout. I failed self.ComboxCategories = QComboBox() self.stackedWidgetComboxesAlgorithms = QStackedWidget() self.select_cat_alg_vbox_layout.addWidget(self.ComboxCategories) self.select_cat_alg_vbox_layout.addWidget(self.stackedWidgetComboxesAlgorithms) self.ComboxCategories.hide() """ This function is concerned with drawing all non static elements into the GUI. """ """self.set_pip_title("A. Junius2") self.set_preset(["A.Junius", "test", "test", "test"]) self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_pip_entry("../assets/images/P.png", "Preprocessing - adaptive trehsold watershed") self.add_cat_image("../assets/images/seg_fav.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.add_cat_image("../assets/images/wing.jpeg", "Preprocessing") self.main_image_label.setPixmap(QtGui.QPixmap("wing.jpeg")) category_combo_box = ComboBoxWidget("type") category_combo_box.add_item("Preprocessing", "../assets/images/P.png") category_combo_box.add_item("Segmentation", "../assets/images/S.png") category_combo_box.add_item("Graph Detection", "../assets/images/D.png") category_combo_box.add_item("Graph Filtering", "../assets/images/F.png") alg_combo_box = ComboBoxWidget("algorithm") alg_combo_box.add_item("Otsus") alg_combo_box.add_item("Guo Hall") alg_combo_box.add_item("Adaptive Treshold") slider_1 = SliderWidget("slider1das", 0, 10, 1, 4, True) slider_2 = SliderWidget("slider1", 0, 10, 2, 4, False) slider_3 = SliderWidget("sliderböadsad", 0, 10, 1, 4, True) slider_4 = SliderWidget("sliderböadsad", 0, 10, 1, 4, True) slider_5 = SliderWidget("sliderböadsad", 0, 10, 1, 4, True) checkbox_1 = CheckBoxWidget("checkbox1", True) self.setting_widget_vbox_layout.addWidget(category_combo_box) self.setting_widget_vbox_layout.addWidget(alg_combo_box) self.setting_widget_vbox_layout.addWidget(slider_1) self.setting_widget_vbox_layout.addWidget(slider_2) self.setting_widget_vbox_layout.addWidget(slider_3) self.setting_widget_vbox_layout.addWidget(slider_4) self.setting_widget_vbox_layout.addWidget(slider_5) self.setting_widget_vbox_layout.addWidget(checkbox_1) self.setting_widget_vbox_layout.setAlignment(Qt.AlignTop)""" def set_pip_title(self, title): """ Sets the title of the current selected pipeline in the ui. Args: | *title*: the title of the pipeline | *label_ref*: the reference to the label. """ self.current_pip_label.setText(title) def load_dark_theme(self, application): """ This function is called to load the white theme with all its icons for the buttons and the css file. Args: application: the cureent app instance """ # load buttons pixmap_icon = QtGui.QPixmap("./assets/images/add_white.png") q_icon = QtGui.QIcon(pixmap_icon) self.add_btn.setIcon(q_icon) pixmap_icon = QtGui.QPixmap("./assets/images/trash_white.png") q_icon = QtGui.QIcon(pixmap_icon) self.delete_btn.setIcon(q_icon) pixmap_icon = QtGui.QPixmap("./assets/images/diskette_white.png") q_icon = QtGui.QIcon(pixmap_icon) self.save_btn.setIcon(q_icon) pixmap_icon = QtGui.QPixmap("./assets/images/up-arrow_white.png") q_icon = QtGui.QIcon(pixmap_icon) self.input_btn.setIcon(q_icon) pixmap_icon = QtGui.QPixmap("./assets/images/folder_white.png") q_icon = QtGui.QIcon(pixmap_icon) self.output_btn.setIcon(q_icon) @pyqtSlot(int) def select_default_pip(self, index): """ This is the slot for the Pipeline combobox in the ui Args: index: index of the option currently selected """ # delete current pipeline self.trash_pipeline() # get url and name name, url = self.default_pips[index - 1] # parse the json in the model self.pipeline.load_pipeline_json(url) print("PARSER" + str(self.pipeline.executed_cats[0].active_algorithm)) print("PARSER" + str(self.pipeline.executed_cats[1].active_algorithm)) # set the title self.set_pip_title(name) # Create an entry in the pipeline widget for every step in the pipeline for i in range(0, len(self.pipeline.executed_cats)): self.add_pipe_entry_new(i) self.scroll_down_pip() """for widget in alg_widgets: self.setting_widget_vbox_layout.addWidget(widget)""" def trash_pipeline(self): """ This method clears the complete pipeline while users clicked the trash button. """ # remove all entries in the pipeline list while self.pip_widget_vbox_layout.count(): child = self.pip_widget_vbox_layout.takeAt(0) child.widget().deleteLater() while self.stackedWidget_Settings.currentWidget() is not None: self.stackedWidget_Settings.removeWidget(self.stackedWidget_Settings.currentWidget()) self.settings_collapsable.setTitle("") # remove the pipeline name self.set_pip_title("") # remove all entries int the executed_cats of the model pipeline del self.pipeline.executed_cats[:] # remove all widgets del self.pip_widgets[:] # remove category algorith dropdown self.remove_cat_alg_dropdown() # remove all entries from the pipeline model del self.pipeline.executed_cats[:] @pyqtSlot() def run(self): """ This method runs the the pipeline by calling the process methode in pipeline """ self.pipeline.process() @pyqtSlot() def set_input_url(self): """ This method sets the url for the input image in the pipeline. """ url = QtWidgets.QFileDialog.getOpenFileNames() if url[0]: print(url[0]) print(url[0][0]) self.lineEdit.setText(url[0][0]) self.pipeline.set_input(url[0][0]) @pyqtSlot() def set_output_url(self): """ This method sets the url for the output folder in the pipeline. Args: url: the url to the output folder a user selected in the ui """ url = QtWidgets.QFileDialog.getExistingDirectory() if url: print(url) print(url) self.custom_line_edit.setText(url) self.pipeline.set_output_dir(url) def load_favorite_pipelines(self): """ Scans the directory for default pipelines to display all available items """ self.fav_pips_combo_box.addItem("Please Select") # scan the directory for default pipelines for file in os.listdir("./_default_pipelines"): if file.endswith(".json"): name = file.split(".")[0] url = os.path.abspath("./_default_pipelines" + "/" + file) self.default_pips.append([name, url]) self.fav_pips_combo_box.addItem(name) @pyqtSlot() def save_pipeline(self): """ Saves the pipeline as a json at the users file system. """ url = str(QtWidgets.QFileDialog.getSaveFileName()[0]) split_list = url.split("/") name = split_list[len(split_list) - 1].split(".")[0] del split_list[len(split_list) - 1] url = url.replace(name, "") self.pipeline.save_pipeline_json(name, url) @pyqtSlot(int) def remove_pip_entry(self, pipe_entry_widget, settings_widget, cat=None): """ Removes the pip entry at the given position in the ui Args: pipeline_index (object): settings_widget: position: position at which the pip entry gets removed """ # remove pipeline entry widget from ui self.pip_widget_vbox_layout.removeWidget(pipe_entry_widget) pipe_entry_widget.deleteLater() # remove it settings widgets from ui if settings_widget is not None: if self.stackedWidget_Settings.currentWidget() == settings_widget: self.stackedWidget_Settings.hide() self.remove_cat_alg_dropdown() self.settings_collapsable.setTitle("Settings") self.stackedWidget_Settings.removeWidget(settings_widget) # remove in model if cat is not None: print("Remove entry at pos " + str(self.pipeline.get_index(cat)) + " " + str(cat)) self.pipeline.delete_category(self.pipeline.get_index(cat)) def change_pip_entry_alg(self, position, new_category, new_algorithm, pipe_entry_widget, settings_widget): """ Changes the selected algorithm of the pipeline entry at the position. Afterwards create all widgets for this algorithm instance Args: position: the position of the pipeline entry algorithm: the selected algorithm for this category """ print("Position to be changed:" + str(position)) print("Pipeline length: " + str(len(self.pipeline.executed_cats))) old_cat = self.pipeline.executed_cats[position] old_alg = old_cat.active_algorithm print("Old Cat found in pipeline: " + str(old_cat)) print("Old Alg: found in pipeline:" + str(old_alg)) print("New Category given:" + str(new_category)) print("New Algorithm given:" + str(new_algorithm)) # set in model self.pipeline.change_category(new_category, position) self.pipeline.change_algorithm(new_algorithm, position) new_cat = self.pipeline.executed_cats[position] new_alg = new_cat.active_algorithm # change settings widgets self.remove_pip_entry(pipe_entry_widget, settings_widget) (new_pipe_entry_widget, new_settings_widget) = self.add_pipe_entry_new(position) self.stackedWidget_Settings.show() self.stackedWidget_Settings.setCurrentIndex(position) self.settings_collapsable.setTitle(new_alg.get_name() + " Settings") self.remove_cat_alg_dropdown() self.create_cat_alg_dropdown(position, new_pipe_entry_widget, new_settings_widget) self.set_cat_alg_dropdown(new_cat, new_alg) print("New Cat found in pipeline: " + str(new_cat)) print("New Alg found in pipeline: " + str(new_alg)) def load_settings_widgets_from_pipeline_groupbox(self, position): """ Extracts all widgets from a single algorithm and returns a QBoxLayout Args: alg: the alg instance we extract from Returns: a QBoxLayout containing all widgets for this particular alg. """ alg = self.pipeline.executed_cats[position].active_algorithm print("alg " + str(alg)) print("cat " + str(self.pipeline.executed_cats[position])) empty_flag = True groupOfSliders = QGroupBox() sp = QSizePolicy() sp.setVerticalPolicy(QSizePolicy.Preferred) # groupOfSliders.setSizePolicy(sp) groupOfSliderssLayout = QBoxLayout(QBoxLayout.TopToBottom) groupOfSliderssLayout.setContentsMargins(0, -0, -0, 0) groupOfSliderssLayout.setAlignment(Qt.AlignTop) groupOfSliderssLayout.setSpacing(0) print("Build Slider @ "+ str(position)) # create integer sliders for slider in alg.integer_sliders: empty_flag = False print("slider.value " + str(slider.value)) print("slider " + str(slider)) #print(alg.get_name() + ": add slider (int).") groupOfSliderssLayout.addWidget( SliderWidget(slider.name, slider.lower, slider.upper, slider.step_size, slider.value, slider.set_value, False)) # create float sliders for slider in alg.float_sliders: empty_flag = False #print(alg.get_name() + ": add slider (float).") groupOfSliderssLayout.addWidget( SliderWidget(slider.name, slider.lower, slider.upper, slider.step_size, slider.value, slider.set_value, True), 0, Qt.AlignTop) # create checkboxes for checkbox in alg.checkboxes: empty_flag = False #print(alg.get_name() + ": add checkbox.") groupOfSliderssLayout.addWidget(CheckBoxWidget(checkbox.name, checkbox.value, checkbox.set_value), 0, Qt.AlignTop) # create dropdowns for combobox in alg.drop_downs: empty_flag = False #print(alg.get_name() + ": add combobox.") groupOfSliderssLayout.addWidget( ComboBoxWidget(combobox.name, combobox.options, combobox.set_value, combobox.value), 0, Qt.AlignTop) if empty_flag: label = QLabel() label.setText("This algorithm has no Settings.") groupOfSliderssLayout.addWidget(label, 0, Qt.AlignHCenter) groupOfSliders.setLayout(groupOfSliderssLayout) return groupOfSliders def create_cat_alg_dropdown(self, cat_position, pipe_entry_widget, settings_widget): """ Args: last_cat (object): """ layout = self.select_cat_alg_vbox_layout cat = self.pipeline.executed_cats[cat_position] last_cat = None # Show only allowed categories in dropdown if len(self.pipeline.executed_cats) > 1: last_cat = self.pipeline.executed_cats[cat_position - 1] # Combobox for selecting Category self.ComboxCategories.show() self.ComboxCategories.setFixedHeight(30) self.ComboxCategories.addItem("<Please Select Category>") self.stackedWidgetComboxesAlgorithms = QStackedWidget() self.stackedWidgetComboxesAlgorithms.setFixedHeight(30) self.stackedWidgetComboxesAlgorithms.hide() def setCurrentIndexCat(index): #print("Set Cat") if self.ComboxCategories.currentIndex() == 0: self.stackedWidgetComboxesAlgorithms.hide() else: self.stackedWidgetComboxesAlgorithms.show() self.stackedWidgetComboxesAlgorithms.setCurrentIndex(index - 1) for category_name in self.pipeline.report_available_cats(last_cat): # Add Category to combobox self.ComboxCategories.addItem(category_name) tmp1 = QComboBox() tmp1.addItem("<Please Select Algorithm>") tmp1.setFixedHeight(30) category = self.pipeline.get_category(category_name) #self.current_index = -1 def setCurrentIndexAlg(index): if self.ComboxCategories.currentIndex() == 0 or self.stackedWidgetComboxesAlgorithms.currentWidget().currentIndex() == 0: pass else: #self.current_index != index: self.change_pip_entry_alg(self.pipeline.get_index(cat), self.ComboxCategories.currentText(), self.stackedWidgetComboxesAlgorithms.currentWidget().currentText(), pipe_entry_widget, settings_widget) #self.current_index = index tmp1.activated.connect(setCurrentIndexAlg) for algorithm_name in self.pipeline.get_all_algorithm_list(category): tmp1.addItem(algorithm_name) self.stackedWidgetComboxesAlgorithms.addWidget(tmp1) layout.addWidget(self.ComboxCategories) layout.addWidget(self.stackedWidgetComboxesAlgorithms) self.ComboxCategories.activated.connect(setCurrentIndexCat) def set_cat_alg_dropdown(self, category, algorithm): indexC = self.ComboxCategories.findText(category.get_name()) #print("IndexC " + str(indexC)) self.ComboxCategories.setCurrentIndex(indexC) self.stackedWidgetComboxesAlgorithms.show() self.stackedWidgetComboxesAlgorithms.setCurrentIndex(indexC - 1) indexA = self.stackedWidgetComboxesAlgorithms.currentWidget().findText(algorithm.get_name()) #print("IndexA " + str(indexA)) self.stackedWidgetComboxesAlgorithms.currentWidget().setCurrentIndex(indexA) def remove_cat_alg_dropdown(self): """ Returns: object: """ self.ComboxCategories.clear() while self.stackedWidgetComboxesAlgorithms.currentWidget() is not None: self.stackedWidgetComboxesAlgorithms.removeWidget(self.stackedWidgetComboxesAlgorithms.currentWidget()) while self.select_cat_alg_vbox_layout.count(): child = self.select_cat_alg_vbox_layout.takeAt(0) child.widget().hide() def scroll_down_pip(self): self.pip_scroll.verticalScrollBar().setSliderPosition(self.pip_scroll.verticalScrollBar().maximum()) def add_pipe_entry_new(self, position=None): """ Creates a entry in the ui pipeline with a given position in pipeline. It also creates the corresponding settings widget. """ # create an widget that displays the pip entry in the ui and connect the remove button pip_main_widget = QWidget() pip_main_widget.setFixedHeight(70) pip_main_widget.setFixedWidth(300) pip_main_layout = QHBoxLayout() pip_main_widget.setLayout(pip_main_layout) new_marker = False if position is None: position = len(self.pipeline.executed_cats) cat = self.pipeline.new_category(position) label = "<Click to specify new step>" icon = None new_marker = True else: cat = self.pipeline.executed_cats[position] alg = cat.active_algorithm label = alg.get_name() icon = cat.get_icon() new_marker = False pixmap = QPixmap(icon) pixmap_scaled_keeping_aspec = pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio) pixmap_label = QtWidgets.QLabel() pixmap_label.setPixmap(pixmap_scaled_keeping_aspec) pip_up_down = QWidget() pip_up_down.setFixedHeight(70) pip_up_down_layout = QVBoxLayout() pip_up_down.setLayout(pip_up_down_layout) up_btn = QToolButton() dw_btn = QToolButton() up_btn.setArrowType(Qt.UpArrow) up_btn.setFixedHeight(25) dw_btn.setArrowType(Qt.DownArrow) dw_btn.setFixedHeight(25) pip_up_down_layout.addWidget(up_btn) pip_up_down_layout.addWidget(dw_btn) string_label = QLabel() string_label.setText(label) string_label.setFixedWidth(210) btn = QtWidgets.QPushButton() btn.setFixedSize(20, 20) pixmap_icon = QtGui.QPixmap("./assets/images/delete_x_white.png") q_icon = QtGui.QIcon(pixmap_icon) btn.setIcon(q_icon) pip_main_layout.addWidget(pip_up_down, Qt.AlignVCenter) pip_main_layout.addWidget(pixmap_label, Qt.AlignVCenter) pip_main_layout.addWidget(string_label, Qt.AlignLeft) pip_main_layout.addWidget(btn, Qt.AlignVCenter) self.pip_widget_vbox_layout.insertWidget(position, pip_main_widget) # Create the corresponding settings widget and connect it self.settings_collapsable.setTitle("Settings") self.stackedWidget_Settings.hide() settings_main_widget = None if not new_marker: settings_main_widget = self.load_settings_widgets_from_pipeline_groupbox(position) self.stackedWidget_Settings.insertWidget(position, settings_main_widget) def show_settings(): # Set background color while widget is selected. Doesn't work because of theme? *TODO* p = pip_main_widget.palette() p.setColor(pip_main_widget.backgroundRole(), Qt.red) pip_main_widget.setPalette(p) if not new_marker: self.stackedWidget_Settings.show() self.stackedWidget_Settings.setCurrentIndex(self.pipeline.get_index(cat)) self.settings_collapsable.setTitle(alg.get_name() + " Settings") else: self.stackedWidget_Settings.hide() # Create drop down for cats and algs self.remove_cat_alg_dropdown() self.create_cat_alg_dropdown(self.pipeline.get_index(cat), pip_main_widget, settings_main_widget) if not new_marker: self.set_cat_alg_dropdown(cat, alg) # Connect Button to remove step from pipeline def delete_button_clicked(): self.remove_cat_alg_dropdown() self.remove_pip_entry(pip_main_widget, settings_main_widget, cat) self.clickable(pixmap_label).connect(show_settings) self.clickable(string_label).connect(show_settings) btn.clicked.connect(delete_button_clicked) return (pip_main_widget, settings_main_widget) def add_pip_entry_empty(self): """ Creates an blank entry in the ui pipeline since the user still needs to specify a type and an algorithm of the category. It also creates the corresponding settings widget. """ # create an widget that displays the pip entry in the ui and connect the remove button pip_main_widget = QWidget() pip_main_widget.setFixedHeight(70) pip_main_widget.setFixedWidth(300) pip_main_layout = QHBoxLayout() pip_main_widget.setLayout(pip_main_layout) label = "<Click to specify new step>" icon = None pixmap = QPixmap(icon) pixmap_scaled_keeping_aspec = pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio) pixmap_label = QtWidgets.QLabel() pixmap_label.setPixmap(pixmap_scaled_keeping_aspec) pip_up_down = QWidget() pip_up_down.setFixedHeight(70) pip_up_down_layout = QVBoxLayout() pip_up_down.setLayout(pip_up_down_layout) up_btn = QToolButton() dw_btn = QToolButton() up_btn.setArrowType(Qt.UpArrow) up_btn.setFixedHeight(25) dw_btn.setArrowType(Qt.DownArrow) dw_btn.setFixedHeight(25) pip_up_down_layout.addWidget(up_btn) pip_up_down_layout.addWidget(dw_btn) string_label = QLabel() string_label.setText(label) string_label.setFixedWidth(210) btn = QtWidgets.QPushButton() btn.setFixedSize(20, 20) pixmap_icon = QtGui.QPixmap("./assets/images/delete_x_white.png") q_icon = QtGui.QIcon(pixmap_icon) btn.setIcon(q_icon) pip_main_layout.addWidget(pip_up_down, Qt.AlignVCenter) pip_main_layout.addWidget(pixmap_label, Qt.AlignVCenter) pip_main_layout.addWidget(string_label, Qt.AlignLeft) pip_main_layout.addWidget(btn, Qt.AlignVCenter) cat_position = len(self.pipeline.executed_cats) self.pip_widget_vbox_layout.insertWidget(cat_position, pip_main_widget) index = self.pip_widget_vbox_layout.indexOf(pip_main_widget) #print(index) # Create the corresponding empty settings widget and connect it # settings = self.load_widgets_from_cat_groupbox(cat_position) *TODO* EMPTY self.settings_collapsable.setTitle("Settings") self.stackedWidget_Settings.hide() # Add new step to pipeline new_category = self.pipeline.new_category(cat_position) print("Create new entry " + str(new_category)) print("Pipeline length: " + str(len(self.pipeline.executed_cats)) + ".") settings_main_widget = None # Connect pipeline entry with corresponding settings widget def show_settings(): #print("click") self.stackedWidget_Settings.show() self.remove_cat_alg_dropdown() # Create drop down for cats and algs self.create_cat_alg_dropdown(self.pipeline.get_index(new_category), pip_main_widget, settings_main_widget) self.stackedWidget_Settings.hide() # Connect Button to remove step from pipeline def delete_button_clicked(): self.remove_cat_alg_dropdown() self.remove_pip_entry(pip_main_widget, settings_main_widget, new_category) self.clickable(pixmap_label).connect(show_settings) self.clickable(string_label).connect(show_settings) btn.clicked.connect(delete_button_clicked) self.scroll_down_pip() def add_pip_entry(self, cat_position): """ Creates a entry in the ui pipeline with a given position in pipeline. It also creates the corresponding settings widget. """ # create an widget that displays the pip entry in the ui and connect the remove button pip_main_widget = QWidget() pip_main_widget.setFixedHeight(70) pip_main_widget.setFixedWidth(300) pip_main_layout = QHBoxLayout() pip_main_widget.setLayout(pip_main_layout) cat = self.pipeline.executed_cats[cat_position] alg = cat.active_algorithm label = alg.get_name() icon = cat.get_icon() pixmap = QPixmap(icon) pixmap_scaled_keeping_aspec = pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio) pixmap_label = QtWidgets.QLabel() pixmap_label.setPixmap(pixmap_scaled_keeping_aspec) pip_up_down = QWidget() pip_up_down.setFixedHeight(70) pip_up_down_layout = QVBoxLayout() pip_up_down.setLayout(pip_up_down_layout) up_btn = QToolButton() dw_btn = QToolButton() up_btn.setArrowType(Qt.UpArrow) up_btn.setFixedHeight(25) dw_btn.setArrowType(Qt.DownArrow) dw_btn.setFixedHeight(25) pip_up_down_layout.addWidget(up_btn) pip_up_down_layout.addWidget(dw_btn) string_label = QLabel() string_label.setText(label) string_label.setFixedWidth(210) btn = QtWidgets.QPushButton() btn.setFixedSize(20, 20) pixmap_icon = QtGui.QPixmap("./assets/images/delete_x_white.png") q_icon = QtGui.QIcon(pixmap_icon) btn.setIcon(q_icon) pip_main_layout.addWidget(pip_up_down, Qt.AlignVCenter) pip_main_layout.addWidget(pixmap_label, Qt.AlignVCenter) pip_main_layout.addWidget(string_label, Qt.AlignLeft) pip_main_layout.addWidget(btn, Qt.AlignVCenter) self.pip_widget_vbox_layout.insertWidget(cat_position, pip_main_widget) index = self.pip_widget_vbox_layout.indexOf(pip_main_widget) #print(index) # Create the corresponding settings widget and connect it settings_main_widget = self.load_settings_widgets_from_pipeline_groupbox(cat_position) self.settings_collapsable.setTitle("Settings") self.stackedWidget_Settings.hide() self.stackedWidget_Settings.insertWidget(cat_position, settings_main_widget) #print("Read from pipeline entry " + str(cat)) #print("Pipeline length: " + str(len(self.pipeline.executed_cats)) + ".") def show_settings(): # Set background color while widget is selected. Doesn't work because of theme? *TODO* p = pip_main_widget.palette() p.setColor(pip_main_widget.backgroundRole(), Qt.red) pip_main_widget.setPalette(p) self.stackedWidget_Settings.show() self.stackedWidget_Settings.setCurrentIndex(self.pipeline.get_index(cat)) self.settings_collapsable.setTitle(alg.get_name() + " Settings") self.remove_cat_alg_dropdown() # Create drop down for cats and algs self.create_cat_alg_dropdown(self.pipeline.get_index(cat), pip_main_widget, settings_main_widget) #print(cat) #print(alg) self.set_cat_alg_dropdown(cat, alg) # Connect Button to remove step from pipeline def delete_button_clicked(): self.remove_pip_entry(pip_main_widget, settings_main_widget, cat) self.clickable(pixmap_label).connect(show_settings) self.clickable(string_label).connect(show_settings) btn.clicked.connect(delete_button_clicked) return (pip_main_widget, settings_main_widget) # https://wiki.python.org/moin/PyQt/Making%20non-clickable%20widgets%20clickable def clickable(self, widget): """ Convert any widget to a clickable widget. """ class Filter(QObject): clicked = pyqtSignal() def eventFilter(self, obj, event): if obj == widget: if event.type() == QEvent.MouseButtonPress: if obj.rect().contains(event.pos()): self.clicked.emit() # The developer can opt for .emit(obj) to get the object within the slot. return True return False filter = Filter(widget) widget.installEventFilter(filter) return filter.clicked
class GstMediaSettings(SettingsSection): Name = 'Media Settings' def __init__(self, size, cue=None, parent=None): super().__init__(size, cue=cue, parent=parent) self._pipe = '' self._conf = {} self._check = False self.glayout = QGridLayout(self) self.listWidget = QListWidget(self) self.glayout.addWidget(self.listWidget, 0, 0) self.pipeButton = QPushButton('Change Pipe', self) self.glayout.addWidget(self.pipeButton, 1, 0) self.elements = QStackedWidget(self) self.glayout.addWidget(self.elements, 0, 1, 2, 1) self.glayout.setColumnStretch(0, 2) self.glayout.setColumnStretch(1, 5) self.listWidget.currentItemChanged.connect(self.__change_page) self.pipeButton.clicked.connect(self.__edit_pipe) def set_configuration(self, conf): # Get the media section of the cue configuration if conf is not None: conf = conf.get('media', {}) # Activate the layout, so we can get the right widgets size self.glayout.activate() # Create a local copy of the configuration self._conf = deepcopy(conf) # Create the widgets sections = sections_by_element_name() for element in conf.get('pipe', '').split('!'): widget = sections.get(element) if widget is not None: widget = widget(self.elements.size(), element, self) widget.set_configuration(self._conf['elements']) self.elements.addWidget(widget) item = QListWidgetItem(widget.NAME) self.listWidget.addItem(item) self.listWidget.setCurrentRow(0) def get_configuration(self): conf = {'elements': {}} for el in self.elements.children(): if isinstance(el, SettingsSection): conf['elements'].update(el.get_configuration()) # If in check mode the pipeline is not returned if not self._check: conf['pipe'] = self._conf['pipe'] return {'media': conf} def enable_check(self, enable): self._check = enable for element in self.elements.children(): if isinstance(element, SettingsSection): element.enable_check(enable) def __change_page(self, current, previous): if not current: current = previous self.elements.setCurrentIndex(self.listWidget.row(current)) def __edit_pipe(self): # Backup the settings self._conf.update(self.get_configuration()['media']) # Show the dialog dialog = GstPipeEdit(self._conf.get('pipe', ''), parent=self) if dialog.exec_() == dialog.Accepted: # Reset the view for _ in range(self.elements.count()): self.elements.removeWidget(self.elements.widget(0)) self.listWidget.clear() # Reload with the new pipeline self._conf['pipe'] = dialog.get_pipe() self.set_configuration({'media': self._conf}) self.enable_check(self._check)
class TableWidget(QSplitter): def __init__(self): super(TableWidget, self).__init__() # vbox = QVBoxLayout(self) # vbox.setContentsMargins(0, 0, 0, 0) self._tabs = QTabWidget() self._tabs.setAutoFillBackground(True) p = self._tabs.palette() p.setColor(p.Window, QColor("white")) self._tabs.setPalette(p) self._other_tab = QTabWidget() self._other_tab.setAutoFillBackground(True) self._other_tab.setPalette(p) self.addWidget(self._tabs) self.addWidget(self._other_tab) self.setSizes([1, 1]) self._other_tab.hide() self.relations = {} # Stack self.stacked = QStackedWidget() self._tabs.addTab(self.stacked, "Workspace") self.stacked_result = QStackedWidget() self._tabs.addTab(self.stacked_result, self.tr("Resultados")) btn_split = QToolButton() btn_split.setToolTip(self.tr("Click para dividir la pantalla")) btn_split.setAutoRaise(True) btn_split.setIcon(QIcon(":img/split")) self._tabs.setCornerWidget(btn_split) btn_split.clicked.connect(self._split) btn_split = QToolButton() btn_split.setToolTip(self.tr("Click para juntar las pantallas")) btn_split.setAutoRaise(True) btn_split.setIcon(QIcon(":img/split")) btn_split.clicked.connect(self._unsplit) self._other_tab.setCornerWidget(btn_split) # self.setContextMenuPolicy(Qt.CustomContextMenu) # self.customContextMenuRequested.connect(self._show_menu) lateral_widget = Pireal.get_service("lateral_widget") lateral_widget.resultClicked.connect(self._on_result_list_clicked) lateral_widget.resultSelectionChanged.connect( lambda index: self.stacked_result.setCurrentIndex(index)) # lateral_widget.newRowsRequested.connect(self._insert_rows) def insert_rows(self, tuplas): current_view = self.current_table() if current_view is not None: model = current_view.model() for tupla in tuplas: model.insertRow(model.rowCount(), tupla) current_view.adjust_columns() def _on_result_list_clicked(self, index): self.stacked_result.setCurrentIndex(index) if not self._other_tab.isVisible(): self._tabs.setCurrentIndex(1) def _unsplit(self): self._other_tab.hide() result_widget = self._other_tab.widget(0) self._tabs.addTab(result_widget, self.tr("Resultados")) self._tabs.cornerWidget().show() def _split(self): result_widget = self._tabs.widget(1) self._other_tab.addTab(result_widget, self.tr("Resultados")) self._other_tab.show() self.setSizes([1, 1]) self._tabs.cornerWidget().hide() self.setOrientation(Qt.Horizontal) def _show_menu(self, position): menu = QMenu(self) if self.count() > 0: add_tuple_action = menu.addAction(self.tr("Agregar Tupla")) add_col_action = menu.addAction(self.tr("Add Column")) add_tuple_action.triggered.connect(self.add_tuple) add_col_action.triggered.connect(self.add_column) menu.addSeparator() add_relation_action = menu.addAction(self.tr("Create new Relation")) add_relation_action.triggered.connect(self.__new_relation) menu.exec_(self.mapToGlobal(position)) def __new_relation(self): central_service = Pireal.get_service("central") central_service.create_new_relation() def count(self): return self.stacked.count() def remove_table(self, index): widget = self.stacked.widget(index) self.stacked.removeWidget(widget) del widget def current_table(self): return self.stacked.currentWidget() def remove_relation(self, name): del self.relations[name] def add_relation(self, name, rela): if self.relations.get(name, None) is None: self.relations[name] = rela return True return False def add_table(self, rela, name, table): """ Add new table from New Relation Dialog """ self.add_relation(name, rela) self.stacked.addWidget(table) def add_tuple(self): current_view = self.current_table() if current_view is not None: model = current_view.model() model.insertRow(model.rowCount()) def add_column(self): current_view = self.current_table() if current_view is not None: model = current_view.model() model.insertColumn(model.columnCount()) def delete_tuple(self): current_view = self.current_table() if current_view is not None: model = current_view.model() selection = current_view.selectionModel() if selection.hasSelection(): selection = selection.selection() rows = set([index.row() for index in selection.indexes()]) rows = sorted(list(rows)) previous = -1 i = len(rows) - 1 while i >= 0: current = rows[i] if current != previous: model.removeRow(current) i -= 1 def delete_column(self): """ Elimina la/las columnas seleccionadas """ current_view = self.current_table() if current_view is not None: model = current_view.model() selection = current_view.selectionModel() if selection.hasSelection(): selection = selection.selection() columns = set( [index.column() for index in selection.indexes()]) columns = sorted(list(columns)) previous = -1 i = len(columns) - 1 while i >= 0: current = columns[i] if current != previous: model.removeColumn(current) i -= 1 def create_table(self, rela, editable=True): """ Se crea la vista y el modelo """ _view = view.View() _model = model.Model(rela) if not editable: _model.editable = False _view.setModel(_model) _view.setItemDelegate(delegate.Delegate()) _view.setHorizontalHeader(view.Header()) return _view
class Window(QWidget): def __init__(self): super(Window, self).__init__() global current_state current_state = dict(screen_data) self.createControls() self.createCommandBox() self.brightnessSliders = SlidersGroup(self.commandBox) self.stackedWidget = QStackedWidget() self.stackedWidget.addWidget(self.brightnessSliders) layout = QBoxLayout(QBoxLayout.TopToBottom) layout.addWidget(self.stackedWidget) layout.addWidget(self.commandBoxGroup) layout.addWidget(self.controlsGroup) self.setLayout(layout) self.setWindowTitle("Brightness editor") self.resize(850, 500) command = "# get display names and brightness values\n" + \ get_brightness_command + "\n" + \ "# results (formatted): " + str(screen_data) if primary != None: command += " primary: " + screen_data[primary][0] self.commandBox.document().setPlainText(command) def createControls(self): self.controlsGroup = QGroupBox("") applyBtn = QPushButton("Apply") applyBtn.clicked.connect(self.applyClicked) controlsLayout = QGridLayout() controlsLayout.addWidget(applyBtn, 2, 1) self.controlsGroup.setLayout(controlsLayout) def createCommandBox(self): self.commandBoxGroup = QGroupBox("Command info") self.commandBoxGroup.setMaximumHeight(105) self.commandBox = QPlainTextEdit() self.commandBox.setReadOnly(True) self.commandBox.setStyleSheet("font-family: monospace; font-size: 13px") layout = QGridLayout() layout.addWidget(self.commandBox, 2, 1) self.commandBoxGroup.setLayout(layout) # apply changes def applyClicked(self): from os import system for command in get_apply_commands(): system(command) global current_state current_state = dict(screen_data) self.brightnessSliders.refreshCurrentValues()
class AppSettings(QDialog): SettingsWidgets = [] def __init__(self, conf, **kwargs): super().__init__(**kwargs) self.conf = conf self.setWindowTitle('LiSP preferences') self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMaximumSize(635, 530) self.setMinimumSize(635, 530) self.resize(635, 530) self.listWidget = QListWidget(self) self.listWidget.setGeometry(QtCore.QRect(5, 10, 185, 470)) self.sections = QStackedWidget(self) self.sections.setGeometry(QtCore.QRect(200, 10, 430, 470)) for widget in self.SettingsWidgets: widget = widget(QtCore.QSize(430, 465), self) widget.set_configuration(self.conf) self.listWidget.addItem(widget.NAME) self.sections.addWidget(widget) if len(self.SettingsWidgets) > 0: self.listWidget.setCurrentRow(0) self.listWidget.currentItemChanged.connect(self._change_page) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setGeometry(10, 495, 615, 30) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.dialogButtons.rejected.connect(self.reject) self.dialogButtons.accepted.connect(self.accept) def get_configuraton(self): conf = {} for n in range(self.sections.count()): widget = self.sections.widget(n) newconf = widget.get_configuration() deep_update(conf, newconf) return conf @classmethod def register_settings_widget(cls, widget): if widget not in cls.SettingsWidgets: cls.SettingsWidgets.append(widget) @classmethod def unregister_settings_widget(cls, widget): if widget not in cls.SettingsWidgets: cls.SettingsWidgets.remove(widget) def _change_page(self, current, previous): if not current: current = previous self.sections.setCurrentIndex(self.listWidget.row(current))
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.horizontalSliders = SlidersGroup(Qt.Horizontal, "Horizontal") self.verticalSliders = SlidersGroup(Qt.Vertical, "Vertical") self.stackedWidget = QStackedWidget() self.stackedWidget.addWidget(self.horizontalSliders) self.stackedWidget.addWidget(self.verticalSliders) self.createControls("Controls") self.horizontalSliders.valueChanged.connect(self.verticalSliders.setValue) self.verticalSliders.valueChanged.connect(self.valueSpinBox.setValue) self.valueSpinBox.valueChanged.connect(self.horizontalSliders.setValue) layout = QHBoxLayout() layout.addWidget(self.controlsGroup) layout.addWidget(self.stackedWidget) self.setLayout(layout) self.minimumSpinBox.setValue(0) self.maximumSpinBox.setValue(20) self.valueSpinBox.setValue(5) self.setWindowTitle("Sliders") def createControls(self, title): self.controlsGroup = QGroupBox(title) minimumLabel = QLabel("Minimum value:") maximumLabel = QLabel("Maximum value:") valueLabel = QLabel("Current value:") invertedAppearance = QCheckBox("Inverted appearance") invertedKeyBindings = QCheckBox("Inverted key bindings") self.minimumSpinBox = QSpinBox() self.minimumSpinBox.setRange(-100, 100) self.minimumSpinBox.setSingleStep(1) self.maximumSpinBox = QSpinBox() self.maximumSpinBox.setRange(-100, 100) self.maximumSpinBox.setSingleStep(1) self.valueSpinBox = QSpinBox() self.valueSpinBox.setRange(-100, 100) self.valueSpinBox.setSingleStep(1) orientationCombo = QComboBox() orientationCombo.addItem("Horizontal slider-like widgets") orientationCombo.addItem("Vertical slider-like widgets") orientationCombo.activated.connect(self.stackedWidget.setCurrentIndex) self.minimumSpinBox.valueChanged.connect(self.horizontalSliders.setMinimum) self.minimumSpinBox.valueChanged.connect(self.verticalSliders.setMinimum) self.maximumSpinBox.valueChanged.connect(self.horizontalSliders.setMaximum) self.maximumSpinBox.valueChanged.connect(self.verticalSliders.setMaximum) invertedAppearance.toggled.connect(self.horizontalSliders.invertAppearance) invertedAppearance.toggled.connect(self.verticalSliders.invertAppearance) invertedKeyBindings.toggled.connect(self.horizontalSliders.invertKeyBindings) invertedKeyBindings.toggled.connect(self.verticalSliders.invertKeyBindings) controlsLayout = QGridLayout() controlsLayout.addWidget(minimumLabel, 0, 0) controlsLayout.addWidget(maximumLabel, 1, 0) controlsLayout.addWidget(valueLabel, 2, 0) controlsLayout.addWidget(self.minimumSpinBox, 0, 1) controlsLayout.addWidget(self.maximumSpinBox, 1, 1) controlsLayout.addWidget(self.valueSpinBox, 2, 1) controlsLayout.addWidget(invertedAppearance, 0, 2) controlsLayout.addWidget(invertedKeyBindings, 1, 2) controlsLayout.addWidget(orientationCombo, 3, 0, 1, 3) self.controlsGroup.setLayout(controlsLayout)
class ViewSpace(QWidget): """A ViewSpace manages a stack of views, one of them is visible. The ViewSpace also has a statusbar, accessible in the status attribute. The viewChanged(View) signal is emitted when the current view for this ViewSpace changes. Also, when a ViewSpace is created (e.g. when a window is created or split), the app.viewSpaceCreated(space) signal is emitted. You can use the app.viewSpaceCreated() and the ViewSpace.viewChanged() signals to implement things on a per ViewSpace basis, e.g. in the statusbar of a ViewSpace. """ viewChanged = pyqtSignal(view_.View) def __init__(self, manager, parent=None): super(ViewSpace, self).__init__(parent) self.manager = weakref.ref(manager) self.views = [] layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.stack = QStackedWidget(self) layout.addWidget(self.stack) self.status = ViewStatusBar(self) self.status.setEnabled(False) layout.addWidget(self.status) app.languageChanged.connect(self.updateStatusBar) app.viewSpaceCreated(self) def activeView(self): if self.views: return self.views[-1] def document(self): """Returns the currently active document in this space. If there are no views, returns None. """ if self.views: return self.views[-1].document() def showDocument(self, doc): """Shows the document, creating a View if necessary.""" if doc is self.document(): return cur = self.activeView() for view in self.views[:-1]: if doc is view.document(): self.views.remove(view) break else: view = view_.View(doc) self.stack.addWidget(view) self.views.append(view) if cur: self.disconnectView(cur) self.connectView(view) self.stack.setCurrentWidget(view) self.updateStatusBar() def removeDocument(self, doc): active = doc is self.document() if active: self.disconnectView(self.activeView()) for view in self.views: if doc is view.document(): self.views.remove(view) view.deleteLater() break else: return if active and self.views: self.connectView(self.views[-1]) self.stack.setCurrentWidget(self.views[-1]) self.updateStatusBar() def connectView(self, view): view.installEventFilter(self) view.cursorPositionChanged.connect(self.updateCursorPosition) view.modificationChanged.connect(self.updateModificationState) view.document().urlChanged.connect(self.updateDocumentName) self.viewChanged.emit(view) def disconnectView(self, view): view.removeEventFilter(self) view.cursorPositionChanged.disconnect(self.updateCursorPosition) view.modificationChanged.disconnect(self.updateModificationState) view.document().urlChanged.disconnect(self.updateDocumentName) def eventFilter(self, view, ev): if ev.type() == QEvent.FocusIn: self.setActiveViewSpace() return False def setActiveViewSpace(self): self.manager().setActiveViewSpace(self) def updateStatusBar(self): """Update all info in the statusbar, e.g. on document change.""" if self.views: self.updateCursorPosition() self.updateModificationState() self.updateDocumentName() def updateCursorPosition(self): cur = self.activeView().textCursor() line = cur.blockNumber() + 1 try: column = cur.positionInBlock() except AttributeError: # only in very recent PyQt5 column = cur.position() - cur.block().position() self.status.positionLabel.setText(_("Line: {line}, Col: {column}").format( line = line, column = column)) def updateModificationState(self): modified = self.document().isModified() pixmap = icons.get('document-save').pixmap(16) if modified else QPixmap() self.status.stateLabel.setPixmap(pixmap) def updateDocumentName(self): self.status.infoLabel.setText(self.document().documentName())
class ConfigDialog(QDialog): def __init__(self, parent=None): super(ConfigDialog, self).__init__(parent) self.contentsWidget = QListWidget() self.contentsWidget.setViewMode(QListView.IconMode) self.contentsWidget.setIconSize(QSize(96, 84)) self.contentsWidget.setMovement(QListView.Static) self.contentsWidget.setMaximumWidth(128) self.contentsWidget.setSpacing(12) self.pagesWidget = QStackedWidget() self.pagesWidget.addWidget(ConfigurationPage()) self.pagesWidget.addWidget(UpdatePage()) self.pagesWidget.addWidget(QueryPage()) closeButton = QPushButton("Close") self.createIcons() self.contentsWidget.setCurrentRow(0) closeButton.clicked.connect(self.close) horizontalLayout = QHBoxLayout() horizontalLayout.addWidget(self.contentsWidget) horizontalLayout.addWidget(self.pagesWidget, 1) buttonsLayout = QHBoxLayout() buttonsLayout.addStretch(1) buttonsLayout.addWidget(closeButton) mainLayout = QVBoxLayout() mainLayout.addLayout(horizontalLayout) mainLayout.addStretch(1) mainLayout.addSpacing(12) mainLayout.addLayout(buttonsLayout) self.setLayout(mainLayout) self.setWindowTitle("Config Dialog") def changePage(self, current, previous): if not current: current = previous self.pagesWidget.setCurrentIndex(self.contentsWidget.row(current)) def createIcons(self): configButton = QListWidgetItem(self.contentsWidget) configButton.setIcon(QIcon(':/images/config.png')) configButton.setText("Configuration") configButton.setTextAlignment(Qt.AlignHCenter) configButton.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) updateButton = QListWidgetItem(self.contentsWidget) updateButton.setIcon(QIcon(':/images/update.png')) updateButton.setText("Update") updateButton.setTextAlignment(Qt.AlignHCenter) updateButton.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) queryButton = QListWidgetItem(self.contentsWidget) queryButton.setIcon(QIcon(':/images/query.png')) queryButton.setText("Query") queryButton.setTextAlignment(Qt.AlignHCenter) queryButton.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.contentsWidget.currentItemChanged.connect(self.changePage)
class DataExportGui(QWidget): """ Manages all GUI elements in the data selection applet. This class itself is the central widget and also owns/manages the applet drawer widgets. """ ########################################### ### AppletGuiInterface Concrete Methods ### ########################################### def centralWidget( self ): return self def appletDrawer(self): return self.drawer def menus( self ): return [] def viewerControlWidget(self): return self._viewerControlWidgetStack def setImageIndex(self, index): pass def stopAndCleanUp(self): for editor in list(self.layerViewerGuis.values()): self.viewerStack.removeWidget( editor ) editor.stopAndCleanUp() self.layerViewerGuis.clear() def imageLaneAdded(self, laneIndex): pass def imageLaneRemoved(self, laneIndex, finalLength): pass def allowLaneSelectionChange(self): return False ########################################### ########################################### def __init__(self, parentApplet, topLevelOperator): super(DataExportGui, self).__init__() self.drawer = None self.topLevelOperator = topLevelOperator self.threadRouter = ThreadRouter(self) self._thunkEventHandler = ThunkEventHandler(self) self._initAppletDrawerUic() self.initCentralUic() self.initViewerControls() self.parentApplet = parentApplet self.progressSignal = parentApplet.progressSignal self.overwrite = False @threadRoutedWithRouter(self.threadRouter) def handleNewDataset( multislot, index ): # Make room in the GUI table self.batchOutputTableWidget.insertRow( index ) # Update the table row data when this slot has new data # We can't bind in the row here because the row may change in the meantime. multislot[index].notifyReady( bind( self.updateTableForSlot ) ) if multislot[index].ready(): self.updateTableForSlot( multislot[index] ) multislot[index].notifyUnready( self._updateExportButtons ) multislot[index].notifyReady( self._updateExportButtons ) self.topLevelOperator.ExportPath.notifyInserted( bind( handleNewDataset ) ) # For each dataset that already exists, update the GUI for i, subslot in enumerate(self.topLevelOperator.ExportPath): handleNewDataset( self.topLevelOperator.ExportPath, i ) if subslot.ready(): self.updateTableForSlot(subslot) @threadRoutedWithRouter(self.threadRouter) def handleLaneRemoved( multislot, index, finalLength ): if self.batchOutputTableWidget.rowCount() <= finalLength: return # Remove the row we don't need any more self.batchOutputTableWidget.removeRow( index ) # Remove the viewer for this dataset imageMultiSlot = self.topLevelOperator.Inputs[index] if imageMultiSlot in list(self.layerViewerGuis.keys()): layerViewerGui = self.layerViewerGuis[imageMultiSlot] self.viewerStack.removeWidget( layerViewerGui ) self._viewerControlWidgetStack.removeWidget( layerViewerGui.viewerControlWidget() ) layerViewerGui.stopAndCleanUp() self.topLevelOperator.Inputs.notifyRemove( bind( handleLaneRemoved ) ) def _initAppletDrawerUic(self, drawerPath=None): """ Load the ui file for the applet drawer, which we own. """ if drawerPath is None: localDir = os.path.split(__file__)[0] drawerPath = os.path.join( localDir, "dataExportDrawer.ui") self.drawer = uic.loadUi(drawerPath) self.drawer.settingsButton.clicked.connect( self._chooseSettings ) self.drawer.exportAllButton.clicked.connect( self.exportAllResults ) self.drawer.exportAllButton.setIcon( QIcon(ilastikIcons.Save) ) self.drawer.deleteAllButton.clicked.connect( self.deleteAllResults ) self.drawer.deleteAllButton.setIcon( QIcon(ilastikIcons.Clear) ) @threadRoutedWithRouter(self.threadRouter) def _handleNewSelectionNames( *args ): input_names = self.topLevelOperator.SelectionNames.value self.drawer.inputSelectionCombo.addItems( input_names ) self.topLevelOperator.SelectionNames.notifyDirty( _handleNewSelectionNames ) _handleNewSelectionNames() self.drawer.inputSelectionCombo.currentIndexChanged.connect(self._handleInputComboSelectionChanged) def _handleInputComboSelectionChanged( self, index ): assert index < len(self.topLevelOperator.SelectionNames.value) if self.drawer.inputSelectionCombo.currentText() == self.topLevelOperator.TableOnlyName.value: self.topLevelOperator.TableOnly.setValue(True) else: self.topLevelOperator.TableOnly.setValue(False) self.topLevelOperator.InputSelection.setValue( index ) def initCentralUic(self): """ Load the GUI from the ui file into this class and connect it with event handlers. """ # Load the ui file into this class (find it in our own directory) localDir = os.path.split(__file__)[0] uic.loadUi(localDir+"/dataExport.ui", self) self.batchOutputTableWidget.resizeRowsToContents() self.batchOutputTableWidget.resizeColumnsToContents() self.batchOutputTableWidget.setAlternatingRowColors(True) self.batchOutputTableWidget.setShowGrid(False) self.batchOutputTableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive) self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.Dataset, 200) self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.ExportLocation, 250) self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.Action, 100) self.batchOutputTableWidget.verticalHeader().hide() # Set up handlers self.batchOutputTableWidget.itemSelectionChanged.connect(self.handleTableSelectionChange) # Set up the viewer area self.initViewerStack() self.splitter.setSizes([150, 850]) def initViewerStack(self): self.layerViewerGuis = {} self.viewerStack.addWidget( QWidget() ) def initViewerControls(self): self._viewerControlWidgetStack = QStackedWidget(parent=self) def showEvent(self, event): super( DataExportGui, self ).showEvent(event) self.showSelectedDataset() def hideEvent(self, event): super( DataExportGui, self ).hideEvent(event) # Make sure all 'on disk' layers are discarded so we aren't using those files any more. for opLaneView in self.topLevelOperator: opLaneView.cleanupOnDiskView() def _chooseSettings(self): opExportModelOp, opSubRegion = get_model_op( self.topLevelOperator ) if opExportModelOp is None: QMessageBox.information( self, "Image not ready for export", "Export isn't possible yet: No images are ready for export. " "Please configure upstream pipeline with valid settings, " "check that images were specified in the (batch) input applet and try again." ) return settingsDlg = DataExportOptionsDlg(self, opExportModelOp) if settingsDlg.exec_() == DataExportOptionsDlg.Accepted: # Copy the settings from our 'model op' into the real op setting_slots = [ opExportModelOp.RegionStart, opExportModelOp.RegionStop, opExportModelOp.InputMin, opExportModelOp.InputMax, opExportModelOp.ExportMin, opExportModelOp.ExportMax, opExportModelOp.ExportDtype, opExportModelOp.OutputAxisOrder, opExportModelOp.OutputFilenameFormat, opExportModelOp.OutputInternalPath, opExportModelOp.OutputFormat ] # Disconnect the special 'transaction' slot to prevent these # settings from triggering many calls to setupOutputs. self.topLevelOperator.TransactionSlot.disconnect() for model_slot in setting_slots: real_inslot = getattr(self.topLevelOperator, model_slot.name) if model_slot.ready(): real_inslot.setValue( model_slot.value ) else: real_inslot.disconnect() # Re-connect the 'transaction' slot to apply all settings at once. self.topLevelOperator.TransactionSlot.setValue(True) # Discard the temporary model op opExportModelOp.cleanUp() opSubRegion.cleanUp() # Update the gui with the new export paths for index, slot in enumerate(self.topLevelOperator.ExportPath): self.updateTableForSlot(slot) def getSlotIndex(self, multislot, subslot ): # Which index is this slot? for index, slot in enumerate(multislot): if slot == subslot: return index return -1 @threadRouted def updateTableForSlot(self, slot): """ Update the table row that corresponds to the given slot of the top-level operator (could be either input slot) """ row = self.getSlotIndex( self.topLevelOperator.ExportPath, slot ) assert row != -1, "Unknown input slot!" if not self.topLevelOperator.ExportPath[row].ready() or\ not self.topLevelOperator.RawDatasetInfo[row].ready(): return try: nickname = self.topLevelOperator.RawDatasetInfo[row].value.nickname exportPath = self.topLevelOperator.ExportPath[row].value except Slot.SlotNotReadyError: # Sadly, it is possible to get here even though we checked for .ready() immediately beforehand. # That's because the graph has a diamond-shaped DAG of connections, but the graph has no transaction mechanism # (It's therefore possible for RawDatasetInfo[row] to be ready() even though it's upstream partner is NOT ready. return self.batchOutputTableWidget.setItem( row, Column.Dataset, QTableWidgetItem( nickname ) ) self.batchOutputTableWidget.setItem( row, Column.ExportLocation, QTableWidgetItem( exportPath ) ) exportNowButton = QPushButton("Export") exportNowButton.setToolTip("Generate individual batch output dataset.") exportNowButton.clicked.connect( bind(self.exportResultsForSlot, self.topLevelOperator[row] ) ) self.batchOutputTableWidget.setCellWidget( row, Column.Action, exportNowButton ) # Select a row if there isn't one already selected. selectedRanges = self.batchOutputTableWidget.selectedRanges() if len(selectedRanges) == 0: self.batchOutputTableWidget.selectRow(0) def setEnabledIfAlive(self, widget, enable): if not sip.isdeleted(widget): widget.setEnabled(enable) def _updateExportButtons(self, *args): """Called when at least one dataset became 'unready', so we have to disable the export button.""" all_ready = True # Enable/disable the appropriate export buttons in the table. # Use ThunkEvents to ensure that this happens in the Gui thread. for row, slot in enumerate( self.topLevelOperator.ImageToExport ): all_ready &= slot.ready() export_button = self.batchOutputTableWidget.cellWidget( row, Column.Action ) if export_button is not None: executable_event = ThunkEvent( partial(self.setEnabledIfAlive, export_button, slot.ready()) ) QApplication.instance().postEvent( self, executable_event ) # Disable the "Export all" button unless all slots are ready. executable_event = ThunkEvent( partial(self.setEnabledIfAlive, self.drawer.exportAllButton, all_ready) ) QApplication.instance().postEvent( self, executable_event ) def handleTableSelectionChange(self): """ Any time the user selects a new item, select the whole row. """ self.selectEntireRow() self.showSelectedDataset() def selectEntireRow(self): # FIXME: There is a better way to do this... # Figure out which row is selected selectedItemRows = set() selectedRanges = self.batchOutputTableWidget.selectedRanges() for rng in selectedRanges: for row in range(rng.topRow(), rng.bottomRow()+1): selectedItemRows.add(row) # Disconnect from selection change notifications while we do this self.batchOutputTableWidget.itemSelectionChanged.disconnect(self.handleTableSelectionChange) for row in selectedItemRows: self.batchOutputTableWidget.selectRow(row) # Reconnect now that we're finished self.batchOutputTableWidget.itemSelectionChanged.connect(self.handleTableSelectionChange) def exportSlots(self, laneViewList ): try: # Set the busy flag so the workflow knows not to allow # upstream changes or shell changes while we're exporting self.parentApplet.busy = True self.parentApplet.appletStateUpdateRequested() # Disable our own gui QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self.drawer, False) ) ) QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self, False) ) ) # Start with 1% so the progress bar shows up self.progressSignal(0) self.progressSignal(1) def signalFileProgress(slotIndex, percent): self.progressSignal(old_div((100 * slotIndex + percent), len(laneViewList))) # Client hook self.parentApplet.prepare_for_entire_export() for i, opLaneView in enumerate(laneViewList): lane_index = self.topLevelOperator.innerOperators.index(opLaneView) logger.debug("Exporting result {}".format(i)) # If the operator provides a progress signal, use it. slotProgressSignal = opLaneView.progressSignal slotProgressSignal.subscribe(partial(signalFileProgress, i)) try: # Client hook self.parentApplet.prepare_lane_for_export(lane_index) # Export the image opLaneView.run_export() # Client hook if self.parentApplet.postprocessCanCheckForExistingFiles(): exportSuccessful = self.parentApplet.post_process_lane_export(lane_index, checkOverwriteFiles=True) if not exportSuccessful: userSelection = [None] self.showOverwriteQuestion(userSelection) if userSelection[0]: self.parentApplet.post_process_lane_export(lane_index, checkOverwriteFiles=False) else: self.parentApplet.post_process_lane_export(lane_index) except Exception as ex: if opLaneView.ExportPath.ready(): msg = "Failed to generate export file: \n" msg += opLaneView.ExportPath.value msg += "\n{}".format( ex ) else: msg = "Failed to generate export file." msg += "\n{}".format( ex ) log_exception( logger, msg ) self.showExportError(msg) # We're finished with this file. self.progressSignal(100 * (i + 1) / float(len(laneViewList))) # Client hook self.parentApplet.post_process_entire_export() # Ensure the shell knows we're really done. self.progressSignal(100) except: # Cancel our progress. self.progressSignal(0, True) raise finally: # We're not busy any more. Tell the workflow. self.parentApplet.busy = False self.parentApplet.appletStateUpdateRequested() # Re-enable our own gui QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self.drawer, True) ) ) QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self, True) ) ) def postProcessLane(self, lane_index): """ Called immediately after the result for each lane is exported. Can be overridden by subclasses for post-processing purposes. """ pass @threadRouted def showExportError(self, msg): QMessageBox.critical(self, "Failed to export", msg ) @threadRouted def showOverwriteQuestion(self, userSelection): assert isinstance(userSelection, list) reply = QMessageBox.question(self, 'Warning!', 'This filename already exists. Are you sure you want to overwrite?', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: userSelection[0] = True else: userSelection[0] = False def exportResultsForSlot(self, opLane): # Make sure all 'on disk' layers are discarded so we aren't using those files any more. for opLaneView in self.topLevelOperator: opLaneView.cleanupOnDiskView() # Do this in a separate thread so the UI remains responsive exportThread = threading.Thread(target=bind(self.exportSlots, [opLane]), name="DataExportThread") exportThread.start() def exportAllResults(self): # Make sure all 'on disk' layers are discarded so we aren't using those files any more. for opLaneView in self.topLevelOperator: opLaneView.cleanupOnDiskView() # Do this in a separate thread so the UI remains responsive exportThread = threading.Thread(target=bind(self.exportSlots, self.topLevelOperator), name="DataExportThread") exportThread.start() def deleteAllResults(self): for innerOp in self.topLevelOperator: operatorView = innerOp operatorView.cleanupOnDiskView() pathComp = PathComponents(operatorView.ExportPath.value, operatorView.WorkingDirectory.value) if os.path.exists(pathComp.externalPath): os.remove(pathComp.externalPath) operatorView.setupOnDiskView() # we need to toggle the dirts state in order to enforce a frech dirty signal operatorView.Dirty.setValue( False ) operatorView.Dirty.setValue( True ) def showSelectedDataset(self): """ Show the exported file in the viewer """ # Get the selected row and corresponding slot value selectedRanges = self.batchOutputTableWidget.selectedRanges() if len(selectedRanges) == 0: return row = selectedRanges[0].topRow() # Hide all layers that come from the disk. for opLaneView in self.topLevelOperator: opLaneView.cleanupOnDiskView() # Activate the 'on disk' layers for this lane (if possible) opLane = self.topLevelOperator.getLane(row) opLane.setupOnDiskView() # Create if necessary imageMultiSlot = self.topLevelOperator.Inputs[row] if imageMultiSlot not in list(self.layerViewerGuis.keys()): layerViewer = self.createLayerViewer(opLane) # Maximize the x-y view by default. layerViewer.volumeEditorWidget.quadview.ensureMaximized(2) self.layerViewerGuis[imageMultiSlot] = layerViewer self.viewerStack.addWidget( layerViewer ) self._viewerControlWidgetStack.addWidget( layerViewer.viewerControlWidget() ) # Show the right one layerViewer = self.layerViewerGuis[imageMultiSlot] self.viewerStack.setCurrentWidget( layerViewer ) self._viewerControlWidgetStack.setCurrentWidget( layerViewer.viewerControlWidget() ) def createLayerViewer(self, opLane): """ This method provides an instance of LayerViewerGui for the given data lane. If this GUI class is subclassed, this method can be reimplemented to provide custom layer types for the exported layers. """ return DataExportLayerViewerGui(self.parentApplet, opLane)
class MainWindow(QMainWindow): def __init__(self, app): QMainWindow.__init__(self, None) self.documentPath = None self.doc = DocumentModel(app=app.model) self.app = app self._setupUi() # Create base elements self.model = MainWindowModel(document=self.doc) self.model2view = {} self.alookup = Lookup(self, model=self.model.account_lookup) self.clookup = Lookup(self, model=self.model.completion_lookup) self.drsel = DateRangeSelector(mainwindow=self, view=self.dateRangeSelectorView) self.sfield = SearchField(model=self.model.search_field, view=self.searchLineEdit) self.recentDocuments = Recent(self.app, 'recentDocuments') self.recentDocuments.addMenu(self.menuOpenRecent) self.doc.view = self self.model.view = self self._updateUndoActions() self._bindSignals() def _setupUi(self): # has to take place *before* base elements creation self.setWindowTitle("moneyGuru") self.resize(700, 580) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) self.verticalLayout.setSpacing(0) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.topBar = QWidget(self.centralwidget) self.horizontalLayout_2 = QHBoxLayout(self.topBar) self.horizontalLayout_2.setContentsMargins(2, 0, 2, 0) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem) self.dateRangeSelectorView = DateRangeSelectorView(self.topBar) self.dateRangeSelectorView.setMinimumSize(QSize(220, 0)) self.horizontalLayout_2.addWidget(self.dateRangeSelectorView) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.searchLineEdit = SearchEdit(self.topBar) self.searchLineEdit.setMaximumSize(QSize(240, 16777215)) self.horizontalLayout_2.addWidget(self.searchLineEdit) self.verticalLayout.addWidget(self.topBar) self.tabBar = TabBarPlus(self.centralwidget) self.tabBar.setMinimumSize(QSize(0, 20)) self.verticalLayout.addWidget(self.tabBar) self.mainView = QStackedWidget(self.centralwidget) self.verticalLayout.addWidget(self.mainView) # Bottom buttons & status label self.bottomBar = QWidget(self.centralwidget) self.horizontalLayout = QHBoxLayout(self.bottomBar) self.horizontalLayout.setContentsMargins(2, 2, 2, 2) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.newItemButton = QPushButton(self.bottomBar) buttonSizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) buttonSizePolicy.setHorizontalStretch(0) buttonSizePolicy.setVerticalStretch(0) buttonSizePolicy.setHeightForWidth(self.newItemButton.sizePolicy().hasHeightForWidth()) self.newItemButton.setSizePolicy(buttonSizePolicy) self.newItemButton.setIcon(QIcon(QPixmap(':/plus_8'))) self.horizontalLayout.addWidget(self.newItemButton) self.deleteItemButton = QPushButton(self.bottomBar) self.deleteItemButton.setSizePolicy(buttonSizePolicy) self.deleteItemButton.setIcon(QIcon(QPixmap(':/minus_8'))) self.horizontalLayout.addWidget(self.deleteItemButton) self.editItemButton = QPushButton(self.bottomBar) self.editItemButton.setSizePolicy(buttonSizePolicy) self.editItemButton.setIcon(QIcon(QPixmap(':/info_gray_12'))) self.horizontalLayout.addWidget(self.editItemButton) self.horizontalLayout.addItem(horizontalSpacer(size=20)) self.graphVisibilityButton = QPushButton() self.graphVisibilityButton.setSizePolicy(buttonSizePolicy) self.graphVisibilityButton.setIcon(QIcon(QPixmap(':/graph_visibility_on_16'))) self.horizontalLayout.addWidget(self.graphVisibilityButton) self.piechartVisibilityButton = QPushButton() self.piechartVisibilityButton.setSizePolicy(buttonSizePolicy) self.piechartVisibilityButton.setIcon(QIcon(QPixmap(':/piechart_visibility_on_16'))) self.horizontalLayout.addWidget(self.piechartVisibilityButton) self.columnsVisibilityButton = QPushButton() self.columnsVisibilityButton.setSizePolicy(buttonSizePolicy) self.columnsVisibilityButton.setIcon(QIcon(QPixmap(':/columns_16'))) self.horizontalLayout.addWidget(self.columnsVisibilityButton) self.statusLabel = QLabel(tr("Status")) self.statusLabel.setAlignment(Qt.AlignCenter) self.horizontalLayout.addWidget(self.statusLabel) self.verticalLayout.addWidget(self.bottomBar) self.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 700, 20)) self.menuFile = QMenu(tr("File")) self.menuOpenRecent = QMenu(tr("Open Recent")) self.menuView = QMenu(tr("View")) self.menuDateRange = QMenu(tr("Date Range")) self.menuEdit = QMenu(tr("Edit")) self.menuHelp = QMenu(tr("Help")) self.setMenuBar(self.menubar) self.actionOpenDocument = QAction(tr("Open..."), self) self.actionOpenDocument.setShortcut("Ctrl+O") self.actionShowNetWorth = QAction(tr("Net Worth"), self) self.actionShowNetWorth.setShortcut("Ctrl+1") self.actionShowNetWorth.setIcon(QIcon(QPixmap(':/balance_sheet_48'))) self.actionShowProfitLoss = QAction(escapeamp(tr("Profit & Loss")), self) self.actionShowProfitLoss.setShortcut("Ctrl+2") self.actionShowProfitLoss.setIcon(QIcon(QPixmap(':/income_statement_48'))) self.actionShowTransactions = QAction(tr("Transactions"), self) self.actionShowTransactions.setShortcut("Ctrl+3") self.actionShowTransactions.setIcon(QIcon(QPixmap(':/transaction_table_48'))) self.actionShowSelectedAccount = QAction(tr("Show Account"), self) self.actionShowSelectedAccount.setShortcut("Ctrl+]") self.actionNewItem = QAction(tr("New Item"), self) self.actionNewItem.setShortcut("Ctrl+N") self.actionDeleteItem = QAction(tr("Remove Selected"), self) self.actionEditItem = QAction(tr("Show Info"), self) self.actionEditItem.setShortcut("Ctrl+I") self.actionToggleGraph = QAction(tr("Toggle Graph"), self) self.actionToggleGraph.setShortcut("Ctrl+Alt+G") self.actionTogglePieChart = QAction(tr("Toggle Pie Chart"), self) self.actionTogglePieChart.setShortcut("Ctrl+Alt+P") self.actionMoveUp = QAction(tr("Move Up"), self) self.actionMoveUp.setShortcut("Ctrl++") self.actionMoveDown = QAction(tr("Move Down"), self) self.actionMoveDown.setShortcut("Ctrl+-") self.actionNavigateBack = QAction(tr("Go Back"), self) self.actionNavigateBack.setShortcut("Ctrl+[") self.actionNewAccountGroup = QAction(tr("New Account Group"), self) self.actionNewAccountGroup.setShortcut("Ctrl+Shift+N") self.actionShowNextView = QAction(tr("Next View"), self) self.actionShowNextView.setShortcut("Ctrl+Shift+]") self.actionShowPreviousView = QAction(tr("Previous View"), self) self.actionShowPreviousView.setShortcut("Ctrl+Shift+[") self.actionNewDocument = QAction(tr("New Document"), self) self.actionImport = QAction(tr("Import..."), self) self.actionImport.setShortcut("Ctrl+Alt+I") self.actionExport = QAction(tr("Export..."), self) self.actionExport.setShortcut("Ctrl+Alt+E") self.actionSave = QAction(tr("Save"), self) self.actionSave.setShortcut("Ctrl+S") self.actionSaveAs = QAction(tr("Save As..."), self) self.actionSaveAs.setShortcut("Ctrl+Shift+S") self.actionAbout = QAction(tr("About moneyGuru"), self) self.actionToggleReconciliationMode = QAction(tr("Toggle Reconciliation Mode"), self) self.actionToggleReconciliationMode.setShortcut("Ctrl+Shift+R") self.actionToggleAccountExclusion = QAction(tr("Toggle Exclusion Status of Account"), self) self.actionToggleAccountExclusion.setShortcut("Ctrl+Shift+X") self.actionShowSchedules = QAction(tr("Schedules"), self) self.actionShowSchedules.setShortcut("Ctrl+4") self.actionShowSchedules.setIcon(QIcon(QPixmap(':/schedules_48'))) self.actionReconcileSelected = QAction(tr("Reconcile Selection"), self) self.actionReconcileSelected.setShortcut("Ctrl+R") self.actionMakeScheduleFromSelected = QAction(tr("Make Schedule from Selected"), self) self.actionMakeScheduleFromSelected.setShortcut("Ctrl+M") self.actionShowPreferences = QAction(tr("Preferences..."), self) self.actionPrint = QAction(tr("Print..."), self) self.actionPrint.setShortcut("Ctrl+P") self.actionQuit = QAction(tr("Quit moneyGuru"), self) self.actionQuit.setShortcut("Ctrl+Q") self.actionUndo = QAction(tr("Undo"), self) self.actionUndo.setShortcut("Ctrl+Z") self.actionRedo = QAction(tr("Redo"), self) self.actionRedo.setShortcut("Ctrl+Y") self.actionShowHelp = QAction(tr("moneyGuru Help"), self) self.actionShowHelp.setShortcut("F1") self.actionDuplicateTransaction = QAction(tr("Duplicate Transaction"), self) self.actionDuplicateTransaction.setShortcut("Ctrl+D") self.actionJumpToAccount = QAction(tr("Jump to Account..."), self) self.actionJumpToAccount.setShortcut("Ctrl+Shift+A") self.actionNewTab = QAction(tr("New Tab"), self) self.actionNewTab.setShortcut("Ctrl+T") self.actionCloseTab = QAction(tr("Close Tab"), self) self.actionCloseTab.setShortcut("Ctrl+W") self.menuFile.addAction(self.actionNewDocument) self.menuFile.addAction(self.actionNewTab) self.menuFile.addAction(self.actionOpenDocument) self.menuFile.addAction(self.menuOpenRecent.menuAction()) self.menuFile.addAction(self.actionImport) self.menuFile.addSeparator() self.menuFile.addAction(self.actionCloseTab) self.menuFile.addAction(self.actionSave) self.menuFile.addAction(self.actionSaveAs) self.menuFile.addAction(self.actionExport) self.menuFile.addAction(self.actionPrint) self.menuFile.addAction(self.actionQuit) self.menuView.addAction(self.actionShowNetWorth) self.menuView.addAction(self.actionShowProfitLoss) self.menuView.addAction(self.actionShowTransactions) self.menuView.addAction(self.actionShowSchedules) self.menuView.addAction(self.actionShowPreviousView) self.menuView.addAction(self.actionShowNextView) self.menuView.addAction(self.menuDateRange.menuAction()) self.menuView.addAction(self.actionShowPreferences) self.menuView.addAction(self.actionToggleGraph) self.menuView.addAction(self.actionTogglePieChart) self.menuEdit.addAction(self.actionNewItem) self.menuEdit.addAction(self.actionNewAccountGroup) self.menuEdit.addAction(self.actionDeleteItem) self.menuEdit.addAction(self.actionEditItem) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionMoveUp) self.menuEdit.addAction(self.actionMoveDown) self.menuEdit.addAction(self.actionDuplicateTransaction) self.menuEdit.addAction(self.actionMakeScheduleFromSelected) self.menuEdit.addAction(self.actionReconcileSelected) self.menuEdit.addAction(self.actionToggleReconciliationMode) self.menuEdit.addAction(self.actionToggleAccountExclusion) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionShowSelectedAccount) self.menuEdit.addAction(self.actionNavigateBack) self.menuEdit.addAction(self.actionJumpToAccount) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionUndo) self.menuEdit.addAction(self.actionRedo) self.menuHelp.addAction(self.actionShowHelp) self.menuHelp.addAction(self.actionAbout) mainmenus = [self.menuFile, self.menuEdit, self.menuView, self.menuHelp] for menu in mainmenus: self.menubar.addAction(menu.menuAction()) setAccelKeys(menu) setAccelKeys(self.menubar) self.tabBar.setMovable(True) self.tabBar.setTabsClosable(True) self.tabBar.setExpanding(False) seq = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Right) self._shortcutNextTab = QShortcut(seq, self) seq = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Left) self._shortcutPrevTab = QShortcut(seq, self) def _bindSignals(self): self.newItemButton.clicked.connect(self.actionNewItem.trigger) self.deleteItemButton.clicked.connect(self.actionDeleteItem.trigger) self.editItemButton.clicked.connect(self.actionEditItem.trigger) self.graphVisibilityButton.clicked.connect(self.actionToggleGraph.trigger) self.piechartVisibilityButton.clicked.connect(self.actionTogglePieChart.trigger) self.columnsVisibilityButton.clicked.connect(self.columnsVisibilityButtonClicked) self.recentDocuments.mustOpenItem.connect(self.open) self.tabBar.currentChanged.connect(self.currentTabChanged) self.tabBar.tabCloseRequested.connect(self.tabCloseRequested) self.tabBar.tabMoved.connect(self.tabMoved) self.tabBar.plusClicked.connect(self.model.new_tab) # Views self.actionShowNetWorth.triggered.connect(self.showNetWorthTriggered) self.actionShowProfitLoss.triggered.connect(self.showProfitLossTriggered) self.actionShowTransactions.triggered.connect(self.showTransactionsTriggered) self.actionShowSchedules.triggered.connect(self.showSchedulesTriggered) self.actionShowPreviousView.triggered.connect(self.showPreviousViewTriggered) self.actionShowNextView.triggered.connect(self.showNextViewTriggered) self.actionShowPreferences.triggered.connect(self.app.showPreferences) self.actionToggleGraph.triggered.connect(self.toggleGraphTriggered) self.actionTogglePieChart.triggered.connect(self.togglePieChartTriggered) # Document Edition self.actionNewItem.triggered.connect(self.newItemTriggered) self.actionNewAccountGroup.triggered.connect(self.newAccountGroupTriggered) self.actionDeleteItem.triggered.connect(self.deleteItemTriggered) self.actionEditItem.triggered.connect(self.editItemTriggered) self.actionMoveUp.triggered.connect(self.moveUpTriggered) self.actionMoveDown.triggered.connect(self.moveDownTriggered) self.actionDuplicateTransaction.triggered.connect(self.model.duplicate_item) self.actionUndo.triggered.connect(self.model.undo) self.actionRedo.triggered.connect(self.model.redo) # Open / Save / Import / Export / New self.actionNewDocument.triggered.connect(self.new) self.actionOpenDocument.triggered.connect(self.openDocument) self.actionImport.triggered.connect(self.importDocument) self.actionSave.triggered.connect(self.save) self.actionSaveAs.triggered.connect(self.saveAs) self.actionExport.triggered.connect(self.model.export) # Misc self.actionNewTab.triggered.connect(self.model.new_tab) self.actionCloseTab.triggered.connect(self.closeTabTriggered) self.actionShowSelectedAccount.triggered.connect(self.model.show_account) self.actionNavigateBack.triggered.connect(self.navigateBackTriggered) self.actionJumpToAccount.triggered.connect(self.jumpToAccountTriggered) self.actionMakeScheduleFromSelected.triggered.connect(self.makeScheduleFromSelectedTriggered) self.actionReconcileSelected.triggered.connect(self.reconcileSelectedTriggered) self.actionToggleReconciliationMode.triggered.connect(self.toggleReconciliationModeTriggered) self.actionToggleAccountExclusion.triggered.connect(self.toggleAccountExclusionTriggered) self.actionPrint.triggered.connect(self._print) self.actionShowHelp.triggered.connect(self.app.showHelp) self.actionAbout.triggered.connect(self.aboutTriggered) self.actionQuit.triggered.connect(self.close) # Extra Shortcuts self._shortcutNextTab.activated.connect(self.showNextViewTriggered) self._shortcutPrevTab.activated.connect(self.showPreviousViewTriggered) # --- QWidget overrides def closeEvent(self, event): if self.confirmDestructiveAction(): event.accept() else: event.ignore() # --- Private def _print(self): dialog = QPrintDialog(self) if dialog.exec_() != QPrintDialog.Accepted: return printer = dialog.printer() currentView = self.mainView.currentWidget() viewPrinter = ViewPrinter(printer, currentView) currentView.fitViewsForPrint(viewPrinter) viewPrinter.render() def _getViewforPane(self, pane_type, pane_view): if pane_view in self.model2view: view = self.model2view[pane_view] else: view = PANETYPE2VIEWCLASS[pane_type](model=pane_view, mainwindow=self) self.model2view[pane_view] = view self.mainView.addWidget(view) view.restoreSubviewsSize() return view def _setTabIndex(self, index): if not self.tabBar.count(): return self.tabBar.setCurrentIndex(index) self._updateActionsState() pane_type = self.model.pane_type(index) pane_view = self.model.pane_view(index) view = self._getViewforPane(pane_type, pane_view) self.mainView.setCurrentWidget(view) view.setFocus() def _activeView(self): paneIndex = self.model.current_pane_index return self.model.pane_view(paneIndex) def _updateActionsState(self): # Updates enable/disable checked/unchecked state of all actions. These state can change # under various conditions: main view change, date range type change and when reconciliation # mode is toggled # Determine what actions are enabled view = self._activeView() viewType = view.VIEW_TYPE isSheet = viewType in {PaneType.NetWorth, PaneType.Profit} isTransactionOrEntryTable = viewType in {PaneType.Transaction, PaneType.Account} canToggleReconciliation = viewType == PaneType.Account and view.can_toggle_reconciliation_mode newItemLabel = { PaneType.NetWorth: tr("New Account"), PaneType.Profit: tr("New Account"), PaneType.Transaction: tr("New Transaction"), PaneType.Account: tr("New Transaction"), PaneType.Schedule: tr("New Schedule"), PaneType.GeneralLedger: tr("New Transaction"), }.get(viewType, tr("New Item")) # XXX make "New Item" disabled self.actionNewItem.setText(newItemLabel) self.actionNewAccountGroup.setEnabled(isSheet) self.actionMoveDown.setEnabled(isTransactionOrEntryTable) self.actionMoveUp.setEnabled(isTransactionOrEntryTable) self.actionDuplicateTransaction.setEnabled(isTransactionOrEntryTable) self.actionMakeScheduleFromSelected.setEnabled(isTransactionOrEntryTable) self.actionReconcileSelected.setEnabled(viewType == PaneType.Account and view.reconciliation_mode) self.actionShowNextView.setEnabled(self.model.current_pane_index < self.model.pane_count-1) self.actionShowPreviousView.setEnabled(self.model.current_pane_index > 0) self.actionShowSelectedAccount.setEnabled(isSheet or isTransactionOrEntryTable) self.actionNavigateBack.setEnabled(viewType == PaneType.Account) self.actionToggleReconciliationMode.setEnabled(canToggleReconciliation) self.actionToggleAccountExclusion.setEnabled(isSheet) def _updateUndoActions(self): if self.doc.can_undo(): self.actionUndo.setEnabled(True) self.actionUndo.setText(tr("Undo {0}").format(self.doc.undo_description())) else: self.actionUndo.setEnabled(False) self.actionUndo.setText(tr("Undo")) if self.doc.can_redo(): self.actionRedo.setEnabled(True) self.actionRedo.setText(tr("Redo {0}").format(self.doc.redo_description())) else: self.actionRedo.setEnabled(False) self.actionRedo.setText(tr("Redo")) # --- Actions # Document open/save/close def confirmDestructiveAction(self): # Asks whether the user wants to continue before continuing with an action that will replace # the current document. Will save the document as needed. Returns True if the action can # continue. if not self.model.document.is_dirty(): return True title = tr("Unsaved Document") msg = tr("Do you want to save your changes before continuing?") buttons = QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard result = QMessageBox.question(self.app.mainWindow, title, msg, buttons) if result == QMessageBox.Save: self.save() if self.model.document.is_dirty(): # "save as" was cancelled return False else: return True elif result == QMessageBox.Cancel: return False elif result == QMessageBox.Discard: return True def new(self): if not self.confirmDestructiveAction(): return self.model.close() self.documentPath = None self.model.clear() self.documentPathChanged() def open(self, docpath, initial=False): # initial flag is true when open() is called at the document's initialization. When that's # the case, we need to create a new document when we fail opening this one. if not self.confirmDestructiveAction(): return self.model.close() try: self.model.load_from_xml(docpath) self.documentPath = docpath except FileFormatError as e: QMessageBox.warning(self.app.mainWindow, tr("Cannot load file"), str(e)) if initial: self.new() self.documentPathChanged() self.recentDocuments.insertItem(docpath) def openDocument(self): title = tr("Select a document to load") filters = tr("moneyGuru Documents (*.moneyguru)") docpath, filetype = QFileDialog.getOpenFileName(self.app.mainWindow, title, '', filters) if docpath: self.open(docpath) def save(self): if self.documentPath is not None: self.model.save_to_xml(self.documentPath) else: self.saveAs() def saveAs(self): title = tr("Save As") filters = tr("moneyGuru Documents (*.moneyguru)") docpath = QFileDialog.getSaveFileName(self.app.mainWindow, title, '', filters)[0] if docpath: if not docpath.endswith('.moneyguru'): docpath += '.moneyguru' self.model.save_to_xml(docpath) self.documentPath = docpath self.documentPathChanged() self.recentDocuments.insertItem(docpath) # Views def showNetWorthTriggered(self): self.model.select_pane_of_type(PaneType.NetWorth) def showProfitLossTriggered(self): self.model.select_pane_of_type(PaneType.Profit) def showTransactionsTriggered(self): self.model.select_pane_of_type(PaneType.Transaction) def showSchedulesTriggered(self): self.model.select_pane_of_type(PaneType.Schedule) def showPreviousViewTriggered(self): self.model.select_previous_view() def showNextViewTriggered(self): self.model.select_next_view() # Document Edition def newItemTriggered(self): self.model.new_item() def newAccountGroupTriggered(self): self.model.new_group() def deleteItemTriggered(self): self.model.delete_item() def editItemTriggered(self): self.model.edit_item() def moveUpTriggered(self): self.model.move_up() def moveDownTriggered(self): self.model.move_down() # Misc def closeTabTriggered(self): self.model.close_pane(self.model.current_pane_index) def navigateBackTriggered(self): self.model.navigate_back() def jumpToAccountTriggered(self): self.model.jump_to_account() def makeScheduleFromSelectedTriggered(self): self.model.make_schedule_from_selected() def reconcileSelectedTriggered(self): self._activeView().etable.toggle_reconciled() def toggleReconciliationModeTriggered(self): self._activeView().toggle_reconciliation_mode() self._updateActionsState() def toggleAccountExclusionTriggered(self): viewType = self.model.pane_type(self.model.current_pane_index) if viewType in {PaneType.NetWorth, PaneType.Profit}: self._activeView().sheet.toggle_excluded() def toggleGraphTriggered(self): self.model.toggle_area_visibility(PaneArea.BottomGraph) def togglePieChartTriggered(self): self.model.toggle_area_visibility(PaneArea.RightChart) def columnsVisibilityButtonClicked(self): items = self.model.column_menu_items() if not items: return menu = QMenu() for i, (display, marked) in enumerate(items): action = menu.addAction(display) action.setCheckable(True) action.setChecked(marked) action.setData(i) action.triggered.connect(self.columnsMenuItemWasClicked) self._columnMenuHolder = menu # we need to hold a reference to it while it popups button = self.columnsVisibilityButton menu.popup(button.parentWidget().mapToGlobal(button.geometry().topLeft())) def columnsMenuItemWasClicked(self): action = self.sender() if action is not None: index = action.data() self.model.toggle_column_menu_item(index) def aboutTriggered(self): self.app.showAboutBox() def importDocument(self): title = tr("Select a document to import") filters = tr("Supported files (*.moneyguru *.ofx *.qfx *.qif *.csv *.txt);;All files (*)") docpath, filetype = QFileDialog.getOpenFileName(self.app.mainWindow, title, '', filters) # There's a strange glitch under GNOME where, right after the dialog is gone, the main # window isn't the active window, but it will become active if we give it enough time. If we # start showing the import window before that happens, we'll end up with an import window # under the main window, which is bad. Therefore, we process events until this happens. We # do this in a big forloop instead of a while to avoid a possible infinite loop. for i in range(10000): if self.app.mainWindow.isActiveWindow(): break QApplication.processEvents() if docpath: try: self.model.parse_file_for_import(docpath) except FileFormatError as e: QMessageBox.warning(self.app.mainWindow, tr("Cannot import file"), str(e)) # --- Other Signals def currentTabChanged(self, index): self.model.current_pane_index = index self._setTabIndex(index) def documentPathChanged(self): if self.documentPath: title = "moneyGuru ({})".format(self.documentPath) else: title = "moneyGuru" self.setWindowTitle(title) def tabCloseRequested(self, index): self.model.close_pane(index) def tabMoved(self, fromIndex, toIndex): # We don't refresh panes because tabMoved is apparently now called *during* drag operations. # If we start a full pane refresh during a drag operation, we segfault. self.model.move_pane(fromIndex, toIndex, refresh_panes=False) # --- document model --> view def query_for_schedule_scope(self): if QApplication.keyboardModifiers() & Qt.ShiftModifier: return ScheduleScope.Global if not self.app.model.show_schedule_scope_dialog: return ScheduleScope.Local dialog = ScheduleScopeDialog(self) return dialog.queryForScope() # --- model --> view def change_current_pane(self): self._setTabIndex(self.model.current_pane_index) def get_panel_view(self, model): if isinstance(model, CustomDateRangePanelModel): return CustomDateRangePanel(model, self) elif isinstance(model, CSVOptionsModel): return CSVOptionsWindow(model, self) elif isinstance(model, ImportWindowModel): return ImportWindow(model, self, self.app.prefs) else: return ExportPanel(model, self) def refresh_panes(self): while self.tabBar.count() < self.model.pane_count: self.tabBar.addTab('') for i in range(self.model.pane_count): pane_label = self.model.pane_label(i) pane_label = escapeamp(pane_label) self.tabBar.setTabText(i, pane_label) pane_type = self.model.pane_type(i) pane_view = self.model.pane_view(i) # Ensure that the view's "view" has been created and bound self._getViewforPane(pane_type, pane_view) iconname = PANETYPE2ICON.get(pane_type) icon = QIcon(QPixmap(':/{0}'.format(iconname))) if iconname else QIcon() self.tabBar.setTabIcon(i, icon) # It's important that we proceed with tab removal *after* we've completed tab initialization. # We're walking on eggshells here. refresh_panes() can be called in multiple situations, one # of them is during the opening of a document. When that happens when another document was # previously opened, all views' model are uninitalized and don't have their "view" attribute # set yet. If we proceed with the setCurrentIndex() call below before _getViewforPane() # could be called above, we get a crash. if self.tabBar.currentIndex() >= self.model.pane_count: # Normally, we don't touch the tabBar index here and wait for change_current_pane, # but when we remove tabs, it's possible that currentTabChanged end up being called and # then the tab selection is bugged. I tried disconnecting/reconnecting the signal, but # this is buggy. So when a selected tab is about to be removed and is out of bounds, # we change the selection to the last index in the model. We don't use # self.model.current_pane_index because in some cases, it's -1 and prevents this crash # preventer from preventing its crash. self.tabBar.setCurrentIndex(self.model.pane_count - 1) while self.tabBar.count() > self.model.pane_count: self.tabBar.removeTab(self.tabBar.count()-1) self.tabBar.setTabsClosable(self.model.pane_count > 1) def refresh_status_line(self): self.statusLabel.setText(self.model.status_line) def refresh_undo_actions(self): self._updateUndoActions() def restore_window_frame(self, frame): self.setGeometry(*frame) def save_window_frame(self): r = self.geometry() return (r.x(), r.y(), r.width(), r.height()) def show_message(self, msg): title = tr("Warning") QMessageBox.warning(self, title, msg) def update_area_visibility(self): hidden = self.model.hidden_areas graphimg = ':/graph_visibility_{}_16'.format('off' if PaneArea.BottomGraph in hidden else 'on') pieimg = ':/piechart_visibility_{}_16'.format('off' if PaneArea.RightChart in hidden else 'on') self.graphVisibilityButton.setIcon(QIcon(QPixmap(graphimg))) self.piechartVisibilityButton.setIcon(QIcon(QPixmap(pieimg))) def view_closed(self, index): self.tabBar.removeTab(index) self.tabBar.setTabsClosable(self.model.pane_count > 1)
class ParamsByType(QWidget, MooseWidget): """ Has a QComboBox for the different allowed types. On switching type a new ParamsByGroup is shown. """ needBlockList = pyqtSignal(list) blockRenamed = pyqtSignal(object, str) changed = pyqtSignal() def __init__(self, block, type_block_map, **kwds): """ Constructor. Input: block[BlockInfo]: The block to show. """ super(ParamsByType, self).__init__(**kwds) self.block = block self.combo = QComboBox() self.types = [] self.type_params_map = {} self.type_block_map = type_block_map self.table_stack = QStackedWidget() self.type_table_map = {} for t in sorted(self.block.types.keys()): self.types.append(t) params_list = [] for p in self.block.parameters_list: params_list.append(self.block.parameters[p]) t_block = self.block.types[t] for p in t_block.parameters_list: params_list.append(t_block.parameters[p]) self.type_params_map[t] = params_list self.combo.addItems(sorted(self.block.types.keys())) self.combo.currentTextChanged.connect(self.setBlockType) self.top_layout = WidgetUtils.addLayout(vertical=True) self.top_layout.addWidget(self.combo) self.top_layout.addWidget(self.table_stack) self.setLayout(self.top_layout) self.user_params = [] self.setDefaultBlockType() self.setup() def _syncUserParams(self, current, to): """ Sync user added parameters that are on the main block into each type ParamsByGroup. Input: current[ParamsByGroup]: The current group parameter table to[ParamsByGroup]: The new group parameter table """ ct = current.findTable("Main") tot = to.findTable("Main") if not ct or not tot or ct == tot: return tot.removeUserParams() params = ct.getUserParams() tot.addUserParams(params) to.syncParamsFrom(current) # Make sure the name parameter stays the same idx = ct.findRow("Name") if idx >= 0: name = ct.item(idx, 1).text() idx = tot.findRow("Name") if idx >= 0: tot.item(idx, 1).setText(name) def currentType(self): return self.combo.currentText() def save(self): """ Look at the user params in self.block.parameters. update the type tables Save type on block """ t = self.getTable() if t: t.save() self.block.setBlockType(self.combo.currentText()) def reset(self): t = self.getTable() t.reset() def getOrCreateTypeTable(self, type_name): """ Gets the table for the type name or create it if it doesn't exist. Input: type_name[str]: Name of the type Return: ParamsByGroup: The parameters corresponding to the type """ t = self.type_table_map.get(type_name) if t: return t t = ParamsByGroup(self.block, self.type_params_map.get(type_name, self.block.orderedParameters()), self.type_block_map) t.needBlockList.connect(self.needBlockList) t.blockRenamed.connect(self.blockRenamed) t.changed.connect(self.changed) self.type_table_map[type_name] = t self.table_stack.addWidget(t) return t def setDefaultBlockType(self): param = self.block.getParamInfo("type") if param and param.value: self.setBlockType(param.value) elif self.block.types: self.setBlockType(sorted(self.block.types.keys())[0]) def setBlockType(self, type_name): if type_name not in self.block.types: return t = self.getOrCreateTypeTable(type_name) t.updateWatchers() self.combo.blockSignals(True) self.combo.setCurrentText(type_name) self.combo.blockSignals(False) t.updateType(type_name) current = self.table_stack.currentWidget() self._syncUserParams(current, t) self.table_stack.setCurrentWidget(t) self.changed.emit() def addUserParam(self, param): t = self.table_stack.currentWidget() t.addUserParam(param) def setWatchedBlockList(self, path, children): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.setWatchedBlockList(path, children) def updateWatchers(self): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) t.updateWatchers() def getTable(self): return self.table_stack.currentWidget() def paramValue(self, name): for i in range(self.table_stack.count()): t = self.table_stack.widget(i) if t.paramValue(name): return t.paramValue(name)
class SettingsDialog(QDialog): worker = None config = None configfile = None saved = QtCore.pyqtSignal() def __init__(self, parent, worker, config, configfile): QDialog.__init__(self, parent) self.worker = worker self.config = config self.configfile = configfile self.setStyleSheet("QGroupBox { font-weight: bold; } ") self.setWindowTitle('Settings') layout = QGridLayout() # Categories self.category_list = QListWidget() category_media = QListWidgetItem(getIcon('media-playback-start'), 'Media', self.category_list) category_sync = QListWidgetItem(getIcon('view-refresh'), 'Sync', self.category_list) category_ui = QListWidgetItem(getIcon('window-new'), 'User Interface', self.category_list) category_theme = QListWidgetItem(getIcon('applications-graphics'), 'Theme', self.category_list) self.category_list.setSelectionMode(QAbstractItemView.SingleSelection) self.category_list.setCurrentRow(0) self.category_list.setMaximumWidth(self.category_list.sizeHintForColumn(0) + 15) self.category_list.setFocus() self.category_list.currentItemChanged.connect(self.s_switch_page) # Media tab page_media = QWidget() page_media_layout = QVBoxLayout() page_media_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Media settings g_media = QGroupBox('Media settings') g_media.setFlat(True) g_media_layout = QFormLayout() self.tracker_enabled = QCheckBox() self.tracker_enabled.toggled.connect(self.tracker_type_change) self.tracker_type = QComboBox() for (n, label) in utils.available_trackers: self.tracker_type.addItem(label, n) self.tracker_type.currentIndexChanged.connect(self.tracker_type_change) self.tracker_interval = QSpinBox() self.tracker_interval.setRange(5, 1000) self.tracker_interval.setMaximumWidth(60) self.tracker_process = QLineEdit() self.tracker_update_wait = QSpinBox() self.tracker_update_wait.setRange(0, 1000) self.tracker_update_wait.setMaximumWidth(60) self.tracker_update_close = QCheckBox() self.tracker_update_prompt = QCheckBox() self.tracker_not_found_prompt = QCheckBox() g_media_layout.addRow('Enable tracker', self.tracker_enabled) g_media_layout.addRow('Tracker type', self.tracker_type) g_media_layout.addRow('Tracker interval (seconds)', self.tracker_interval) g_media_layout.addRow('Process name (regex)', self.tracker_process) g_media_layout.addRow('Wait before updating (seconds)', self.tracker_update_wait) g_media_layout.addRow('Wait until the player is closed', self.tracker_update_close) g_media_layout.addRow('Ask before updating', self.tracker_update_prompt) g_media_layout.addRow('Ask to add new shows', self.tracker_not_found_prompt) g_media.setLayout(g_media_layout) # Group: Plex settings g_plex = QGroupBox('Plex Media Server') g_plex.setFlat(True) self.plex_host = QLineEdit() self.plex_port = QLineEdit() self.plex_user = QLineEdit() self.plex_passw = QLineEdit() self.plex_passw.setEchoMode(QLineEdit.Password) self.plex_obey_wait = QCheckBox() g_plex_layout = QGridLayout() g_plex_layout.addWidget(QLabel('Host and Port'), 0, 0, 1, 1) g_plex_layout.addWidget(self.plex_host, 0, 1, 1, 1) g_plex_layout.addWidget(self.plex_port, 0, 2, 1, 2) g_plex_layout.addWidget(QLabel('Use "wait before updating" time'), 1, 0, 1, 1) g_plex_layout.addWidget(self.plex_obey_wait, 1, 2, 1, 1) g_plex_layout.addWidget(QLabel('myPlex login (claimed server)'), 2, 0, 1, 1) g_plex_layout.addWidget(self.plex_user, 2, 1, 1, 1) g_plex_layout.addWidget(self.plex_passw, 2, 2, 1, 2) g_plex.setLayout(g_plex_layout) # Group: Library g_playnext = QGroupBox('Library') g_playnext.setFlat(True) self.player = QLineEdit() self.player_browse = QPushButton('Browse...') self.player_browse.clicked.connect(self.s_player_browse) lbl_searchdirs = QLabel('Media directories') lbl_searchdirs.setAlignment(QtCore.Qt.AlignTop) self.searchdirs = QListWidget() self.searchdirs_add = QPushButton('Add...') self.searchdirs_add.clicked.connect(self.s_searchdirs_add) self.searchdirs_remove = QPushButton('Remove') self.searchdirs_remove.clicked.connect(self.s_searchdirs_remove) self.searchdirs_buttons = QVBoxLayout() self.searchdirs_buttons.setAlignment(QtCore.Qt.AlignTop) self.searchdirs_buttons.addWidget(self.searchdirs_add) self.searchdirs_buttons.addWidget(self.searchdirs_remove) self.searchdirs_buttons.addWidget(QSplitter()) self.library_autoscan = QCheckBox() self.scan_whole_list = QCheckBox() self.library_full_path = QCheckBox() g_playnext_layout = QGridLayout() g_playnext_layout.addWidget(QLabel('Player'), 0, 0, 1, 1) g_playnext_layout.addWidget(self.player, 0, 1, 1, 1) g_playnext_layout.addWidget(self.player_browse, 0, 2, 1, 1) g_playnext_layout.addWidget(lbl_searchdirs, 1, 0, 1, 1) g_playnext_layout.addWidget(self.searchdirs, 1, 1, 1, 1) g_playnext_layout.addLayout(self.searchdirs_buttons, 1, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Rescan Library at startup'), 2, 0, 1, 2) g_playnext_layout.addWidget(self.library_autoscan, 2, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Scan through whole list'), 3, 0, 1, 2) g_playnext_layout.addWidget(self.scan_whole_list, 3, 2, 1, 1) g_playnext_layout.addWidget(QLabel('Take subdirectory name into account'), 4, 0, 1, 2) g_playnext_layout.addWidget(self.library_full_path, 4, 2, 1, 1) g_playnext.setLayout(g_playnext_layout) # Media form page_media_layout.addWidget(g_media) page_media_layout.addWidget(g_plex) page_media_layout.addWidget(g_playnext) page_media.setLayout(page_media_layout) # Sync tab page_sync = QWidget() page_sync_layout = QVBoxLayout() page_sync_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Autoretrieve g_autoretrieve = QGroupBox('Autoretrieve') g_autoretrieve.setFlat(True) self.autoretrieve_off = QRadioButton('Disabled') self.autoretrieve_always = QRadioButton('Always at start') self.autoretrieve_days = QRadioButton('After n days') self.autoretrieve_days.toggled.connect(self.s_autoretrieve_days) self.autoretrieve_days_n = QSpinBox() self.autoretrieve_days_n.setRange(1, 100) g_autoretrieve_layout = QGridLayout() g_autoretrieve_layout.setColumnStretch(0, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_off, 0, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_always, 1, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_days, 2, 0, 1, 1) g_autoretrieve_layout.addWidget(self.autoretrieve_days_n, 2, 1, 1, 1) g_autoretrieve.setLayout(g_autoretrieve_layout) # Group: Autosend g_autosend = QGroupBox('Autosend') g_autosend.setFlat(True) self.autosend_off = QRadioButton('Disabled') self.autosend_always = QRadioButton('Immediately after every change') self.autosend_minutes = QRadioButton('After n minutes') self.autosend_minutes.toggled.connect(self.s_autosend_minutes) self.autosend_minutes_n = QSpinBox() self.autosend_minutes_n.setRange(1, 1000) self.autosend_size = QRadioButton('After the queue reaches n items') self.autosend_size.toggled.connect(self.s_autosend_size) self.autosend_size_n = QSpinBox() self.autosend_size_n.setRange(2, 20) self.autosend_at_exit = QCheckBox('At exit') g_autosend_layout = QGridLayout() g_autosend_layout.setColumnStretch(0, 1) g_autosend_layout.addWidget(self.autosend_off, 0, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_always, 1, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_minutes, 2, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_minutes_n, 2, 1, 1, 1) g_autosend_layout.addWidget(self.autosend_size, 3, 0, 1, 1) g_autosend_layout.addWidget(self.autosend_size_n, 3, 1, 1, 1) g_autosend_layout.addWidget(self.autosend_at_exit, 4, 0, 1, 1) g_autosend.setLayout(g_autosend_layout) # Group: Extra g_extra = QGroupBox('Additional options') g_extra.setFlat(True) self.auto_status_change = QCheckBox('Change status automatically') self.auto_status_change.toggled.connect(self.s_auto_status_change) self.auto_status_change_if_scored = QCheckBox('Change status automatically only if scored') self.auto_date_change = QCheckBox('Change start and finish dates automatically') g_extra_layout = QVBoxLayout() g_extra_layout.addWidget(self.auto_status_change) g_extra_layout.addWidget(self.auto_status_change_if_scored) g_extra_layout.addWidget(self.auto_date_change) g_extra.setLayout(g_extra_layout) # Sync layout page_sync_layout.addWidget(g_autoretrieve) page_sync_layout.addWidget(g_autosend) page_sync_layout.addWidget(g_extra) page_sync.setLayout(page_sync_layout) # UI tab page_ui = QWidget() page_ui_layout = QFormLayout() page_ui_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Icon g_icon = QGroupBox('Notification Icon') g_icon.setFlat(True) self.tray_icon = QCheckBox('Show tray icon') self.tray_icon.toggled.connect(self.s_tray_icon) self.close_to_tray = QCheckBox('Close to tray') self.start_in_tray = QCheckBox('Start minimized to tray') self.tray_api_icon = QCheckBox('Use API icon as tray icon') self.notifications = QCheckBox('Show notification when tracker detects new media') g_icon_layout = QVBoxLayout() g_icon_layout.addWidget(self.tray_icon) g_icon_layout.addWidget(self.close_to_tray) g_icon_layout.addWidget(self.start_in_tray) g_icon_layout.addWidget(self.tray_api_icon) g_icon_layout.addWidget(self.notifications) g_icon.setLayout(g_icon_layout) # Group: Window g_window = QGroupBox('Window') g_window.setFlat(True) self.remember_geometry = QCheckBox('Remember window size and position') self.remember_columns = QCheckBox('Remember column layouts and widths') self.columns_per_api = QCheckBox('Use different visible columns per API') g_window_layout = QVBoxLayout() g_window_layout.addWidget(self.remember_geometry) g_window_layout.addWidget(self.remember_columns) g_window_layout.addWidget(self.columns_per_api) g_window.setLayout(g_window_layout) # Group: Lists g_lists = QGroupBox('Lists') g_lists.setFlat(True) self.filter_bar_position = QComboBox() filter_bar_positions = [(FilterBar.PositionHidden, 'Hidden'), (FilterBar.PositionAboveLists, 'Above lists'), (FilterBar.PositionBelowLists, 'Below lists')] for (n, label) in filter_bar_positions: self.filter_bar_position.addItem(label, n) self.inline_edit = QCheckBox('Enable in-line editing') g_lists_layout = QFormLayout() g_lists_layout.addRow('Filter bar position:', self.filter_bar_position) g_lists_layout.addRow(self.inline_edit) g_lists.setLayout(g_lists_layout) # UI layout page_ui_layout.addWidget(g_icon) page_ui_layout.addWidget(g_window) page_ui_layout.addWidget(g_lists) page_ui.setLayout(page_ui_layout) # Theming tab page_theme = QWidget() page_theme_layout = QFormLayout() page_theme_layout.setAlignment(QtCore.Qt.AlignTop) # Group: Episode Bar g_ep_bar = QGroupBox('Episode Bar') g_ep_bar.setFlat(True) self.ep_bar_style = QComboBox() ep_bar_styles = [(ShowsTableDelegate.BarStyleBasic, 'Basic'), (ShowsTableDelegate.BarStyle04, 'Trackma'), (ShowsTableDelegate.BarStyleHybrid, 'Hybrid')] for (n, label) in ep_bar_styles: self.ep_bar_style.addItem(label, n) self.ep_bar_style.currentIndexChanged.connect(self.s_ep_bar_style) self.ep_bar_text = QCheckBox('Show text label') g_ep_bar_layout = QFormLayout() g_ep_bar_layout.addRow('Style:', self.ep_bar_style) g_ep_bar_layout.addRow(self.ep_bar_text) g_ep_bar.setLayout(g_ep_bar_layout) # Group: Colour scheme g_scheme = QGroupBox('Color Scheme') g_scheme.setFlat(True) col_tabs = [('rows', '&Row highlights'), ('progress', '&Progress widget')] self.colors = {} self.colors['rows'] = [('is_playing', 'Playing'), ('is_queued', 'Queued'), ('new_episode', 'New Episode'), ('is_airing', 'Airing'), ('not_aired', 'Unaired')] self.colors['progress'] = [('progress_bg', 'Background'), ('progress_fg', 'Watched bar'), ('progress_sub_bg', 'Aired episodes'), ('progress_sub_fg', 'Stored episodes'), ('progress_complete', 'Complete')] self.color_buttons = [] self.syscolor_buttons = [] g_scheme_layout = QGridLayout() tw_scheme = QTabWidget() for (key, tab_title) in col_tabs: page = QFrame() page_layout = QGridLayout() col = 0 # Generate widgets from the keys and values for (key, label) in self.colors[key]: self.color_buttons.append(QPushButton()) # self.color_buttons[-1].setStyleSheet('background-color: ' + getColor(self.config['colors'][key]).name()) self.color_buttons[-1].setFocusPolicy(QtCore.Qt.NoFocus) self.color_buttons[-1].clicked.connect(self.s_color_picker(key, False)) self.syscolor_buttons.append(QPushButton('System Colors')) self.syscolor_buttons[-1].clicked.connect(self.s_color_picker(key, True)) page_layout.addWidget(QLabel(label), col, 0, 1, 1) page_layout.addWidget(self.color_buttons[-1], col, 1, 1, 1) page_layout.addWidget(self.syscolor_buttons[-1], col, 2, 1, 1) col += 1 page.setLayout(page_layout) tw_scheme.addTab(page, tab_title) g_scheme_layout.addWidget(tw_scheme) g_scheme.setLayout(g_scheme_layout) # UI layout page_theme_layout.addWidget(g_ep_bar) page_theme_layout.addWidget(g_scheme) page_theme.setLayout(page_theme_layout) # Content self.contents = QStackedWidget() self.contents.addWidget(page_media) self.contents.addWidget(page_sync) self.contents.addWidget(page_ui) self.contents.addWidget(page_theme) if pyqt_version is not 5: self.contents.layout().setMargin(0) # Bottom buttons bottombox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel ) bottombox.accepted.connect(self.s_save) bottombox.button(QDialogButtonBox.Apply).clicked.connect(self._save) bottombox.rejected.connect(self.reject) # Main layout finish layout.addWidget(self.category_list, 0, 0, 1, 1) layout.addWidget(self.contents, 0, 1, 1, 1) layout.addWidget(bottombox, 1, 0, 1, 2) layout.setColumnStretch(1, 1) self._load() self.update_colors() self.setLayout(layout) def _add_dir(self, path): self.searchdirs.addItem(path) def _load(self): engine = self.worker.engine tracker_type = self.tracker_type.findData(engine.get_config('tracker_type')) autoretrieve = engine.get_config('autoretrieve') autosend = engine.get_config('autosend') self.tracker_enabled.setChecked(engine.get_config('tracker_enabled')) self.tracker_type.setCurrentIndex(max(0, tracker_type)) self.tracker_interval.setValue(engine.get_config('tracker_interval')) self.tracker_process.setText(engine.get_config('tracker_process')) self.tracker_update_wait.setValue(engine.get_config('tracker_update_wait_s')) self.tracker_update_close.setChecked(engine.get_config('tracker_update_close')) self.tracker_update_prompt.setChecked(engine.get_config('tracker_update_prompt')) self.tracker_not_found_prompt.setChecked(engine.get_config('tracker_not_found_prompt')) self.player.setText(engine.get_config('player')) self.library_autoscan.setChecked(engine.get_config('library_autoscan')) self.scan_whole_list.setChecked(engine.get_config('scan_whole_list')) self.library_full_path.setChecked(engine.get_config('library_full_path')) self.plex_host.setText(engine.get_config('plex_host')) self.plex_port.setText(engine.get_config('plex_port')) self.plex_obey_wait.setChecked(engine.get_config('plex_obey_update_wait_s')) self.plex_user.setText(engine.get_config('plex_user')) self.plex_passw.setText(engine.get_config('plex_passwd')) for path in engine.get_config('searchdir'): self._add_dir(path) if autoretrieve == 'always': self.autoretrieve_always.setChecked(True) elif autoretrieve == 'days': self.autoretrieve_days.setChecked(True) else: self.autoretrieve_off.setChecked(True) self.autoretrieve_days_n.setValue(engine.get_config('autoretrieve_days')) if autosend == 'always': self.autosend_always.setChecked(True) elif autosend in ('minutes', 'hours'): self.autosend_minutes.setChecked(True) elif autosend == 'size': self.autosend_size.setChecked(True) else: self.autosend_off.setChecked(True) self.autosend_minutes_n.setValue(engine.get_config('autosend_minutes')) self.autosend_size_n.setValue(engine.get_config('autosend_size')) self.autosend_at_exit.setChecked(engine.get_config('autosend_at_exit')) self.auto_status_change.setChecked(engine.get_config('auto_status_change')) self.auto_status_change_if_scored.setChecked(engine.get_config('auto_status_change_if_scored')) self.auto_date_change.setChecked(engine.get_config('auto_date_change')) self.tray_icon.setChecked(self.config['show_tray']) self.close_to_tray.setChecked(self.config['close_to_tray']) self.start_in_tray.setChecked(self.config['start_in_tray']) self.tray_api_icon.setChecked(self.config['tray_api_icon']) self.notifications.setChecked(self.config['notifications']) self.remember_geometry.setChecked(self.config['remember_geometry']) self.remember_columns.setChecked(self.config['remember_columns']) self.columns_per_api.setChecked(self.config['columns_per_api']) self.filter_bar_position.setCurrentIndex(self.filter_bar_position.findData(self.config['filter_bar_position'])) self.inline_edit.setChecked(self.config['inline_edit']) self.ep_bar_style.setCurrentIndex(self.ep_bar_style.findData(self.config['episodebar_style'])) self.ep_bar_text.setChecked(self.config['episodebar_text']) self.autoretrieve_days_n.setEnabled(self.autoretrieve_days.isChecked()) self.autosend_minutes_n.setEnabled(self.autosend_minutes.isChecked()) self.autosend_size_n.setEnabled(self.autosend_size.isChecked()) self.close_to_tray.setEnabled(self.tray_icon.isChecked()) self.start_in_tray.setEnabled(self.tray_icon.isChecked()) self.notifications.setEnabled(self.tray_icon.isChecked()) self.color_values = self.config['colors'].copy() self.tracker_type_change(None) def _save(self): engine = self.worker.engine engine.set_config('tracker_enabled', self.tracker_enabled.isChecked()) engine.set_config('tracker_type', self.tracker_type.itemData(self.tracker_type.currentIndex())) engine.set_config('tracker_interval', self.tracker_interval.value()) engine.set_config('tracker_process', str(self.tracker_process.text())) engine.set_config('tracker_update_wait_s', self.tracker_update_wait.value()) engine.set_config('tracker_update_close', self.tracker_update_close.isChecked()) engine.set_config('tracker_update_prompt', self.tracker_update_prompt.isChecked()) engine.set_config('tracker_not_found_prompt', self.tracker_not_found_prompt.isChecked()) engine.set_config('player', self.player.text()) engine.set_config('library_autoscan', self.library_autoscan.isChecked()) engine.set_config('scan_whole_list', self.scan_whole_list.isChecked()) engine.set_config('library_full_path', self.library_full_path.isChecked()) engine.set_config('plex_host', self.plex_host.text()) engine.set_config('plex_port', self.plex_port.text()) engine.set_config('plex_obey_update_wait_s', self.plex_obey_wait.isChecked()) engine.set_config('plex_user', self.plex_user.text()) engine.set_config('plex_passwd', self.plex_passw.text()) engine.set_config('searchdir', [self.searchdirs.item(i).text() for i in range(self.searchdirs.count())]) if self.autoretrieve_always.isChecked(): engine.set_config('autoretrieve', 'always') elif self.autoretrieve_days.isChecked(): engine.set_config('autoretrieve', 'days') else: engine.set_config('autoretrieve', 'off') engine.set_config('autoretrieve_days', self.autoretrieve_days_n.value()) if self.autosend_always.isChecked(): engine.set_config('autosend', 'always') elif self.autosend_minutes.isChecked(): engine.set_config('autosend', 'minutes') elif self.autosend_size.isChecked(): engine.set_config('autosend', 'size') else: engine.set_config('autosend', 'off') engine.set_config('autosend_minutes', self.autosend_minutes_n.value()) engine.set_config('autosend_size', self.autosend_size_n.value()) engine.set_config('autosend_at_exit', self.autosend_at_exit.isChecked()) engine.set_config('auto_status_change', self.auto_status_change.isChecked()) engine.set_config('auto_status_change_if_scored', self.auto_status_change_if_scored.isChecked()) engine.set_config('auto_date_change', self.auto_date_change.isChecked()) engine.save_config() self.config['show_tray'] = self.tray_icon.isChecked() self.config['close_to_tray'] = self.close_to_tray.isChecked() self.config['start_in_tray'] = self.start_in_tray.isChecked() self.config['tray_api_icon'] = self.tray_api_icon.isChecked() self.config['notifications'] = self.notifications.isChecked() self.config['remember_geometry'] = self.remember_geometry.isChecked() self.config['remember_columns'] = self.remember_columns.isChecked() self.config['columns_per_api'] = self.columns_per_api.isChecked() self.config['filter_bar_position'] = self.filter_bar_position.itemData(self.filter_bar_position.currentIndex()) self.config['inline_edit'] = self.inline_edit.isChecked() self.config['episodebar_style'] = self.ep_bar_style.itemData(self.ep_bar_style.currentIndex()) self.config['episodebar_text'] = self.ep_bar_text.isChecked() self.config['colors'] = self.color_values utils.save_config(self.config, self.configfile) self.saved.emit() def s_save(self): self._save() self.accept() def tracker_type_change(self, checked): if self.tracker_enabled.isChecked(): self.tracker_interval.setEnabled(True) self.tracker_update_wait.setEnabled(True) self.tracker_type.setEnabled(True) if self.tracker_type.itemData(self.tracker_type.currentIndex()) == 'plex': self.plex_host.setEnabled(True) self.plex_port.setEnabled(True) self.plex_obey_wait.setEnabled(True) self.plex_user.setEnabled(True) self.plex_passw.setEnabled(True) self.tracker_process.setEnabled(False) else: self.tracker_process.setEnabled(True) self.plex_host.setEnabled(False) self.plex_port.setEnabled(False) self.plex_user.setEnabled(False) self.plex_passw.setEnabled(False) self.plex_obey_wait.setEnabled(False) else: self.tracker_type.setEnabled(False) self.plex_host.setEnabled(False) self.plex_port.setEnabled(False) self.plex_user.setEnabled(False) self.plex_passw.setEnabled(False) self.plex_obey_wait.setEnabled(False) self.tracker_process.setEnabled(False) self.tracker_interval.setEnabled(False) self.tracker_update_wait.setEnabled(False) def s_autoretrieve_days(self, checked): self.autoretrieve_days_n.setEnabled(checked) def s_autosend_minutes(self, checked): self.autosend_minutes_n.setEnabled(checked) def s_autosend_size(self, checked): self.autosend_size_n.setEnabled(checked) def s_tray_icon(self, checked): self.close_to_tray.setEnabled(checked) self.start_in_tray.setEnabled(checked) self.tray_api_icon.setEnabled(checked) self.notifications.setEnabled(checked) def s_ep_bar_style(self, index): if self.ep_bar_style.itemData(index) == ShowsTableDelegate.BarStyle04: self.ep_bar_text.setEnabled(False) else: self.ep_bar_text.setEnabled(True) def s_auto_status_change(self, checked): self.auto_status_change_if_scored.setEnabled(checked) def s_player_browse(self): if pyqt_version is 5: self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')[0]) else: self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')) def s_searchdirs_add(self): self._add_dir(QFileDialog.getExistingDirectory(caption='Choose media directory')) def s_searchdirs_remove(self): row = self.searchdirs.currentRow() if row != -1: self.searchdirs.takeItem(row) def s_switch_page(self, new, old): if not new: new = old self.contents.setCurrentIndex(self.category_list.row(new)) def s_color_picker(self, key, system): return lambda: self.color_picker(key, system) def color_picker(self, key, system): if system is True: current = self.color_values[key] result = ThemedColorPicker.do() if result is not None and result is not current: self.color_values[key] = result self.update_colors() else: current = getColor(self.color_values[key]) result = QColorDialog.getColor(current) if result.isValid() and result is not current: self.color_values[key] = str(result.name()) self.update_colors() def update_colors(self): for ((key, label), color) in zip(self.colors['rows']+self.colors['progress'], self.color_buttons): color.setStyleSheet('background-color: ' + getColor(self.color_values[key]).name())
class E5SideBar(QWidget): """ Class implementing a sidebar with a widget area, that is hidden or shown, if the current tab is clicked again. """ Version = 1 North = 0 East = 1 South = 2 West = 3 def __init__(self, orientation=None, delay=200, parent=None): """ Constructor @param orientation orientation of the sidebar widget (North, East, South, West) @param delay value for the expand/shrink delay in milliseconds (integer) @param parent parent widget (QWidget) """ super(E5SideBar, self).__init__(parent) self.__tabBar = QTabBar() self.__tabBar.setDrawBase(True) self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setUsesScrollButtons(True) self.__tabBar.setDrawBase(False) self.__stackedWidget = QStackedWidget(self) self.__stackedWidget.setContentsMargins(0, 0, 0, 0) self.__autoHideButton = QToolButton() self.__autoHideButton.setCheckable(True) self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) self.__autoHideButton.setChecked(True) self.__autoHideButton.setToolTip( self.tr("Deselect to activate automatic collapsing")) self.barLayout = QBoxLayout(QBoxLayout.LeftToRight) self.barLayout.setContentsMargins(0, 0, 0, 0) self.layout = QBoxLayout(QBoxLayout.TopToBottom) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.barLayout.addWidget(self.__autoHideButton) self.barLayout.addWidget(self.__tabBar) self.layout.addLayout(self.barLayout) self.layout.addWidget(self.__stackedWidget) self.setLayout(self.layout) # initialize the delay timer self.__actionMethod = None self.__delayTimer = QTimer(self) self.__delayTimer.setSingleShot(True) self.__delayTimer.setInterval(delay) self.__delayTimer.timeout.connect(self.__delayedAction) self.__minimized = False self.__minSize = 0 self.__maxSize = 0 self.__bigSize = QSize() self.splitter = None self.splitterSizes = [] self.__hasFocus = False # flag storing if this widget or any child has the focus self.__autoHide = False self.__tabBar.installEventFilter(self) self.__orientation = E5SideBar.North if orientation is None: orientation = E5SideBar.North self.setOrientation(orientation) self.__tabBar.currentChanged[int].connect( self.__stackedWidget.setCurrentIndex) e5App().focusChanged[QWidget, QWidget].connect(self.__appFocusChanged) self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled) def setSplitter(self, splitter): """ Public method to set the splitter managing the sidebar. @param splitter reference to the splitter (QSplitter) """ self.splitter = splitter self.splitter.splitterMoved.connect(self.__splitterMoved) self.splitter.setChildrenCollapsible(False) index = self.splitter.indexOf(self) self.splitter.setCollapsible(index, False) def __splitterMoved(self, pos, index): """ Private slot to react on splitter moves. @param pos new position of the splitter handle (integer) @param index index of the splitter handle (integer) """ if self.splitter: self.splitterSizes = self.splitter.sizes() def __delayedAction(self): """ Private slot to handle the firing of the delay timer. """ if self.__actionMethod is not None: self.__actionMethod() def setDelay(self, delay): """ Public method to set the delay value for the expand/shrink delay in milliseconds. @param delay value for the expand/shrink delay in milliseconds (integer) """ self.__delayTimer.setInterval(delay) def delay(self): """ Public method to get the delay value for the expand/shrink delay in milliseconds. @return value for the expand/shrink delay in milliseconds (integer) """ return self.__delayTimer.interval() def __cancelDelayTimer(self): """ Private method to cancel the current delay timer. """ self.__delayTimer.stop() self.__actionMethod = None def shrink(self): """ Public method to record a shrink request. """ self.__delayTimer.stop() self.__actionMethod = self.__shrinkIt self.__delayTimer.start() def __shrinkIt(self): """ Private method to shrink the sidebar. """ self.__minimized = True self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() if self.splitter: self.splitterSizes = self.splitter.sizes() self.__stackedWidget.hide() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.setFixedHeight(self.__tabBar.minimumSizeHint().height()) else: self.setFixedWidth(self.__tabBar.minimumSizeHint().width()) self.__actionMethod = None def expand(self): """ Public method to record a expand request. """ self.__delayTimer.stop() self.__actionMethod = self.__expandIt self.__delayTimer.start() def __expandIt(self): """ Private method to expand the sidebar. """ self.__minimized = False self.__stackedWidget.show() self.resize(self.__bigSize) if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = max(self.__minSize, self.minimumSizeHint().height()) self.setMinimumHeight(minSize) self.setMaximumHeight(self.__maxSize) else: minSize = max(self.__minSize, self.minimumSizeHint().width()) self.setMinimumWidth(minSize) self.setMaximumWidth(self.__maxSize) if self.splitter: self.splitter.setSizes(self.splitterSizes) self.__actionMethod = None def isMinimized(self): """ Public method to check the minimized state. @return flag indicating the minimized state (boolean) """ return self.__minimized def isAutoHiding(self): """ Public method to check, if the auto hide function is active. @return flag indicating the state of auto hiding (boolean) """ return self.__autoHide def eventFilter(self, obj, evt): """ Public method to handle some events for the tabbar. @param obj reference to the object (QObject) @param evt reference to the event object (QEvent) @return flag indicating, if the event was handled (boolean) """ if obj == self.__tabBar: if evt.type() == QEvent.MouseButtonPress: pos = evt.pos() for i in range(self.__tabBar.count()): if self.__tabBar.tabRect(i).contains(pos): break if i == self.__tabBar.currentIndex(): if self.isMinimized(): self.expand() else: self.shrink() return True elif self.isMinimized(): self.expand() elif evt.type() == QEvent.Wheel: if qVersion() >= "5.0.0": delta = evt.angleDelta().y() else: delta = evt.delta() if delta > 0: self.prevTab() else: self.nextTab() return True return QWidget.eventFilter(self, obj, evt) def addTab(self, widget, iconOrLabel, label=None): """ Public method to add a tab to the sidebar. @param widget reference to the widget to add (QWidget) @param iconOrLabel reference to the icon or the label text of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.addTab(iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.addTab(iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.addWidget(widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def insertTab(self, index, widget, iconOrLabel, label=None): """ Public method to insert a tab into the sidebar. @param index the index to insert the tab at (integer) @param widget reference to the widget to insert (QWidget) @param iconOrLabel reference to the icon or the labeltext of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.insertTab(index, iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.insertTab(index, iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.insertWidget(index, widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def removeTab(self, index): """ Public method to remove a tab. @param index the index of the tab to remove (integer) """ self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index)) self.__tabBar.removeTab(index) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def clear(self): """ Public method to remove all tabs. """ while self.count() > 0: self.removeTab(0) def prevTab(self): """ Public slot used to show the previous tab. """ ind = self.currentIndex() - 1 if ind == -1: ind = self.count() - 1 self.setCurrentIndex(ind) self.currentWidget().setFocus() def nextTab(self): """ Public slot used to show the next tab. """ ind = self.currentIndex() + 1 if ind == self.count(): ind = 0 self.setCurrentIndex(ind) self.currentWidget().setFocus() def count(self): """ Public method to get the number of tabs. @return number of tabs in the sidebar (integer) """ return self.__tabBar.count() def currentIndex(self): """ Public method to get the index of the current tab. @return index of the current tab (integer) """ return self.__stackedWidget.currentIndex() def setCurrentIndex(self, index): """ Public slot to set the current index. @param index the index to set as the current index (integer) """ self.__tabBar.setCurrentIndex(index) self.__stackedWidget.setCurrentIndex(index) if self.isMinimized(): self.expand() def currentWidget(self): """ Public method to get a reference to the current widget. @return reference to the current widget (QWidget) """ return self.__stackedWidget.currentWidget() def setCurrentWidget(self, widget): """ Public slot to set the current widget. @param widget reference to the widget to become the current widget (QWidget) """ self.__stackedWidget.setCurrentWidget(widget) self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex()) if self.isMinimized(): self.expand() def indexOf(self, widget): """ Public method to get the index of the given widget. @param widget reference to the widget to get the index of (QWidget) @return index of the given widget (integer) """ return self.__stackedWidget.indexOf(widget) def isTabEnabled(self, index): """ Public method to check, if a tab is enabled. @param index index of the tab to check (integer) @return flag indicating the enabled state (boolean) """ return self.__tabBar.isTabEnabled(index) def setTabEnabled(self, index, enabled): """ Public method to set the enabled state of a tab. @param index index of the tab to set (integer) @param enabled enabled state to set (boolean) """ self.__tabBar.setTabEnabled(index, enabled) def orientation(self): """ Public method to get the orientation of the sidebar. @return orientation of the sidebar (North, East, South, West) """ return self.__orientation def setOrientation(self, orient): """ Public method to set the orientation of the sidebar. @param orient orientation of the sidebar (North, East, South, West) """ if orient == E5SideBar.North: self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.TopToBottom) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.East: self.__tabBar.setShape(QTabBar.RoundedEast) self.__tabBar.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.RightToLeft) self.layout.setAlignment(self.barLayout, Qt.AlignTop) elif orient == E5SideBar.South: self.__tabBar.setShape(QTabBar.RoundedSouth) self.__tabBar.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.BottomToTop) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.West: self.__tabBar.setShape(QTabBar.RoundedWest) self.__tabBar.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.LeftToRight) self.layout.setAlignment(self.barLayout, Qt.AlignTop) self.__orientation = orient def tabIcon(self, index): """ Public method to get the icon of a tab. @param index index of the tab (integer) @return icon of the tab (QIcon) """ return self.__tabBar.tabIcon(index) def setTabIcon(self, index, icon): """ Public method to set the icon of a tab. @param index index of the tab (integer) @param icon icon to be set (QIcon) """ self.__tabBar.setTabIcon(index, icon) def tabText(self, index): """ Public method to get the text of a tab. @param index index of the tab (integer) @return text of the tab (string) """ return self.__tabBar.tabText(index) def setTabText(self, index, text): """ Public method to set the text of a tab. @param index index of the tab (integer) @param text text to set (string) """ self.__tabBar.setTabText(index, text) def tabToolTip(self, index): """ Public method to get the tooltip text of a tab. @param index index of the tab (integer) @return tooltip text of the tab (string) """ return self.__tabBar.tabToolTip(index) def setTabToolTip(self, index, tip): """ Public method to set the tooltip text of a tab. @param index index of the tab (integer) @param tip tooltip text to set (string) """ self.__tabBar.setTabToolTip(index, tip) def tabWhatsThis(self, index): """ Public method to get the WhatsThis text of a tab. @param index index of the tab (integer) @return WhatsThis text of the tab (string) """ return self.__tabBar.tabWhatsThis(index) def setTabWhatsThis(self, index, text): """ Public method to set the WhatsThis text of a tab. @param index index of the tab (integer) @param text WhatsThis text to set (string) """ self.__tabBar.setTabWhatsThis(index, text) def widget(self, index): """ Public method to get a reference to the widget associated with a tab. @param index index of the tab (integer) @return reference to the widget (QWidget) """ return self.__stackedWidget.widget(index) def saveState(self): """ Public method to save the state of the sidebar. @return saved state as a byte array (QByteArray) """ if len(self.splitterSizes) == 0: if self.splitter: self.splitterSizes = self.splitter.sizes() self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) stream.writeUInt16(self.Version) stream.writeBool(self.__minimized) stream << self.__bigSize stream.writeUInt16(self.__minSize) stream.writeUInt16(self.__maxSize) stream.writeUInt16(len(self.splitterSizes)) for size in self.splitterSizes: stream.writeUInt16(size) stream.writeBool(self.__autoHide) return data def restoreState(self, state): """ Public method to restore the state of the sidebar. @param state byte array containing the saved state (QByteArray) @return flag indicating success (boolean) """ if state.isEmpty(): return False if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = self.layout.minimumSize().height() maxSize = self.maximumHeight() else: minSize = self.layout.minimumSize().width() maxSize = self.maximumWidth() data = QByteArray(state) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) stream.readUInt16() # version minimized = stream.readBool() if minimized and not self.__minimized: self.shrink() stream >> self.__bigSize self.__minSize = max(stream.readUInt16(), minSize) self.__maxSize = max(stream.readUInt16(), maxSize) count = stream.readUInt16() self.splitterSizes = [] for i in range(count): self.splitterSizes.append(stream.readUInt16()) self.__autoHide = stream.readBool() self.__autoHideButton.setChecked(not self.__autoHide) if not minimized: self.expand() return True ####################################################################### ## methods below implement the autohide functionality ####################################################################### def __autoHideToggled(self, checked): """ Private slot to handle the toggling of the autohide button. @param checked flag indicating the checked state of the button (boolean) """ self.__autoHide = not checked if self.__autoHide: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOn.png")) else: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) def __appFocusChanged(self, old, now): """ Private slot to handle a change of the focus. @param old reference to the widget, that lost focus (QWidget or None) @param now reference to the widget having the focus (QWidget or None) """ self.__hasFocus = self.isAncestorOf(now) if self.__autoHide and not self.__hasFocus and not self.isMinimized(): self.shrink() elif self.__autoHide and self.__hasFocus and self.isMinimized(): self.expand() def enterEvent(self, event): """ Protected method to handle the mouse entering this widget. @param event reference to the event (QEvent) """ if self.__autoHide and self.isMinimized(): self.expand() else: self.__cancelDelayTimer() def leaveEvent(self, event): """ Protected method to handle the mouse leaving this widget. @param event reference to the event (QEvent) """ if self.__autoHide and not self.__hasFocus and not self.isMinimized(): self.shrink() else: self.__cancelDelayTimer() def shutdown(self): """ Public method to shut down the object. This method does some preparations so the object can be deleted properly. It disconnects from the focusChanged signal in order to avoid trouble later on. """ e5App().focusChanged[QWidget, QWidget].disconnect( self.__appFocusChanged)
class AddDialog(QDialog): worker = None selected_show = None results = [] def __init__(self, parent, worker, current_status, default=None): QDialog.__init__(self, parent) self.resize(950, 700) self.setWindowTitle('Search/Add from Remote') self.worker = worker self.current_status = current_status self.default = default if default: self.setWindowTitle('Search/Add from Remote for new show: %s' % default) # Get available search methods and default to keyword search if not reported by the API search_methods = self.worker.engine.mediainfo.get('search_methods', [utils.SEARCH_METHOD_KW]) layout = QVBoxLayout() # Create top layout top_layout = QHBoxLayout() if utils.SEARCH_METHOD_KW in search_methods: self.search_rad = QRadioButton('By keyword:') self.search_rad.setChecked(True) self.search_txt = QLineEdit() self.search_txt.returnPressed.connect(self.s_search) if default: self.search_txt.setText(default) self.search_btn = QPushButton('Search') self.search_btn.clicked.connect(self.s_search) top_layout.addWidget(self.search_rad) top_layout.addWidget(self.search_txt) else: top_layout.setAlignment(QtCore.Qt.AlignRight) top_layout.addWidget(self.search_btn) # Create filter line filters_layout = QHBoxLayout() if utils.SEARCH_METHOD_SEASON in search_methods: self.season_rad = QRadioButton('By season:') self.season_combo = QComboBox() self.season_combo.addItem('Winter', utils.SEASON_WINTER) self.season_combo.addItem('Spring', utils.SEASON_SPRING) self.season_combo.addItem('Summer', utils.SEASON_SUMMER) self.season_combo.addItem('Fall', utils.SEASON_FALL) self.season_year = QSpinBox() today = date.today() current_season = (today.month - 1) / 3 self.season_year.setRange(1900, today.year) self.season_year.setValue(today.year) self.season_combo.setCurrentIndex(current_season) filters_layout.addWidget(self.season_rad) filters_layout.addWidget(self.season_combo) filters_layout.addWidget(self.season_year) filters_layout.setAlignment(QtCore.Qt.AlignLeft) filters_layout.addWidget(QSplitter()) else: filters_layout.setAlignment(QtCore.Qt.AlignRight) view_combo = QComboBox() view_combo.addItem('Table view') view_combo.addItem('Card view') view_combo.currentIndexChanged.connect(self.s_change_view) filters_layout.addWidget(view_combo) # Create central content self.contents = QStackedWidget() # Set up views tableview = AddTableDetailsView(None, self.worker) tableview.changed.connect(self.s_selected) self.contents.addWidget(tableview) cardview = AddCardView(api_info=self.worker.engine.api_info) cardview.changed.connect(self.s_selected) cardview.activated.connect(self.s_show_details) self.contents.addWidget(cardview) # Use for testing #self.set_results([{'id': 1, 'title': 'Hola', 'image': 'https://omaera.org/icon.png'}]) bottom_buttons = QDialogButtonBox() bottom_buttons.addButton("Cancel", QDialogButtonBox.RejectRole) self.add_btn = bottom_buttons.addButton("Add", QDialogButtonBox.AcceptRole) self.add_btn.setEnabled(False) bottom_buttons.accepted.connect(self.s_add) bottom_buttons.rejected.connect(self.close) # Finish layout layout.addLayout(top_layout) layout.addLayout(filters_layout) layout.addWidget(self.contents) layout.addWidget(bottom_buttons) self.setLayout(layout) if utils.SEARCH_METHOD_SEASON in search_methods: self.search_txt.setFocus() def worker_call(self, function, ret_function, *args, **kwargs): # Run worker in a thread self.worker.set_function(function, ret_function, *args, **kwargs) self.worker.start() def _enable_widgets(self, enable): self.search_btn.setEnabled(enable) self.contents.currentWidget().setEnabled(enable) def set_results(self, results): self.results = results self.contents.currentWidget().setResults(self.results) # Slots def s_show_details(self): detailswindow = DetailsDialog(self, self.worker, self.selected_show) detailswindow.setModal(True) detailswindow.show() def s_change_view(self, item): self.contents.currentWidget().getModel().setResults(None) self.contents.setCurrentIndex(item) self.contents.currentWidget().getModel().setResults(self.results) def s_search(self): if self.search_rad.isChecked(): criteria = self.search_txt.text().strip() if not criteria: return method = utils.SEARCH_METHOD_KW elif self.season_rad.isChecked(): criteria = (self.season_combo.itemData(self.season_combo.currentIndex()), self.season_year.value()) method = utils.SEARCH_METHOD_SEASON self.contents.currentWidget().clearSelection() self.selected_show = None self._enable_widgets(False) self.add_btn.setEnabled(False) self.worker_call('search', self.r_searched, criteria, method) def s_selected(self, show): self.selected_show = show self.add_btn.setEnabled(True) def s_add(self): if self.selected_show: self.worker_call('add_show', self.r_added, self.selected_show, self.current_status) # Worker responses def r_searched(self, result): self._enable_widgets(True) if result['success']: self.set_results(result['result']) """ if self.table.currentRow() is 0: # Row number hasn't changed but the data probably has! self.s_show_selected(self.table.item(0, 0)) self.table.setCurrentItem(self.table.item(0, 0))""" else: self.set_results(None) def r_added(self, result): if result['success']: if self.default: self.accept()
class FlowDialog(QWidget): """ Class for controlling the order of screens in the experiment. """ def __init__(self, parent=None): super(FlowDialog, self).__init__(parent) self._n_steps = 1 # number of performed steps self.pages_widget = QStackedWidget() # create widgets self.experiment_setup = ExperimentSetup() self.instructions = InstructionsScreen("./instructions/he-informed-consent.txt") self.new_user_form = NewUserForm() self.data_widget = DataWidget() # add widgets to pages_widget self.pages_widget.addWidget(self.experiment_setup) self.pages_widget.addWidget(self.instructions) self.pages_widget.addWidget(self.new_user_form) self.pages_widget.addWidget(self.data_widget) # next button self.next_button = QPushButton("&Next") self.next_button_layout = QHBoxLayout() self.next_button_layout.addStretch(1) self.next_button_layout.addWidget(self.next_button) self.next_button.pressed.connect(self.nextPressed) main_layout = QGridLayout() # set screen shoulders main_layout.setRowMinimumHeight(0, 80) main_layout.setRowMinimumHeight(3, 80) main_layout.setColumnMinimumWidth(0, 80) main_layout.setColumnMinimumWidth(2, 80) main_layout.addWidget(self.pages_widget, 1, 1) main_layout.addLayout(self.next_button_layout, 2, 1) self.setLayout(main_layout) self.pages_widget.setCurrentIndex(0) def nextPressed(self): """ control the order and number of repetitions of the experiment """ self.data_widget.count_down_timer.restart_timer() # restart timer # if on setup screen if self.pages_widget.currentIndex() == 0: # get condition index number condition_index = self.experiment_setup.conditions_combo.currentIndex() # DEBUGGING: debug_condition = config.CONDITIONS['a'][condition_index] print(debug_condition) self.raise_index() # welcome screen elif self.pages_widget.currentIndex() == 1: self.raise_index() # if on new_user screen elif self.pages_widget.currentIndex() == 2: self.new_user_form.save_results() self.raise_index() # if on data screen elif self.pages_widget.currentIndex() == 3: if self._n_steps < config.NUM_STEPS: self.data_widget.add_log_row() self._n_steps += 1 print("Step number {}".format(self._n_steps)) # DEBUGGING else: self.raise_index() elif self.pages_widget.currentIndex() == 4: print("That's it") # DEBUGGING else: # If reached wrong index raise RuntimeError("No such index") def raise_index(self): current = self.pages_widget.currentIndex() self.pages_widget.setCurrentIndex(current + 1)
class ConfigurationWidget(QWidget): """ Class implementing a dialog for the configuration of eric6. @signal preferencesChanged() emitted after settings have been changed @signal masterPasswordChanged(str, str) emitted after the master password has been changed with the old and the new password @signal accepted() emitted to indicate acceptance of the changes @signal rejected() emitted to indicate rejection of the changes """ preferencesChanged = pyqtSignal() masterPasswordChanged = pyqtSignal(str, str) accepted = pyqtSignal() rejected = pyqtSignal() DefaultMode = 0 HelpBrowserMode = 1 TrayStarterMode = 2 HexEditorMode = 3 def __init__(self, parent=None, fromEric=True, displayMode=DefaultMode, expandedEntries=[]): """ Constructor @param parent The parent widget of this dialog. (QWidget) @keyparam fromEric flag indicating a dialog generation from within the eric6 ide (boolean) @keyparam displayMode mode of the configuration dialog (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode) @exception RuntimeError raised to indicate an invalid dialog mode @keyparam expandedEntries list of entries to be shown expanded (list of strings) """ assert displayMode in ( ConfigurationWidget.DefaultMode, ConfigurationWidget.HelpBrowserMode, ConfigurationWidget.TrayStarterMode, ConfigurationWidget.HexEditorMode, ) super(ConfigurationWidget, self).__init__(parent) self.fromEric = fromEric self.displayMode = displayMode self.__setupUi() self.itmDict = {} if not fromEric: from PluginManager.PluginManager import PluginManager try: self.pluginManager = e5App().getObject("PluginManager") except KeyError: self.pluginManager = PluginManager(self) e5App().registerObject("PluginManager", self.pluginManager) if displayMode == ConfigurationWidget.DefaultMode: self.configItems = { # key : [display string, pixmap name, dialog module name or # page creation function, parent key, # reference to configuration page (must always be last)] # The dialog module must have the module function 'create' to # create the configuration page. This must have the method # 'save' to save the settings. "applicationPage": [self.tr("Application"), "preferences-application.png", "ApplicationPage", None, None], "cooperationPage": [self.tr("Cooperation"), "preferences-cooperation.png", "CooperationPage", None, None], "corbaPage": [self.tr("CORBA"), "preferences-orbit.png", "CorbaPage", None, None], "emailPage": [self.tr("Email"), "preferences-mail_generic.png", "EmailPage", None, None], "graphicsPage": [self.tr("Graphics"), "preferences-graphics.png", "GraphicsPage", None, None], "hexEditorPage": [self.tr("Hex Editor"), "hexEditor.png", "HexEditorPage", None, None], "iconsPage": [self.tr("Icons"), "preferences-icons.png", "IconsPage", None, None], "ircPage": [self.tr("IRC"), "irc.png", "IrcPage", None, None], "logViewerPage": [self.tr("Log-Viewer"), "preferences-logviewer.png", "LogViewerPage", None, None], "mimeTypesPage": [self.tr("Mimetypes"), "preferences-mimetypes.png", "MimeTypesPage", None, None], "networkPage": [self.tr("Network"), "preferences-network.png", "NetworkPage", None, None], "notificationsPage": [self.tr("Notifications"), "preferences-notifications.png", "NotificationsPage", None, None], "pluginManagerPage": [self.tr("Plugin Manager"), "preferences-pluginmanager.png", "PluginManagerPage", None, None], "printerPage": [self.tr("Printer"), "preferences-printer.png", "PrinterPage", None, None], "pythonPage": [self.tr("Python"), "preferences-python.png", "PythonPage", None, None], "qtPage": [self.tr("Qt"), "preferences-qtlogo.png", "QtPage", None, None], "securityPage": [self.tr("Security"), "preferences-security.png", "SecurityPage", None, None], "shellPage": [self.tr("Shell"), "preferences-shell.png", "ShellPage", None, None], "tasksPage": [self.tr("Tasks"), "task.png", "TasksPage", None, None], "templatesPage": [self.tr("Templates"), "preferences-template.png", "TemplatesPage", None, None], "trayStarterPage": [self.tr("Tray Starter"), "erict.png", "TrayStarterPage", None, None], "vcsPage": [self.tr("Version Control Systems"), "preferences-vcs.png", "VcsPage", None, None], "0debuggerPage": [self.tr("Debugger"), "preferences-debugger.png", None, None, None], "debuggerGeneralPage": [self.tr("General"), "preferences-debugger.png", "DebuggerGeneralPage", "0debuggerPage", None], "debuggerPythonPage": [self.tr("Python"), "preferences-pyDebugger.png", "DebuggerPythonPage", "0debuggerPage", None], "debuggerPython3Page": [self.tr("Python3"), "preferences-pyDebugger.png", "DebuggerPython3Page", "0debuggerPage", None], "0editorPage": [self.tr("Editor"), "preferences-editor.png", None, None, None], "editorAPIsPage": [self.tr("APIs"), "preferences-api.png", "EditorAPIsPage", "0editorPage", None], "editorAutocompletionPage": [self.tr("Autocompletion"), "preferences-autocompletion.png", "EditorAutocompletionPage", "0editorPage", None], "editorAutocompletionQScintillaPage": [self.tr("QScintilla"), "qscintilla.png", "EditorAutocompletionQScintillaPage", "editorAutocompletionPage", None], "editorCalltipsPage": [self.tr("Calltips"), "preferences-calltips.png", "EditorCalltipsPage", "0editorPage", None], "editorCalltipsQScintillaPage": [self.tr("QScintilla"), "qscintilla.png", "EditorCalltipsQScintillaPage", "editorCalltipsPage", None], "editorGeneralPage": [self.tr("General"), "preferences-general.png", "EditorGeneralPage", "0editorPage", None], "editorFilePage": [self.tr("Filehandling"), "preferences-filehandling.png", "EditorFilePage", "0editorPage", None], "editorSearchPage": [self.tr("Searching"), "preferences-search.png", "EditorSearchPage", "0editorPage", None], "editorSpellCheckingPage": [self.tr("Spell checking"), "preferences-spellchecking.png", "EditorSpellCheckingPage", "0editorPage", None], "editorStylesPage": [self.tr("Style"), "preferences-styles.png", "EditorStylesPage", "0editorPage", None], "editorSyntaxPage": [self.tr("Code Checkers"), "preferences-debugger.png", "EditorSyntaxPage", "0editorPage", None], "editorTypingPage": [self.tr("Typing"), "preferences-typing.png", "EditorTypingPage", "0editorPage", None], "editorExportersPage": [self.tr("Exporters"), "preferences-exporters.png", "EditorExportersPage", "0editorPage", None], "1editorLexerPage": [self.tr("Highlighters"), "preferences-highlighting-styles.png", None, "0editorPage", None], "editorHighlightersPage": [self.tr("Filetype Associations"), "preferences-highlighter-association.png", "EditorHighlightersPage", "1editorLexerPage", None], "editorHighlightingStylesPage": [self.tr("Styles"), "preferences-highlighting-styles.png", "EditorHighlightingStylesPage", "1editorLexerPage", None], "editorKeywordsPage": [self.tr("Keywords"), "preferences-keywords.png", "EditorKeywordsPage", "1editorLexerPage", None], "editorPropertiesPage": [self.tr("Properties"), "preferences-properties.png", "EditorPropertiesPage", "1editorLexerPage", None], "1editorMouseClickHandlers": [self.tr("Mouse Click Handlers"), "preferences-mouse-click-handler.png", "EditorMouseClickHandlerPage", "0editorPage", None], "0helpPage": [self.tr("Help"), "preferences-help.png", None, None, None], "helpDocumentationPage": [self.tr("Help Documentation"), "preferences-helpdocumentation.png", "HelpDocumentationPage", "0helpPage", None], "helpViewersPage": [self.tr("Help Viewers"), "preferences-helpviewers.png", "HelpViewersPage", "0helpPage", None], "0projectPage": [self.tr("Project"), "preferences-project.png", None, None, None], "projectBrowserPage": [self.tr("Project Viewer"), "preferences-project.png", "ProjectBrowserPage", "0projectPage", None], "projectPage": [self.tr("Project"), "preferences-project.png", "ProjectPage", "0projectPage", None], "multiProjectPage": [self.tr("Multiproject"), "preferences-multiproject.png", "MultiProjectPage", "0projectPage", None], "0interfacePage": [self.tr("Interface"), "preferences-interface.png", None, None, None], "interfacePage": [self.tr("Interface"), "preferences-interface.png", "InterfacePage", "0interfacePage", None], "viewmanagerPage": [self.tr("Viewmanager"), "preferences-viewmanager.png", "ViewmanagerPage", "0interfacePage", None], } try: from PyQt5 import QtWebKit # __IGNORE_WARNING__ self.configItems.update({ "helpAppearancePage": [self.tr("Appearance"), "preferences-styles.png", "HelpAppearancePage", "0helpPage", None], "helpFlashCookieManagerPage": [self.tr("Flash Cookie Manager"), "flashCookie16.png", "HelpFlashCookieManagerPage", "0helpPage", None], "helpVirusTotalPage": [self.tr("VirusTotal Interface"), "virustotal.png", "HelpVirusTotalPage", "0helpPage", None], "helpWebBrowserPage": [self.tr("eric6 Web Browser"), "ericWeb.png", "HelpWebBrowserPage", "0helpPage", None], }) except ImportError: pass self.configItems.update( e5App().getObject("PluginManager").getPluginConfigData()) elif displayMode == ConfigurationWidget.HelpBrowserMode: self.configItems = { # key : [display string, pixmap name, dialog module name or # page creation function, parent key, # reference to configuration page (must always be last)] # The dialog module must have the module function 'create' to # create the configuration page. This must have the method # 'save' to save the settings. "interfacePage": [self.tr("Interface"), "preferences-interface.png", "HelpInterfacePage", None, None], "networkPage": [self.tr("Network"), "preferences-network.png", "NetworkPage", None, None], "printerPage": [self.tr("Printer"), "preferences-printer.png", "PrinterPage", None, None], "securityPage": [self.tr("Security"), "preferences-security.png", "SecurityPage", None, None], "0helpPage": [self.tr("Help"), "preferences-help.png", None, None, None], "helpDocumentationPage": [self.tr("Help Documentation"), "preferences-helpdocumentation.png", "HelpDocumentationPage", "0helpPage", None], } try: from PyQt5 import QtWebKit # __IGNORE_WARNING__ self.configItems.update({ "helpAppearancePage": [self.tr("Appearance"), "preferences-styles.png", "HelpAppearancePage", "0helpPage", None], "helpFlashCookieManagerPage": [self.tr("Flash Cookie Manager"), "flashCookie16.png", "HelpFlashCookieManagerPage", "0helpPage", None], "helpVirusTotalPage": [self.tr("VirusTotal Interface"), "virustotal.png", "HelpVirusTotalPage", "0helpPage", None], "helpWebBrowserPage": [self.tr("eric6 Web Browser"), "ericWeb.png", "HelpWebBrowserPage", "0helpPage", None], }) except ImportError: pass elif displayMode == ConfigurationWidget.TrayStarterMode: self.configItems = { # key : [display string, pixmap name, dialog module name or # page creation function, parent key, # reference to configuration page (must always be last)] # The dialog module must have the module function 'create' to # create the configuration page. This must have the method # 'save' to save the settings. "trayStarterPage": [self.tr("Tray Starter"), "erict.png", "TrayStarterPage", None, None], } elif displayMode == ConfigurationWidget.HexEditorMode: self.configItems = { # key : [display string, pixmap name, dialog module name or # page creation function, parent key, # reference to configuration page (must always be last)] # The dialog module must have the module function 'create' to # create the configuration page. This must have the method # 'save' to save the settings. "hexEditorPage": [self.tr("Hex Editor"), "hexEditor.png", "HexEditorPage", None, None], } else: raise RuntimeError("Illegal mode value: {0}".format(displayMode)) # generate the list entries self.__expandedEntries = [] for key in sorted(self.configItems.keys()): pageData = self.configItems[key] if pageData[3]: if pageData[3] in self.itmDict: pitm = self.itmDict[pageData[3]] # get the parent item else: continue else: pitm = self.configList self.itmDict[key] = ConfigurationPageItem(pitm, pageData[0], key, pageData[1]) self.itmDict[key].setData(0, Qt.UserRole, key) if (not self.fromEric or displayMode != ConfigurationWidget.DefaultMode or key in expandedEntries): self.itmDict[key].setExpanded(True) self.configList.sortByColumn(0, Qt.AscendingOrder) # set the initial size of the splitter self.configSplitter.setSizes([200, 600]) self.configList.itemActivated.connect(self.__showConfigurationPage) self.configList.itemClicked.connect(self.__showConfigurationPage) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.rejected) if displayMode in [ConfigurationWidget.HelpBrowserMode, ConfigurationWidget.TrayStarterMode, ConfigurationWidget.HexEditorMode]: self.configListSearch.hide() if displayMode not in [ConfigurationWidget.TrayStarterMode, ConfigurationWidget.HexEditorMode]: self.__initLexers() def accept(self): """ Public slot to accept the buttonBox accept signal. """ if not isMacPlatform(): wdg = self.focusWidget() if wdg == self.configList: return self.accepted.emit() def __setupUi(self): """ Private method to perform the general setup of the configuration widget. """ self.setObjectName("ConfigurationDialog") self.resize(900, 650) self.verticalLayout_2 = QVBoxLayout(self) self.verticalLayout_2.setSpacing(6) self.verticalLayout_2.setContentsMargins(6, 6, 6, 6) self.verticalLayout_2.setObjectName("verticalLayout_2") self.configSplitter = QSplitter(self) self.configSplitter.setOrientation(Qt.Horizontal) self.configSplitter.setObjectName("configSplitter") self.configListWidget = QWidget(self.configSplitter) self.leftVBoxLayout = QVBoxLayout(self.configListWidget) self.leftVBoxLayout.setContentsMargins(0, 0, 0, 0) self.leftVBoxLayout.setSpacing(0) self.leftVBoxLayout.setObjectName("leftVBoxLayout") self.configListSearch = E5ClearableLineEdit( self, self.tr("Enter search text...")) self.configListSearch.setObjectName("configListSearch") self.leftVBoxLayout.addWidget(self.configListSearch) self.configList = QTreeWidget() self.configList.setObjectName("configList") self.leftVBoxLayout.addWidget(self.configList) self.configListSearch.textChanged.connect(self.__searchTextChanged) self.scrollArea = QScrollArea(self.configSplitter) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.scrollArea.setWidgetResizable(False) self.scrollArea.setObjectName("scrollArea") self.configStack = QStackedWidget() self.configStack.setFrameShape(QFrame.Box) self.configStack.setFrameShadow(QFrame.Sunken) self.configStack.setObjectName("configStack") self.scrollArea.setWidget(self.configStack) self.emptyPage = QWidget() self.emptyPage.setGeometry(QRect(0, 0, 372, 591)) self.emptyPage.setObjectName("emptyPage") self.vboxlayout = QVBoxLayout(self.emptyPage) self.vboxlayout.setSpacing(6) self.vboxlayout.setContentsMargins(6, 6, 6, 6) self.vboxlayout.setObjectName("vboxlayout") spacerItem = QSpacerItem( 20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.vboxlayout.addItem(spacerItem) self.emptyPagePixmap = QLabel(self.emptyPage) self.emptyPagePixmap.setAlignment(Qt.AlignCenter) self.emptyPagePixmap.setObjectName("emptyPagePixmap") self.emptyPagePixmap.setPixmap( QPixmap(os.path.join(getConfig('ericPixDir'), 'eric.png'))) self.vboxlayout.addWidget(self.emptyPagePixmap) self.textLabel1 = QLabel(self.emptyPage) self.textLabel1.setAlignment(Qt.AlignCenter) self.textLabel1.setObjectName("textLabel1") self.vboxlayout.addWidget(self.textLabel1) spacerItem1 = QSpacerItem( 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.vboxlayout.addItem(spacerItem1) self.configStack.addWidget(self.emptyPage) self.verticalLayout_2.addWidget(self.configSplitter) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Apply | QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Reset) self.buttonBox.setObjectName("buttonBox") if not self.fromEric and \ self.displayMode == ConfigurationWidget.DefaultMode: self.buttonBox.button(QDialogButtonBox.Apply).hide() self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False) self.verticalLayout_2.addWidget(self.buttonBox) self.setWindowTitle(self.tr("Preferences")) self.configList.header().hide() self.configList.header().setSortIndicator(0, Qt.AscendingOrder) self.configList.setSortingEnabled(True) self.textLabel1.setText( self.tr("Please select an entry of the list \n" "to display the configuration page.")) QMetaObject.connectSlotsByName(self) self.setTabOrder(self.configList, self.configStack) self.configStack.setCurrentWidget(self.emptyPage) self.configList.setFocus() def __searchTextChanged(self, text): """ Private slot to handle a change of the search text. @param text text to search for (string) """ self.__searchChildItems(self.configList.invisibleRootItem(), text) def __searchChildItems(self, parent, text): """ Private method to enable child items based on a search string. @param parent reference to the parent item (QTreeWidgetItem) @param text text to search for (string) @return flag indicating an enabled child item (boolean) """ childEnabled = False text = text.lower() for index in range(parent.childCount()): itm = parent.child(index) if itm.childCount() > 0: enable = self.__searchChildItems(itm, text) or \ text == "" or text in itm.text(0).lower() else: enable = text == "" or text in itm.text(0).lower() if enable: childEnabled = True itm.setDisabled(not enable) return childEnabled def __initLexers(self): """ Private method to initialize the dictionary of preferences lexers. """ import QScintilla.Lexers from .PreferencesLexer import PreferencesLexer, \ PreferencesLexerLanguageError self.lexers = {} for language in QScintilla.Lexers.getSupportedLanguages(): if language not in self.lexers: try: self.lexers[language] = PreferencesLexer(language, self) except PreferencesLexerLanguageError: pass def __importConfigurationPage(self, name): """ Private method to import a configuration page module. @param name name of the configuration page module (string) @return reference to the configuration page module """ modName = "Preferences.ConfigurationPages.{0}".format(name) try: mod = __import__(modName) components = modName.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod except ImportError: E5MessageBox.critical( self, self.tr("Configuration Page Error"), self.tr("""<p>The configuration page <b>{0}</b>""" """ could not be loaded.</p>""").format(name)) return None def __showConfigurationPage(self, itm, column): """ Private slot to show a selected configuration page. @param itm reference to the selected item (QTreeWidgetItem) @param column column that was selected (integer) (ignored) """ pageName = itm.getPageName() self.showConfigurationPageByName(pageName, setCurrent=False) def __initPage(self, pageData): """ Private method to initialize a configuration page. @param pageData data structure for the page to initialize @return reference to the initialized page """ page = None if isinstance(pageData[2], types.FunctionType): page = pageData[2](self) else: mod = self.__importConfigurationPage(pageData[2]) if mod: page = mod.create(self) if page is not None: self.configStack.addWidget(page) pageData[-1] = page try: page.setMode(self.displayMode) except AttributeError: pass return page def showConfigurationPageByName(self, pageName, setCurrent=True): """ Public slot to show a named configuration page. @param pageName name of the configuration page to show (string) @param setCurrent flag indicating to set the current item (boolean) """ if pageName == "empty" or pageName not in self.configItems: page = self.emptyPage else: pageData = self.configItems[pageName] if pageData[-1] is None and pageData[2] is not None: # the page was not loaded yet, create it page = self.__initPage(pageData) else: page = pageData[-1] if page is None: page = self.emptyPage elif setCurrent: items = self.configList.findItems( pageData[0], Qt.MatchFixedString | Qt.MatchRecursive) for item in items: if item.data(0, Qt.UserRole) == pageName: self.configList.setCurrentItem(item) self.configStack.setCurrentWidget(page) ssize = self.scrollArea.size() if self.scrollArea.horizontalScrollBar(): ssize.setHeight( ssize.height() - self.scrollArea.horizontalScrollBar().height() - 2) if self.scrollArea.verticalScrollBar(): ssize.setWidth( ssize.width() - self.scrollArea.verticalScrollBar().width() - 2) psize = page.minimumSizeHint() self.configStack.resize(max(ssize.width(), psize.width()), max(ssize.height(), psize.height())) if page != self.emptyPage: page.polishPage() self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(True) else: self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False) # reset scrollbars for sb in [self.scrollArea.horizontalScrollBar(), self.scrollArea.verticalScrollBar()]: if sb: sb.setValue(0) self.__currentConfigurationPageName = pageName def getConfigurationPageName(self): """ Public method to get the page name of the current page. @return page name of the current page (string) """ return self.__currentConfigurationPageName def calledFromEric(self): """ Public method to check, if invoked from within eric. @return flag indicating invocation from within eric (boolean) """ return self.fromEric def getPage(self, pageName): """ Public method to get a reference to the named page. @param pageName name of the configuration page (string) @return reference to the page or None, indicating page was not loaded yet """ return self.configItems[pageName][-1] def getLexers(self): """ Public method to get a reference to the lexers dictionary. @return reference to the lexers dictionary """ return self.lexers def setPreferences(self): """ Public method called to store the selected values into the preferences storage. """ for key, pageData in list(self.configItems.items()): if pageData[-1]: pageData[-1].save() # page was loaded (and possibly modified) QApplication.processEvents() # ensure HMI is responsive def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @param button button that was clicked (QAbstractButton) """ if button == self.buttonBox.button(QDialogButtonBox.Apply): self.on_applyButton_clicked() elif button == self.buttonBox.button(QDialogButtonBox.Reset): self.on_resetButton_clicked() @pyqtSlot() def on_applyButton_clicked(self): """ Private slot called to apply the settings of the current page. """ if self.configStack.currentWidget() != self.emptyPage: page = self.configStack.currentWidget() savedState = page.saveState() page.save() self.preferencesChanged.emit() if savedState is not None: page.setState(savedState) page.polishPage() @pyqtSlot() def on_resetButton_clicked(self): """ Private slot called to reset the settings of the current page. """ if self.configStack.currentWidget() != self.emptyPage: currentPage = self.configStack.currentWidget() savedState = currentPage.saveState() pageName = self.configList.currentItem().getPageName() self.configStack.removeWidget(currentPage) if pageName == "editorHighlightingStylesPage": self.__initLexers() self.configItems[pageName][-1] = None self.showConfigurationPageByName(pageName) if savedState is not None: self.configStack.currentWidget().setState(savedState) def getExpandedEntries(self): """ Public method to get a list of expanded entries. @return list of expanded entries (list of string) """ return self.__expandedEntries @pyqtSlot(QTreeWidgetItem) def on_configList_itemCollapsed(self, item): """ Private slot handling a list entry being collapsed. @param item reference to the collapsed item (QTreeWidgetItem) """ pageName = item.data(0, Qt.UserRole) if pageName in self.__expandedEntries: self.__expandedEntries.remove(pageName) @pyqtSlot(QTreeWidgetItem) def on_configList_itemExpanded(self, item): """ Private slot handling a list entry being expanded. @param item reference to the expanded item (QTreeWidgetItem) """ pageName = item.data(0, Qt.UserRole) if pageName not in self.__expandedEntries: self.__expandedEntries.append(pageName)