class ViewSourceDialogTabber(QMainWindow): def __init__(self, parent=None, title="Source"): super(ViewSourceDialogTabber, self).__init__(parent) self.setWindowTitle(title) self.tabs = QTabWidget(self) self.tabs.setElideMode(Qt.ElideRight) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.removeTab) self.setCentralWidget(self.tabs) self.tabs.currentChanged.connect(self.updateWindowTitle) def sizeHint(self): return QSize(640, 480) def removeTab(self): self.tabs.removeTab(self.tabs.currentIndex()) def addTab(self, title="New Tab", data=""): vsd = ViewSourceDialog(self, str(title)) vsd.setPlainText(data) self.tabs.addTab(vsd, tr("Source of %s") % (title, )) self.tabs.setCurrentIndex(self.tabs.count() - 1) self.raise_() self.activateWindow() def updateWindowTitle(self): try: self.setWindowTitle( tr("Source of %s") % (self.tabs.currentWidget().windowTitle(), )) except: pass if self.tabs.count() == 0: self.hide()
class ViewSourceDialogTabber(QMainWindow): def __init__(self, parent=None, title="Source"): super(ViewSourceDialogTabber, self).__init__(parent) self.setWindowTitle(title) self.tabs = QTabWidget(self) self.tabs.setElideMode(Qt.ElideRight) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.removeTab) self.setCentralWidget(self.tabs) self.tabs.currentChanged.connect(self.updateWindowTitle) def sizeHint(self): return QSize(640, 480) def removeTab(self): self.tabs.removeTab(self.tabs.currentIndex()) def addTab(self, title="New Tab", data=""): vsd = ViewSourceDialog(self, str(title)) vsd.setPlainText(data) self.tabs.addTab(vsd, tr("Source of %s") % (title,)) self.tabs.setCurrentIndex(self.tabs.count()-1) self.raise_() self.activateWindow() def updateWindowTitle(self): try: self.setWindowTitle(tr("Source of %s") % (self.tabs.currentWidget().windowTitle(),)) except: pass if self.tabs.count() == 0: self.hide()
class SettingsDialog(QDialog): __tab_classes = [SettingsDialogGeneral, SettingsDialogUpdates] def __init__(self, main_window): super().__init__(main_window) if os.name == 'nt': self.setWindowTitle(_("Options")) else: self.setWindowTitle(_("Preferences")) layout = QVBoxLayout() self.__tabs = QTabWidget() layout.addWidget(self.__tabs) for tab_class in self.__tab_classes: self.__tabs.addTab(tab_class(self), "") self.__button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Apply) self.__button_box.button(QDialogButtonBox.Apply).clicked.connect( self.__apply_clicked) self.__button_box.accepted.connect(self.__accept_clicked) self.__button_box.rejected.connect(self.reject) layout.addWidget(self.__button_box) self.setLayout(layout) Application().event_dispatcher.subscribe(LanguageChangedEvent, self.__language_changed) self.__reload_texts() def __apply_clicked(self, checked=False): self.__apply_settings() def __accept_clicked(self): self.__apply_settings() self.accept() def __apply_settings(self): for no in range(self.__tabs.count()): self.__tabs.widget(no).apply_settings() def __language_changed(self, event): self.__reload_texts() def __reload_texts(self): self.__button_box.button(QDialogButtonBox.Ok).setText(_("Ok")) self.__button_box.button(QDialogButtonBox.Cancel).setText(_("Cancel")) self.__button_box.button(QDialogButtonBox.Apply).setText(_("Apply")) for no in range(self.__tabs.count()): widget = self.__tabs.widget(no) widget.reload_texts() self.__tabs.setTabText(no, widget.get_name())
class WholeTestView(QDialog): def __init__(self, fnames): super(WholeTestView, self).__init__() self.pageList = fnames self.numberOfPages = len(fnames) grid = QGridLayout() self.pageTabs = QTabWidget() self.tabs = {} self.closeButton = QPushButton("&Close") self.maxNormButton = QPushButton("&Max/Norm") self.pButton = QPushButton("&Previous") self.nButton = QPushButton("&Next") grid.addWidget(self.pageTabs, 1, 1, 6, 6) grid.addWidget(self.pButton, 7, 1) grid.addWidget(self.nButton, 7, 2) grid.addWidget(self.closeButton, 7, 7) grid.addWidget(self.maxNormButton, 1, 7) self.setLayout(grid) self.pButton.clicked.connect(self.previousTab) self.nButton.clicked.connect(self.nextTab) self.closeButton.clicked.connect(self.closeWindow) self.maxNormButton.clicked.connect(self.swapMaxNorm) self.setMinimumSize(500, 500) self.show() self.buildTabs() def swapMaxNorm(self): """Toggles the window size between max and normal""" if self.windowState() != Qt.WindowMaximized: self.setWindowState(Qt.WindowMaximized) else: self.setWindowState(Qt.WindowNoState) def closeEvent(self, event): self.closeWindow() def closeWindow(self): self.close() def nextTab(self): t = self.pageTabs.currentIndex() + 1 if t >= self.pageTabs.count(): t = 0 self.pageTabs.setCurrentIndex(t) self.tabs[t].forceRedrawOrSomeBullshit() def previousTab(self): t = self.pageTabs.currentIndex() - 1 if t < 0: t = self.pageTabs.count() - 1 self.pageTabs.setCurrentIndex(t) self.tabs[t].forceRedrawOrSomeBullshit() def buildTabs(self): for k in range(0, self.numberOfPages): self.tabs[k] = ExamViewWindow(self.pageList[k]) self.pageTabs.addTab(self.tabs[k], "{}".format(k + 1))
class MainWidget(QWidget): def __init__(self, width, height, manager, perm_dict): super().__init__() self.manager = manager self.perm_dict = perm_dict self.translation = Translation(perm_dict) self.setWindowTitle(Constants.WINDOW_NAME) self.resize(width, height) layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) self.tab_widget = QTabWidget() layout.addWidget(self.tab_widget) self.setLayout(layout) self.show() self.restart() def restart(self): while self.tab_widget.count() > 0: self.tab_widget.removeTab(0) # TODO uncomment once finished self.tab_widget.addTab(OverviewPage(self.manager, self.perm_dict), self.translation.get_text("overview"))
class QTabManager(object): parent_tab_widget = None max_tab_count = 7 current_index_count = 0 @classmethod def setParentWidget(self, parent_widget): self.parent_tab_widget = parent_widget @classmethod def createTab(self, title): if self.parent_tab_widget is None: self.parent_tab_widget = QTabWidget() self.parent_tab_widget.setMovable(True) if self.parent_tab_widget.count() < self.max_tab_count: figure_tab = QWidget() self.parent_tab_widget.addTab( figure_tab, title + " " + str(self.current_index_count + 1)) layout = QVBoxLayout() else: figure_tab = self.parent_tab_widget.widget( self.current_index_count) figure_tab.setWindowTitle(title + " " + str(self.current_index_count + 1)) layout = figure_tab.layout() clearLayout(layout) # Update index and connect event self.current_index_count = (self.current_index_count + 1) % self.max_tab_count return figure_tab, layout
class Form(QWidget): def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.tbw = QTabWidget() self.init_widget() def init_widget(self): """ 현재 위젯의 모양등을 초기화 """ self.setWindowTitle("Tab Widget") form_lbx = QBoxLayout(QBoxLayout.TopToBottom, parent=self) self.setLayout(form_lbx) # 탭 추가 버튼 생성 tbw_addbtn = QToolButton() self.tbw.setCornerWidget(tbw_addbtn, Qt.TopLeftCorner) # 버튼 위치 tbw_addbtn.setAutoRaise(True) # 마우스가 올라오면 올라옴 tbw_addbtn.setIcon(QIcon("plus_icon.png")) # 아이콘 지정 tbw_addbtn.clicked.connect(self.add_new_tab) # 클릭시 시그널 지정 form_lbx.addWidget(self.tbw) # 기본 탭 생성 self.add_new_tab() @pyqtSlot() def add_new_tab(self): """ 텍스트 에디트를 가진 텝을 생성 """ self.tbw.addTab(QTextEdit(), "tab #%d" % (self.tbw.count() + 1))
def subElementRect(self, element, option, widget): rect = QProxyStyle.subElementRect(element, option, widget) tabWidget = QTabWidget(widget) if not tabWidget: if element == QStyle.SE_TabWidgetLeftCorner: if not tabWidget.count(): rect.setHeight(tabWidget.cornerWidget(Qt.TopLeftCorner).height()) elif element == QStyle.SE_TabWidgetRightCorner: if not tabWidget.count(): rect.setHeight(tabWidget.cornerWidget(Qt.TopRightCorner).height()) if element == QStyle.SE_TabWidgetTabPane: if not tabWidget.count(): rect.setTop(tabWidget.cornerWidget(Qt.TopLeftCorner).height()) return rect
class ProductLists(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): super().__init__() self.tabsList = [] self.layout = QVBoxLayout(self) self.reader = JsonReader() self.tabs = QTabWidget() self.addTabs() self.layout.addWidget(self.tabs) self.setLayout(self.layout) def addTabs(self): self.lists = self.reader.readLists() for list in self.lists: self.tabsList.append(DisplayList(list)) self.tabsList.append(CreateNewList(self.reloadList)) for index, tab in enumerate(self.tabsList): if (index < len(self.tabsList) - 1): self.tabs.addTab(tab, f'{self.lists[index]["list_name"]}') else: self.tabs.addTab(tab, '+') def reloadList(self): self.lists = self.reader.readLists() list = self.lists[self.tabs.count() - 1] self.tabs.addTab(DisplayList(list), f'{list["list_name"]}')
class TransactionTab(QWidget): def __init__(self, parent): super(TransactionTab, self).__init__(parent=parent) layout = QVBoxLayout(self) self.table = TransactionTable(self) self.table.objectSelected.connect(self.set_instance) layout.addWidget(self.table) self.tabs = QTabWidget() self.debit_credit = DebitCreditTab(self) self.tabs.addTab(self.debit_credit, "Transaction") self.transfer = TransferTab(self) self.tabs.addTab(self.transfer, "Transfer") self.opening_balance = OpeningBalanceTab(self) self.tabs.addTab(self.opening_balance, "Opening Balance") layout.addWidget(self.tabs) self.setLayout(layout) self.debit_credit.refresh.connect(self.table.refresh) self.transfer.refresh.connect(self.table.refresh) def set_instance(self, instance): ix = self.get_tab_index(instance) for i in range(0, self.tabs.count()): tab: TransactionForm = self.tabs.widget(i) tab.set_instance(instance if i == ix else None) self.tabs.setCurrentIndex(ix) _types = {"C": 0, "D": 0, "T": 1, "O": 2} def get_tab_index(self, instance): return self._types.get(instance.type) if instance else 0
class MainScreen(AsyncWidget): def __init__(self, interface, parent=None): super().__init__(parent) self.interface = interface self.setWindowTitle('Main screen') self.tab_widget = QTabWidget() layout = QHBoxLayout(self) layout.addWidget(self.tab_widget) @property def tabs(self): n_tabs = self.tab_widget.count() tabs = [self.tab_widget.widget(i) for i in range(n_tabs)] return tabs def add_tab(self, tab, name): self.tab_widget.addTab(tab, name) def remove_tab(self, tab): try: index = self.tabs.index(tab) self.tab_widget.removeTab(index) except ValueError: raise ValueError(f'{tab} is not in the list of tabs')
def test_add_operator(self, operators_tab_widget: QTabWidget, controller): controller.add_operator(GaussianBlur) size = operators_tab_widget.count() assert size == 1 operator_name = list(controller.operators.keys())[0] name_in_list = operators_tab_widget.tabText(0) assert name_in_list == operator_name
class MonoidPreferencesWindow(QDialog): """ Applications Preferences window. """ def __init__(self, app, *args, **kwargs): super(MonoidPreferencesWindow, self).__init__(*args, **kwargs) self.setWindowTitle("Settings") self.app = app # Place the tab widget inside layout to get some margin. self.tabs = QTabWidget(self) self.tabs.currentChanged.connect(self.updateHeight) layout = QVBoxLayout(self) layout.addWidget(self.tabs) # Create each tab. self.generalTab = GeneralTab(self, app.settings) self.headerTab = HeaderTab(self, app.settings) # Add all the tabs. self.tabs.addTab(self.generalTab, "General") self.tabs.addTab(self.headerTab, "Header") def show(self, *args): """ Set the size to a fixed one after showing the window to disable the minimize and maximize button. """ super(MonoidPreferencesWindow, self).show(*args) self.setFixedSize(self.minimumSizeHint()) def updateHeight(self, index): """ Set the window height to the minimum required space. :param index: index of the currently selected tab """ # Resize all tabs. for i in range(self.tabs.count()): if i != index: widget = self.tabs.widget(i) widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) widget.resize(widget.sizeHint()) widget.adjustSize() widget = self.tabs.widget(index) if widget: widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) widget.resize(widget.minimumSizeHint()) widget.adjustSize() # Resize the tab widget. self.tabs.resize(self.tabs.minimumSizeHint()) self.tabs.adjustSize() # Resize the window to the fixed minimum size. self.setFixedSize(self.minimumSizeHint()) self.adjustSize()
class OuterWidget(QWidget): def __init__(self, callerObject): super(QWidget, self).__init__(callerObject) self.caller = callerObject self.layout = QVBoxLayout(self) self.tabs = QTabWidget() self.tab1 = Form(self) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.tabs.removeTab) self.tab1.refreshRead() self.tabs.addTab(self.tab1, "Home") self.layout.addWidget(self.tabs) self.setLayout(self.layout) #This function is to load a tab for Average of Temperature and Humidity def aver(self): tempAvg = np.mean(listTemperature) humidAvg = np.mean(listHumidity) dateTime = datetime.today() print("listTemperature: ", listTemperature[0:100], "\n") print('Average Temperature: {0:0.1f}'.format(tempAvg)) self.tab2 = Average(self) temper = '{0:0.2f}'.format(tempAvg) + ' C' humid = '{0:0.2f}'.format(humidAvg) + ' %' date = '{:%Y-%m-%d %H:%M}'.format(dateTime) self.tab2.textBrowser.setText(temper) self.tab2.textBrowser_2.setText(humid) self.tab2.textBrowser_3.setText(date) if self.tabs.count() < 2: averageIndex = self.tabs.addTab(self.tab2, "Mean Values") self.tabs.setCurrentIndex(averageIndex) else: self.tabs.setCurrentIndex(1) #This function is used to close this rudimentary thermostat application def closeApp(self): self.tabs.removeTab(1) self.tabs.removeTab(0) self.layout.removeWidget(self.tabs) self.caller.close() #This function is used to close the tab that was opened for Mean Data of temperature and Humidity def closeAvgTab(self): index = self.tabs.currentIndex() self.tabs.removeTab(index)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('SEASide') self.resize(1000, 560) # Connection self._config = Configuration() server_names = tuple(sorted(SERVERS.keys())) # Interface self._widg = QWidget(self) self.setCentralWidget(self._widg) # Server selection dropdown self._dropdown = QComboBox() self._dropdown.addItems(server_names) default_server_name = self._config.get_default_server() self._dropdown.setCurrentIndex( self._dropdown.findText(default_server_name)) # Connect button self._button = QPushButton('Connect') self._button.clicked.connect(self._new_tab) # Tab view self._tab_view = QTabWidget() self._tab_view.setTabsClosable(True) self._tab_view.tabCloseRequested.connect(self._close_tab) # Default tab default_tab = MasterTab(SERVERS[self._config.get_default_server()], self._config) self._tab_view.addTab(default_tab, default_server_name) # Layout setup conn_layout = QHBoxLayout() top_layout = QVBoxLayout() # conn_layout.addStretch(1) conn_layout.addWidget(self._button) conn_layout.addWidget(self._dropdown) conn_widg = QWidget() conn_widg.setLayout(conn_layout) top_layout.addWidget(conn_widg) top_layout.addWidget(self._tab_view) self._widg.setLayout(top_layout) @pyqtSlot() def _new_tab(self): server_name = str(self._dropdown.currentText()) server = SERVERS[server_name] self._config.set_default_server(server_name) tab = MasterTab(server, self._config) self._tab_view.addTab(tab, server_name) self._tab_view.setCurrentIndex(self._tab_view.count() - 1) @pyqtSlot(int) def _close_tab(self, index): conn = self._tab_view.widget(index).get_connection() conn.close_connection() self._tab_view.removeTab(index) def get_username(self): return self._config.get_username()
class ByteBufferWidget(QWidget): on_data_selected = pyqtSignal(QObject) def __init__(self): super().__init__() self.initUI() def initUI(self): #toolbar = QToolBar() #self.splitIntoPacketsAction = toolbar.addAction("Split into packets") #self.splitIntoPacketsAction.triggered.connect(self.splitIntoPackets) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.tabWidget = QTabWidget() self.tabWidget.setContentsMargins(0, 0, 0, 0) self.tabWidget.setDocumentMode(True) layout.addWidget(self.tabWidget) self.textbox = HexView2() self.textbox.on_data_selected.connect(self.on_data_selected.emit) self.textbox.onNewSubflowCategory.connect(self.newSubflowCategory) self.textbox.formatInfoUpdated.connect(self.onFormatInfoUpdated) self.tabWidget.addTab(self.textbox, "Raw buffer") #layout.addWidget(toolbar) def setContents(self, bufObj): self.bufferObject = bufObj self.setWindowTitle(str(bufObj)) self.textbox.setBuffer(bufObj) self.bufferObject.on_new_data.connect(self.onNewData) def onFormatInfoUpdated(self): self.tabWidget.clear() self.tabWidget.addTab(self.textbox, "Raw buffer") def newSubflowCategory(self, category, parse_context): for i in range(self.tabWidget.count()): if self.tabWidget.tabText(i) == category: break widget = PacketListWidget() widget.setContents(parse_context.subflow_categories[category]) self.tabWidget.addTab(widget, category) widget.on_data_selected.connect(self.on_data_selected.emit) def onNewData(self): #self.textbox.showHex(bufObj.buffer) self.textbox.redraw() def splitIntoPackets(self): pass def run_ndis(self): pass
class SesionTab(QWidget): def __init__(self, conn, confName, holder=None, parent=None): super(SesionTab, self).__init__(parent) self.conn = conn self.confName = confName self.holder = holder self.tabQueries = QTabWidget() self.tabQueries.setTabShape(QTabWidget.Triangular) meatLayout = QVBoxLayout() meatLayout.addWidget(self.tabQueries) self.setLayout(meatLayout) self.addQuery() def addQuery(self): #self.tabQueries.addTab(QueryTab(self.conn,self.confName,holder=self.holder),"{}:{}".format(self.confName, self.tabQueries.count() +1)) self.tabQueries.addTab( myQueryTab(self.conn, holder=self.holder), "{}:{}".format(self.confName, self.tabQueries.count() + 1)) self.tabQueries.setCurrentIndex(self.tabQueries.count() - 1) def openQuery(self): #self.tabQueries.addTab(QueryTab(self.conn,self.confName,holder=self.holder),"{}:{}".format(self.confName, #self.tabQueries.count() +1)) #self.tabQueries.setCurrentIndex(self.tabQueries.count() -1) self.tabQueries.currentWidget().readQuery() def saveQuery(self): self.tabQueries.currentWidget().writeQuery() def saveAsQuery(self): self.tabQueries.currentWidget().writeQuery(True) def closeQuery(self): tabId = self.tabQueries.currentIndex() self.tabQueries.removeTab(tabId) def execute(self): self.tabQueries.currentWidget().execute() def reformat(self): self.tabQueries.currentWidget().reformat()
class CueSettings(QDialog): on_apply = QtCore.pyqtSignal(dict) def __init__(self, widgets=[], cue=None, check=False, **kwargs): super().__init__(**kwargs) conf = {} if(cue is not None): conf = deepcopy(cue.properties()) self.setWindowTitle(conf['name']) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMaximumSize(635, 530) self.setMinimumSize(635, 530) self.resize(635, 530) self.sections = QTabWidget(self) self.sections.setGeometry(QtCore.QRect(5, 10, 625, 470)) wsize = QtCore.QSize(625, 470 - self.sections.tabBar().height()) for widget in widgets: widget = widget(wsize, cue) widget.set_configuration(conf) widget.enable_check(check) self.sections.addTab(widget, widget.Name) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setGeometry(10, 490, 615, 30) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Apply) self.dialogButtons.rejected.connect(self.reject) self.dialogButtons.accepted.connect(self.accept) apply = self.dialogButtons.button(QDialogButtonBox.Apply) apply.clicked.connect(self.apply) def accept(self): self.apply() super().accept() def apply(self): new_conf = {} for n in range(self.sections.count()): deep_update(new_conf, self.sections.widget(n).get_configuration()) self.on_apply.emit(new_conf)
class ExcelTabContainer(QWidget): emit_dict = pyqtSignal(list) graph_xaxis = pyqtSignal(list) def __init__(self, title='Text Window', parent=None): super(ExcelTabContainer, self).__init__(parent) self.setWindowTitle(title) self.tabs = QTabWidget(self) v_box_layout = QVBoxLayout() v_box_layout.addWidget(self.tabs, alignment=Qt.AlignCenter) h_box = QHBoxLayout() h_box.addLayout(v_box_layout) self.setLayout(h_box) self.tabs.currentChanged.connect(self.tab_change_event) self.hide() def tab_change_event(self): index = self.tabs.currentIndex() handle = self.tabs.widget(index).children() try: use_handle = handle[1] use_handle.selected_data.connect(self.get_page_data) except Exception as e: print("passed due to {}".format(e)) def get_page_data(self, stuff): try: indexices = self.tabs.count() for thing in stuff: client = thing.get_name() for list_position, curve in enumerate(thing.curves): for index in range(indexices): value = self.tabs.widget( index).children()[1].return_cell_value( client, curve) thing.data[curve].append('%s-%s' % (index, value)) self.emit_dict.emit(stuff) except Exception as e: print("passed due to {}".format(e)) def append_spreadsheet(self, spreadsheet, sheet_title): tab = QWidget(self) vBoxlayout = QVBoxLayout() excel_window = ExcelPageWidget(spreadsheet, sheet_title) vBoxlayout.addWidget(excel_window, alignment=Qt.AlignCenter) h_box = QHBoxLayout() h_box.addLayout(vBoxlayout) tab.setLayout(h_box) self.tabs.addTab(tab, sheet_title) self.show()
class BrowserWindow(QMainWindow): name = "PyQt5-WebBrowser" version = "2.0 Beta 3" date = "2018.12.03" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle(self.name + " " + self.version) self.setWindowIcon(QIcon('Assets/main.png')) self.resize(1200, 900) self.tabs = QTabWidget() self.tabs.setTabsClosable(True) self.tabs.setMovable(True) self.tabs.setTabShape(0) self.setCentralWidget(self.tabs) self.tabs.tabCloseRequested.connect(self.close_current_tab) # self.tabs.currentChanged.connect(lambda i: self.setWindowTitle(self.tabs.tabText(i) + " - " + self.name)) self.init_tab = BrowserTab(self) self.init_tab.browser.load(QUrl("http://www.hao123.com/")) self.add_new_tab(self.init_tab) def add_blank_tab(self): blank_tab = BrowserTab(self) self.add_new_tab(blank_tab) def add_new_tab(self, tab): i = self.tabs.addTab(tab, "") self.tabs.setCurrentIndex(i) tab.back_button.triggered.connect(tab.browser.back) tab.next_button.triggered.connect(tab.browser.forward) tab.stop_button.triggered.connect(tab.browser.stop) tab.refresh_button.triggered.connect(tab.browser.reload) tab.home_button.triggered.connect(tab.navigate_to_home) tab.enter_button.triggered.connect(tab.navigate_to_url) tab.add_button.triggered.connect(self.add_blank_tab) tab.set_button.triggered.connect(tab.create_about_window) tab.url_text_bar.returnPressed.connect(tab.navigate_to_url) tab.browser.urlChanged.connect(tab.renew_urlbar) tab.browser.loadProgress.connect(tab.renew_progress_bar) tab.browser.titleChanged.connect(lambda title: (self.tabs.setTabText( i, title), self.tabs.setTabToolTip(i, title))) tab.browser.iconChanged.connect( lambda icon: self.tabs.setTabIcon(i, icon)) def close_current_tab(self, i): if self.tabs.count() > 1: self.tabs.removeTab(i) else: self.close()
class TAOutputTab(QWidget): def __init__(self, ctx): super(TAOutputTab, self).__init__() self.ctx = ctx stacked_widget = QWidget() self.stacked_layout = QStackedLayout() self.stacked_layout.addWidget(self.create_empty_widget()) self.stacked_layout.addWidget(self.create_data_widget()) stacked_widget.setLayout(self.stacked_layout) layout = QHBoxLayout() layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(stacked_widget) self.setLayout(layout) def display_empty(self): self.stacked_layout.setCurrentIndex(0) def display_data(self): self.stacked_layout.setCurrentIndex(1) def create_empty_widget(self): label = QLabel("You first have to successfully run the allocation.") label.setAlignment(Qt.AlignCenter) return label def create_data_widget(self): self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.South) return self.tabs def not_empty(self): m_tab = self.tableWidget.rowCount() n_tab = self.tableWidget.columnCount() return any( self.tableWidget.item(i, j).text() for j in range(n_tab) for i in range(m_tab)) def update_tables_from_data(self): for i in range(self.tabs.count()): self.tabs.removeTab(0) for i in range(len(self.ctx.app_data.results)): self.tabs.addTab(TAAllocationOutputTab(self.ctx, i), "Allocation {}".format(i + 1)) return
class MacroWidget(QWidget): # Tabs containing both int and active macros def __init__(self, client, *args, **kwargs): self.client = client QWidget.__init__(self, *args, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.tab_widg = QTabWidget() self.active_widg = ActiveMacroWidget(client) self.active_ind = self.tab_widg.count() self.tab_widg.addTab(self.active_widg, "Active") self.int_widg = IntMacroWidget(client) self.int_ind = self.tab_widg.count() self.tab_widg.addTab(self.int_widg, "Intercepting") self.warning_widg = QLabel( "<h1>Warning! Macros may cause instability</h1><p>Macros load and run python files into the Guppy process. If you're not careful when you write them you may cause Guppy to crash. If an active macro ends up in an infinite loop you may need to force kill the application when you quit.</p><p><b>PROCEED WITH CAUTION</b></p>" ) self.warning_widg.setWordWrap(True) self.tab_widg.addTab(self.warning_widg, "Warning") self.layout().addWidget(self.tab_widg) def show_active(self): self.tab_widg.setCurrentIndex(self.active_ind) def show_int(self): self.tab_widg.setCurrentIndex(self.int_ind) def add_requests(self, reqs): # Add requests to active macro inputw self.active_widg.add_requests(reqs)
class WritePanel(SubWindow, WindowSystemController): ''' 'Write' main panel. Detachable ''' def __init__(self, parent=None, parent_window_system_controller=None): ''' ''' super(WritePanel, self).__init__( parent=parent, parent_window_system_controller=parent_window_system_controller) self.setWindowTitle(_("Write")) self.setObjectName("write_sub_window") self.dock_system = DockSystem( self, self, DockSystem.DockTypes.WritePanelDock) # connect core signal to open sheets : cfg.core.tree_sheet_manager.sheet_is_opening.connect( self.open_write_tab) # Project tree view dock : self.dock_system.add_dock("write-tree-dock", Qt.LeftDockWidgetArea) # set TabWidget: self.tab_widget = QTabWidget(self) self.setCentralWidget(self.tab_widget) # subscribe cfg.core.subscriber.subscribe_update_func_to_domain( self._clear_project, "core.project.close") def open_write_tab(self, tree_sheet): new_write_tab = WriteTab(self, self) new_write_tab.tree_sheet = tree_sheet self.tab_widget.addTab(new_write_tab, new_write_tab.tab_title) # temp for test: new_write_tab.dock_system.add_dock("properties-dock") def _clear_project(self): # TODO: make that cleaner for i in range(0, self.tab_widget.count()): widget = self.tab_widget.widget(i) widget.close() widget.deleteLater() self.tab_widget.clear()
class WritePanel(SubWindow, WindowSystemController): ''' 'Write' main panel. Detachable ''' def __init__(self, parent=None, parent_window_system_controller=None): ''' ''' super(WritePanel, self).__init__( parent=parent, parent_window_system_controller=parent_window_system_controller) self.setWindowTitle(_("Write")) self.setObjectName("write_sub_window") self.dock_system = DockSystem(self, self, DockSystem.DockTypes.WritePanelDock) # connect core signal to open sheets : cfg.core.tree_sheet_manager.sheet_is_opening.connect( self.open_write_tab) # Project tree view dock : self.dock_system.add_dock("write-tree-dock", Qt.LeftDockWidgetArea) # set TabWidget: self.tab_widget = QTabWidget(self) self.setCentralWidget(self.tab_widget) # subscribe cfg.core.subscriber.subscribe_update_func_to_domain( self._clear_project, "core.project.close") def open_write_tab(self, tree_sheet): new_write_tab = WriteTab(self, self) new_write_tab.tree_sheet = tree_sheet self.tab_widget.addTab(new_write_tab, new_write_tab.tab_title) # temp for test: new_write_tab.dock_system.add_dock("properties-dock") def _clear_project(self): # TODO: make that cleaner for i in range(0, self.tab_widget.count()): widget = self.tab_widget.widget(i) widget.close() widget.deleteLater() self.tab_widget.clear()
class VectorFrame(GenericFrame): def __init__(self, parent=None): super().__init__(QHBoxLayout(), 'Vector Frame', parent=parent) self.tabs = QTabWidget() self.vectors = None self.init_frame() def init_frame(self): self.setFrameShape(QFrame.StyledPanel) self.populate_tabs() self.layout.addWidget(self.tabs) def populate_tabs(self): self.tabs.clear() self.vectors = get_vector_list() if self.vectors: for vector in self.vectors: print(vector) self.initialize_tab(vector) # TODO: Program to open vector from vector db # self.tabs.addTab(QWidget(), '+') self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(lambda i: self.tabs.removeTab(i)) def initialize_tab(self, vector): frame = GenericFrame(QHBoxLayout(), 'vector tab', self) splitter = QSplitter(Qt.Horizontal) # TODO: Make generic table class, populate with vector nodes & significant log entries splitter.addWidget(NodeTableFrame()) splitter.addWidget(GraphFrame(vector, frame)) splitter.setSizes([600, 600]) frame.layout.addWidget(splitter) self.tabs.addTab(frame, vector.get("Name")) def delete_tab(self, vector): for i in range(0, self.tabs.count()): if vector.get("Name") == self.tabs.tabText(i): self.tabs.removeTab(i)
class AccountsTabsWidget(QWidget): def __init__(self, accounts_thread): super().__init__() self.accounts_thread = accounts_thread layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) self.tab_widget = QTabWidget() layout.addWidget(self.tab_widget) self.setLayout(layout) self.show() self.restart() def restart(self): while self.tab_widget.count() > 0: self.tab_widget.removeTab(0) self.tab_widget.addTab(Overview(self.accounts_thread), Translation.get_text("overview")) self.tab_widget.addTab(AddAccount(self.accounts_thread), Translation.get_text("add_accounts"))
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.tabs = QTabWidget() self.init_ui() def update_file(self): self.tabs.clear() for f in QApplication.instance().diagram_list(): self.add(f) def init_ui(self): file_menu = QMenu("&File", self) update_action = file_menu.addAction("update") update_action.triggered.connect(self.update_file) quit_action = file_menu.addAction("E&xit") quit_action.triggered.connect(QApplication.instance().quit) self.menuBar().addMenu(file_menu) self.setCentralWidget(self.tabs) def add(self, file): tab_name = os.path.basename(file) view = None for i in range(self.tabs.count()): if self.tabs.tabText(i) == tab_name: view = self.tabs.widget(i) break if view is None: m = magic.from_file(file) if m[:3] == 'PNG': view = PngView() elif m[:3] == 'SVG': view = SvgView() self.tabs.addTab(view, tab_name) view.open_file(file)
class DiffWindows(QWidget): def __init__(self): super().__init__() self.setWindowTitle("Diffs") self.app = QCoreApplication.instance() self.git = self.app.findChild(Git) self.init_tab() self.grid = QGridLayout() self.grid.setSpacing(5) self.grid.setContentsMargins(5, 5, 5, 5) self.grid.addWidget(self.tabw, 0, 0, 1, -1) self.setLayout(self.grid) self.grid.addWidget(QPushButton("Refresh"), 1, 0) self.grid.itemAt(1).widget().clicked.connect( self.git.refresh) # the function refresh_tab after refresh git def init_tab(self): self.tabw = QTabWidget() tab_lst = copy(VULNS_INITIAL), DBHandler.vulns(), DBHandler.vulns_git( ), add_vuln_initial obj = ObjectsGit("vulns", tab_lst, self) self.tabw.addTab(obj, "Vulns") tab_lst = copy(AUDITORS_INITIAL), DBHandler.auditors( ), DBHandler.auditors_git(), add_auditor_initial obj = ObjectsGit("auditors", tab_lst, self) self.tabw.addTab(obj, "Auditors") tab_lst = copy(AUDITORS_INITIAL), DBHandler.clients( ), DBHandler.clients_git(), add_auditor_initial obj = ObjectsGit("clients", tab_lst, self) self.tabw.addTab(obj, "Clients") def refresh_tab_widget(self): """Refresh all the ObjectGit in the window""" for i in range(self.tabw.count()): self.tabw.widget(i).refresh_tab_widget()
class PluginPreferences(QWidget): """ Plugins section widget in NINJA-IDE Preferences """ def __init__(self): super(PluginPreferences, self).__init__() self.plugin_manager = plugin_manager.PluginManager() vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) #load widgets self._load_widgets() def _load_widgets(self): logger.info("Loading plugins preferences widgets") #Collect the preferences widget for each active plugin for plugin in self.plugin_manager.get_active_plugins(): plugin_name = plugin.metadata.get('name') try: preferences_widget = plugin.get_preferences_widget() if preferences_widget: self._tabs.addTab(preferences_widget, plugin_name) except Exception as reason: logger.error("Unable to add the preferences widget (%s): %s", plugin_name, reason) continue def save(self): logger.info("Saving plugins preferences") for i in range(self._tabs.count()): try: self._tabs.widget(i).save() except Exception as reason: logger.error("Unable to save preferences (%s): %s", self._tabs.tabText(i), reason) continue
class ProtocolViewerDialog(QDialog): def __init__(self, **kwargs): super().__init__(**kwargs) self.setWindowTitle('Protocol Viewer') self.setMinimumSize(600, 800) self.setLayout(QVBoxLayout()) self.tab_widget = QTabWidget(parent=self) self.tab_widget.tabBar().setDrawBase(False) for tab in protocols.Tabs: self.tab_widget.addTab(tab(parent=self.tab_widget), tab.tabname) self.layout().addWidget(self.tab_widget) def done(self, *args, **kwargs): super().done(*args, **kwargs) for idx in range(self.tab_widget.count()): self.tab_widget.widget(idx).on_close() def view(self): return self.tab_widget
class ReTextWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.resize(950, 700) screenRect = QDesktopWidget().screenGeometry() if globalSettings.windowGeometry: self.restoreGeometry(globalSettings.windowGeometry) else: self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2) if not screenRect.contains(self.geometry()): self.showMaximized() if globalSettings.iconTheme: QIcon.setThemeName(globalSettings.iconTheme) if QIcon.themeName() in ('hicolor', ''): if not QFile.exists(icon_path + 'document-new.png'): QIcon.setThemeName(get_icon_theme()) if QFile.exists(icon_path+'retext.png'): self.setWindowIcon(QIcon(icon_path+'retext.png')) elif QFile.exists('/usr/share/pixmaps/retext.png'): self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png')) else: self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor'))) self.tabWidget = QTabWidget(self) self.initTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.currentChanged.connect(self.changeIndex) self.tabWidget.tabCloseRequested.connect(self.closeTab) toolBar = QToolBar(self.tr('File toolbar'), self) self.addToolBar(Qt.TopToolBarArea, toolBar) self.editBar = QToolBar(self.tr('Edit toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.editBar) self.searchBar = QToolBar(self.tr('Search toolbar'), self) self.addToolBar(Qt.BottomToolBarArea, self.searchBar) toolBar.setVisible(not globalSettings.hideToolBar) self.editBar.setVisible(not globalSettings.hideToolBar) self.actionNew = self.act(self.tr('New'), 'document-new', self.createNew, shct=QKeySequence.New) self.actionNew.setPriority(QAction.LowPriority) self.actionOpen = self.act(self.tr('Open'), 'document-open', self.openFile, shct=QKeySequence.Open) self.actionOpen.setPriority(QAction.LowPriority) self.actionSetEncoding = self.act(self.tr('Set encoding'), trig=self.showEncodingDialog) self.actionSetEncoding.setEnabled(False) self.actionReload = self.act(self.tr('Reload'), 'view-refresh', lambda: self.currentTab.readTextFromFile()) self.actionReload.setEnabled(False) self.actionSave = self.act(self.tr('Save'), 'document-save', self.saveFile, shct=QKeySequence.Save) self.actionSave.setEnabled(False) self.actionSave.setPriority(QAction.LowPriority) self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as', self.saveFileAs, shct=QKeySequence.SaveAs) self.actionNextTab = self.act(self.tr('Next tab'), 'go-next', lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown) self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous', lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp) self.actionPrint = self.act(self.tr('Print'), 'document-print', self.printFile, shct=QKeySequence.Print) self.actionPrint.setPriority(QAction.LowPriority) self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview', self.printPreview) self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml) self.actionChangeEditorFont = self.act(self.tr('Change editor font'), trig=self.changeEditorFont) self.actionChangePreviewFont = self.act(self.tr('Change preview font'), trig=self.changePreviewFont) self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find) self.actionSearch.setCheckable(True) self.actionSearch.triggered[bool].connect(self.searchBar.setVisible) self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged) self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview) if QIcon.hasThemeIcon('document-preview'): self.actionPreview.setIcon(QIcon.fromTheme('document-preview')) elif QIcon.hasThemeIcon('preview-file'): self.actionPreview.setIcon(QIcon.fromTheme('preview-file')) elif QIcon.hasThemeIcon('x-office-document'): self.actionPreview.setIcon(QIcon.fromTheme('x-office-document')) else: self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png')) self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L, trigbool=self.enableLivePreview) menuPreview = QMenu() menuPreview.addAction(self.actionLivePreview) self.actionPreview.setMenu(menuPreview) self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T, trigbool=lambda x: self.currentTab.editBox.enableTableMode(x)) if ReTextFakeVimHandler: self.actionFakeVimMode = self.act(self.tr('FakeVim mode'), shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode) if globalSettings.useFakeVim: self.actionFakeVimMode.setChecked(True) self.enableFakeVimMode(True) self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen', shct=Qt.Key_F11, trigbool=self.enableFullScreen) self.actionFullScreen.setPriority(QAction.LowPriority) self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system', trig=self.openConfigDialog) self.actionConfig.setMenuRole(QAction.PreferencesRole) self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml) self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf) self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf) self.getExportExtensionsList() self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit) self.actionQuit.setMenuRole(QAction.QuitRole) self.actionQuit.triggered.connect(self.close) self.actionUndo = self.act(self.tr('Undo'), 'edit-undo', lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo) self.actionRedo = self.act(self.tr('Redo'), 'edit-redo', lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo) self.actionCopy = self.act(self.tr('Copy'), 'edit-copy', lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy) self.actionCut = self.act(self.tr('Cut'), 'edit-cut', lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut) self.actionPaste = self.act(self.tr('Paste'), 'edit-paste', lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste) self.actionUndo.setEnabled(False) self.actionRedo.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.setEnabled(False) qApp = QApplication.instance() qApp.clipboard().dataChanged.connect(self.clipboardDataChanged) self.clipboardDataChanged() if enchant_available: self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck) self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale) self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit) self.actionWebKit.setChecked(globalSettings.useWebKit) self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir) self.actionFind = self.act(self.tr('Next'), 'go-next', self.find, shct=QKeySequence.FindNext) self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous', lambda: self.find(back=True), shct=QKeySequence.FindPrevious) self.actionCloseSearch = self.act(self.tr('Close'), 'window-close', lambda: self.searchBar.setVisible(False)) self.actionCloseSearch.setPriority(QAction.LowPriority) self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp) self.aboutWindowTitle = self.tr('About ReText') self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog) self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAboutQt = self.act(self.tr('About Qt')) self.actionAboutQt.setMenuRole(QAction.AboutQtRole) self.actionAboutQt.triggered.connect(qApp.aboutQt) availableMarkups = markups.get_available_markups() if not availableMarkups: print('Warning: no markups are available!') self.defaultMarkup = availableMarkups[0] if availableMarkups else None if globalSettings.defaultMarkup: mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup) if mc and mc.available(): self.defaultMarkup = mc if len(availableMarkups) > 1: self.chooseGroup = QActionGroup(self) markupActions = [] for markup in availableMarkups: markupAction = self.act(markup.name, trigbool=self.markupFunction(markup)) if markup == self.defaultMarkup: markupAction.setChecked(True) self.chooseGroup.addAction(markupAction) markupActions.append(markupAction) self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertFormatting('bold')) self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertFormatting('italic')) self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, trig=lambda: self.insertFormatting('underline')) self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering', 'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote') self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr', 'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', 'rarr', 'rsquo', 'times') self.formattingBox = QComboBox(self.editBar) self.formattingBox.addItem(self.tr('Formatting')) self.formattingBox.addItems(self.usefulTags) self.formattingBox.activated[str].connect(self.insertFormatting) self.symbolBox = QComboBox(self.editBar) self.symbolBox.addItem(self.tr('Symbols')) self.symbolBox.addItems(self.usefulChars) self.symbolBox.activated.connect(self.insertSymbol) self.updateStyleSheet() menubar = self.menuBar() menuFile = menubar.addMenu(self.tr('File')) menuEdit = menubar.addMenu(self.tr('Edit')) menuHelp = menubar.addMenu(self.tr('Help')) menuFile.addAction(self.actionNew) menuFile.addAction(self.actionOpen) self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent')) self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles) menuFile.addAction(self.actionShow) menuFile.addAction(self.actionSetEncoding) menuFile.addAction(self.actionReload) menuFile.addSeparator() menuFile.addAction(self.actionSave) menuFile.addAction(self.actionSaveAs) menuFile.addSeparator() menuFile.addAction(self.actionNextTab) menuFile.addAction(self.actionPrevTab) menuFile.addSeparator() menuExport = menuFile.addMenu(self.tr('Export')) menuExport.addAction(self.actionSaveHtml) menuExport.addAction(self.actionOdf) menuExport.addAction(self.actionPdf) if self.extensionActions: menuExport.addSeparator() for action, mimetype in self.extensionActions: menuExport.addAction(action) menuExport.aboutToShow.connect(self.updateExtensionsVisibility) menuFile.addAction(self.actionPrint) menuFile.addAction(self.actionPrintPreview) menuFile.addSeparator() menuFile.addAction(self.actionQuit) menuEdit.addAction(self.actionUndo) menuEdit.addAction(self.actionRedo) menuEdit.addSeparator() menuEdit.addAction(self.actionCut) menuEdit.addAction(self.actionCopy) menuEdit.addAction(self.actionPaste) menuEdit.addSeparator() if enchant_available: menuSC = menuEdit.addMenu(self.tr('Spell check')) menuSC.addAction(self.actionEnableSC) menuSC.addAction(self.actionSetLocale) menuEdit.addAction(self.actionSearch) menuEdit.addAction(self.actionChangeEditorFont) menuEdit.addAction(self.actionChangePreviewFont) menuEdit.addSeparator() if len(availableMarkups) > 1: self.menuMode = menuEdit.addMenu(self.tr('Default markup')) for markupAction in markupActions: self.menuMode.addAction(markupAction) menuFormat = menuEdit.addMenu(self.tr('Formatting')) menuFormat.addAction(self.actionBold) menuFormat.addAction(self.actionItalic) menuFormat.addAction(self.actionUnderline) menuEdit.addAction(self.actionWebKit) menuEdit.addSeparator() menuEdit.addAction(self.actionViewHtml) menuEdit.addAction(self.actionPreview) menuEdit.addAction(self.actionTableMode) if ReTextFakeVimHandler: menuEdit.addAction(self.actionFakeVimMode) menuEdit.addSeparator() menuEdit.addAction(self.actionFullScreen) menuEdit.addAction(self.actionConfig) menuHelp.addAction(self.actionHelp) menuHelp.addSeparator() menuHelp.addAction(self.actionAbout) menuHelp.addAction(self.actionAboutQt) toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) toolBar.addAction(self.actionNew) toolBar.addSeparator() toolBar.addAction(self.actionOpen) toolBar.addAction(self.actionSave) toolBar.addAction(self.actionPrint) toolBar.addSeparator() toolBar.addAction(self.actionPreview) toolBar.addAction(self.actionFullScreen) self.editBar.addAction(self.actionUndo) self.editBar.addAction(self.actionRedo) self.editBar.addSeparator() self.editBar.addAction(self.actionCut) self.editBar.addAction(self.actionCopy) self.editBar.addAction(self.actionPaste) self.editBar.addSeparator() self.editBar.addWidget(self.formattingBox) self.editBar.addWidget(self.symbolBox) self.searchEdit = QLineEdit(self.searchBar) self.searchEdit.setPlaceholderText(self.tr('Search')) self.searchEdit.returnPressed.connect(self.find) self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar) self.searchBar.addWidget(self.searchEdit) self.searchBar.addSeparator() self.searchBar.addWidget(self.csBox) self.searchBar.addAction(self.actionFindPrev) self.searchBar.addAction(self.actionFind) self.searchBar.addAction(self.actionCloseSearch) self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBar.setVisible(False) self.autoSaveEnabled = globalSettings.autoSave if self.autoSaveEnabled: timer = QTimer(self) timer.start(60000) timer.timeout.connect(self.saveAll) self.ind = None if enchant_available: self.sl = globalSettings.spellCheckLocale if self.sl: try: enchant.Dict(self.sl) except Exception as e: print(e, file=sys.stderr) self.sl = None if globalSettings.spellCheck: self.actionEnableSC.setChecked(True) self.fileSystemWatcher = QFileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self.fileChanged) def iterateTabs(self): for i in range(self.tabWidget.count()): yield self.tabWidget.widget(i).tab def updateStyleSheet(self): if globalSettings.styleSheet: sheetfile = QFile(globalSettings.styleSheet) sheetfile.open(QIODevice.ReadOnly) self.ss = QTextStream(sheetfile).readAll() sheetfile.close() else: palette = QApplication.palette() self.ss = 'html { color: %s; }\n' % palette.color(QPalette.WindowText).name() self.ss += 'td, th { border: 1px solid #c3c3c3; padding: 0 3px 0 3px; }\n' self.ss += 'table { border-collapse: collapse; }\n' def initTabWidget(self): def dragEnterEvent(e): e.acceptProposedAction() def dropEvent(e): fn = bytes(e.mimeData().data('text/plain')).decode().rstrip() if fn.startswith('file:'): fn = QUrl(fn).toLocalFile() self.openFileWrapper(fn) self.tabWidget.setTabsClosable(True) self.tabWidget.setAcceptDrops(True) self.tabWidget.setMovable(True) self.tabWidget.dragEnterEvent = dragEnterEvent self.tabWidget.dropEvent = dropEvent def act(self, name, icon=None, trig=None, trigbool=None, shct=None): if not isinstance(shct, QKeySequence): shct = QKeySequence(shct) if icon: action = QAction(self.actIcon(icon), name, self) else: action = QAction(name, self) if trig: action.triggered.connect(trig) elif trigbool: action.setCheckable(True) action.triggered[bool].connect(trigbool) if shct: action.setShortcut(shct) return action def actIcon(self, name): return QIcon.fromTheme(name, QIcon(icon_path+name+'.png')) def printError(self): import traceback print('Exception occured while parsing document:', file=sys.stderr) traceback.print_exc() def createTab(self, fileName): self.currentTab = ReTextTab(self, fileName, previewState=int(globalSettings.livePreviewByDefault)) self.tabWidget.addTab(self.currentTab.getSplitter(), self.tr("New document")) def closeTab(self, ind): if self.maybeSave(ind): if self.tabWidget.count() == 1: self.createTab("") currentWidget = self.tabWidget.widget(ind) if currentWidget.tab.fileName: self.fileSystemWatcher.removePath(currentWidget.tab.fileName) del currentWidget.tab self.tabWidget.removeTab(ind) def docTypeChanged(self): markupClass = self.currentTab.getMarkupClass() if type(self.currentTab.markup) != markupClass: self.currentTab.setMarkupClass(markupClass) self.currentTab.updatePreviewBox() dtMarkdown = (markupClass == markups.MarkdownMarkup) dtMkdOrReST = dtMarkdown or (markupClass == markups.ReStructuredTextMarkup) self.formattingBox.setEnabled(dtMarkdown) self.symbolBox.setEnabled(dtMarkdown) self.actionUnderline.setEnabled(dtMarkdown) self.actionBold.setEnabled(dtMkdOrReST) self.actionItalic.setEnabled(dtMkdOrReST) canReload = bool(self.currentTab.fileName) and not self.autoSaveActive() self.actionSetEncoding.setEnabled(canReload) self.actionReload.setEnabled(canReload) def changeIndex(self, ind): self.currentTab = self.tabWidget.currentWidget().tab editBox = self.currentTab.editBox previewState = self.currentTab.previewState self.actionUndo.setEnabled(editBox.document().isUndoAvailable()) self.actionRedo.setEnabled(editBox.document().isRedoAvailable()) self.actionCopy.setEnabled(editBox.textCursor().hasSelection()) self.actionCut.setEnabled(editBox.textCursor().hasSelection()) self.actionPreview.setChecked(previewState >= PreviewLive) self.actionLivePreview.setChecked(previewState == PreviewLive) self.actionTableMode.setChecked(editBox.tableModeEnabled) self.editBar.setEnabled(previewState < PreviewNormal) self.ind = ind if self.currentTab.fileName: self.setCurrentFile() else: self.setWindowTitle(self.tr('New document') + '[*]') self.docTypeChanged() self.modificationChanged(editBox.document().isModified()) editBox.setFocus(Qt.OtherFocusReason) def changeEditorFont(self): font, ok = QFontDialog.getFont(globalSettings.editorFont, self) if ok: globalSettings.editorFont = font for tab in self.iterateTabs(): tab.editBox.updateFont() def changePreviewFont(self): font, ok = QFontDialog.getFont(globalSettings.font, self) if ok: globalSettings.font = font for tab in self.iterateTabs(): tab.updatePreviewBox() def preview(self, viewmode): self.currentTab.previewState = viewmode * 2 self.actionLivePreview.setChecked(False) self.editBar.setDisabled(viewmode) self.currentTab.updateBoxesVisibility() if viewmode: self.currentTab.updatePreviewBox() def enableLivePreview(self, livemode): self.currentTab.previewState = int(livemode) self.actionPreview.setChecked(livemode) self.editBar.setEnabled(True) self.currentTab.updateBoxesVisibility() if livemode: self.currentTab.updatePreviewBox() def enableWebKit(self, enable): globalSettings.useWebKit = enable for i in range(self.tabWidget.count()): splitter = self.tabWidget.widget(i) tab = splitter.tab tab.previewBox.disconnectExternalSignals() tab.previewBox.setParent(None) tab.previewBox.deleteLater() tab.previewBox = tab.createPreviewBox(tab.editBox) tab.previewBox.setMinimumWidth(125) splitter.addWidget(tab.previewBox) splitter.setSizes((50, 50)) tab.updatePreviewBox() tab.updateBoxesVisibility() def enableCopy(self, copymode): self.actionCopy.setEnabled(copymode) self.actionCut.setEnabled(copymode) def enableFullScreen(self, yes): if yes: self.showFullScreen() else: self.showNormal() def openConfigDialog(self): dlg = ConfigDialog(self) dlg.setWindowTitle(self.tr('Preferences')) dlg.show() def enableFakeVimMode(self, yes): globalSettings.useFakeVim = yes if yes: FakeVimMode.init(self) for tab in self.iterateTabs(): tab.installFakeVimHandler() else: FakeVimMode.exit(self) def enableSpellCheck(self, yes): if yes: self.setAllDictionaries(enchant.Dict(self.sl or None)) else: self.setAllDictionaries(None) globalSettings.spellCheck = yes def setAllDictionaries(self, dictionary): for tab in self.iterateTabs(): hl = tab.highlighter hl.dictionary = dictionary hl.rehighlight() def changeLocale(self): if self.sl: localedlg = LocaleDialog(self, defaultText=self.sl) else: localedlg = LocaleDialog(self) if localedlg.exec() != QDialog.Accepted: return sl = localedlg.localeEdit.text() setdefault = localedlg.checkBox.isChecked() if sl: try: sl = str(sl) enchant.Dict(sl) except Exception as e: QMessageBox.warning(self, '', str(e)) else: self.sl = sl self.enableSpellCheck(self.actionEnableSC.isChecked()) else: self.sl = None self.enableSpellCheck(self.actionEnableSC.isChecked()) if setdefault: globalSettings.spellCheckLocale = sl def searchBarVisibilityChanged(self, visible): self.actionSearch.setChecked(visible) if visible: self.searchEdit.setFocus(Qt.ShortcutFocusReason) def find(self, back=False): flags = QTextDocument.FindFlags() if back: flags |= QTextDocument.FindBackward if self.csBox.isChecked(): flags |= QTextDocument.FindCaseSensitively text = self.searchEdit.text() editBox = self.currentTab.editBox cursor = editBox.textCursor() newCursor = editBox.document().find(text, cursor, flags) if not newCursor.isNull(): editBox.setTextCursor(newCursor) return self.setSearchEditColor(True) cursor.movePosition(QTextCursor.End if back else QTextCursor.Start) newCursor = editBox.document().find(text, cursor, flags) if not newCursor.isNull(): editBox.setTextCursor(newCursor) return self.setSearchEditColor(True) self.setSearchEditColor(False) def setSearchEditColor(self, found): palette = self.searchEdit.palette() palette.setColor(QPalette.Active, QPalette.Base, Qt.white if found else QColor(255, 102, 102)) self.searchEdit.setPalette(palette) def showInDir(self): if self.currentTab.fileName: path = QFileInfo(self.currentTab.fileName).path() QDesktopServices.openUrl(QUrl.fromLocalFile(path)) else: QMessageBox.warning(self, '', self.tr("Please, save the file somewhere.")) def setCurrentFile(self): self.setWindowTitle("") self.tabWidget.setTabText(self.ind, self.currentTab.getDocumentTitle(baseName=True)) self.tabWidget.setTabToolTip(self.ind, self.currentTab.fileName or '') self.setWindowFilePath(self.currentTab.fileName) files = readListFromSettings("recentFileList") while self.currentTab.fileName in files: files.remove(self.currentTab.fileName) files.insert(0, self.currentTab.fileName) if len(files) > 10: del files[10:] writeListToSettings("recentFileList", files) QDir.setCurrent(QFileInfo(self.currentTab.fileName).dir().path()) self.docTypeChanged() def createNew(self, text=None): self.createTab("") self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if text: self.currentTab.editBox.textCursor().insertText(text) def switchTab(self, shift=1): self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count()) def updateRecentFiles(self): self.menuRecentFiles.clear() self.recentFilesActions = [] filesOld = readListFromSettings("recentFileList") files = [] for f in filesOld: if QFile.exists(f): files.append(f) self.recentFilesActions.append(self.act(f, trig=self.openFunction(f))) writeListToSettings("recentFileList", files) for action in self.recentFilesActions: self.menuRecentFiles.addAction(action) def markupFunction(self, markup): return lambda: self.setDefaultMarkup(markup) def openFunction(self, fileName): return lambda: self.openFileWrapper(fileName) def extensionFunction(self, data): return lambda: \ self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension']) def getExportExtensionsList(self): extensions = [] for extsprefix in datadirs: extsdir = QDir(extsprefix+'/export-extensions/') if extsdir.exists(): for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable): extensions.append(self.readExtension(fileInfo.filePath())) locale = QLocale.system().name() self.extensionActions = [] for extension in extensions: try: if ('Name[%s]' % locale) in extension: name = extension['Name[%s]' % locale] elif ('Name[%s]' % locale.split('_')[0]) in extension: name = extension['Name[%s]' % locale.split('_')[0]] else: name = extension['Name'] data = {} for prop in ('FileFilter', 'DefaultExtension', 'Exec'): if 'X-ReText-'+prop in extension: data[prop] = extension['X-ReText-'+prop] elif prop in extension: data[prop] = extension[prop] else: data[prop] = '' action = self.act(name, trig=self.extensionFunction(data)) if 'Icon' in extension: action.setIcon(self.actIcon(extension['Icon'])) mimetype = extension['MimeType'] if 'MimeType' in extension else None except KeyError: print('Failed to parse extension: Name is required', file=sys.stderr) else: self.extensionActions.append((action, mimetype)) def updateExtensionsVisibility(self): markupClass = self.currentTab.getMarkupClass() for action in self.extensionActions: if markupClass is None: action[0].setEnabled(False) continue mimetype = action[1] if mimetype == None: enabled = True elif markupClass == markups.MarkdownMarkup: enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown")) elif markupClass == markups.ReStructuredTextMarkup: enabled = (mimetype in ("text/x-retext-rst", "text/x-rst")) else: enabled = False action[0].setEnabled(enabled) def readExtension(self, fileName): extFile = QFile(fileName) extFile.open(QIODevice.ReadOnly) extension = {} stream = QTextStream(extFile) while not stream.atEnd(): line = stream.readLine() if '=' in line: index = line.index('=') extension[line[:index].rstrip()] = line[index+1:].lstrip() extFile.close() return extension def openFile(self): supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;' fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "", self.tr("Supported files") + fileFilter + self.tr("All files (*)")) for fileName in fileNames[0]: self.openFileWrapper(fileName) def openFileWrapper(self, fileName): if not fileName: return fileName = QFileInfo(fileName).canonicalFilePath() exists = False for i, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: exists = True ex = i if exists: self.tabWidget.setCurrentIndex(ex) elif QFile.exists(fileName): noEmptyTab = ( (self.ind is None) or self.currentTab.fileName or self.currentTab.editBox.toPlainText() or self.currentTab.editBox.document().isModified() ) if noEmptyTab: self.createTab(fileName) self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if fileName: self.fileSystemWatcher.addPath(fileName) self.currentTab.fileName = fileName self.currentTab.readTextFromFile() editBox = self.currentTab.editBox self.setCurrentFile() self.setWindowModified(editBox.document().isModified()) def showEncodingDialog(self): if not self.maybeSave(self.ind): return encoding, ok = QInputDialog.getItem(self, '', self.tr('Select file encoding from the list:'), [bytes(b).decode() for b in QTextCodec.availableCodecs()], 0, False) if ok: self.currentTab.readTextFromFile(encoding) def saveFile(self): self.saveFileMain(dlg=False) def saveFileAs(self): self.saveFileMain(dlg=True) def saveAll(self): for tab in self.iterateTabs(): if tab.fileName and QFileInfo(tab.fileName).isWritable(): tab.saveTextToFile() tab.editBox.document().setModified(False) def saveFileMain(self, dlg): if (not self.currentTab.fileName) or dlg: markupClass = self.currentTab.getMarkupClass() if (markupClass is None) or not hasattr(markupClass, 'default_extension'): defaultExt = self.tr("Plain text (*.txt)") ext = ".txt" else: defaultExt = self.tr('%s files', 'Example of final string: Markdown files') \ % markupClass.name + ' (' + str.join(' ', ('*'+extension for extension in markupClass.file_extensions)) + ')' if markupClass == markups.MarkdownMarkup: ext = globalSettings.markdownDefaultFileExtension elif markupClass == markups.ReStructuredTextMarkup: ext = globalSettings.restDefaultFileExtension else: ext = markupClass.default_extension newFileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)[0] if newFileName: if not QFileInfo(newFileName).suffix(): newFileName += ext if self.currentTab.fileName: self.fileSystemWatcher.removePath(self.currentTab.fileName) self.currentTab.fileName = newFileName self.actionSetEncoding.setDisabled(self.autoSaveActive()) if self.currentTab.fileName: if self.currentTab.saveTextToFile(): self.setCurrentFile() self.currentTab.editBox.document().setModified(False) self.setWindowModified(False) return True else: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return False def saveHtml(self, fileName): if not QFileInfo(fileName).suffix(): fileName += ".html" try: htmltext = self.currentTab.getHtml(includeStyleSheet=False, webenv=True) except Exception: return self.printError() htmlFile = QFile(fileName) htmlFile.open(QIODevice.WriteOnly) html = QTextStream(htmlFile) if globalSettings.defaultCodec: html.setCodec(globalSettings.defaultCodec) html << htmltext htmlFile.close() def textDocument(self): td = QTextDocument() td.setMetaInformation(QTextDocument.DocumentTitle, self.currentTab.getDocumentTitle()) if self.ss: td.setDefaultStyleSheet(self.ss) td.setHtml(self.currentTab.getHtml()) td.setDefaultFont(globalSettings.font) return td def saveOdf(self): try: document = self.textDocument() except Exception: return self.printError() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))[0] if not QFileInfo(fileName).suffix(): fileName += ".odt" writer = QTextDocumentWriter(fileName) writer.setFormat(b"odf") writer.write(document) def saveFileHtml(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))[0] if fileName: self.saveHtml(fileName) def getDocumentForPrint(self): if globalSettings.useWebKit: return self.currentTab.previewBox try: return self.textDocument() except Exception: self.printError() def standardPrinter(self): printer = QPrinter(QPrinter.HighResolution) printer.setDocName(self.currentTab.getDocumentTitle()) printer.setCreator('ReText %s' % app_version) return printer def savePdf(self): self.currentTab.updatePreviewBox() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), "", self.tr("PDF files (*.pdf)"))[0] if fileName: if not QFileInfo(fileName).suffix(): fileName += ".pdf" printer = self.standardPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(fileName) document = self.getDocumentForPrint() if document != None: document.print(printer) def printFile(self): self.currentTab.updatePreviewBox() printer = self.standardPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(self.tr("Print document")) if (dlg.exec() == QDialog.Accepted): document = self.getDocumentForPrint() if document != None: document.print(printer) def printPreview(self): document = self.getDocumentForPrint() if document == None: return printer = self.standardPrinter() preview = QPrintPreviewDialog(printer, self) preview.paintRequested.connect(document.print) preview.exec() def runExtensionCommand(self, command, filefilter, defaultext): of = ('%of' in command) html = ('%html' in command) if of: if defaultext and not filefilter: filefilter = '*'+defaultext fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)[0] if not fileName: return if defaultext and not QFileInfo(fileName).suffix(): fileName += defaultext basename = '.%s.retext-temp' % self.currentTab.getDocumentTitle(baseName=True) if html: tmpname = basename+'.html' self.saveHtml(tmpname) else: tmpname = basename + self.currentTab.getMarkupClass().default_extension self.currentTab.saveTextToFile(fileName=tmpname, addToWatcher=False) command = command.replace('%of', '"out'+defaultext+'"') command = command.replace('%html' if html else '%if', '"'+tmpname+'"') try: Popen(str(command), shell=True).wait() except Exception as error: errorstr = str(error) QMessageBox.warning(self, '', self.tr('Failed to execute the command:') + '\n' + errorstr) QFile(tmpname).remove() if of: QFile('out'+defaultext).rename(fileName) def autoSaveActive(self, ind=None): tab = self.currentTab if ind is None else self.tabWidget.widget(ind).tab return (self.autoSaveEnabled and tab.fileName and QFileInfo(tab.fileName).isWritable()) def modificationChanged(self, changed): if self.autoSaveActive(): changed = False self.actionSave.setEnabled(changed) self.setWindowModified(changed) def clipboardDataChanged(self): mimeData = QApplication.instance().clipboard().mimeData() if mimeData is not None: self.actionPaste.setEnabled(mimeData.hasText()) def insertFormatting(self, formatting): cursor = self.currentTab.editBox.textCursor() text = cursor.selectedText() moveCursorTo = None def c(cursor): nonlocal moveCursorTo moveCursorTo = cursor.position() def ensurenl(cursor): if not cursor.atBlockStart(): cursor.insertText('\n\n') toinsert = { 'header': (ensurenl, '# ', text), 'italic': ('*', text, c, '*'), 'bold': ('**', text, c, '**'), 'underline': ('<u>', text, c, '</u>'), 'numbering': (ensurenl, ' 1. ', text), 'bullets': (ensurenl, ' * ', text), 'image': ('![', text or self.tr('Alt text'), c, '](', self.tr('URL'), ')'), 'link': ('[', text or self.tr('Link text'), c, '](', self.tr('URL'), ')'), 'inline code': ('`', text, c, '`'), 'code block': (ensurenl, ' ', text), 'blockquote': (ensurenl, '> ', text), } if formatting not in toinsert: return cursor.beginEditBlock() for token in toinsert[formatting]: if callable(token): token(cursor) else: cursor.insertText(token) cursor.endEditBlock() self.formattingBox.setCurrentIndex(0) # Bring back the focus on the editor self.currentTab.editBox.setFocus(Qt.OtherFocusReason) if moveCursorTo: cursor.setPosition(moveCursorTo) self.currentTab.editBox.setTextCursor(cursor) def insertSymbol(self, num): if num: self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';') self.symbolBox.setCurrentIndex(0) def fileChanged(self, fileName): ind = None for testind, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: ind = testind if ind is None: self.fileSystemWatcher.removePath(fileName) self.tabWidget.setCurrentIndex(ind) if not QFile.exists(fileName): self.currentTab.editBox.document().setModified(True) QMessageBox.warning(self, '', self.tr( 'This file has been deleted by other application.\n' 'Please make sure you save the file before exit.')) elif not self.currentTab.editBox.document().isModified(): # File was not modified in ReText, reload silently self.currentTab.readTextFromFile() self.currentTab.updatePreviewBox() else: text = self.tr( 'This document has been modified by other application.\n' 'Do you want to reload the file (this will discard all ' 'your changes)?\n') if self.autoSaveEnabled: text += self.tr( 'If you choose to not reload the file, auto save mode will ' 'be disabled for this session to prevent data loss.') messageBox = QMessageBox(QMessageBox.Warning, '', text) reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole) messageBox.addButton(QMessageBox.Cancel) messageBox.exec() if messageBox.clickedButton() is reloadButton: self.currentTab.readTextFromFile() self.currentTab.updatePreviewBox() else: self.autoSaveEnabled = False self.currentTab.editBox.document().setModified(True) if fileName not in self.fileSystemWatcher.files(): # https://github.com/retext-project/retext/issues/137 self.fileSystemWatcher.addPath(fileName) def maybeSave(self, ind): tab = self.tabWidget.widget(ind).tab if self.autoSaveActive(ind): tab.saveTextToFile() return True if not tab.editBox.document().isModified(): return True self.tabWidget.setCurrentIndex(ind) ret = QMessageBox.warning(self, '', self.tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.saveFileMain(False) elif ret == QMessageBox.Cancel: return False return True def closeEvent(self, closeevent): for ind in range(self.tabWidget.count()): if not self.maybeSave(ind): return closeevent.ignore() if globalSettings.saveWindowGeometry and not self.isMaximized(): globalSettings.windowGeometry = self.saveGeometry() closeevent.accept() def viewHtml(self): htmlDlg = HtmlDialog(self) try: htmltext = self.currentTab.getHtml(includeStyleSheet=False) except Exception: return self.printError() winTitle = self.currentTab.getDocumentTitle(baseName=True) htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")") htmlDlg.textEdit.setPlainText(htmltext.rstrip()) htmlDlg.hl.rehighlight() htmlDlg.show() htmlDlg.raise_() htmlDlg.activateWindow() def openHelp(self): QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki')) def aboutDialog(self): QMessageBox.about(self, self.aboutWindowTitle, '<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__)) +'</b></p>' + self.tr('Simple but powerful editor' ' for Markdown and reStructuredText') +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011–2016') +'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website') +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">' +self.tr('Markdown syntax') +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' +self.tr('reStructuredText syntax')+'</a></p>') def setDefaultMarkup(self, markupClass): self.defaultMarkup = markupClass defaultName = markups.get_available_markups()[0].name writeToSettings('defaultMarkup', markupClass.name, defaultName) for tab in self.iterateTabs(): if not tab.fileName: tab.setMarkupClass(markupClass) tab.updatePreviewBox() self.docTypeChanged()
class SimulatorWindow(QMainWindow): def __init__(self, argv): QMainWindow.__init__(self) self.setWindowTitle("SimSo: Real-Time Scheduling Simulator") # Possible actions: style = QApplication.style() # New self._newAction = QAction( style.standardIcon(QStyle.SP_FileDialogNewFolder), '&New', None) self._newAction.setShortcut(Qt.CTRL + Qt.Key_N) self._newAction.triggered.connect(self.fileNew) # Open self._openAction = QAction( style.standardIcon(QStyle.SP_DialogOpenButton), '&Open', None) self._openAction.setShortcut(Qt.CTRL + Qt.Key_O) self._openAction.triggered.connect(self.fileOpen) # Save self._saveAction = QAction( style.standardIcon(QStyle.SP_DialogSaveButton), '&Save', None) self._saveAction.setShortcut(Qt.CTRL + Qt.Key_S) self._saveAction.triggered.connect(self.fileSave) # Save As self._saveAsAction = QAction( style.standardIcon(QStyle.SP_DialogSaveButton), 'Save &As', None) self._saveAsAction.setShortcut(Qt.CTRL + Qt.SHIFT + Qt.Key_S) self._saveAsAction.triggered.connect(self.fileSaveAs) # Run self._runAction = QAction( style.standardIcon(QStyle.SP_MediaPlay), '&Run', None) self._runAction.setShortcut(Qt.CTRL + Qt.Key_R) self._runAction.triggered.connect(self.fileRun) # Show Model data self._modelAction = QAction('&Model data', None) self._modelAction.setShortcut(Qt.CTRL + Qt.Key_M) #self._ganttAction.setCheckable(True) self._modelAction.triggered.connect(self.showModelWindow) # Show Gantt self._ganttAction = QAction('&Gantt', None) self._ganttAction.setShortcut(Qt.CTRL + Qt.Key_G) self._ganttAction.setEnabled(False) #self._ganttAction.setCheckable(True) self._ganttAction.triggered.connect(self.showGantt) # Show Results self._metricsAction = QAction('&Results', None) self._metricsAction.setShortcut(Qt.CTRL + Qt.Key_I) self._metricsAction.setEnabled(False) #self._metricsAction.setCheckable(True) self._metricsAction.triggered.connect(self.showResults) # Show Doc self._docAction = QAction('&Documentation', None) self._docAction.triggered.connect(self.showDocumentation) self._aboutAction = QAction('&About SimSo', None) self._aboutAction.triggered.connect(self.showAbout) # Recent files self._recentFileActions = [] for i in range(5): act = QAction(self) act.setVisible(False) act.triggered.connect(self.openRecentFile) self._recentFileActions.append(act) # File Menu: file_menu = QMenu('&File', self) file_menu.addAction(self._newAction) file_menu.addAction(self._openAction) file_menu.addAction(self._saveAction) file_menu.addAction(self._saveAsAction) file_menu.addAction(self._runAction) file_menu.addSeparator() for act in self._recentFileActions: file_menu.addAction(act) file_menu.addSeparator() file_menu.addAction('&Quit', self.fileQuit, Qt.CTRL + Qt.Key_Q) self.updateRecentFileActions() # View Menu: view_menu = QMenu('&View', self) view_menu.addAction(self._modelAction) view_menu.addAction(self._ganttAction) view_menu.addAction(self._metricsAction) # Help Menu: help_menu = QMenu('&Help', self) help_menu.addAction(self._docAction) help_menu.addAction(self._aboutAction) # Add menus to menuBar: self.menuBar().addMenu(file_menu) self.menuBar().addMenu(view_menu) self.menuBar().addMenu(help_menu) # ToolBar: self.toolBar = QToolBar("Main ToolBar") self.addToolBar(self.toolBar) self.toolBar.addAction(self._newAction) self.toolBar.addAction(self._openAction) self.toolBar.addAction(self._saveAction) self.toolBar.addAction(self._runAction) self.toolBar.addAction(self._ganttAction) self.toolBar.addAction(self._metricsAction) # Tab: self.main_tab = QTabWidget() self.main_tab.setTabsClosable(True) self.main_tab.setMovable(True) self.main_tab.tabCloseRequested.connect(self.tabCloseRequested) self.main_tab.currentChanged.connect(self.tabChanged) self.setCentralWidget(self.main_tab) # Init statusBar: self.statusBar().showMessage("", 2000) self._documentation = None if argv: for arg in argv: try: self.open_file(arg) except Exception as e: print(e) else: self.fileNew() def openRecentFile(self): try: self.open_file(self.sender().data().toString()) except AttributeError: self.open_file(self.sender().data()) def updateRecentFileActions(self): settings = QSettings() files = settings.value("recentFileList", defaultValue=[], type='QString') for i in range(5): if i < len(files): text = "&{} {}".format(i + 1, QFileInfo(files[i]).fileName()) self._recentFileActions[i].setText(text) self._recentFileActions[i].setData(files[i]) self._recentFileActions[i].setVisible(True) else: self._recentFileActions[i].setVisible(False) def setCurrentFile(self, filename): filename = QFileInfo(filename).absoluteFilePath() settings = QSettings() files = settings.value("recentFileList", defaultValue=[], type='QString') if filename in files: files.remove(filename) files.insert(0, filename) while len(files) > 5: del files[-1] settings.setValue("recentFileList", files) self.updateRecentFileActions() def showAbout(self): QMessageBox.about( self, "About SimSo", "<b>SimSo - Simulation of Multiprocessor Scheduling with Overheads</b><br/><br/>" "Version: SimSo {}, Graphical User Interface {}<br/><br/>" "SimSo is a free software developed by Maxime Cheramy (LAAS-CNRS).<br/>" "This software is distributed under the <a href='http://www.cecill.info'>CECILL license</a>, " "compatible with the GNU GPL.<br/><br/>" "Contact: <a href='mailto:[email protected]'>[email protected]</a>".format(simso.__version__, simsogui.__version__) ) def showDocumentation(self): if self._documentation is None: doc = QWebView(self) doc.load(QUrl("doc/html/index.html")) self._documentation = QDockWidget("Documentation", self) self._documentation.setWidget(doc) self._documentation.closeEvent = lambda _: self.hide_documentation() self.addDockWidget(Qt.LeftDockWidgetArea, self._documentation) def showGantt(self): self.main_tab.currentWidget().showGantt() def showModelWindow(self): self.main_tab.currentWidget().showModelWindow() def showResults(self): self.main_tab.currentWidget().showResults() def hide_documentation(self): self._documentation = None def fileNew(self): self.main_tab.addTab(SimulationTab(self), 'Unsaved') def fileOpen(self): simulation_file = QFileDialog.getOpenFileName( filter="*.xml", caption="Open XML simulation file.")[0] if simulation_file: self.open_file(simulation_file) def open_file(self, simulation_file): try: simulation_file = unicode(simulation_file) except NameError: pass try: self.setCurrentFile(simulation_file) sim = SimulationTab(self, simulation_file) if (self.main_tab.currentWidget() and not self.main_tab.currentWidget().simulation_file and self.main_tab.currentWidget().configuration.is_saved() and self.main_tab.count() == 1): self.main_tab.removeTab(0) self.main_tab.addTab(sim, os.path.split(simulation_file)[1]) self.main_tab.setCurrentWidget(sim) self.updateMenus() except Exception: QMessageBox.critical( self, "Could not open file", "The file {} could not be opened.".format(simulation_file)) print(traceback.format_exc()) def fileSave(self): try: self.main_tab.currentWidget().save() except: self.fileSaveAs() def fileSaveAs(self): simulation_file = QFileDialog.getSaveFileName( filter="*.xml", caption="Save XML simulation file.")[0] try: simulation_file = unicode(simulation_file) except NameError: pass if simulation_file: if simulation_file[-4:] != '.xml': simulation_file += '.xml' self.main_tab.currentWidget().save_as(simulation_file) self.setCurrentFile(simulation_file) def fileRun(self): self._runAction.setEnabled(False) self.main_tab.currentWidget().run() def fileQuit(self): self.close() def setTabText(self, tab, text): self.main_tab.setTabText(self.main_tab.indexOf(tab), text) def tabChanged(self, index): self.updateMenus() def tabCloseRequested(self, index): if self.main_tab.widget(index).close(): self.main_tab.removeTab(index) self.updateMenus() def closeEvent(self, event): while self.main_tab.count() > 0: if self.main_tab.widget(0).close(): self.main_tab.removeTab(0) else: event.ignore() return def updateMenus(self): if self.main_tab.count() > 0: widget = self.main_tab.currentWidget() self._runAction.setEnabled(True) self._modelAction.setEnabled(True) self._ganttAction.setEnabled(widget._model is not None) self._metricsAction.setEnabled(widget._model is not None) else: self._runAction.setEnabled(False) self._modelAction.setEnabled(False) self._ganttAction.setEnabled(False) self._metricsAction.setEnabled(False)
class StudentGroupsTabs(QWidget): def __init__(self, parent, student_listings=None): super().__init__(parent) if student_listings is None: self.student_listings = students.StudentListings() inserted_group = students.StudentGroup(0, 'INSERTED') self.student_listings.create_listing(inserted_group) else: self.student_listings = student_listings layout = QVBoxLayout(self) self.setLayout(layout) self.tabs = QTabWidget(self) # Special tab for creating a new group: self.tabs.addTab(QWidget(), ' + ') # Group 0 is special: don't show it for listing in self.student_listings[1:]: self._add_group_tab(listing) # At least one normal group needs to be present: if len(self.student_listings) == 1: self._create_default_group() button_load = QPushButton(_('Add students from file'), parent=self) button_new_student = QPushButton( QIcon(utils.resource_path('new_id.svg')), _('New student'), parent=self) button_remove = QPushButton( QIcon(utils.resource_path('discard.svg')), _('Remove group'), parent=self) layout.addWidget(self.tabs) layout.addWidget(button_load) layout.addWidget(button_new_student) layout.addWidget(button_remove) layout.setAlignment(button_load, Qt.AlignHCenter) layout.setAlignment(button_new_student, Qt.AlignHCenter) layout.setAlignment(button_remove, Qt.AlignHCenter) self.tabs.setCurrentIndex(0) self._active_tab = 0 self.tabs.currentChanged.connect(self._tab_changed) self.tabs.tabBarDoubleClicked.connect(self._rename_group) button_load.clicked.connect(self._load_students) button_new_student.clicked.connect(self._new_student) button_remove.clicked.connect(self._remove_group) def _load_students(self): index = self.tabs.currentIndex() file_name, __ = QFileDialog.getOpenFileName( self, _('Select the student list file'), '', FileNameFilters.student_list, None, QFileDialog.DontUseNativeDialog) try: if file_name: with students.StudentReader.create(file_name) as reader: student_list = list(reader.students()) # Flag duplicate student ids: warn_duplicates = False for s in self.student_listings.find_duplicates(student_list): s.is_duplicate = True warn_duplicates = True if warn_duplicates: QMessageBox.warning( self, _('Importing a student list'), _('Some student ids are already in the list. ' 'Remove them or cancel the import operation.')) column_map = reader.column_map.normalize() preview_dialog = DialogPreviewStudents( self, student_list, column_map) result = preview_dialog.exec_() if result == QMessageBox.Accepted: self.tabs.widget(index).add_students(student_list) except Exception as e: QMessageBox.critical( self, _('Error in student list'), file_name + '\n\n' + str(e)) def _new_student(self): index = self.tabs.currentIndex() dialog = NewStudentDialog( self.student_listings, group_index=index, parent=self) student = dialog.exec_() if student is not None: self.tabs.widget(index).listing_updated() def _remove_group(self): index = self.tabs.currentIndex() if len(self.student_listings[index + 1]) > 0: result = QMessageBox.warning( self, _('Warning'), _('This group and its students will be removed. ' 'Are you sure you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) remove = (result == QMessageBox.Yes) else: remove = True if remove: try: self.student_listings.remove_at(index + 1) if len(self.student_listings) > 1: if index == self.tabs.count() - 2: self.tabs.setCurrentIndex(index - 1) else: self.tabs.setCurrentIndex(index + 1) else: self._create_default_group() self.tabs.setCurrentIndex(1) self.tabs.removeTab(index) except students.CantRemoveGroupException: QMessageBox.critical( self, _('Error'), _('This group cannot be removed because ' 'exams have been graded for some of its students.')) def _add_group_tab(self, listing, show=False): self.tabs.insertTab( self.tabs.count() - 1, GroupWidget(listing, self), listing.group.name ) if show: self.tabs.setCurrentIndex(self.tabs.count() - 2) def _new_group(self): group_name = GroupNameDialog(parent=self).exec_() if group_name is not None: group = students.StudentGroup(None, group_name) listing = self.student_listings.create_listing(group) self._add_group_tab(listing, show=True) else: self.tabs.setCurrentIndex(self._active_tab) def _create_default_group(self): group = students.StudentGroup(None, _('Students')) listing = self.student_listings.create_listing(group) self._add_group_tab(listing, show=True) def _rename_group(self, index): name = self.student_listings[index + 1].group.name new_name = GroupNameDialog(group_name=name, parent=self).exec_() if new_name is not None and new_name != name: self.student_listings[index + 1].rename(new_name) self.tabs.setTabText(index, new_name) def _tab_changed(self, index): if index == self.tabs.count() - 1: # The last (special) tab has been activated: create a new group self._new_group() self._active_tab = self.tabs.currentIndex()
class ReTextWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.resize(950, 700) screenRect = QDesktopWidget().screenGeometry() if globalSettings.windowGeometry: self.restoreGeometry(globalSettings.windowGeometry) else: self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2) if not screenRect.contains(self.geometry()): self.showMaximized() if globalSettings.iconTheme: QIcon.setThemeName(globalSettings.iconTheme) if QIcon.themeName() in ('hicolor', ''): if not QFile.exists(icon_path + 'document-new.png'): QIcon.setThemeName(get_icon_theme()) if QFile.exists(icon_path+'retext.png'): self.setWindowIcon(QIcon(icon_path+'retext.png')) elif QFile.exists('/usr/share/pixmaps/retext.png'): self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png')) else: self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor'))) self.editBoxes = [] self.previewBoxes = [] self.highlighters = [] self.markups = [] self.fileNames = [] self.actionPreviewChecked = [] self.actionLivePreviewChecked = [] self.tabWidget = QTabWidget(self) self.initTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.currentChanged.connect(self.changeIndex) self.tabWidget.tabCloseRequested.connect(self.closeTab) toolBar = QToolBar(self.tr('File toolbar'), self) self.addToolBar(Qt.TopToolBarArea, toolBar) self.editBar = QToolBar(self.tr('Edit toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.editBar) self.searchBar = QToolBar(self.tr('Search toolbar'), self) self.addToolBar(Qt.BottomToolBarArea, self.searchBar) toolBar.setVisible(not globalSettings.hideToolBar) self.editBar.setVisible(not globalSettings.hideToolBar) self.actionNew = self.act(self.tr('New'), 'document-new', self.createNew, shct=QKeySequence.New) self.actionNew.setPriority(QAction.LowPriority) self.actionOpen = self.act(self.tr('Open'), 'document-open', self.openFile, shct=QKeySequence.Open) self.actionOpen.setPriority(QAction.LowPriority) self.actionSetEncoding = self.act(self.tr('Set encoding'), trig=self.showEncodingDialog) self.actionSetEncoding.setEnabled(False) self.actionReload = self.act(self.tr('Reload'), 'view-refresh', trig=self.openFileMain) self.actionReload.setEnabled(False) self.actionSave = self.act(self.tr('Save'), 'document-save', self.saveFile, shct=QKeySequence.Save) self.actionSave.setEnabled(False) self.actionSave.setPriority(QAction.LowPriority) self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as', self.saveFileAs, shct=QKeySequence.SaveAs) self.actionNextTab = self.act(self.tr('Next tab'), 'go-next', lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown) self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous', lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp) self.actionPrint = self.act(self.tr('Print'), 'document-print', self.printFile, shct=QKeySequence.Print) self.actionPrint.setPriority(QAction.LowPriority) self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview', self.printPreview) self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml) self.actionChangeEditorFont = self.act(self.tr('Change editor font'), trig=self.changeEditorFont) self.actionChangePreviewFont = self.act(self.tr('Change preview font'), trig=self.changePreviewFont) self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find) self.actionSearch.setCheckable(True) self.actionSearch.triggered[bool].connect(self.searchBar.setVisible) self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged) self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview) if QIcon.hasThemeIcon('document-preview'): self.actionPreview.setIcon(QIcon.fromTheme('document-preview')) elif QIcon.hasThemeIcon('preview-file'): self.actionPreview.setIcon(QIcon.fromTheme('preview-file')) elif QIcon.hasThemeIcon('x-office-document'): self.actionPreview.setIcon(QIcon.fromTheme('x-office-document')) else: self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png')) self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L, trigbool=self.enableLivePreview) self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T, trigbool=lambda x: self.editBoxes[self.ind].enableTableMode(x)) if ReTextFakeVimHandler: self.actionFakeVimMode = self.act(self.tr('FakeVim mode'), shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode) if globalSettings.useFakeVim: self.actionFakeVimMode.setChecked(True) self.enableFakeVimMode(True) self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen', shct=Qt.Key_F11, trigbool=self.enableFullScreen) self.actionFullScreen.setPriority(QAction.LowPriority) self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system', trig=self.openConfigDialog) self.actionConfig.setMenuRole(QAction.PreferencesRole) self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml) self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf) self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf) self.getExportExtensionsList() self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit) self.actionQuit.setMenuRole(QAction.QuitRole) self.actionQuit.triggered.connect(self.close) self.actionUndo = self.act(self.tr('Undo'), 'edit-undo', lambda: self.editBoxes[self.ind].undo(), shct=QKeySequence.Undo) self.actionRedo = self.act(self.tr('Redo'), 'edit-redo', lambda: self.editBoxes[self.ind].redo(), shct=QKeySequence.Redo) self.actionCopy = self.act(self.tr('Copy'), 'edit-copy', lambda: self.editBoxes[self.ind].copy(), shct=QKeySequence.Copy) self.actionCut = self.act(self.tr('Cut'), 'edit-cut', lambda: self.editBoxes[self.ind].cut(), shct=QKeySequence.Cut) self.actionPaste = self.act(self.tr('Paste'), 'edit-paste', lambda: self.editBoxes[self.ind].paste(), shct=QKeySequence.Paste) self.actionUndo.setEnabled(False) self.actionRedo.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.setEnabled(False) qApp = QApplication.instance() qApp.clipboard().dataChanged.connect(self.clipboardDataChanged) self.clipboardDataChanged() if enchant_available: self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck) self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale) self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit) self.actionWebKit.setChecked(globalSettings.useWebKit) self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir) self.actionFind = self.act(self.tr('Next'), 'go-next', self.find, shct=QKeySequence.FindNext) self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous', lambda: self.find(back=True), shct=QKeySequence.FindPrevious) self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp) self.aboutWindowTitle = self.tr('About ReText') self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog) self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAboutQt = self.act(self.tr('About Qt')) self.actionAboutQt.setMenuRole(QAction.AboutQtRole) self.actionAboutQt.triggered.connect(qApp.aboutQt) availableMarkups = markups.get_available_markups() if not availableMarkups: print('Warning: no markups are available!') self.defaultMarkup = availableMarkups[0] if availableMarkups else None if globalSettings.defaultMarkup: mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup) if mc and mc.available(): self.defaultMarkup = mc if len(availableMarkups) > 1: self.chooseGroup = QActionGroup(self) markupActions = [] for markup in availableMarkups: markupAction = self.act(markup.name, trigbool=self.markupFunction(markup)) if markup == self.defaultMarkup: markupAction.setChecked(True) self.chooseGroup.addAction(markupAction) markupActions.append(markupAction) self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertChars('**')) self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertChars('*')) self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, trig=lambda: self.insertTag('u')) self.usefulTags = ('a', 'big', 'center', 'img', 's', 'small', 'span', 'table', 'td', 'tr', 'u') self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr', 'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', 'rarr', 'rsquo', 'times') self.tagsBox = QComboBox(self.editBar) self.tagsBox.addItem(self.tr('Tags')) self.tagsBox.addItems(self.usefulTags) self.tagsBox.activated.connect(self.insertTag) self.symbolBox = QComboBox(self.editBar) self.symbolBox.addItem(self.tr('Symbols')) self.symbolBox.addItems(self.usefulChars) self.symbolBox.activated.connect(self.insertSymbol) self.updateStyleSheet() menubar = QMenuBar(self) menubar.setGeometry(QRect(0, 0, 800, 25)) self.setMenuBar(menubar) menuFile = menubar.addMenu(self.tr('File')) menuEdit = menubar.addMenu(self.tr('Edit')) menuHelp = menubar.addMenu(self.tr('Help')) menuFile.addAction(self.actionNew) menuFile.addAction(self.actionOpen) self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent')) self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles) menuFile.addMenu(self.menuRecentFiles) menuFile.addAction(self.actionShow) menuFile.addAction(self.actionSetEncoding) menuFile.addAction(self.actionReload) menuFile.addSeparator() menuFile.addAction(self.actionSave) menuFile.addAction(self.actionSaveAs) menuFile.addSeparator() menuFile.addAction(self.actionNextTab) menuFile.addAction(self.actionPrevTab) menuFile.addSeparator() menuExport = menuFile.addMenu(self.tr('Export')) menuExport.addAction(self.actionSaveHtml) menuExport.addAction(self.actionOdf) menuExport.addAction(self.actionPdf) if self.extensionActions: menuExport.addSeparator() for action, mimetype in self.extensionActions: menuExport.addAction(action) menuExport.aboutToShow.connect(self.updateExtensionsVisibility) menuFile.addAction(self.actionPrint) menuFile.addAction(self.actionPrintPreview) menuFile.addSeparator() menuFile.addAction(self.actionQuit) menuEdit.addAction(self.actionUndo) menuEdit.addAction(self.actionRedo) menuEdit.addSeparator() menuEdit.addAction(self.actionCut) menuEdit.addAction(self.actionCopy) menuEdit.addAction(self.actionPaste) menuEdit.addSeparator() if enchant_available: menuSC = menuEdit.addMenu(self.tr('Spell check')) menuSC.addAction(self.actionEnableSC) menuSC.addAction(self.actionSetLocale) menuEdit.addAction(self.actionSearch) menuEdit.addAction(self.actionChangeEditorFont) menuEdit.addAction(self.actionChangePreviewFont) menuEdit.addSeparator() if len(availableMarkups) > 1: self.menuMode = menuEdit.addMenu(self.tr('Default markup')) for markupAction in markupActions: self.menuMode.addAction(markupAction) menuFormat = menuEdit.addMenu(self.tr('Formatting')) menuFormat.addAction(self.actionBold) menuFormat.addAction(self.actionItalic) menuFormat.addAction(self.actionUnderline) menuEdit.addAction(self.actionWebKit) menuEdit.addSeparator() menuEdit.addAction(self.actionViewHtml) menuEdit.addAction(self.actionLivePreview) menuEdit.addAction(self.actionPreview) menuEdit.addAction(self.actionTableMode) if ReTextFakeVimHandler: menuEdit.addAction(self.actionFakeVimMode) menuEdit.addSeparator() menuEdit.addAction(self.actionFullScreen) menuEdit.addAction(self.actionConfig) menuHelp.addAction(self.actionHelp) menuHelp.addSeparator() menuHelp.addAction(self.actionAbout) menuHelp.addAction(self.actionAboutQt) menubar.addMenu(menuFile) menubar.addMenu(menuEdit) menubar.addMenu(menuHelp) toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) toolBar.addAction(self.actionNew) toolBar.addSeparator() toolBar.addAction(self.actionOpen) toolBar.addAction(self.actionSave) toolBar.addAction(self.actionPrint) toolBar.addSeparator() toolBar.addAction(self.actionPreview) toolBar.addAction(self.actionFullScreen) self.editBar.addAction(self.actionUndo) self.editBar.addAction(self.actionRedo) self.editBar.addSeparator() self.editBar.addAction(self.actionCut) self.editBar.addAction(self.actionCopy) self.editBar.addAction(self.actionPaste) self.editBar.addSeparator() self.editBar.addWidget(self.tagsBox) self.editBar.addWidget(self.symbolBox) self.searchEdit = QLineEdit(self.searchBar) self.searchEdit.setPlaceholderText(self.tr('Search')) self.searchEdit.returnPressed.connect(self.find) self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar) self.searchBar.addWidget(self.searchEdit) self.searchBar.addSeparator() self.searchBar.addWidget(self.csBox) self.searchBar.addAction(self.actionFindPrev) self.searchBar.addAction(self.actionFind) self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBar.setVisible(False) self.autoSaveEnabled = globalSettings.autoSave if self.autoSaveEnabled: timer = QTimer(self) timer.start(60000) timer.timeout.connect(self.saveAll) self.ind = None if enchant_available: self.sl = globalSettings.spellCheckLocale if self.sl: try: enchant.Dict(self.sl) except Exception as e: print(e, file=sys.stderr) self.sl = None if globalSettings.spellCheck: self.actionEnableSC.setChecked(True) self.enableSpellCheck(True) self.fileSystemWatcher = QFileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self.fileChanged) def updateStyleSheet(self): if globalSettings.styleSheet: sheetfile = QFile(globalSettings.styleSheet) sheetfile.open(QIODevice.ReadOnly) self.ss = QTextStream(sheetfile).readAll() sheetfile.close() else: self.ss = '' def initTabWidget(self): def dragEnterEvent(e): e.acceptProposedAction() def dropEvent(e): fn = bytes(e.mimeData().data('text/plain')).decode().rstrip() if fn.startswith('file:'): fn = QUrl(fn).toLocalFile() self.openFileWrapper(fn) self.tabWidget.setTabsClosable(True) self.tabWidget.setAcceptDrops(True) self.tabWidget.dragEnterEvent = dragEnterEvent self.tabWidget.dropEvent = dropEvent def act(self, name, icon=None, trig=None, trigbool=None, shct=None): if not isinstance(shct, QKeySequence): shct = QKeySequence(shct) if icon: action = QAction(self.actIcon(icon), name, self) else: action = QAction(name, self) if trig: action.triggered.connect(trig) elif trigbool: action.setCheckable(True) action.triggered[bool].connect(trigbool) if shct: action.setShortcut(shct) return action def actIcon(self, name): return QIcon.fromTheme(name, QIcon(icon_path+name+'.png')) def printError(self): import traceback print('Exception occured while parsing document:', file=sys.stderr) traceback.print_exc() def getSplitter(self, index): splitter = QSplitter(Qt.Horizontal) # Give both boxes a minimum size so the minimumSizeHint will be # ignored when splitter.setSizes is called below for widget in self.editBoxes[index], self.previewBoxes[index]: widget.setMinimumWidth(125) splitter.addWidget(widget) splitter.setSizes((50, 50)) splitter.setChildrenCollapsible(False) return splitter def getWebView(self): webView = QWebView() if not globalSettings.handleWebLinks: webView.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks) webView.page().linkClicked.connect(QDesktopServices.openUrl) settings = webView.settings() settings.setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False) settings.setDefaultTextEncoding('utf-8') return webView def createTab(self, fileName): self.previewBlocked = False self.editBoxes.append(ReTextEdit(self)) self.highlighters.append(ReTextHighlighter(self.editBoxes[-1].document())) if enchant_available and self.actionEnableSC.isChecked(): self.highlighters[-1].dictionary = \ enchant.Dict(self.sl) if self.sl else enchant.Dict() self.highlighters[-1].rehighlight() if globalSettings.useWebKit: self.previewBoxes.append(self.getWebView()) else: self.previewBoxes.append(QTextBrowser()) self.previewBoxes[-1].setOpenExternalLinks(True) self.previewBoxes[-1].setVisible(False) self.fileNames.append(fileName) markupClass = self.getMarkupClass(fileName) self.markups.append(self.getMarkup(fileName)) self.highlighters[-1].docType = (markupClass.name if markupClass else '') liveMode = globalSettings.restorePreviewState and globalSettings.previewState self.actionPreviewChecked.append(liveMode) self.actionLivePreviewChecked.append(liveMode) metrics = QFontMetrics(self.editBoxes[-1].font()) self.editBoxes[-1].setTabStopWidth(globalSettings.tabWidth * metrics.width(' ')) self.editBoxes[-1].textChanged.connect(self.updateLivePreviewBox) self.editBoxes[-1].undoAvailable.connect(self.actionUndo.setEnabled) self.editBoxes[-1].redoAvailable.connect(self.actionRedo.setEnabled) self.editBoxes[-1].copyAvailable.connect(self.enableCopy) self.editBoxes[-1].document().modificationChanged.connect(self.modificationChanged) if globalSettings.useFakeVim: self.installFakeVimHandler(self.editBoxes[-1]) return self.getSplitter(-1) def closeTab(self, ind): if self.maybeSave(ind): if self.tabWidget.count() == 1: self.tabWidget.addTab(self.createTab(""), self.tr("New document")) if self.fileNames[ind]: self.fileSystemWatcher.removePath(self.fileNames[ind]) del self.editBoxes[ind] del self.previewBoxes[ind] del self.highlighters[ind] del self.markups[ind] del self.fileNames[ind] del self.actionPreviewChecked[ind] del self.actionLivePreviewChecked[ind] self.tabWidget.removeTab(ind) def getMarkupClass(self, fileName=None): if fileName is None: fileName = self.fileNames[self.ind] if fileName: markupClass = markups.get_markup_for_file_name( fileName, return_class=True) if markupClass: return markupClass return self.defaultMarkup def getMarkup(self, fileName=None): if fileName is None: fileName = self.fileNames[self.ind] markupClass = self.getMarkupClass(fileName=fileName) if markupClass and markupClass.available(): return markupClass(filename=fileName) def docTypeChanged(self): oldType = self.highlighters[self.ind].docType markupClass = self.getMarkupClass() newType = markupClass.name if markupClass else '' if oldType != newType: self.markups[self.ind] = self.getMarkup() self.updatePreviewBox() self.highlighters[self.ind].docType = newType self.highlighters[self.ind].rehighlight() dtMarkdown = (newType == DOCTYPE_MARKDOWN) dtMkdOrReST = (newType in (DOCTYPE_MARKDOWN, DOCTYPE_REST)) self.tagsBox.setEnabled(dtMarkdown) self.symbolBox.setEnabled(dtMarkdown) self.actionUnderline.setEnabled(dtMarkdown) self.actionBold.setEnabled(dtMkdOrReST) self.actionItalic.setEnabled(dtMkdOrReST) canReload = bool(self.fileNames[self.ind]) and not self.autoSaveActive() self.actionSetEncoding.setEnabled(canReload) self.actionReload.setEnabled(canReload) def changeIndex(self, ind): if ind > -1: self.actionUndo.setEnabled(self.editBoxes[ind].document().isUndoAvailable()) self.actionRedo.setEnabled(self.editBoxes[ind].document().isRedoAvailable()) self.actionCopy.setEnabled(self.editBoxes[ind].textCursor().hasSelection()) self.actionCut.setEnabled(self.editBoxes[ind].textCursor().hasSelection()) self.actionPreview.setChecked(self.actionPreviewChecked[ind]) self.actionLivePreview.setChecked(self.actionLivePreviewChecked[ind]) self.actionTableMode.setChecked(self.editBoxes[ind].tableModeEnabled) self.editBar.setDisabled(self.actionPreviewChecked[ind]) self.ind = ind if self.fileNames[ind]: self.setCurrentFile() else: self.setWindowTitle(self.tr('New document') + '[*]') self.docTypeChanged() self.modificationChanged(self.editBoxes[ind].document().isModified()) if globalSettings.restorePreviewState: globalSettings.previewState = self.actionLivePreviewChecked[ind] if self.actionLivePreviewChecked[ind]: self.enableLivePreview(True) self.editBoxes[self.ind].setFocus(Qt.OtherFocusReason) def changeEditorFont(self): font, ok = QFontDialog.getFont(globalSettings.editorFont, self) if ok: globalSettings.editorFont = font for editor in self.editBoxes: editor.updateFont() def changePreviewFont(self): font, ok = QFontDialog.getFont(globalSettings.font, self) if ok: globalSettings.font = font self.updatePreviewBox() def preview(self, viewmode): self.actionPreviewChecked[self.ind] = viewmode if self.actionLivePreview.isChecked(): self.actionLivePreview.setChecked(False) return self.enableLivePreview(False) self.editBar.setDisabled(viewmode) self.editBoxes[self.ind].setVisible(not viewmode) self.previewBoxes[self.ind].setVisible(viewmode) if viewmode: self.updatePreviewBox() def enableLivePreview(self, livemode): if globalSettings.restorePreviewState: globalSettings.previewState = livemode self.actionLivePreviewChecked[self.ind] = livemode self.actionPreviewChecked[self.ind] = livemode self.actionPreview.setChecked(livemode) self.editBar.setEnabled(True) self.previewBoxes[self.ind].setVisible(livemode) self.editBoxes[self.ind].setVisible(True) if livemode: self.updatePreviewBox() def enableWebKit(self, enable): globalSettings.useWebKit = enable oldind = self.ind self.tabWidget.clear() for self.ind in range(len(self.editBoxes)): if enable: self.previewBoxes[self.ind] = self.getWebView() else: self.previewBoxes[self.ind] = QTextBrowser() self.previewBoxes[self.ind].setOpenExternalLinks(True) splitter = self.getSplitter(self.ind) self.tabWidget.addTab(splitter, self.getDocumentTitle(baseName=True)) self.updatePreviewBox() self.previewBoxes[self.ind].setVisible(self.actionPreviewChecked[self.ind]) self.ind = oldind self.tabWidget.setCurrentIndex(self.ind) def enableCopy(self, copymode): self.actionCopy.setEnabled(copymode) self.actionCut.setEnabled(copymode) def enableFullScreen(self, yes): if yes: self.showFullScreen() else: self.showNormal() def openConfigDialog(self): dlg = ConfigDialog(self) dlg.setWindowTitle(self.tr('Preferences')) dlg.show() def installFakeVimHandler(self, editor): if ReTextFakeVimHandler: fakeVimEditor = ReTextFakeVimHandler(editor, self) fakeVimEditor.setSaveAction(self.actionSave) fakeVimEditor.setQuitAction(self.actionQuit) self.actionFakeVimMode.triggered.connect(fakeVimEditor.remove) def enableFakeVimMode(self, yes): globalSettings.useFakeVim = yes if yes: FakeVimMode.init(self) for editor in self.editBoxes: self.installFakeVimHandler(editor) else: FakeVimMode.exit(self) def enableSpellCheck(self, yes): if yes: if self.sl: self.setAllDictionaries(enchant.Dict(self.sl)) else: self.setAllDictionaries(enchant.Dict()) else: self.setAllDictionaries(None) globalSettings.spellCheck = yes def setAllDictionaries(self, dictionary): for hl in self.highlighters: hl.dictionary = dictionary hl.rehighlight() def changeLocale(self): if self.sl: localedlg = LocaleDialog(self, defaultText=self.sl) else: localedlg = LocaleDialog(self) if localedlg.exec() != QDialog.Accepted: return sl = localedlg.localeEdit.text() setdefault = localedlg.checkBox.isChecked() if sl: try: sl = str(sl) enchant.Dict(sl) except Exception as e: QMessageBox.warning(self, '', str(e)) else: self.sl = sl self.enableSpellCheck(self.actionEnableSC.isChecked()) else: self.sl = None self.enableSpellCheck(self.actionEnableSC.isChecked()) if setdefault: globalSettings.spellCheckLocale = sl def searchBarVisibilityChanged(self, visible): self.actionSearch.setChecked(visible) if visible: self.searchEdit.setFocus(Qt.ShortcutFocusReason) def find(self, back=False): flags = QTextDocument.FindFlags() if back: flags |= QTextDocument.FindBackward if self.csBox.isChecked(): flags |= QTextDocument.FindCaseSensitively text = self.searchEdit.text() editBox = self.editBoxes[self.ind] cursor = editBox.textCursor() newCursor = editBox.document().find(text, cursor, flags) if not newCursor.isNull(): editBox.setTextCursor(newCursor) return self.setSearchEditColor(True) cursor.movePosition(QTextCursor.End if back else QTextCursor.Start) newCursor = editBox.document().find(text, cursor, flags) if not newCursor.isNull(): editBox.setTextCursor(newCursor) return self.setSearchEditColor(True) self.setSearchEditColor(False) def setSearchEditColor(self, found): palette = self.searchEdit.palette() palette.setColor(QPalette.Active, QPalette.Base, Qt.white if found else QColor(255, 102, 102)) self.searchEdit.setPalette(palette) def getHtml(self, includeStyleSheet=True, includeTitle=True, includeMeta=False, webenv=False): if self.markups[self.ind] is None: markupClass = self.getMarkupClass() errMsg = self.tr('Could not parse file contents, check if ' 'you have the <a href="%s">necessary module</a> installed!') try: errMsg %= markupClass.attributes[MODULE_HOME_PAGE] except (AttributeError, KeyError): # Remove the link if markupClass doesn't have the needed attribute errMsg = errMsg.replace('<a href="%s">', '') errMsg = errMsg.replace('</a>', '') return '<p style="color: red">%s</p>' % errMsg text = self.editBoxes[self.ind].toPlainText() headers = '' if includeStyleSheet: headers += '<style type="text/css">\n' + self.ss + '</style>\n' cssFileName = self.getDocumentTitle(baseName=True)+'.css' if QFile(cssFileName).exists(): headers += '<link rel="stylesheet" type="text/css" href="%s">\n' \ % cssFileName if includeMeta: headers += ('<meta name="generator" content="ReText %s">\n' % app_version) fallbackTitle = self.getDocumentTitle() if includeTitle else '' return self.markups[self.ind].get_whole_html(text, custom_headers=headers, include_stylesheet=includeStyleSheet, fallback_title=fallbackTitle, webenv=webenv) def updatePreviewBox(self): self.previewBlocked = False pb = self.previewBoxes[self.ind] textedit = isinstance(pb, QTextEdit) if textedit: scrollbar = pb.verticalScrollBar() disttobottom = scrollbar.maximum() - scrollbar.value() else: frame = pb.page().mainFrame() scrollpos = frame.scrollPosition() try: html = self.getHtml() except Exception: return self.printError() if textedit: pb.setHtml(html) pb.document().setDefaultFont(globalSettings.font) scrollbar.setValue(scrollbar.maximum() - disttobottom) else: pb.settings().setFontFamily(QWebSettings.StandardFont, globalSettings.font.family()) pb.settings().setFontSize(QWebSettings.DefaultFontSize, globalSettings.font.pointSize()) pb.setHtml(html, QUrl.fromLocalFile(self.fileNames[self.ind])) frame.setScrollPosition(scrollpos) def updateLivePreviewBox(self): if self.actionLivePreview.isChecked() and self.previewBlocked == False: self.previewBlocked = True QTimer.singleShot(1000, self.updatePreviewBox) def showInDir(self): if self.fileNames[self.ind]: path = QFileInfo(self.fileNames[self.ind]).path() QDesktopServices.openUrl(QUrl.fromLocalFile(path)) else: QMessageBox.warning(self, '', self.tr("Please, save the file somewhere.")) def setCurrentFile(self): self.setWindowTitle("") self.tabWidget.setTabText(self.ind, self.getDocumentTitle(baseName=True)) self.setWindowFilePath(self.fileNames[self.ind]) files = readListFromSettings("recentFileList") while self.fileNames[self.ind] in files: files.remove(self.fileNames[self.ind]) files.insert(0, self.fileNames[self.ind]) if len(files) > 10: del files[10:] writeListToSettings("recentFileList", files) QDir.setCurrent(QFileInfo(self.fileNames[self.ind]).dir().path()) self.docTypeChanged() def createNew(self, text=None): self.tabWidget.addTab(self.createTab(""), self.tr("New document")) self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if text: self.editBoxes[self.ind].textCursor().insertText(text) def switchTab(self, shift=1): self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count()) def updateRecentFiles(self): self.menuRecentFiles.clear() self.recentFilesActions = [] filesOld = readListFromSettings("recentFileList") files = [] for f in filesOld: if QFile.exists(f): files.append(f) self.recentFilesActions.append(self.act(f, trig=self.openFunction(f))) writeListToSettings("recentFileList", files) for action in self.recentFilesActions: self.menuRecentFiles.addAction(action) def markupFunction(self, markup): return lambda: self.setDefaultMarkup(markup) def openFunction(self, fileName): return lambda: self.openFileWrapper(fileName) def extensionFuntion(self, data): return lambda: \ self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension']) def getExportExtensionsList(self): extensions = [] for extsprefix in datadirs: extsdir = QDir(extsprefix+'/export-extensions/') if extsdir.exists(): for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable): extensions.append(self.readExtension(fileInfo.filePath())) locale = QLocale.system().name() self.extensionActions = [] for extension in extensions: try: if ('Name[%s]' % locale) in extension: name = extension['Name[%s]' % locale] elif ('Name[%s]' % locale.split('_')[0]) in extension: name = extension['Name[%s]' % locale.split('_')[0]] else: name = extension['Name'] data = {} for prop in ('FileFilter', 'DefaultExtension', 'Exec'): if 'X-ReText-'+prop in extension: data[prop] = extension['X-ReText-'+prop] elif prop in extension: data[prop] = extension[prop] else: data[prop] = '' action = self.act(name, trig=self.extensionFuntion(data)) if 'Icon' in extension: action.setIcon(self.actIcon(extension['Icon'])) mimetype = extension['MimeType'] if 'MimeType' in extension else None except KeyError: print('Failed to parse extension: Name is required', file=sys.stderr) else: self.extensionActions.append((action, mimetype)) def updateExtensionsVisibility(self): markupClass = self.getMarkupClass() for action in self.extensionActions: if markupClass is None: action[0].setEnabled(False) continue mimetype = action[1] if mimetype == None: enabled = True elif markupClass == markups.MarkdownMarkup: enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown")) elif markupClass == markups.ReStructuredTextMarkup: enabled = (mimetype in ("text/x-retext-rst", "text/x-rst")) else: enabled = False action[0].setEnabled(enabled) def readExtension(self, fileName): extFile = QFile(fileName) extFile.open(QIODevice.ReadOnly) extension = {} stream = QTextStream(extFile) while not stream.atEnd(): line = stream.readLine() if '=' in line: index = line.index('=') extension[line[:index].rstrip()] = line[index+1:].lstrip() extFile.close() return extension def openFile(self): supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;' fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "", self.tr("Supported files") + fileFilter + self.tr("All files (*)")) for fileName in fileNames[0]: self.openFileWrapper(fileName) def openFileWrapper(self, fileName): if not fileName: return fileName = QFileInfo(fileName).canonicalFilePath() exists = False for i in range(self.tabWidget.count()): if self.fileNames[i] == fileName: exists = True ex = i if exists: self.tabWidget.setCurrentIndex(ex) elif QFile.exists(fileName): noEmptyTab = ( (self.ind is None) or self.fileNames[self.ind] or self.editBoxes[self.ind].toPlainText() or self.editBoxes[self.ind].document().isModified() ) if noEmptyTab: self.tabWidget.addTab(self.createTab(fileName), "") self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if fileName: self.fileSystemWatcher.addPath(fileName) self.fileNames[self.ind] = fileName self.openFileMain() def openFileMain(self, encoding=None): openfile = QFile(self.fileNames[self.ind]) openfile.open(QIODevice.ReadOnly) stream = QTextStream(openfile) if encoding: stream.setCodec(encoding) elif globalSettings.defaultCodec: stream.setCodec(globalSettings.defaultCodec) text = stream.readAll() openfile.close() markupClass = markups.get_markup_for_file_name( self.fileNames[self.ind], return_class=True) self.highlighters[self.ind].docType = (markupClass.name if markupClass else '') self.markups[self.ind] = self.getMarkup() if self.defaultMarkup: self.highlighters[self.ind].docType = self.defaultMarkup.name editBox = self.editBoxes[self.ind] modified = bool(encoding) and (editBox.toPlainText() != text) editBox.setPlainText(text) self.setCurrentFile() editBox.document().setModified(modified) self.setWindowModified(modified) def showEncodingDialog(self): if not self.maybeSave(self.ind): return encoding, ok = QInputDialog.getItem(self, '', self.tr('Select file encoding from the list:'), [bytes(b).decode() for b in QTextCodec.availableCodecs()], 0, False) if ok: self.openFileMain(encoding) def saveFile(self): self.saveFileMain(dlg=False) def saveFileAs(self): self.saveFileMain(dlg=True) def saveAll(self): oldind = self.ind for self.ind in range(self.tabWidget.count()): if self.fileNames[self.ind] and QFileInfo(self.fileNames[self.ind]).isWritable(): self.saveFileCore(self.fileNames[self.ind]) self.editBoxes[self.ind].document().setModified(False) self.ind = oldind def saveFileMain(self, dlg): if (not self.fileNames[self.ind]) or dlg: markupClass = self.getMarkupClass() if (markupClass is None) or not hasattr(markupClass, 'default_extension'): defaultExt = self.tr("Plain text (*.txt)") ext = ".txt" else: defaultExt = self.tr('%s files', 'Example of final string: Markdown files') \ % markupClass.name + ' (' + str.join(' ', ('*'+extension for extension in markupClass.file_extensions)) + ')' if markupClass == markups.MarkdownMarkup: ext = globalSettings.markdownDefaultFileExtension elif markupClass == markups.ReStructuredTextMarkup: ext = globalSettings.restDefaultFileExtension else: ext = markupClass.default_extension newFileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)[0] if newFileName: if not QFileInfo(newFileName).suffix(): newFileName += ext if self.fileNames[self.ind]: self.fileSystemWatcher.removePath(self.fileNames[self.ind]) self.fileNames[self.ind] = newFileName self.actionSetEncoding.setDisabled(self.autoSaveActive()) if self.fileNames[self.ind]: result = self.saveFileCore(self.fileNames[self.ind]) if result: self.setCurrentFile() self.editBoxes[self.ind].document().setModified(False) self.setWindowModified(False) return True else: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return False def saveFileCore(self, fn, addToWatcher=True): self.fileSystemWatcher.removePath(fn) savefile = QFile(fn) result = savefile.open(QIODevice.WriteOnly) if result: savestream = QTextStream(savefile) if globalSettings.defaultCodec: savestream.setCodec(globalSettings.defaultCodec) savestream << self.editBoxes[self.ind].toPlainText() savefile.close() if result and addToWatcher: self.fileSystemWatcher.addPath(fn) return result def saveHtml(self, fileName): if not QFileInfo(fileName).suffix(): fileName += ".html" try: htmltext = self.getHtml(includeStyleSheet=False, includeMeta=True, webenv=True) except Exception: return self.printError() htmlFile = QFile(fileName) htmlFile.open(QIODevice.WriteOnly) html = QTextStream(htmlFile) if globalSettings.defaultCodec: html.setCodec(globalSettings.defaultCodec) html << htmltext htmlFile.close() def textDocument(self): td = QTextDocument() td.setMetaInformation(QTextDocument.DocumentTitle, self.getDocumentTitle()) if self.ss: td.setDefaultStyleSheet(self.ss) td.setHtml(self.getHtml()) td.setDefaultFont(globalSettings.font) return td def saveOdf(self): try: document = self.textDocument() except Exception: return self.printError() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))[0] if not QFileInfo(fileName).suffix(): fileName += ".odt" writer = QTextDocumentWriter(fileName) writer.setFormat("odf") writer.write(document) def saveFileHtml(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))[0] if fileName: self.saveHtml(fileName) def getDocumentForPrint(self): if globalSettings.useWebKit: return self.previewBoxes[self.ind] try: return self.textDocument() except Exception: self.printError() def standardPrinter(self): printer = QPrinter(QPrinter.HighResolution) printer.setDocName(self.getDocumentTitle()) printer.setCreator('ReText %s' % app_version) return printer def savePdf(self): self.updatePreviewBox() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), "", self.tr("PDF files (*.pdf)"))[0] if fileName: if not QFileInfo(fileName).suffix(): fileName += ".pdf" printer = self.standardPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(fileName) document = self.getDocumentForPrint() if document != None: document.print(printer) def printFile(self): self.updatePreviewBox() printer = self.standardPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(self.tr("Print document")) if (dlg.exec() == QDialog.Accepted): document = self.getDocumentForPrint() if document != None: document.print(printer) def printPreview(self): document = self.getDocumentForPrint() if document == None: return printer = self.standardPrinter() preview = QPrintPreviewDialog(printer, self) preview.paintRequested.connect(document.print) preview.exec() def runExtensionCommand(self, command, filefilter, defaultext): of = ('%of' in command) html = ('%html' in command) if of: if defaultext and not filefilter: filefilter = '*'+defaultext fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)[0] if not fileName: return if defaultext and not QFileInfo(fileName).suffix(): fileName += defaultext basename = '.%s.retext-temp' % self.getDocumentTitle(baseName=True) if html: tmpname = basename+'.html' self.saveHtml(tmpname) else: tmpname = basename+self.getMarkupClass().default_extension self.saveFileCore(tmpname, addToWatcher=False) command = command.replace('%of', '"out'+defaultext+'"') command = command.replace('%html' if html else '%if', '"'+tmpname+'"') try: Popen(str(command), shell=True).wait() except Exception as error: errorstr = str(error) QMessageBox.warning(self, '', self.tr('Failed to execute the command:') + '\n' + errorstr) QFile(tmpname).remove() if of: QFile('out'+defaultext).rename(fileName) def getDocumentTitle(self, baseName=False): markup = self.markups[self.ind] realTitle = '' if markup and not baseName: text = self.editBoxes[self.ind].toPlainText() try: realTitle = markup.get_document_title(text) except Exception: self.printError() if realTitle: return realTitle elif self.fileNames[self.ind]: fileinfo = QFileInfo(self.fileNames[self.ind]) basename = fileinfo.completeBaseName() return (basename if basename else fileinfo.fileName()) return self.tr("New document") def autoSaveActive(self): return self.autoSaveEnabled and self.fileNames[self.ind] and \ QFileInfo(self.fileNames[self.ind]).isWritable() def modificationChanged(self, changed): if self.autoSaveActive(): changed = False self.actionSave.setEnabled(changed) self.setWindowModified(changed) def clipboardDataChanged(self): mimeData = QApplication.instance().clipboard().mimeData() if mimeData is not None: self.actionPaste.setEnabled(mimeData.hasText()) def insertChars(self, chars): tc = self.editBoxes[self.ind].textCursor() if tc.hasSelection(): selection = tc.selectedText() if selection.startswith(chars) and selection.endswith(chars): if len(selection) > 2*len(chars): selection = selection[len(chars):-len(chars)] tc.insertText(selection) else: tc.insertText(chars+tc.selectedText()+chars) else: tc.insertText(chars) def insertTag(self, ut): if not ut: return if isinstance(ut, int): ut = self.usefulTags[ut - 1] arg = ' style=""' if ut == 'span' else '' tc = self.editBoxes[self.ind].textCursor() if ut == 'img': toinsert = ('<a href="' + tc.selectedText() + '" target="_blank"><img src="' + tc.selectedText() + '"/></a>') elif ut == 'a': toinsert = ('<a href="' + tc.selectedText() + '" target="_blank">' + tc.selectedText() + '</a>') else: toinsert = '<'+ut+arg+'>'+tc.selectedText()+'</'+ut+'>' tc.insertText(toinsert) self.tagsBox.setCurrentIndex(0) def insertSymbol(self, num): if num: self.editBoxes[self.ind].insertPlainText('&'+self.usefulChars[num-1]+';') self.symbolBox.setCurrentIndex(0) def fileChanged(self, fileName): ind = self.fileNames.index(fileName) self.tabWidget.setCurrentIndex(ind) if not QFile.exists(fileName): self.editBoxes[ind].document().setModified(True) QMessageBox.warning(self, '', self.tr( 'This file has been deleted by other application.\n' 'Please make sure you save the file before exit.')) elif not self.editBoxes[ind].document().isModified(): # File was not modified in ReText, reload silently self.openFileMain() self.updatePreviewBox() else: text = self.tr( 'This document has been modified by other application.\n' 'Do you want to reload the file (this will discard all ' 'your changes)?\n') if self.autoSaveEnabled: text += self.tr( 'If you choose to not reload the file, auto save mode will ' 'be disabled for this session to prevent data loss.') messageBox = QMessageBox(QMessageBox.Warning, '', text) reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole) messageBox.addButton(QMessageBox.Cancel) messageBox.exec() if messageBox.clickedButton() is reloadButton: self.openFileMain() self.updatePreviewBox() else: self.autoSaveEnabled = False self.editBoxes[ind].document().setModified(True) if fileName not in self.fileSystemWatcher.files(): # https://github.com/retext-project/retext/issues/137 self.fileSystemWatcher.addPath(fileName) def maybeSave(self, ind): if self.autoSaveActive(): self.saveFileCore(self.fileNames[self.ind]) return True if not self.editBoxes[ind].document().isModified(): return True self.tabWidget.setCurrentIndex(ind) ret = QMessageBox.warning(self, '', self.tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.saveFileMain(False) elif ret == QMessageBox.Cancel: return False return True def closeEvent(self, closeevent): for self.ind in range(self.tabWidget.count()): if not self.maybeSave(self.ind): return closeevent.ignore() if globalSettings.saveWindowGeometry and not self.isMaximized(): globalSettings.windowGeometry = self.saveGeometry() closeevent.accept() def viewHtml(self): htmlDlg = HtmlDialog(self) try: htmltext = self.getHtml(includeStyleSheet=False, includeTitle=False) except Exception: return self.printError() winTitle = self.getDocumentTitle(baseName=True) htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")") htmlDlg.textEdit.setPlainText(htmltext.rstrip()) htmlDlg.hl.rehighlight() htmlDlg.show() htmlDlg.raise_() htmlDlg.activateWindow() def openHelp(self): QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki')) def aboutDialog(self): QMessageBox.about(self, self.aboutWindowTitle, '<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__)) +'</b></p>' + self.tr('Simple but powerful editor' ' for Markdown and reStructuredText') +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011\u2013' '2015') +'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website') +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">' +self.tr('Markdown syntax') +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' +self.tr('reStructuredText syntax')+'</a></p>') def setDefaultMarkup(self, markup): self.defaultMarkup = markup defaultName = markups.get_available_markups()[0].name writeToSettings('defaultMarkup', markup.name, defaultName) oldind = self.ind for self.ind in range(len(self.previewBoxes)): self.docTypeChanged() self.ind = oldind
class CueSettings(QDialog): on_apply = QtCore.pyqtSignal(dict) def __init__(self, cue=None, cue_class=None, **kwargs): """ :param cue: Target cue, or None for multi-editing :param cue_class: when cue is None, used to specify the reference class """ super().__init__(**kwargs) if cue is not None: cue_class = cue.__class__ cue_properties = deepcopy(cue.properties()) self.setWindowTitle(cue_properties['name']) else: cue_properties = {} if cue_class is None: cue_class = Cue self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMaximumSize(635, 530) self.setMinimumSize(635, 530) self.resize(635, 530) self.sections = QTabWidget(self) self.sections.setGeometry(QtCore.QRect(5, 10, 625, 470)) for widget in sorted(CueSettingsRegistry().filter(cue_class), key=lambda w: w.Name): if issubclass(widget, CueSettingsPage): settings_widget = widget(cue_class) else: settings_widget = widget() settings_widget.load_settings(cue_properties) settings_widget.enable_check(cue is None) self.sections.addTab(settings_widget, settings_widget.Name) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setGeometry(10, 490, 615, 30) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Apply) self.dialogButtons.rejected.connect(self.reject) self.dialogButtons.accepted.connect(self.accept) apply = self.dialogButtons.button(QDialogButtonBox.Apply) apply.clicked.connect(self.apply) def accept(self): self.apply() super().accept() def apply(self): settings = {} for n in range(self.sections.count()): deep_update(settings, self.sections.widget(n).get_settings()) self.on_apply.emit(settings)
class RunWidget(QWidget): allTabsClosed = pyqtSignal() projectExecuted = pyqtSignal(str) fileExecuted = pyqtSignal(str) def __init__(self): QWidget.__init__(self) self.__programs = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) connections = ( { "target": "tools_dock", "signal_name": "executeFile", "slot": self.execute_file }, { "target": "tools_dock", "signal_name": "executeProject", "slot": self.execute_project }, { "target": "tools_dock", "signal_name": "executeSelection", "slot": self.execute_selection }, { "target": "tools_dock", "signal_name": "stopApplication", "slot": self.kill_application } ) IDE.register_signals("tools_dock", connections) self._tabs = QTabWidget() self._tabs.setTabsClosable(True) self._tabs.setMovable(True) self._tabs.setDocumentMode(True) vbox.addWidget(self._tabs) # Menu for tab self._tabs.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) self._tabs.tabBar().customContextMenuRequested.connect( self._menu_for_tabbar) self._tabs.tabCloseRequested.connect(self.close_tab) IDE.register_service("run_widget", self) _ToolsDock.register_widget(translations.TR_OUTPUT, self) def install(self): ninjaide = IDE.get_service("ide") ninjaide.goingDown.connect(self._kill_processes) def _kill_processes(self): """Stop all applications""" for program in self.__programs: program.kill() def kill_application(self): """Stop application by current tab index""" index = self._tabs.currentIndex() if index == -1: return program = self.__programs[index] program.kill() def _menu_for_tabbar(self, position): menu = QMenu() close_action = menu.addAction(translations.TR_CLOSE_TAB) close_all_action = menu.addAction(translations.TR_CLOSE_ALL_TABS) close_other_action = menu.addAction(translations.TR_CLOSE_OTHER_TABS) qaction = menu.exec_(self.mapToGlobal(position)) if qaction == close_action: index = self._tabs.tabBar().tabAt(position) self.close_tab(index) elif qaction == close_all_action: self.close_all_tabs() elif qaction == close_other_action: self.close_all_tabs_except_this() def close_tab(self, tab_index): program = self.__programs[tab_index] self.__programs.remove(program) self._tabs.removeTab(tab_index) # Close process and delete OutputWidget program.main_process.close() program.outputw.deleteLater() del program.outputw if self._tabs.count() == 0: # Hide widget tools = IDE.get_service("tools_dock") tools.hide_widget(self) def close_all_tabs(self): for _ in range(self._tabs.count()): self.close_tab(0) def close_all_tabs_except_this(self): self._tabs.tabBar().moveTab(self._tabs.currentIndex(), 0) for _ in range(self._tabs.count()): if self._tabs.count() > 1: self.close_tab(1) def execute_file(self): """Execute the current file""" main_container = IDE.get_service("main_container") editor_widget = main_container.get_current_editor() if editor_widget is not None and (editor_widget.is_modified or editor_widget.file_path): main_container.save_file(editor_widget) file_path = editor_widget.file_path if file_path is None: return # Emit signal for plugin! self.fileExecuted.emit(editor_widget.file_path) extension = file_manager.get_file_extension(file_path) # TODO: Remove the IF statment and use Handlers if extension == "py": self.start_process(filename=file_path) def execute_selection(self): """Execute selected text or current line if not have a selection""" main_container = IDE.get_service("main_container") editor_widget = main_container.get_current_editor() if editor_widget is not None: text = editor_widget.selected_text().splitlines() if not text: # Execute current line text = [editor_widget.line_text()] code = [] for line_text in text: # Get part before firs '#' code.append(line_text.split("#", 1)[0]) # Join to execute with python -c command final_code = ";".join([line.strip() for line in code if line]) # Highlight code to be executed editor_widget.show_run_cursor() # Ok run! self.start_process( filename=editor_widget.file_path, code=final_code) def execute_project(self): """Execute the project marked as Main Project.""" projects_explorer = IDE.get_service("projects_explorer") if projects_explorer is None: return nproject = projects_explorer.current_project if nproject: main_file = nproject.main_file if not main_file: # Open project properties to specify the main file projects_explorer.current_tree.open_project_properties() else: # Save project files projects_explorer.save_project() # Emit a signal for plugin! self.projectExecuted.emit(nproject.path) main_file = file_manager.create_path( nproject.path, nproject.main_file) self.start_process( filename=main_file, python_exec=nproject.python_exec, pre_exec_script=nproject.pre_exec_script, post_exec_script=nproject.post_exec_script, program_params=nproject.program_params ) def start_process(self, **kwargs): # First look if we can reuse a tab fname = kwargs.get("filename") program = None for prog in self.__programs: if prog.filename == fname: if not prog.is_running(): program = prog break if program is not None: index = self.__programs.index(program) program.update(**kwargs) self._tabs.setCurrentIndex(index) program.outputw.gray_out_old_text() else: program = Program(**kwargs) # Create new output widget outputw = OutputWidget(self) program.set_output_widget(outputw) self.add_tab(outputw, program.display_name()) self.__programs.append(program) program.start() def add_tab(self, outputw, tab_text): inserted_index = self._tabs.addTab(outputw, tab_text) self._tabs.setCurrentIndex(inserted_index)
class ScoreWizardDialog(QDialog): pitchLanguageChanged = pyqtSignal(str) def __init__(self, mainwindow): super(ScoreWizardDialog, self).__init__(mainwindow) self.addAction(mainwindow.actionCollection.help_whatsthis) self._pitchLanguage = None layout = QVBoxLayout() self.setLayout(layout) self.tabs = QTabWidget() b = self.dialogButtons = QDialogButtonBox() b.setStandardButtons( QDialogButtonBox.Reset | QDialogButtonBox.Ok | QDialogButtonBox.Cancel) b.accepted.connect(self.accept) b.rejected.connect(self.reject) userguide.addButton(b, "scorewiz") b.button(QDialogButtonBox.Reset).clicked.connect(self.reset) self.previewButton = b.addButton('', QDialogButtonBox.ActionRole) self.previewButton.clicked.connect(self.showPreview) layout.addWidget(self.tabs) layout.addWidget(b) self.header = Header(self) self.tabs.addTab(self.header, '') self.parts = Parts(self) self.tabs.addTab(self.parts, '') self.settings = Settings(self) self.tabs.addTab(self.settings, '') self.tabs.setCurrentIndex(0) self.tabs.widget(0).widget() # activate it self.tabs.currentChanged.connect(self.slotCurrentChanged) qutil.saveDialogSize(self, "scorewiz/dialog/size") app.translateUI(self) self.accepted.connect(self.slotAccepted) def translateUI(self): self.setWindowTitle(app.caption(_("Score Setup Wizard"))) for i in range(self.tabs.count()): self.tabs.setTabText(i, self.tabs.widget(i).title()) self.dialogButtons.button(QDialogButtonBox.Reset).setText(_("Clear")) self.dialogButtons.button(QDialogButtonBox.Reset).setToolTip(_( "Clears the current page of the Score Wizard.")) self.previewButton.setText(_("Preview")) def slotCurrentChanged(self, i): """Lazy-loads the tab's page if shown for the first time.""" self.tabs.widget(i).widget() def reset(self): self.tabs.currentWidget().widget().clear() def setPitchLanguage(self, language): if language != self._pitchLanguage: self._pitchLanguage = language self.pitchLanguageChanged.emit(language) def pitchLanguage(self): if self._pitchLanguage is None: # load setting; saving occurs in .settings.py lang = QSettings().value('scorewiz/lilypond/pitch_language', '', str) from .scoreproperties import keyNames if lang not in keyNames: lang = '' self._pitchLanguage = lang return self._pitchLanguage def slotAccepted(self): """Makes the score and puts it in the editor.""" from . import build builder = build.Builder(self) # get the builder text = builder.text() # get the source text lydoc = ly.document.Document(text) # temporarily store it in a lydoc cursor = ly.document.Cursor(lydoc) # make a cursor selecting it indent.indenter().indent(cursor) # indent it according to user prefs doc = app.openUrl(QUrl()) # get a new Frescobaldi document doc.setPlainText(lydoc.plaintext()) # write the text in it doc.setModified(False) # make it "not modified" self.parent().setCurrentDocument(doc) def showPreview(self): """Shows a preview.""" # get the document and fill in some example music from . import preview, build builder = build.Builder(self) doc = builder.document() preview.examplify(doc) # preview it import musicpreview dlg = musicpreview.MusicPreviewDialog(self) dlg.preview(builder.text(doc), _("Score Preview")) dlg.exec_() dlg.cleanup()
class SettingsDialog(QDialog): def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent) # Set layout. _layout = QVBoxLayout(self) _layout.setContentsMargins(0,0,0,0) self.setLayout(_layout) # Set window title. self.setWindowTitle(tr("Settings")) # Tab widget self.tabs = QTabWidget(self) self.layout().addWidget(self.tabs) self.tabs.addTab(GeneralSettingsPanel(self), tr("&General")) self.tabs.addTab(ContentSettingsPanel(self), tr("Con&tent")) self.tabs.addTab(AdremoverSettingsPanel(self), tr("Ad &Remover")) self.tabs.addTab(DataSettingsPanel(self), tr("&Data && Privacy")) self.tabs.addTab(NetworkSettingsPanel(self), tr("N&etwork")) self.tabs.addTab(ExtensionsSettingsPanel(self), tr("E&xtensions")) # Toolbar self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.toolBar.setStyleSheet(common.blank_toolbar) self.layout().addWidget(self.toolBar) # Apply button applyButton = QPushButton(tr("&Apply"), self) applyButton.clicked.connect(self.saveSettings) self.toolBar.addWidget(applyButton) # Reload settings button closeButton = QPushButton(tr("&Close"), self) closeButton.clicked.connect(self.hide) self.toolBar.addWidget(closeButton) # Load settings self.loadSettings() def show(self): super(SettingsDialog, self).show() self.loadSettings() def url(self): return QUrl("") def icon(self): return common.complete_icon("preferences-system") # Method to load all settings. def loadSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).loadSettings() # Method to save all settings. def saveSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).saveSettings() settings.reset_extensions() settings.reload_userscripts() for window in browser.windows: try: window.reloadExtensions() except: pass try: window.applySettings() except: pass
class ObserverWindow(QMainWindow): messageReceived = pyqtSignal(str, str, str, str, str) newFilterSettingsApplied = pyqtSignal(str) resetDisplays = pyqtSignal() #__________________________________________________________________ def __init__(self, client, logger): super(ObserverWindow, self).__init__() self._connectionState = None self._countUntitledDisplay = 0 self._displays = [] self._session = 'Default session' self._host = 'localhost' self._port = 1883 self._rootTopic = '' self._logger = logger self._logger.info(self.tr("Started")) reg = QSettings() if "current session" in reg.childKeys() and reg.value( "current session", '').strip() and reg.value( "current session", '').strip() in reg.childGroups(): self._session = reg.value("current session") self._logger.info(self.tr("Current session : ") + self._session) if self._session not in reg.childGroups(): reg.beginGroup(self._session) reg.setValue("host", self._host) reg.setValue("port", self._port) reg.setValue("root topic", self._rootTopic) reg.endGroup() else: reg.beginGroup(self._session) self._host = reg.value("host", 'localhost') try: self._port = reg.value("port", 1883, type=int) except: pass self._rootTopic = reg.value("root topic", '') reg.endGroup() if "current session" in reg.childKeys() and not reg.value( "current session", '') in reg.childGroups(): reg.remove("current session") self._mqttSwitchingConnection = False self._mqttClient = client self._mqttServerHost = self._host self._mqttServerPort = self._port self._mqttRootTopic = self._rootTopic self._mqttSubTopic = '#' if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' QApplication.desktop().screenCountChanged.connect(self.restoreWindow) QApplication.desktop().resized.connect(self.restoreWindow) self.setWindowTitle(self._session) self.setWindowIcon(QIcon(':/view-eye.svg')) self._tabWidget = QTabWidget() self._cloudLabel = QLabel() self._connectionStateLabel = QLabel() self.builUi() reg.beginGroup(self._session) inbox = reg.value("param inbox", 'inbox') outbox = reg.value("param outbox", 'outbox') regexInbox = reg.value("regex inbox", r'^%ROOT%/(?P<correspondent>.+)/%INBOX%$') regexOutbox = reg.value("regex outbox", r'^%ROOT%/(?P<correspondent>.+)/%OUTBOX%$') regexDefault = reg.value("regex default", r'.*/(?P<correspondent>[^/]+)/[^/]+$') reg.endGroup() regexInbox = regexInbox.replace("%ROOT%", self._rootTopic).replace( "%INBOX%", inbox) regexOutbox = regexOutbox.replace("%ROOT%", self._rootTopic).replace( "%OUTBOX%", outbox) self._topicRegexInbox = None try: self._topicRegexInbox = re.compile(regexInbox) except Exception as e: self._logger.error(self.tr("Failed to compile inbox regex")) self._logger.debug(e) self._topicRegexOutbox = None try: self._topicRegexOutbox = re.compile(regexOutbox) except Exception as e: self._logger.error(self.tr("Failed to compile outbox regex")) self._logger.debug(e) self._topicRegexDefault = None try: self._topicRegexDefault = re.compile(regexDefault) except Exception as e: self._logger.error( self.tr("Failed to compile topic default regex")) self._logger.debug(e) self.addDisplay(self.tr("All messsages")) reg.beginGroup(self._session) for i in reg.childGroups(): self.addDisplay(i) reg.endGroup() self.changeConnectionState(ConnectionState.DISCONNECTED) self._logger.info("{0} : {1}".format(self.tr("MQTT server host"), self._mqttServerHost)) self._logger.info("{0} : {1}".format(self.tr("MQTT server port"), self._mqttServerPort)) self._logger.info("{0} : {1}".format( self.tr("MQTT clientid"), self._mqttClient._client_id.decode("latin1"))) Timer(0, self.layoutLoadSettings).start() Timer(0, self.start).start() #__________________________________________________________________ @pyqtSlot() def addDisplay(self, title=None): if not title: if self._countUntitledDisplay: title = "{0} ({1})".format(self.tr("New observation"), self._countUntitledDisplay) else: title = self.tr("New observation") self._countUntitledDisplay = self._countUntitledDisplay + 1 reg = QSettings() reg.beginGroup(self._session) if title not in reg.childGroups(): reg.beginGroup(title) reg.setValue("display", int(TopicDisplay.EMPHASIZEDCORRESPONDENT)) reg.endGroup() reg.endGroup() reg.sync() root_observation = not len(self._displays) new_display = ObserverDisplayWidget(title, self._logger, self._session, self._mqttServerHost, self._mqttServerPort, self._mqttRootTopic, remove=not root_observation, filter=not root_observation) new_display.addObservation.connect(self.addDisplay) new_display.delObservation.connect(self.removeCurrentDisplay) self.newFilterSettingsApplied.connect(new_display.applyFilterSettings) self._displays.append(new_display) self._tabWidget.addTab(new_display, title) self.resetDisplays.connect(new_display.reset) self.messageReceived.connect(new_display.processMessage) new_display.newFilterSettings.connect(self.applyFilterSettings) new_display.resetWills.connect(self.applyResetWills) self._tabWidget.setCurrentWidget(new_display) self._logger.info("{0} : {1}".format(self.tr("Added observation"), title)) dspmsg = '' for d in self._displays: if dspmsg: dspmsg += ' | ' else: dspmsg = 'Displays : ' dspmsg += d.title() self._logger.debug(dspmsg) #__________________________________________________________________ @pyqtSlot(dict) def applyFilterSettings(self, filter): try: display = None for d in self._displays: if d.title() == filter["observation"]: display = d if not display: self._logger.error( self.tr("Observation not found in displays : ") + filter["observation"]) else: if filter["name"] != filter["observation"]: for d in self._displays: if d.title() == filter["name"]: self._logger.warning( self.tr("Can't rename observation '") + filter["observation"] + self.tr("' : '") + filter["name"] + self.tr("' already exists")) msgbox = QMessageBox() msgbox.setWindowTitle(self.tr("Observer")) msgbox.setWindowIcon( QIcon(':/magnifier-black.svg')) msgbox.setText( self.tr("Ignore apply filter !") + "<br><br><i>" + self. tr("Can't rename observation (name already in use)." ) + "</i><br>") msgbox.setStandardButtons(QMessageBox.Close) msgbox.setAttribute(Qt.WA_DeleteOnClose) msgbox.setWindowFlags( msgbox.windowFlags() & ~Qt.WindowContextHelpButtonHint) msgbox.button(QMessageBox.Close).setText( self.tr("Close")) msgbox.move(self.pos() + QPoint(40, 40)) msgbox.exec() return display.setTitle(filter["name"]) self._tabWidget.setTabText(self._tabWidget.currentIndex(), filter["name"]) reg = QSettings() reg.beginGroup(self._session) reg.remove(filter["observation"]) reg.endGroup() reg = QSettings() reg.beginGroup(self._session) reg.beginGroup(filter["name"]) reg.setValue("display", int(filter["display"])) reg.setValue("filter 1", filter["filter 1"]) reg.setValue("filter 2", filter["filter 2"]) reg.setValue("filter 3", filter["filter 3"]) reg.setValue("filter 4", filter["filter 4"]) reg.setValue("buffer size", filter["buffer size"]) reg.remove("Hidden correspondents") reg.beginGroup("Hidden correspondents") for p in filter["hidden correspondents"]: reg.setValue(p.replace('/', '\\'), '') reg.endGroup() reg.remove("Hidden topics") reg.beginGroup("Hidden topics") for t in filter["hidden topics"]: reg.setValue(t.replace('/', '\\'), '') reg.endGroup() reg.endGroup() reg.endGroup() reg.sync() self.newFilterSettingsApplied.emit(filter["name"]) except Exception as e: self._logger.error("Failed to apply filter settings") self._logger.debug(e) #__________________________________________________________________ @pyqtSlot(list) def applyResetWills(self, topics): for topic in topics: if self._connectionState == ConnectionState.CONNECTED: try: (result, mid) = self._mqttClient.publish(topic, '', qos=0, retain=True) self._logger.info("{0} {1} (mid={2})".format( self.tr("MQTT sending '' to clear will for "), topic, mid)) except Exception as e: self._logger.info("{0} {1} (mid={2})".format( self.tr("MQTT failed to send '' to clear will for "), topic, mid)) self._logger.debug(e) else: self._logger.info("{0} {1}".format( self.tr("MQTT failed to reset will (disconnected) for "), topic)) #__________________________________________________________________ def builUi(self): mw = QWidget() self.setCentralWidget(mw) main_layout = QVBoxLayout(mw) main_layout.setSpacing(12) cloud_image = QLabel() cloud_image.setPixmap(QIcon(":/cloud-data.svg").pixmap(QSize(36, 36))) cloud_image.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) cloud = self._mqttServerHost + ':' + str(self._mqttServerPort) if self._mqttRootTopic: cloud += '/' + self._mqttRootTopic + '/#' else: cloud += '/#' self._cloudLabel.setText(cloud) font = self._cloudLabel.font() font.setPixelSize(12) font.setBold(True) self._cloudLabel.setFont(font) settings_button = QPushButton() settings_button.setIcon(QIcon(":/settings.svg")) settings_button.setFlat(True) settings_button.setToolTip(self.tr("Settings")) settings_button.setIconSize(QSize(16, 16)) settings_button.setFixedSize(QSize(24, 24)) settings_button.setStyleSheet("QPushButton { padding-bottom: 4px }") self._connectionStateLabel = QLabel() self._connectionStateLabel.setPixmap( QIcon(":/led-circle-grey.svg").pixmap(QSize(24, 24))) self._connectionStateLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) header_layout = QHBoxLayout() header_layout.addWidget(cloud_image) header_layout.addWidget(self._cloudLabel) header_layout.addStretch() header_layout.addWidget(settings_button) header_layout.addWidget(self._connectionStateLabel) main_layout.addLayout(header_layout) main_layout.addWidget(self._tabWidget) settings_button.pressed.connect(self.settings) #__________________________________________________________________ def changeConnectionState(self, state): self._connectionState = state if state == ConnectionState.CONNECTED: self._connectionStateLabel.setPixmap( QIcon(":/led-circle-green.svg").pixmap(QSize(24, 24))) self._connectionStateLabel.setToolTip(self.tr("Connected")) elif state == ConnectionState.CONNECTING: self._connectionStateLabel.setPixmap( QIcon(":/led-circle-yellow.svg").pixmap(QSize(24, 24))) self._connectionStateLabel.setToolTip(self.tr("Connecting")) elif state == ConnectionState.DISCONNECTED: self._connectionStateLabel.setPixmap( QIcon(":/led-circle-red.svg").pixmap(QSize(24, 24))) self._connectionStateLabel.setToolTip(self.tr("Disconnected")) else: self._connectionStateLabel.setPixmap( QIcon(":/led-circle-grey.svg").pixmap(QSize(24, 24))) self._connectionStateLabel.setToolTip("") #__________________________________________________________________ def closeEvent(self, event): self._logger.info(self.tr("Done")) #__________________________________________________________________ def layoutLoadSettings(self): reg = QSettings() pos = reg.value("position", QPoint(200, 200)) size = reg.value("size", QSize(400, 400)) self.move(pos) self.resize(size) #__________________________________________________________________ def layoutSaveSettings(self): reg = QSettings() reg.setValue("position", self.pos()) reg.setValue("size", self.size()) reg.sync() #__________________________________________________________________ def moveEvent(self, event): if self.isVisible(): Timer(0, self.layoutSaveSettings).start() #__________________________________________________________________ def mqttConnect(self): self._logger.info(self.tr("May connect to MQTT server...")) if self._mqttSwitchingConnection and self._connectionState == ConnectionState.DISCONNECTED: self._mqttServerHost = self._host self._mqttServerPort = self._port self._mqttRootTopic = self._rootTopic if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' else: self._mqttSubTopic = '#' cloud = self._mqttServerHost + ':' + str(self._mqttServerPort) if self._mqttRootTopic: cloud += '/' + self._mqttRootTopic + '/#' else: cloud += '/#' self._cloudLabel.setText(cloud) clientid = "Observer/" + QUuid.createUuid().toString() self._logger.debug("MQTT clientid %s", clientid) self._mqttClient.reinitialise(client_id=clientid, clean_session=True, userdata=None) self._logger.info(self.tr("MQTT client reinitialised")) self._logger.info("{0} : {1}".format(self.tr("MQTT server host"), self._mqttServerHost)) self._logger.info("{0} : {1}".format(self.tr("MQTT server port"), self._mqttServerPort)) self._logger.info("{0} : {1}".format( self.tr("MQTT clientid"), self._mqttClient._client_id.decode("latin1"))) self._mqttClient.on_connect = self.mqttOnConnect self._mqttClient.on_disconnect = self.mqttOnDisconnect self._mqttClient.on_log = self.mqttOnLog self._mqttClient.on_message = self.mqttOnMessage self._mqttClient.on_publish = self.mqttOnPublish self._mqttClient.on_subscribe = self.mqttOnSubscribe self._mqttClient.on_unsubscribe = self.mqttOnUnsubscribe self._mqttSwitchingConnection = False self._mqttClient.loop_start() if self._connectionState == ConnectionState.CONNECTED or self._connectionState == ConnectionState.CONNECTING: self._logger.info( self.tr("MQTT connect ignored (already ongoing)")) else: self._logger.info(self.tr("Connect to MQTT server")) try: self._mqttClient.connect(self._mqttServerHost, port=self._mqttServerPort, keepalive=MQTT_KEEPALIVE) self.changeConnectionState(ConnectionState.CONNECTING) self._logger.info(self.tr("MQTT connecting")) except Exception as e: self.changeConnectionState(ConnectionState.DISCONNECTED) self._logger.warning( self.tr("Failed to connect to MQTT server")) self._logger.debug(e) Timer(15.000, self.mqttConnect).start() self._logger.debug("Connection state = " + str(self._connectionState)) #__________________________________________________________________ def mqttDisconnect(self): try: self._mqttClient.disconnect() except Exception as e: self._logger.error(self.tr("MQTT disconnection call failed")) self._logger.debug(e) #__________________________________________________________________ def mqttOnConnect(self, client, userdata, flags, rc): self._logger.debug("Connected to MQTT server with result code: " + str(rc) + " and flags: ", flags) # flags is dict if rc == 0: self._logger.info(self.tr("MQTT connected")) self.changeConnectionState(ConnectionState.CONNECTED) mydata = { 'host': self._mqttServerHost, 'port': self._mqttServerPort } self._mqttClient.user_data_set(str(mydata)) try: (result, mid) = self._mqttClient.subscribe(self._mqttSubTopic) self._logger.info("{0} {1} : {2}".format( self.tr("MQTT subscribing to"), mid, self._mqttSubTopic)) except Exception as e: self._logger.error(self.tr("MQTT subscribe call failed")) self._logger.debug(e) elif rc == 1: self._logger.warning( self. tr("MQTT failed to connect : connection refused - incorrect protocol version" )) elif rc == 2: self._logger.warning( self. tr("MQTT failed to connect : connection refused - invalid client identifier" )) elif rc == 3: self._logger.warning( self. tr("MQTT failed to connect : connection refused - server unavailable" )) elif rc == 4: self._logger.warning( self. tr("MQTT failed to connect : connection refused - bad username or password" )) elif rc == 5: self._logger.warning( self. tr("MQTT failed to connect : connection refused - not authorised" )) else: self._logger.warning("{0} : {1}".format( self.tr("MQTT failed to connect : return code"), rc)) self._logger.debug("Connection state = " + str(self._connectionState)) #__________________________________________________________________ def mqttOnDisconnect(self, client, userdata, rc): self.changeConnectionState(ConnectionState.DISCONNECTED) if self._mqttSwitchingConnection: self._logger.info( self.tr( "Disconnected from MQTT server (switching connection)")) Timer(0, self.mqttConnect).start() return else: self._logger.info(self.tr("Disconnected from MQTT server")) serv = '' if isinstance(userdata, str): try: mydata = eval(userdata) if isinstance(mydata, dict) and 'host' in mydata and 'port' in mydata: serv = mydata['host'] + ':' + str(mydata['port']) except Exception as e: self._logger.info( self.tr("MQTT client userdata not as expected")) self._logger.debug(e) if rc == 0: if serv: self._logger.info("{0} {1}".format( self.tr("MQTT disconnected on request from"), serv)) else: self._logger.info(self.tr("MQTT disconnected on request")) else: Timer(15.000, self.mqttConnect).start() if serv: self._logger.warning("{0}{1} {2} {3}".format( self.tr("MQTT disconnected with rc="), rc, self.tr("from"), serv)) else: self._logger.warning("{0}{1}".format( self.tr("MQTT disconnected with rc="), rc)) self._logger.debug("Connection state = " + str(self._connectionState)) #__________________________________________________________________ def mqttOnLog(self, client, userdata, level, buf): self._logger.debug("MQTT log level {0} : {1}".format(level, buf)) #__________________________________________________________________ def mqttOnMessage(self, client, userdata, msg): if self._mqttSwitchingConnection: self._logger.info( self.tr("Ignore MQTT message (switching connection)")) return message = None try: message = msg.payload.decode(encoding="utf-8", errors="strict") except: pass if not message: self._logger.warning("{0} {1}".format( self.tr("MQTT message decoding failed on"), msg.topic)) return self._logger.debug('Message: ' + message + ' in ' + msg.topic) direction = "—" correspondent = None if self._topicRegexInbox: if "correspondent" in self._topicRegexInbox.pattern: try: m = re.match(self._topicRegexInbox, msg.topic) if m: match = m.groupdict() if match["correspondent"]: correspondent = match["correspondent"] direction = "→" except Exception as e: self._logger.debug(e) else: self._logger.warning( self.tr("No 'correspondent' field in inbox regex : ") + self._topicRegexInbox.pattern) if not correspondent and self._topicRegexOutbox: if "correspondent" in self._topicRegexOutbox.pattern: try: m = re.match(self._topicRegexOutbox, msg.topic) if m: match = m.groupdict() if match["correspondent"]: correspondent = match["correspondent"] direction = "←" except Exception as e: self._logger.debug(e) else: self._logger.warning( self.tr("No 'correspondent' field in outbox regex : ") + self._topicRegexInbox.pattern) if not correspondent and self._topicRegexDefault: if "correspondent" in self._topicRegexDefault.pattern: try: m = re.match(self._topicRegexDefault, msg.topic) if m: match = m.groupdict() if match["correspondent"]: correspondent = match["correspondent"] except Exception as e: self._logger.debug(e) else: self._logger.warning( self.tr("No 'correspondent' field in default regex : ") + self._topicRegexInbox.pattern) if not correspondent: self._logger.warning( self.tr("No correspondent defined for topic : ") + msg.topic) now = time.time() msec = repr(now).split('.')[1][:3] timestamp = time.strftime("[%d/%m/%Y %H:%M:%S.{}]".format(msec), time.localtime(now)) self.messageReceived.emit(correspondent, msg.topic, message, timestamp, direction) #__________________________________________________________________ def mqttOnPublish(self, client, userdata, mid): self._logger.debug("userdata=%s mid=%s", userdata, mid) self._logger.info("{0} : mid={1}".format( self.tr("MQTT published"), mid)) # mid is a number (message id) #__________________________________________________________________ def mqttOnSubscribe(self, client, userdata, mid, granted_qos): self._logger.debug("mid=%s granted_qos=%s", mid, granted_qos) # granted_qos is (2,) self._logger.info("{0} : {1} {2} {3}".format( self.tr("MQTT susbcribed to"), mid, self.tr("with QoS"), granted_qos)) # mid is a number (count) #__________________________________________________________________ def mqttOnUnsubscribe(self, client, userdata, mid): self._logger.debug("mid=%s", mid) self._logger.info( "{0} : {1}".format(self.tr("MQTT unsusbcribed from"), mid)) # mid is a number (message id) if self._mqttSwitchingConnection: Timer(0, self.mqttDisconnect).start() #__________________________________________________________________ def mqttReconnect(self): self._logger.info(self.tr("MQTT reconnecting")) if self._mqttSwitchingConnection: self._logger.info( self.tr("Ignore MQTT reconnecting (switching connection)")) return try: self._mqttClient.reconnect() except Exception as e: self._logger.error(self.tr("MQTT reconnection call failed")) Timer(15.000, self.mqttConnect).start() self._logger.debug(e) #__________________________________________________________________ @pyqtSlot() def reload(self): reg = QSettings() if "current session" in reg.childKeys() and reg.value( "current session", '').strip() and reg.value( "current session", '').strip() in reg.childGroups(): self._session = reg.value("current session") self._logger.info(self.tr("Current session : ") + self._session) self.setWindowTitle(self._session) if self._session not in reg.childGroups(): reg.beginGroup(self._session) reg.setValue("host", self._host) reg.setValue("port", self._port) reg.setValue("root topic", self._rootTopic) reg.endGroup() else: reg.beginGroup(self._session) self._host = reg.value("host", 'localhost') try: self._port = reg.value("port", 1883, type=int) except: pass self._rootTopic = reg.value("root topic", '') reg.endGroup() if "current session" in reg.childKeys() and not reg.value( "current session", '') in reg.childGroups(): reg.remove("current session") self._mqttSwitchingConnection = False self._mqttSwitchingSubscription = False if self._host != self._mqttServerHost or self._port != self._mqttServerPort: self._mqttSwitchingConnection = True elif self._rootTopic != self._mqttRootTopic: self._mqttSwitchingSubscription = True self._mqttServerHost = self._host self._mqttServerPort = self._port self._mqttRootTopic = self._rootTopic self._mqttSubTopic = '#' if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' reg.beginGroup(self._session) inbox = reg.value("param inbox", 'inbox') outbox = reg.value("param outbox", 'outbox') regexInbox = reg.value("regex inbox", r'^%ROOT%/(?P<correspondent>.+)/%INBOX%$') regexOutbox = reg.value("regex outbox", r'^%ROOT%/(?P<correspondent>.+)/%OUTBOX%$') regexDefault = reg.value("regex default", r'.*/(?P<correspondent>[^/]+)/[^/]+$') reg.endGroup() regexInbox = regexInbox.replace("%ROOT%", self._rootTopic).replace( "%INBOX%", inbox) regexOutbox = regexOutbox.replace("%ROOT%", self._rootTopic).replace( "%OUTBOX%", outbox) self._topicRegexInbox = None try: self._topicRegexInbox = re.compile(regexInbox) except Exception as e: self._logger.error(self.tr("Failed to compile inbox regex")) self._logger.debug(e) self._topicRegexOutbox = None try: self._topicRegexOutbox = re.compile(regexOutbox) except Exception as e: self._logger.error(self.tr("Failed to compile outbox regex")) self._logger.debug(e) self._topicRegexDefault = None try: self._topicRegexDefault = re.compile(regexDefault) except Exception as e: self._logger.error( self.tr("Failed to compile topic default regex")) self._logger.debug(e) index = self._tabWidget.currentIndex() current = self._tabWidget.currentWidget() for index in (1, self._tabWidget.count()): try: self._displays.remove(self._tabWidget.widget(index)) self._tabWidget.widget(index).deleteLater() self._tabWidget.removeTab(index) except Exception as e: self._logger.error( self. tr("Failed to remove observation : not in display list (index=" ) + str(index) + self.tr(")")) self._logger.debug(e) dspmsg = '' for d in self._displays: if dspmsg: dspmsg += ' | ' else: dspmsg = 'Displays : ' dspmsg += d.title() self._logger.debug(dspmsg) reg.beginGroup(self._session) for i in reg.childGroups(): self.addDisplay(i) reg.endGroup() QCoreApplication.processEvents() if self._mqttSwitchingConnection: self.switchConnection() elif self._mqttSwitchingSubscription: self.switchSubscription() #__________________________________________________________________ @pyqtSlot() def removeCurrentDisplay(self): index = self._tabWidget.currentIndex() current = self._tabWidget.currentWidget() if index > 0 and current == self.sender(): try: title = self._tabWidget.tabText(index) self._tabWidget.removeTab(index) if current in self._displays: self._displays.remove(self.sender()) self.sender().deleteLater() self._logger.info("{0} : {1}".format( self.tr("Remove observation"), title)) reg = QSettings() reg.beginGroup(self._session) reg.remove(title) reg.endGroup() reg.sync() else: self._logger.warning( self. tr("Failed to remove observation : not in display list (index=" ) + str(index) + self.tr(")")) except Exception as e: self._logger.error( self. tr("Failed to remove observation : not in display list (index=" ) + str(index) + self.tr(")")) self._logger.debug(e) dspmsg = '' for d in self._displays: if dspmsg: dspmsg += ' | ' else: dspmsg = 'Displays : ' dspmsg += d.title() self._logger.debug(dspmsg) #__________________________________________________________________ @pyqtSlot() def restoreWindow(self): self.resize(QSize(400, 400)) self.move(QPoint(200, 200)) #__________________________________________________________________ def resizeEvent(self, event): if self.isVisible(): Timer(0, self.layoutSaveSettings).start() #__________________________________________________________________ @pyqtSlot() def settings(self): dlg = ObserverSettingsDialog(self._logger, self._session) dlg.move(self.pos() + QPoint(20, 20)) dlg.correspondentRegex.connect(self.settingsRegex) dlg.reloadSession.connect(self.reload) dlg.exec() reg = QSettings() reg.beginGroup(self._session) self._host = reg.value("host", 'localhost') try: self._port = reg.value("port", 1883, type=int) except: self._port = 1883 self._rootTopic = reg.value("root topic", '') reg.endGroup() if self._host != self._mqttServerHost or self._port != self._mqttServerPort: self.switchConnection() elif self._rootTopic != self._mqttRootTopic: self.switchSubscription() #__________________________________________________________________ @pyqtSlot() def settingsRegex(self): dlg = ObserverRegexDialog(self._logger, self._session) dlg.move(self.pos() + QPoint(20, 20)) dlg.exec() reg = QSettings() reg.beginGroup(self._session) inbox = reg.value("param inbox", 'inbox') outbox = reg.value("param outbox", 'outbox') regexInbox = reg.value("regex inbox", r'^%ROOT%/(?P<correspondent>.+)/%INBOX%$') regexOutbox = reg.value("regex outbox", r'^%ROOT%/(?P<correspondent>.+)/%OUTBOX%$') regexDefault = reg.value("regex default", r'.*/(?P<correspondent>[^/]+)/[^/]+$') reg.endGroup() regexInbox = regexInbox.replace("%ROOT%", self._rootTopic).replace( "%INBOX%", inbox) regexOutbox = regexOutbox.replace("%ROOT%", self._rootTopic).replace( "%OUTBOX%", outbox) self._topicRegexInbox = None try: self._topicRegexInbox = re.compile(regexInbox) except Exception as e: self._logger.error( self.tr("Failed to compile inbox regex :") + regexInbox) self._logger.debug(e) self._topicRegexOutbox = None try: self._topicRegexOutbox = re.compile(regexOutbox) except Exception as e: self._logger.error( self.tr("Failed to compile outbox regex :") + regexOutbox) self._logger.debug(e) self._topicRegexDefault = None try: self._topicRegexDefault = re.compile(regexDefault) except Exception as e: self._logger.error( self.tr("Failed to compile topic default regex :") + regexDefault) self._logger.debug(e) #__________________________________________________________________ def start(self): try: self._mqttClient.on_connect = self.mqttOnConnect self._mqttClient.on_disconnect = self.mqttOnDisconnect self._mqttClient.on_log = self.mqttOnLog self._mqttClient.on_message = self.mqttOnMessage self._mqttClient.on_publish = self.mqttOnPublish self._mqttClient.on_subscribe = self.mqttOnSubscribe self._mqttClient.on_unsubscribe = self.mqttOnUnsubscribe Timer(0, self.mqttConnect).start() except: self._logger.error( self.tr("Can't start MQTT (check definitions in .INI)")) msgbox = QMessageBox() msgbox.setWindowTitle(self.tr("Observer")) msgbox.setWindowIcon(QIcon(':/view-eye.svg')) msgbox.setText( self.tr("Failed to set MQTT client !") + "<br><br><i>" + self.tr("Application will be closed.") + "</i><br>") msgbox.setStandardButtons(QMessageBox.Close) msgbox.setAttribute(Qt.WA_DeleteOnClose) msgbox.setWindowFlags(msgbox.windowFlags() & ~Qt.WindowContextHelpButtonHint) msgbox.button(QMessageBox.Close).setText(self.tr("Close")) msgbox.resize(QSize(400, 300)) msgbox.exec() self._logger.info(self.tr("Done")) Timer(0, QCoreApplication.quit).start() self._logger.debug("Connection state = " + str(self._connectionState)) #__________________________________________________________________ def switchConnection(self): self._mqttSwitchingConnection = True self.resetDisplays.emit() current_topic = self._mqttSubTopic if self._connectionState == ConnectionState.CONNECTED or self._connectionState == ConnectionState.CONNECTING: try: (result, mid) = self._mqttClient.unsubscribe(current_topic) self._logger.info("{0} {1} : {2}".format( self.tr("MQTT unsubscribing from"), mid, current_topic)) except Exception as e: self._logger.debug(e) Timer(0, self.mqttDisconnect).start() else: Timer(0, self.mqttConnect).start() self._logger.debug("Connection state = " + str(self._connectionState)) #__________________________________________________________________ def switchSubscription(self): self.resetDisplays.emit() current_topic = self._mqttSubTopic self._mqttRootTopic = self._rootTopic if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' else: self._mqttSubTopic = '#' cloud = self._mqttServerHost + ':' + str(self._mqttServerPort) if self._mqttRootTopic: cloud += '/' + self._mqttRootTopic + '/#' else: cloud += '/#' self._cloudLabel.setText(cloud) self.resetDisplays.emit() if self._connectionState == ConnectionState.CONNECTED: try: (result, mid) = self._mqttClient.unsubscribe(current_topic) self._logger.info("{0} {1} : {2}".format( self.tr("MQTT unsubscribing from"), mid, current_topic)) (result, mid) = self._mqttClient.subscribe(self._mqttSubTopic) self._logger.info("{0} {1} : {2}".format( self.tr("MQTT subscribing to"), mid, self._mqttSubTopic)) except Exception as e: self._logger.debug(e) else: Timer(0, self.mqttReconnect).start() self._logger.debug("Connection state = " + str(self._connectionState))
class QueryUI(QWidget): def __init__(self): super().__init__() try: self.keyValues = getOfficialKeys() except RequestException: logging.warning( "There was a problem with the internet connection. You will not be able to see the existing keys." ) self.onClearPolygonF = lambda: None self.onPolygonEnabledF = lambda: None self.onPolygonDisabledF = lambda: None self.currentHtml = EMPTY_HTML self.initUI() def initUI(self): self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.requestAreaWidget = QToolBox() self.requestAreaWidget.layout().setSpacing(1) self.requestTabs = QTabWidget() self.requestTabs.setUsesScrollButtons(True) self.requestTabs.currentChanged.connect(self.__updateTabSizes__) self.requestAreaWidget.addItem(self.requestTabs, "Requests") self.requestOps = RequestsOperations(self) self.requestAreaWidget.addItem(self.requestOps, "Operations") self.generalConfig = GlobalOverpassSettingUI(self) self.requestAreaWidget.addItem(self.generalConfig, "General") self.disambiguationWidget = DisambiguationWidget( self.__getRequestByName__, self.__applyTableRow__, self) self.requestAreaWidget.addItem(self.disambiguationWidget, "Disambiguation") self.headers = self.requestAreaWidget.findChildren( QAbstractButton, "qt_toolbox_toolboxbutton") self.requestAreaWidget.currentChanged.connect( self.__onToolTabChanged__) self.headers[0].setIcon(QIcon(os.path.join(picturesDir, "arrowUp.png"))) for i in range(1, len(self.headers)): self.headers[i].setIcon( QIcon(os.path.join(picturesDir, "arrowDown.png"))) self.layout.addWidget(self.requestAreaWidget) self.setLayout(self.layout) def __getRequestByName__(self, requestName): for requestWidget in self.findChildren(RequestWidget): if requestWidget.getName() == requestName: return requestWidget.getRequest() return None def __applyTableRow__(self, name, data): filters, ids = data for requestWidget in self.findChildren(RequestWidget): if requestWidget.getName() == name: for newFilter in filters: requestWidget.addFilter(newFilter) break if len(ids) > 0: idsRequestName = SetNameManagement.getUniqueSetName() request = OverpassRequest(OsmType.WAYS, Surround.NONE, idsRequestName) request.setIds(ids) self.addRequest(request) differenceOpName = SetNameManagement.getUniqueSetName() self.requestOps.addOp(OverpassDiff(name, differenceOpName), [idsRequestName]) self.requestOps.setOutputSet(differenceOpName) logging.info("Configuration from the table row has been applied.") def __onToolTabChanged__(self, i): for h in range(len(self.headers)): if h == i: self.headers[h].setIcon( QIcon(os.path.join(picturesDir, "arrowUp.png"))) else: self.headers[h].setIcon( QIcon(os.path.join(picturesDir, "arrowDown.png"))) def __updateTabSizes__(self, index): for i in range(self.requestTabs.count()): if i != index: self.requestTabs.widget(i).setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Ignored) if index >= 0: self.requestTabs.widget(index).setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred) self.requestTabs.widget(index).resize( self.requestTabs.widget(index).minimumSizeHint()) self.requestTabs.widget(index).adjustSize() def setOnRequestChanged(self, f): self.requestTabs.currentChanged.connect(f) def addRequestByFilters(self, filters=None): requestWidget = RequestWidget(self, self.keyValues) setName = requestWidget.requestName requestWidget.changePage(self.currentHtml) self.requestTabs.addTab(requestWidget, setName) self.requestOps.addRequest(setName) self.disambiguationWidget.addSet(setName) if filters is not None: for filter in filters: requestWidget.addFilter(filter) def addRequest(self, request): if not SetNameManagement.isAvailable(request.name): raise ValueError("There is another request with the same name.") else: SetNameManagement.assign(request.name) requestWidget = RequestWidget(self, self.keyValues, request) requestWidget.changePage(self.currentHtml) self.requestTabs.addTab(requestWidget, request.name) self.requestOps.addRequest(request.name) self.disambiguationWidget.addSet(request.name) def removeRequest(self): requestName = self.requestTabs.currentWidget().getName() self.requestOps.removeSetAndDependencies(requestName) self.disambiguationWidget.removeSet(requestName) currentRequestWidget = self.requestTabs.currentWidget() SetNameManagement.releaseName(currentRequestWidget.requestName) self.requestTabs.removeTab(self.requestTabs.currentIndex()) currentRequestWidget.deleteLater() def requestsCount(self): return self.requestTabs.count() def getQuery(self): query = OverpassQuery(self.requestOps.outputSet()) query.addDate(self.generalConfig.getDate()) for i in range(self.requestTabs.count()): query.addRequest(self.requestTabs.widget(i).getRequest()) for op in self.requestOps.ops: query.addSetsOp(op) return query def setQuery(self, query): self.reset() for request in query.requests: self.addRequest(request) for op in query.ops: self.requestOps.addOp(op) if query.config.get("date") is not None: self.generalConfig.setDate( datetime.strptime(query.config["date"], "%Y-%m-%dT00:00:00Z")) else: self.generalConfig.setDate() self.requestOps.setOutputSet(query.outputSet) def reset(self): while self.requestTabs.count() > 0: self.removeRequest() self.generalConfig.setDate() def updateMaps(self, html): self.currentHtml = html for requestWidget in self.findChildren(RequestWidget): requestWidget.changePage(html) return self.getCurrentMap() def updateMapFromRow(self): return self.updateMaps( self.disambiguationWidget.getHtmlFromSelectedRow()) def getCurrentMap(self): if self.requestTabs.currentWidget() is None: return None else: return self.requestTabs.currentWidget().getMap()
class MainLayout: def __init__(self): self.parameters = {} self.main = QVBoxLayout() container = QHBoxLayout() self.opf = QFormLayout() title = QLabel("Set Operational Parameters") title.setFont(QFont('Helvetica', 14)) title.setAlignment(Qt.AlignCenter) self.opf.addRow(title) self.opf.setVerticalSpacing(20) ops = [('Nm', "Speed of Motor", '1450'), ('Nd', "Speed of Digester Shaft", '109'), ('Nc', "Speed of Cake Breaker Shaft", '109'), ('Na', "Speed of Auger Shaft", '218'), ('Nsp', "Speed of Screw Press Shaft", '60')] self.defaults = QPushButton('Clear') self.defaults.setToolTip('Toggle this button to use optimized values for Operational Parameters') self.defaults.setCheckable(1) self.defaults.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.defaults.toggled.connect(self._defaults) dl = QLabel('Reset') dl.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) for op in ops: opp = QLineEdit() opp.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) opp.setText(op[2]) lb = QLabel(op[1]) lb.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) opp.setPlaceholderText(op[1]) opp.setObjectName(op[1]) opp.setAccessibleName(op[0]) opp.setInputMask('>0000;_') self.opf.addRow(lb, opp) self.opf.addRow(dl, self.defaults) frame = QFrame() frame.setLayout(self.opf) frame.setObjectName('opf') # Layout for Throughput input tpf = QVBoxLayout() self.capacity = QLineEdit() self.capacity.setAlignment(Qt.AlignLeft | Qt.AlignCenter) self.capacity.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) dv = DV(280, 5650, 3, self.capacity) dv.setNotation(DV.StandardNotation) self.capacity.setValidator(dv) self.capacity.setPlaceholderText('Enter value between 280 and 5650') title = QLabel("Set Throughput Capacity") title.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) title.setFont(QFont('Helvetica', 14)) title.setAlignment(Qt.AlignLeft | Qt.AlignCenter) tpf.addWidget(title) tpf.addSpacing(15) tpf.addWidget(self.capacity) tpf.setAlignment(Qt.AlignLeft | Qt.AlignCenter) frame1 = QFrame() frame1.setLayout(tpf) frame1.setObjectName('tpf') container.addWidget(frame1) container.addSpacing(50) container.addWidget(frame) self.bs = QHBoxLayout() self.compute = QPushButton('Compute') self.compute.setCheckable(True) self.compute.clicked.connect(self.run) self.reset = QPushButton('Reset') self.reset.setCheckable(True) self.reset.clicked.connect(self._reset) self.reset.setEnabled(False) self.report = QPushButton('Generate Report') self.report.setCheckable(True) self.report.clicked.connect(self._generate) self.report.setEnabled(False) self.bs.addWidget(self.compute) self.bs.setSpacing(15) self.bs.addWidget(self.report) self.bs.addWidget(self.reset) self.bs.setAlignment(Qt.AlignCenter | Qt.AlignBottom) self.main.addLayout(container) self.main.addSpacing(20) self.main.addLayout(self.bs) mframe = QFrame() mframe.setLayout(self.main) mframe.setObjectName('main') mframe.setFrameShape(QFrame.StyledPanel) mframe.setFrameStyle(QFrame.Raised | QFrame.Panel) mframe.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) ml = QVBoxLayout() ml.addWidget(mframe) ml.setAlignment(Qt.AlignCenter | Qt.AlignTop) self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.North) self.tabs.setMovable(True) self.widget = QWidget() self.widget.setObjectName('mainf') self.widget.setContentsMargins(0, 50, 0, 0) self.widget.setLayout(ml) self.tabs.insertTab(0, self.widget, 'Results') def _defaults(self, s): dp = {'Nm': '1450', 'Nd': '109', 'Nc': '109', 'Na': '218', 'Nsp': '60'} num = self.opf.count() if s: self.defaults.setText('Defaults') else: self.defaults.setText('Clear') for i in range(num): child = self.opf.itemAt(i).widget() if isinstance(child, QLineEdit): if s: child.setText('') else: child.setText(dp[child.accessibleName()]) def _generate(self): path = homedir('m') path = str(path / self.parameters['filename']) file, _ = QFileDialog.getSaveFileName(self.report, 'Save Manual', path, "PDF Format (*.pdf)") if file: BuildDoc(self.parameters, file) def run(self, s): res = self.validate() if res['status']: tpd, op = res['results'][0], res['results'][1:] output = model(tpd, op) self.parameters = format_results(output, tpd) self.buiildTables() self.showImages() self.compute.setDisabled(True) self.reset.setEnabled(True) self.report.setEnabled(True) else: self.msgBox(res['err']) def _reset(self, r): self.compute.setDisabled(False) while self.tabs.count() > 1: self.tabs.removeTab(1) self.reset.setEnabled(False) self.report.setEnabled(False) def validate(self): err = '' inputs = [] try: if (i := self.capacity.text()) and (280 <= int(i) <= 5650): inputs.append(int(i)) else:
class PlotWindow(QWidget): def __init__(self, tab): super(PlotWindow, self).__init__() self.tab = tab self.allAxes = {} self.curveList = [] self.extraLines = [] self.layout = QHBoxLayout() self.graph_layout = QVBoxLayout() self.gbox_layout = QVBoxLayout() self.tabGBActor = QTabWidget() self.dateplot = DatePlot(self) self.customize = Customize(self) self.button_arrow = self.ButtonArrow() self.button_del_graph = self.ButtonDelete() self.gbox_layout.addWidget(self.dateplot) self.gbox_layout.addWidget(self.customize) self.gbox_layout.addWidget(self.tabGBActor) self.gbox_layout.addWidget(self.button_del_graph) self.layout.addLayout(self.graph_layout) self.layout.addWidget(self.button_arrow) self.layout.addLayout(self.gbox_layout) for widget in [self.dateplot, self.customize, self.tabGBActor]: widget.setMaximumWidth(400) self.setLayout(self.layout) @property def mainwindow(self): return self.tab.mainwindow @property def config(self): return self.dateplot.config @property def axes2curves(self): d = {ax: [] for ax in [None] + list(self.allAxes.values())} for curve in self.curveList: d[curve.getAxes()].append(curve) return d @property def line2Curve(self): return {curve.line: curve for curve in self.curveList} @property def axes2id(self): d = {ax: id for id, ax in self.allAxes.items()} d[None] = None return d def createGraph(self, custom): try: self.graph.close() self.graph_layout.removeWidget(self.graph) self.graph.deleteLater() except AttributeError: pass self.graph = Graph(self, custom) self.graph_layout.insertWidget(0, self.graph) def getAxes(self, newType, i=-1): for i, ax in self.allAxes.items(): try: curve = self.axes2curves[ax][0] if curve.type == newType: return ax except IndexError: return ax if i == 3: raise ValueError('No Axe available') return i + 1 def setAxes(self, allAxes): for idAxes, oldAxes in list(self.allAxes.items()): self.unsetLines(oldAxes, allAxes) self.allAxes.pop(idAxes, None) self.allAxes = allAxes def unsetLines(self, axes, newAxes): while axes.lines: line = axes.lines[0] axes.lines.remove(line) try: curve = self.line2Curve[line] curve.line = False if curve.getAxes() not in newAxes.values(): curve.setAxes(None) except KeyError: pass del line def addCurve(self, curveConf): new_curve = Curve(self, curveConf) axes = self.getAxes(new_curve.type) if isinstance(axes, int): idAxes = axes self.customize.allAxes[idAxes].checkbox.setChecked(2) axes = self.allAxes[idAxes] new_curve.setAxes(axes) self.appendCurve(new_curve) self.graph.plotCurves(new_curve) return new_curve def appendCurve(self, new_curve): self.curveList.append(new_curve) self.customize.appendRow(new_curve) def switchCurve(self, axeId, curve): ax = self.allAxes[axeId] if axeId is not None else None self.graph.switchCurve(ax, curve) def removeCurve(self, curve): self.curveList.remove(curve) self.graph.removeCurve(curve) try: checkbox = curve.checkbox checkbox.setCheckable(True) checkbox.setChecked(0) except RuntimeError: pass # Checkbox could have been already deleted def constructGroupbox(self, config): while self.tabGBActor.count(): widget = self.tabGBActor.widget(0) self.clearLayout(widget.layout()) self.tabGBActor.removeTab(0) widget.close() widget.deleteLater() sortedModule = self.sortCfg(config) for actorname in sorted(sortedModule): config = sortedModule[actorname] if config: t = TabActor(self, config) self.tabGBActor.addTab(t, actorname) def showhideConfig(self, button_arrow): if not self.tabGBActor.isHidden(): self.tabGBActor.hide() self.dateplot.hide() self.customize.hide() self.button_del_graph.hide() button_arrow.setIcon(self.mainwindow.icon_arrow_left) else: self.tabGBActor.show() self.button_del_graph.show() self.dateplot.show() self.customize.show() button_arrow.setIcon(self.mainwindow.icon_arrow_right) def ButtonDelete(self): button = QPushButton('Remove Graph') button.clicked.connect(partial(self.removeGraph, self.layout)) return button def ButtonArrow(self): button_arrow = QPushButton() button_arrow.setIcon(self.mainwindow.icon_arrow_right) button_arrow.clicked.connect(partial(self.showhideConfig, button_arrow)) button_arrow.setStyleSheet('border: 0px') return button_arrow def removeGraph(self, layout): self.clearLayout(layout) self.tab.removeGraph(self) def clearLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self.clearLayout(item.layout()) def table2label(self, tablename, mergeAIT=False): for key, label in self.mainwindow.cuArms.items(): if key in tablename: return label if mergeAIT: return 'AIT' else: return tablename.split('__')[0].upper() def sortCfg(self, config): sortedDict = {} for dev in config: label = self.table2label(tablename=dev.tablename, mergeAIT=False) try: sortedDict[label].append(dev) except KeyError: sortedDict[label] = [dev] return sortedDict
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): url = "http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D/" if "url" in kwargs: url = kwargs["url"] del kwargs["url"] if "zeronet_path" in kwargs: self.zeronet_path = kwargs["zeronet_path"] del kwargs["zeronet_path"] super(MainWindow, self).__init__(*args, **kwargs) # Tabs self.tabs = QTabWidget() self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) # New tab button #self.tab_add_button_index = self.tabs.addTab(QWidget(), '+') self.add_tab_button = QToolButton() self.add_tab_button.setText('+') self.add_tab_button.setStyleSheet( 'QToolButton {border: none; margin: 4px 20px 4px 0px; height: 480px; border-left: 1px solid lightgrey; padding: 0px 4px 0px 4px; font-weight: bold; color: #5d5b59}' 'QToolButton:hover { background-color: lightgrey }' 'QToolButton:pressed { background-color: grey }') self.add_tab_button.clicked.connect(self.new_tab_clicked) self.tabs.setCornerWidget(self.add_tab_button) # Navigation bar self.navigation = NavigationBar() self.navigation.url_bar.returnPressed.connect(self.navigate_to_url) # Back self.navigation.back_btn.triggered.connect( lambda: self.tabs.currentWidget().back()) # Next self.navigation.next_btn.triggered.connect( lambda: self.tabs.currentWidget().forward()) # Reload self.navigation.reload_btn.triggered.connect( lambda: self.tabs.currentWidget().reload()) self.navigation.shortcut_reload.activated.connect( lambda: self.tabs.currentWidget().reload()) self.navigation.shortcut_reload_f5.activated.connect( lambda: self.tabs.currentWidget().reload()) # Home self.navigation.home_btn.triggered.connect(self.go_home) # Menu: Edit config action self.navigation.edit_config_action.triggered.connect( self.edit_zeronet_config_file) # Add new tab self.add_new_tab(url, "Home") # Get everything fitting in the main window self.addToolBar(self.navigation) self.setCentralWidget(self.tabs) self.show() self.setWindowTitle("ZeroNet Browser") self.setWindowIcon(QIcon("icons/zeronet-logo.svg")) self.showMaximized() def contextMenuEvent(self, event): print(event) def update_url_bar(self, q, browser=None): if browser != self.tabs.currentWidget(): # If this signal is not from the current tab, ignore return url_array = q.toString().split('/')[3:] formatted_url = '/'.join(str(x) for x in url_array) self.navigation.url_bar.setText('zero://' + formatted_url) self.navigation.url_bar.setCursorPosition(0) if (self.tabs.currentWidget().can_go_back()): self.navigation.back_btn.setDisabled(False) else: self.navigation.back_btn.setDisabled(True) if (self.tabs.currentWidget().can_go_forward()): self.navigation.next_btn.setDisabled(False) else: self.navigation.next_btn.setDisabled(True) def navigate_to_url(self): # Get url url = self.navigation.url_bar.text() if url.startswith('zero://'): # ZeroNet protocol url_array = url.split('/') url = 'http://127.0.0.1:43110/' + url_array[2] elif url.startswith('http://'): # http protocol pass else: # Nothing mentionned url = 'http://127.0.0.1:43110/' + url self.tabs.currentWidget().setUrl(QUrl(url)) def go_home(self): self.tabs.currentWidget().setUrl( QUrl("http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D/")) def new_tab_clicked(self): self.add_new_tab( "http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D/", "Home") def get_link_url_from_context_menu(self): tab = self.tabs.currentWidget() page = tab.page() context = page.contextMenuData() qurl = context.linkUrl() return qurl.url() def open_in_new_tab(self): url = self.get_link_url_from_context_menu() self.add_new_tab(url, "Home") # Doesnt feel right to have it here but it is working def open_in_new_window(self): url = self.get_link_url_from_context_menu() kwargs = {"url": url} self.window = self.__class__(**kwargs) def add_new_tab(self, qurl, label): # Instead of browser it should be called WebView ! browser = Browser() # Triggered open in new tab openLinkInNewTabAction = browser.pageAction( QWebEnginePage.OpenLinkInNewTab) openLinkInNewWindowAction = browser.pageAction( QWebEnginePage.OpenLinkInNewWindow) openLinkInNewTabAction.triggered.connect(self.open_in_new_tab) openLinkInNewWindowAction.triggered.connect(self.open_in_new_window) self.addAction(openLinkInNewTabAction) browser.urlChanged.connect( lambda qurl, browser=browser: self.update_url_bar(qurl, browser)) indexTab = self.tabs.addTab(browser, label) # Maybe change current index after loading? self.tabs.setCurrentIndex(indexTab) # We need to update the url ! if qurl.startswith('zero://'): # ZeroNet protocol url_array = qurl.split('/') qurl = 'http://127.0.0.1:43110/' + url_array[2] elif qurl.startswith('http://'): # http protocol pass else: # Nothing mentionned qurl = 'http://127.0.0.1:43110/' + qurl currentTab = self.tabs.currentWidget() currentTab.loadFinished.connect(self.page_loaded) index = self.tabs.currentIndex() currentTab.titleChanged.connect( lambda title, index=index: self.tabs.setTabText(index, title)) currentTab.iconChanged.connect( lambda icon, index=index: self.tabs.setTabIcon(index, icon)) currentTab.setUrl(QUrl(qurl)) return indexTab def page_loaded(self, ok): if ok: currentTab = self.tabs.currentWidget() index = self.tabs.currentIndex() label = currentTab.title() icon = currentTab.icon() self.tabs.setTabIcon(index, icon) self.tabs.setTabText(index, label) def close_tab(self, index): if self.tabs.count() == 1: self.tabs.currentWidget().setUrl( QUrl( "http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D/" )) return self.tabs.removeTab(index) def edit_zeronet_config_file(self): filepath = os.path.join(os.sep, self.zeronet_path, "zeronet.conf") if sys.platform.startswith('darwin'): # macOS subprocess.run(['open', filepath]) elif sys.platform.startswith('win'): # Windows os.startfile(filepath) else: # linux variants subprocess.run(['xdg-open', filepath])
class SettingsWidget(QWidget, MooseWidget): """ A very simple widget to modify settings. This widget actually doesn't do anything, it just holds various tabs. Other widgets are responsible for creating their own settings widget and adding it here. """ def __init__(self, **kwds): """ Constructor. """ super(SettingsWidget, self).__init__(**kwds) self.top_layout = WidgetUtils.addLayout(vertical=True) self.setLayout(self.top_layout) self.tabs = QTabWidget(parent=self) self.top_layout.addWidget(self.tabs) self.button_layout = WidgetUtils.addLayout() self.top_layout.addLayout(self.button_layout) self.save_button = WidgetUtils.addButton(self.button_layout, self, "&Save", self._save) self.cancel_button = WidgetUtils.addButton(self.button_layout, self, "&Cancel", self._cancel) self.setup() def addTab(self, name, widget, index=-1): """ Add a new tab for settings. Input: name: name of the tab widget: widget of settings index: index of where to put the tab. Default is at the beginning. """ self.removeTab(name) self.tabs.insertTab(index, widget, name) def removeTab(self, name): """ Remove a tab with a given name. Input: name: name of the tab. If it doesn't exist, nothing happens. """ for i in range(self.tabs.count()): if self.tabs.tabText(i) == name: self.tabs.removeTab(i) break def load(self): """ Loads all the settings from the different widgets. """ settings = QSettings() for i in range(self.tabs.count()): w = self.tabs.widget(i) w.load(settings) def _save(self): """ Saves all the settings from the different widgets. """ settings = QSettings() for i in range(self.tabs.count()): w = self.tabs.widget(i) w.save(settings) settings.sync() self.close() def _cancel(self): """ They didn't want to save. """ self.close()