コード例 #1
0
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()
コード例 #2
0
class MainWidget(QWidget):
    def __init__(self, flags, *args, **kwargs):
        super().__init__(flags, *args, **kwargs)

        self.layout_ = QVBoxLayout()
        self.tab = QTabWidget()

        self.file_loader = FileLoader(self)
        self.file_loader.new_result.connect(self.show_result)

        self.tab.addTab(self.file_loader, "Выбор файла")
        self.tab.setTabsClosable(True)
        self.tab.tabCloseRequested.connect(self.tab_close_request)

        self.layout_.addWidget(self.tab)

        self.setLayout(self.layout_)

        self.results = []

    def show_result(self, result, name, in_tabs):
        if in_tabs:
            self.tab.addTab(result, name.split('/')[-1])
            if self.tab.currentIndex() == 0:
                self.tab.setCurrentWidget(result)
        else:
            self.results.append(result)
            result.show()

    def tab_close_request(self, closing_tab_index):
        if closing_tab_index == 0:
            return
        self.tab.removeTab(closing_tab_index)
コード例 #3
0
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"))
コード例 #4
0
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()
コード例 #5
0
ファイル: main_screen.py プロジェクト: devhashtag/2IC80
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')
コード例 #6
0
class MyTableWidget(QWidget):

    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.layout = QVBoxLayout(self)
        # Initialize tab screen
        self.tabs = QTabWidget()
        self.tabs.resize(300, 200)

        # Add tabs
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.closeTab)
        self.tabs.currentChanged['int'].connect(self.tabfun)


        # Add tabs to widget
        self.layout.addWidget(self.tabs)
        #self.tabs.toolTip(self.tabs.tipsText)
        self.setLayout(self.layout)

    def closeTab(self, index):
        tab = self.tabs.widget(index)
        tab.deleteLater()
        self.tabs.removeTab(index)

    def addtab(self, content, fileName):

        self.tabs.addTab(Content(str(content)), str(fileName))



    def tabfun(selfself,index): # test the current index number
        print("The current page is : " +str(index))
コード例 #7
0
ファイル: main.py プロジェクト: dec1/stackoverflow-egs
class CustomWidget(QWidget):
    def __init__(self, parent=None):
        super(CustomWidget, self).__init__(parent)

        self.button = QPushButton("Add tab")
        self.button.clicked.connect(self.buttonClicked)

        self.tabs = QTabWidget()
        self.tabs.setTabBar(TabBar())
        self.tabs.setTabsClosable(True)
        self.tabs.setMovable(True)
        self.tabs.setDocumentMode(True)
        self.tabs.setElideMode(Qt.ElideRight)
        self.tabs.setUsesScrollButtons(True)
        self.tabs.tabCloseRequested.connect(self.closeTab)

        self.tabs.addTab(Container("Very big titleeeeeeeeee"),
                         "Very big titleeeeeeeeeeee")
        self.tabs.addTab(Container("smalltext"), "smalltext")
        self.tabs.addTab(Container("smalltext2"), "smalltext2")

        vbox = QVBoxLayout()
        vbox.addWidget(self.button)
        vbox.addWidget(self.tabs)
        self.setLayout(vbox)

        self.resize(600, 600)

    def closeTab(self, index):
        tab = self.tabs.widget(index)
        tab.deleteLater()
        self.tabs.removeTab(index)

    def buttonClicked(self):
        self.tabs.addTab(Container("smalltext2"), "smalltext2")
コード例 #8
0
class kstImagePanel(QWidget):    	

	def __init__(self):
		""" Class constructor.
		"""
		QWidget.__init__(self)
								
		# Prepare the left tab widget for several image viewers:
		self.tabImageViewers = QTabWidget() 
		self.tabImageViewers.setTabsClosable(True)
		self.tabImageViewers.tabCloseRequested.connect(self.removeTab)

		# Compose layout of the whole widget:
		layout = QHBoxLayout()		
		layout.addWidget(self.tabImageViewers)
		layout.setContentsMargins(0,0,0,0) #left,top,right,bottom
		self.setLayout(layout)


	def addTab(self, data, sourceFile, tabname, type, mode):
		""" Add a new tab with the specified input.
			NOTE: input could be a:
				- 'raw' image (i.e. a 2D raster image ready to be displayed)
				- 'pre-processed' object (i.e. a 4D data structure)
				- 'reconstructed' (i.e. a stack of 2D images)
				- 'post-processed' (i.e. a color RGB image?)
		"""			

		# Get the central image by default:
		if data.ndim == 4:
			im = data[:,:,round(data.shape[2] / 2),round(data.shape[3] / 2)]
		else:
			im = data[:,:,round(data.shape[2] / 2)]

		# Create the image viewer:
		imageViewer = kstImageViewer(sourceFile, data, type, im, mode)

		# Set value of the refocusing slider (even if it might be hidden):
		imageViewer.sldDataset.setMinimum(0)
		imageViewer.sldDataset.setMaximum(data.shape[2]-1)
		imageViewer.sldDataset.setValue(round(data.shape[2]/2))

		if data.ndim == 4:
			imageViewer.sldRepetition.setMinimum(0)
			imageViewer.sldRepetition.setMaximum(data.shape[3]-1)
			imageViewer.sldRepetition.setValue(round(data.shape[3]/2))

		# Add a new tab:
		self.tabImageViewers.addTab(imageViewer, tabname)	

        # To have it active uncomment this line:			
		#self.tabLeftImageViewers.setCurrentIndex(self.tabLeftImageViewers.count() - 1) 	
		

	def removeTab(self, idx):
		""" Called when users want to remove an image viewer.
		"""
		self.tabImageViewers.removeTab(idx)
コード例 #9
0
ファイル: main_window.py プロジェクト: awolk/SEASide-py
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()
コード例 #10
0
ファイル: Project1.py プロジェクト: Prithvi3141/Project1
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)
コード例 #11
0
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()
コード例 #12
0
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
コード例 #13
0
class BisitorMPLWidget(BisitorWidget):
    def __init__(self, user=None):
        super().__init__()

        main_layout = QVBoxLayout()

        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(lambda x: self.tabs.removeTab(x) if x != 0 else None)
        main_layout.addWidget(self.tabs)

        setting_widget = SettingWidget(user)
        self.tabs.addTab(setting_widget, "Настройки")

        def on_accept(items, group_by, plot_type, semester):
            plot = MyMplCanvas(
                items,
                group_by,
                plot_type,
                semester=semester)
            self.tabs.addTab(plot, 'График')
            self.tabs.setCurrentWidget(plot)

        setting_widget.accept.connect(on_accept)

        self.setLayout(main_layout)
コード例 #14
0
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)
コード例 #15
0
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"))
コード例 #16
0
class EvalWindow(QMainWindow):
    def __init__(self):
        super(EvalWindow, self).__init__()
        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True)
        self.tab_widget.setTabShape(1)
        self.tab_widget.tabCloseRequested.connect(self.on_close_tab)

        self.tab_widget.currentChanged.connect(self.tab_changed)
        self.setCentralWidget(self.tab_widget)

        self.data_editor_dock = QDockWidget("Data Inspection", self)
        self.data_editor_dock.setAllowedAreas(Qt.LeftDockWidgetArea
                                              | Qt.RightDockWidgetArea
                                              | Qt.BottomDockWidgetArea)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.data_editor_dock)

        self.data_editor = DataEditor()
        self.data_editor_dock.setWidget(self.data_editor)

        self.navigation_dock = QDockWidget("Navigation Stack", self)

        self.navigation_dock.setAllowedAreas(Qt.LeftDockWidgetArea
                                             | Qt.RightDockWidgetArea
                                             | Qt.BottomDockWidgetArea)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.navigation_dock)
        self.last_tab_widget = None

        self.resize(1000, 700)

    def add_eval(self, gui_eval):
        self.show()
        self.activateWindow()

        self.tab_widget.addTab(gui_eval, gui_eval.view.scene.workflow.name)

    def tab_changed(self, index):
        curr_widget = self.tab_widget.currentWidget()
        curr_widget.view.scene.on_selection_changed()
        self.navigation_dock.setWidget(curr_widget.navigation)
        self.last_tab_widget = curr_widget

    def on_close_tab(self, index):
        self.tab_widget.removeTab(index)
コード例 #17
0
ファイル: danaquery.py プロジェクト: wllacer/dana-cube
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()
コード例 #18
0
class MyTextEditor(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.resize(400, 400)
        self.nb_tabs = 0
        self.tab_dict = {}
        self.init_ui()

    def init_ui(self):
        self.main_layout = QVBoxLayout()
        self.tabs = QTabWidget()
        # self.tabs.setTabsClosable(True)
        # self.tabs.tabCloseRequested.connect(self.close_tab)

        self.open_note("now")
        self.open_note("done")
        self.open_note("log")
        self.open_note("tips")

        # self.tabs.resize(600, 600)
        # self.t1.layout = QVBoxLayout()
        # qte = QTextEdit()
        # self.t1.layout.addWidget(qte)
        # self.t1.setLayout(self.t1.layout)

        # button = QPushButton("Add tab")
        # button.clicked.connect(self.add_tab)

        # self.main_layout.addWidget(button)
        self.main_layout.addWidget(self.tabs)

        self.setLayout(self.main_layout)

    def open_note(self, fname):
        tab = MyQTextEdit(fname)
        self.tab_dict[fname] = tab
        self.tabs.addTab(tab, fname)

    def close_tab(self, index):
        self.tabs.removeTab(index)
コード例 #19
0
ファイル: telemetry.py プロジェクト: wangyeee/MiniGCS
class RadioControlTelemetryWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.isAnyRCChannelsUpdate = False
        self.__defaultWidget = None
        self.setWindowTitle('Radio Control Telemetry')
        self.__createDefaultWidget()
        self.tabs = QTabWidget()
        self.tabs.addTab(self.__defaultWidget, 'RC Telemetry')
        self.ports = {}
        l = QVBoxLayout()
        l.addWidget(self.tabs)
        self.setLayout(l)

    def updateRCChannelValues(self, msg):
        if msg.port not in self.ports:
            if self.isAnyRCChannelsUpdate == False:
                self.isAnyRCChannelsUpdate = True
                self.tabs.removeTab(0)
            self.ports[msg.port] = RadioControlTelemetryPanel()
            self.tabs.addTab(self.ports[msg.port],
                             'Receiver {}'.format(msg.port))

        channels = []
        channels.append(msg.chan1_raw)
        channels.append(msg.chan2_raw)
        channels.append(msg.chan3_raw)
        channels.append(msg.chan4_raw)
        channels.append(msg.chan5_raw)
        channels.append(msg.chan6_raw)
        channels.append(msg.chan7_raw)
        channels.append(msg.chan8_raw)
        self.ports[msg.port].updateValues(channels)

    def __createDefaultWidget(self):
        self.__defaultWidget = QWidget()
        l = QVBoxLayout()
        l.addWidget(QLabel('No RC channel value message has been received.'))
        self.__defaultWidget.setLayout(l)
コード例 #20
0
class DataFileWidget(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self)
        self.parent = parent

        layout = QVBoxLayout(self)

        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.closeFile)
        layout.addWidget(self.tabs)

    def openFile(self, filename):
        var_list = VarListWidget(self, filename)
        # Create a new tab and add the varListWidget to it.
        self.tabs.addTab(var_list, filename)
        self.tabs.setCurrentWidget(var_list)

    def closeFile(self, index):
        # Add function for closing the tab here.
        self.tabs.widget(index).close()
        self.tabs.widget(index).deleteLater()
        self.tabs.removeTab(index)
コード例 #21
0
class MyTableWidget(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.layout = QVBoxLayout(self)
        # Initialize tab screen
        self.tabs = QTabWidget()
        self.tabs.resize(300, 200)

        # Add tabs
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.closeTab)

        # Add tabs to widget
        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)

    def closeTab(self, index):
        tab = self.tabs.widget(index)
        tab.deleteLater()
        self.tabs.removeTab(index)

    def addtab(self, content, fileName):
        self.tabs.addTab(Content(str(content)), str(fileName))
コード例 #22
0
class MyTableWidget(QWidget):
    tab_list = []

    def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        self.layout = QVBoxLayout(self)

        self.tb = QToolButton()
        self.tb.setText("+")
        self.tabs = QTabWidget()
        self.tb.clicked.connect(self.addTab)
        self.tabs.addTab(QLabel("Add tabs by pressing \"+\""), "")
        self.tabs.setTabEnabled(0, False)
        self.tabs.tabBar().setTabButton(0, QTabBar().RightSide, self.tb)

        self.addTab()

        self.tabs.resize(1028, 720)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.removeTab)

        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)

    def addTab(self):
        self.tab_list.append(QWidget())
        self.index = len(self.tab_list) - 1
        self.tabs.insertTab(self.index, self.tab_list[-1], "Option")
        self.tab_list[-1].layout = QGridLayout.QGridLayout()
        self.tab_list[-1].setLayout(self.tab_list[-1].layout)
        self.tabs.setCurrentIndex(self.index)

    def removeTab(self, index):
        self.tabs.removeTab(index)
        self.tab_list.pop(index)
        self.tabs.setCurrentIndex(index - 1)
コード例 #23
0
ファイル: students.py プロジェクト: jfisteus/eyegrade
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()
コード例 #24
0
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)
コード例 #25
0
class EditorMainWindow(QMainWindow):


    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.ui = Ui_ScriptEditor()
        self.ui.setupUi(self)
        #self.ui.actionExit.triggered.connect(self.exit)
        self.splitter = QSplitter(Qt.Vertical, self)
        self.setCentralWidget(self.splitter)
        self.edit_tab = QTabWidget(self.splitter)
        self.console_tab = QTabWidget(self.splitter)
        self.py_console = PythonConsole(self.console_tab)
        self.console_tab.addTab(self.py_console, "&Python console")
        self.js_console = QtQmlConsole(self.console_tab)
        self.console_tab.addTab(self.js_console, "&QtQml console")
        self.editors = []
        self.on_actionNewPython_triggered()

    @pyqtSlot()
    def closeEvent(self, event):
	while(self.editors.__len__()):
	    edit = self.edit_tab.currentWidget()
            if edit:
	        if(edit.isModified()):
		    saveBox = SaveDialog("You have unsaved script. Save it now?")
		    prompt = saveBox.exec_()
		    if(prompt == QMessageBox.Save):
			event.ignore()
		        self.save(True)
		    elif(prompt == QMessageBox.Cancel):
		        event.ignore()
			return
		    elif(prompt == QMessageBox.Discard):
		        event.accept()
                i = self.edit_tab.indexOf(edit)
                self.edit_tab.removeTab(i)
                self.editors.remove(edit)
	event.accept()

	

    @pyqtSlot()
    def on_actionExit_triggered(self):
	while(self.editors.__len__()):
	    edit = self.edit_tab.currentWidget()
            if edit:
	        if(edit.isModified()):
		    saveBox = SaveDialog("You have unsaved script. Save it now?")
		    prompt = saveBox.exec_()
		    if(prompt == QMessageBox.Save):
		        self.save(True)
		    elif(prompt == QMessageBox.Cancel):
		        return
		    elif(prompt == QMessageBox.Discard):
		        pass
		i = self.edit_tab.indexOf(edit)
                self.edit_tab.removeTab(i)
                self.editors.remove(edit)
	self.close()

    @pyqtSlot()
    def on_actionNewPython_triggered(self):
        pyedit = PythonEditorWidget(self.edit_tab)
        pyedit.setPlainText(template_py)
        self.edit_tab.addTab(pyedit, "Python")
        self.edit_tab.setCurrentWidget(pyedit)
        self.editors.append(pyedit)
        self.py_console.attach()
        self.console_tab.setCurrentIndex(0)
        pyedit.setFocus()
        pyedit.view.setFocus()


    @pyqtSlot()
    def on_actionNewQtQml_triggered(self):
        jsedit = QtQmlEditorWidget(self.edit_tab)
        self.edit_tab.addTab(jsedit, "QtQml")
        self.edit_tab.setCurrentWidget(jsedit)
        self.editors.append(jsedit)
        self.js_console.attach()
        self.console_tab.setCurrentIndex(1)


    @pyqtSlot()
    def on_actionClose_triggered(self):
        edit = self.edit_tab.currentWidget()
        if edit:
	    if(edit.isModified()):
		saveBox = SaveDialog("Do you want to save this Script?")
		prompt = saveBox.exec_()
		if(prompt == QMessageBox.Save):
		    self.save(True)
		elif(prompt == QMessageBox.Cancel):
		    return
		elif(prompt == QMessageBox.Discard):
		    pass
            i = self.edit_tab.indexOf(edit)
            self.edit_tab.removeTab(i)
            self.editors.remove(edit)

        
    @pyqtSlot()
    def on_actionClear_triggered(self):
        #edit = self.edit_tab.currentWidget()
	#edit.setPlainText(template_py)
	self.py_console.clear()


    @pyqtSlot()
    def on_actionSave_As_triggered(self):
	self.save()


    @pyqtSlot()
    def on_actionSave_triggered(self):
	self.save(True)


    #Path of the script file in each tab will be stored in tabToolTip
    def save(self, Update = False):
        edit = self.edit_tab.currentWidget()
	contents = str(edit.toPlainText())
	if((Update == False) or (self.edit_tab.tabText(self.edit_tab.currentIndex()) == "Python") ):
	    #Save in its first invocation and Save As will enter  
	    filename = QFileDialog.getSaveFileName(self, "Save File", "", "*.spy")
	    fil = open(filename , 'w')
	    if(filename and self.edit_tab.tabText(self.edit_tab.currentIndex()) == "Python"):
		#Script hasn't been saved before and user specifies a valid filename
	        self.edit_tab.setTabToolTip(self.edit_tab.currentIndex(), filename+'.spy')
	        self.edit_tab.setTabText(self.edit_tab.currentIndex(), os.path.basename(str(filename+'.spy')))
	else:
	    #filename = self.edit_tab.tabText(self.edit_tab.currentIndex())
	    filename = self.edit_tab.tabToolTip(self.edit_tab.currentIndex())
	    fil = open( filename , 'w')
	fil.write(contents)	
	fil.close()
	edit.setModified(False)


    @pyqtSlot()
    def on_actionOpen_triggered(self):
	filename = QFileDialog.getOpenFileName(self,"Open File","","*.spy")
	try:
	    fil = open(filename , 'r')
	except IOError:
	    return
	code = fil.read()
	edit = self.edit_tab.currentWidget()
	self.edit_tab.setTabText(self.edit_tab.currentIndex(), os.path.basename(str(filename)))
	self.edit_tab.setTabToolTip(self.edit_tab.currentIndex(), filename)
	edit.setPlainText(code)
	fil.close()


    @pyqtSlot()
    def on_actionRun_triggered(self):
        self.run()


    @pyqtSlot()
    def on_actionRunConsole_triggered(self):
        self.run(True)


    def run(self, console=False):
        edit = self.edit_tab.currentWidget()
        code = str(edit.toPlainText())
        if isinstance(edit, PythonEditorWidget):
            self.py_console.attach()
            self.console_tab.setCurrentIndex(0)
            if console:
                namespace = self.py_console.namespace
            else:
                namespace = {}
            try:
                exec code in namespace
            except Exception as e:
                traceback.print_exc()
            try:
                Scripter.activeWindow.redraw = True
                Scripter.activeWindow.update()
            except: pass
        else:
            self.js_console.attach()
            self.console_tab.setCurrentIndex(1)
            if console:
                self.js_console.inter.execute(code)
            else:
                self.js_console.inter.execute_code(code)
コード例 #26
0
class DyStockTradeStrategyMarketMonitorWidget(QWidget):
    """
        股票策略实时监控窗口,动态创建
    """
    signal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, eventEngine, strategyCls, strategyState):
        super().__init__()

        self._eventEngine = eventEngine
        self._strategyCls = strategyCls

        self._registerEvent()

        self._initUi(strategyState)

    def _initUi(self, strategyState):
        self._dataWidget = DyStockTradeStrategyMarketMonitorDataWidget(self._strategyCls)
        self._indWidget = DyStockTradeStrategyMarketMonitorIndWidget(self._eventEngine, self._strategyCls, strategyState)

        self._dataLabel = QLabel('数据')
        self._indLabel = QLabel('指示')

        grid = QGridLayout()
        grid.setSpacing(0)

        grid.addWidget(self._dataLabel, 0, 0)
        grid.addWidget(self._dataWidget, 1, 0)
        grid.addWidget(self._indLabel, 2, 0)
        grid.addWidget(self._indWidget, 3, 0)
        
        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 30)
        grid.setRowStretch(2, 1)
        grid.setRowStretch(3, 30)

        self.setLayout(grid)

        # set menu for labels
        self._dataLabel.setContextMenuPolicy(Qt.CustomContextMenu)
        self._dataLabel.customContextMenuRequested.connect(self._showLabelContextMenu)

        self._indLabel.setContextMenuPolicy(Qt.CustomContextMenu)
        self._indLabel.customContextMenuRequested.connect(self._showLabelContextMenu)

        self._labelMenu = QMenu(self) 
        
        action = QAction('叠加', self)
        action.triggered.connect(self._overlapAct)
        self._labelMenu.addAction(action)

    def _stockMarketMonitorUiHandler(self, event):
        if 'data' in event.data:
            data = event.data['data']['data']
            new = event.data['data']['new']

            strategyCls = event.data['class']
            if strategyCls.maxUiDataRowNbr is not None:
                data = data[:strategyCls.maxUiDataRowNbr]

            self._dataWidget.update(data, new)

        if 'ind' in event.data:
            self._indWidget.update(event.data['ind'])

    def _signalEmitWrapper(self, event):
        """ !!!Note: The value of signal.emit will always be changed each time you getting.
        """
        self.signal.emit(event)

    def _registerEvent(self):
        self.signal.connect(self._stockMarketMonitorUiHandler)
        self._eventEngine.register(DyEventType.stockMarketMonitorUi + self._strategyCls.name, self._signalEmitWrapper)

    def _unregisterEvent(self):
        self.signal.disconnect(self._stockMarketMonitorUiHandler)
        self._eventEngine.unregister(DyEventType.stockMarketMonitorUi + self._strategyCls.name, self._signalEmitWrapper)

    def closeEvent(self, event):
        self._dataWidget.close()
        self._indWidget.close()

        self._unregisterEvent()

        return super().closeEvent(event)

    def _showLabelContextMenu(self, position):
        self._labelMenu.popup(QCursor.pos())

    def _overlapAct(self):
        grid = self.layout()

        # remove
        self._dataLabel.setText('')
        self._indLabel.setText('')

        grid.removeWidget(self._dataLabel)
        grid.removeWidget(self._dataWidget)
        grid.removeWidget(self._indLabel)
        grid.removeWidget(self._indWidget)

        # add
        self._tabWidget = QTabWidget()

        self._tabWidget.addTab(self._dataWidget, '数据')
        self._tabWidget.addTab(self._indWidget, '指示')

        grid.addWidget(self._tabWidget, 0, 0)

        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 0)
        grid.setRowStretch(2, 0)
        grid.setRowStretch(3, 0)

        # 设置Tab右键菜单事件
        tabBar = self._tabWidget.tabBar()
        tabBar.setContextMenuPolicy(Qt.CustomContextMenu)
        tabBar.customContextMenuRequested.connect(self._showTabContextMenu)

        # 创建TabBar菜单
        self._tabBarMenu = QMenu(self)

        action = QAction('平铺', self)
        action.triggered.connect(self._flatAct)
        self._tabBarMenu.addAction(action)

    def _showTabContextMenu(self, position):
        self._tabBarMenu.popup(QCursor.pos())

    def _flatAct(self):
        grid = self.layout()

        # remove
        self._tabWidget.removeTab(0)
        self._tabWidget.removeTab(0)

        grid.removeWidget(self._tabWidget)

        self._tabWidget.hide()
        
        # add
        self._dataLabel.setText('数据')
        self._indLabel.setText('指示')

        grid.addWidget(self._dataLabel, 0, 0)
        grid.addWidget(self._dataWidget, 1, 0)
        grid.addWidget(self._indLabel, 2, 0)
        grid.addWidget(self._indWidget, 3, 0)

        self._dataWidget.show()
        self._indWidget.show()
        
        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 30)
        grid.setRowStretch(2, 1)
        grid.setRowStretch(3, 30)
コード例 #27
0
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 = "&mdash;"
        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 = "&rarr;"
                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 = "&larr;"
                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))
コード例 #28
0
ファイル: NoteBook.py プロジェクト: parkerwray/PDielec
class NoteBook(QWidget):
    def __init__(self,
                 parent,
                 program,
                 filename,
                 spreadsheet,
                 debug=False,
                 progressbar=False,
                 scripting=False):
        super(QWidget, self).__init__(parent)
        global debugger
        debugger = Debug(debug, 'NoteBook:')
        self.reader = None
        self.progressbar = progressbar
        self.spreadsheet = None
        self.plottingCalculationRequired = True
        self.analysisCalculationRequired = True
        self.visualerCalculationRequired = True
        self.fittingCalculationRequired = True
        self.debug = debug
        self.old_tab_index = None
        self.scripting = scripting
        self.layout = QVBoxLayout()
        # The number of tabs before we have scenarios
        self.tabOffSet = 2
        #
        # Initialize tab screen
        #
        self.tabs = QTabWidget(self)
        self.tabs.currentChanged.connect(self.on_tabs_currentChanged)
        self.mainTab = MainTab(self,
                               program,
                               filename,
                               spreadsheet,
                               debug=debug)
        self.settingsTab = SettingsTab(self, debug=debug)
        if filename != '' and not self.scripting:
            debugger.print(
                'Refreshing settingsTab in notebook initialisation - filename',
                filename)
            self.settingsTab.refresh()
        #
        # Open more windows
        #
        self.scenarios = []
        self.scenarios.append(ScenarioTab(self, debug=debug))
        self.scenarios[0].setScenarioIndex(0)
        #
        # Open the plotting tab
        #
        self.plottingTab = PlottingTab(self, debug=debug)
        if filename != '' and not self.scripting:
            debugger.print('Refreshing plotting because filename is set')
            self.plottingTab.refresh()
        #
        # Open the Analysis tab
        #
        self.analysisTab = AnalysisTab(self, debug=debug)
        if filename != '' and not self.scripting:
            debugger.print('Refreshing analysis because filename is set')
            self.analysisTab.refresh()
        #
        # Open the Viewer tab
        #
        self.viewerTab = ViewerTab(self, debug=debug)
        #
        # Open the Fitter tab
        #
        self.fitterTab = FitterTab(self, debug=debug)
        #
        # Add tabs
        #
        self.tabs.addTab(self.mainTab, 'Main')
        self.tabs.addTab(self.settingsTab, 'Settings')
        for i, tab in enumerate(self.scenarios):
            self.tabs.addTab(tab, 'Scenario ' + str(i + 1))
        self.tabs.addTab(self.plottingTab, 'Plotting')
        self.tabs.addTab(self.analysisTab, 'Analysis')
        self.tabs.addTab(self.viewerTab, '3D Viewer')
        self.tabs.addTab(self.fitterTab, 'Fitter')

        # Add the tab widget
        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)
        return

    def addScenario(self, copyFromIndex=-2):
        debugger.print('Settings for scenario', copyFromIndex)
        self.plottingCalculationRequired = True
        self.scenarios.append(ScenarioTab(self, self.debug))
        self.scenarios[-1].settings = copy.deepcopy(
            self.scenarios[copyFromIndex].settings)
        # debugger.print('Settings for new scenario')
        self.scenarios[-1].refresh(force=True)
        for i, scenario in enumerate(self.scenarios):
            scenario.setScenarioIndex(i)
        n = len(self.scenarios)
        self.tabs.insertTab(self.tabOffSet + n - 1, self.scenarios[-1],
                            'Scenario ' + str(n))
        self.tabs.setCurrentIndex(self.tabOffSet + n - 1)
        return

    def print_settings(self):
        # Print the settings of all the settings that have been used to a file settings.py
        qf = QFileDialog()
        qf.setWindowTitle('Save the program settings to a file')
        filename, selection = qf.getSaveFileName()
        if filename == '':
            return
        print('Current settings will be saved to ' + filename)
        fd = open(filename, 'w')
        ntabs = 2 + len(self.scenarios) + 4
        self.print_tab_settings(self.mainTab, 'mainTab', fd)
        print('tab.refresh(force=True)', file=fd)
        self.print_tab_settings(self.settingsTab, 'settingsTab', fd)
        print('tab.sigmas_cm1 =', self.settingsTab.sigmas_cm1, file=fd)
        print('tab.refresh(force=True)', file=fd)
        requireNewScenario = False
        for i, tab in enumerate(self.scenarios):
            self.print_tab_settings(tab,
                                    'scenarios[{}]'.format(i),
                                    fd,
                                    new_scenario=requireNewScenario)
            print('tab.refresh(force=True)', file=fd)
            requireNewScenario = True
        self.print_tab_settings(self.plottingTab, 'plottingTab', fd)
        print('tab.refresh(force=True)', file=fd)
        self.print_tab_settings(self.analysisTab, 'analysisTab', fd)
        print('tab.refresh(force=True)', file=fd)
        self.print_tab_settings(self.viewerTab, 'viewerTab', fd)
        print('tab.refresh(force=True)', file=fd)
        self.print_tab_settings(self.fitterTab, 'fitterTab', fd)
        print('tab.refresh(force=True)', file=fd)
        fd.close()
        return

    def print_tab_settings(self, tab, title, fd, new_scenario=False):
        print('#', file=fd)
        print('#', file=fd)
        if new_scenario:
            print('self.notebook.addScenario()', file=fd)
        print('tab = self.notebook.' + title, file=fd)
        for item in tab.settings:
            value = tab.settings[item]
            if 'str' in str(type(value)):
                print('tab.settings[\'' + item +
                      '\'] = \'{}\''.format(tab.settings[item]),
                      file=fd)
            else:
                print('tab.settings[\'' + item + '\'] = ',
                      tab.settings[item],
                      file=fd)

    def deleteScenario(self, index):
        # Don't delete the last scenario
        if len(self.scenarios) > 1:
            self.plottingCalculationRequired = True
            self.tabs.removeTab(self.tabOffSet + index)
            del self.scenarios[index]
            for i, scenario in enumerate(self.scenarios):
                scenario.setScenarioIndex(i)
                self.tabs.setTabText(self.tabOffSet + i,
                                     'Scenario ' + str(i + 1))
        return

    def refresh(self, force=False):
        if self.scripting:
            debugger.print('Notebook aborting refresh because of scripting')
            return
        debugger.print('Notebook refresh changed', force)
        ntabs = 2 + len(self.scenarios) + 4
        self.mainTab.refresh(force=force)
        self.settingsTab.refresh(force=force)
        for tab in self.scenarios:
            tab.refresh(force=force)
        self.tabs.setCurrentIndex(ntabs - 5)
        self.plottingTab.refresh(force=force)
        self.tabs.setCurrentIndex(ntabs - 4)
        self.analysisTab.refresh(force=force)
        self.tabs.setCurrentIndex(ntabs - 3)
        self.viewerTab.refresh(force=force)
        self.tabs.setCurrentIndex(ntabs - 2)
        self.fitterTab.refresh(force=force)
        self.tabs.setCurrentIndex(ntabs - 1)

    def write_spreadsheet(self):
        debugger.print('Write spreadsheet')
        self.mainTab.write_spreadsheet()
        self.settingsTab.write_spreadsheet()
        self.analysisTab.write_spreadsheet()
        self.plottingTab.write_spreadsheet()

    def on_tabs_currentChanged(self, tabindex):
        debugger.print('Tab index changed', tabindex)
        if self.scripting:
            return
        # See if we have to up date a tab we have left
        if self.old_tab_index is not None:
            if self.old_tab_index == 0:
                self.mainTab.refresh()
            elif self.old_tab_index == 1:
                self.settingsTab.refresh()
        # end if
        #       Number of tabs
        ntabs = 2 + len(self.scenarios) + 4
        if tabindex == ntabs - 1:
            # fitter tab
            self.fitterTab.refresh()
        elif tabindex == ntabs - 2:
            # viewer tab
            self.viewerTab.refresh()
        elif tabindex == ntabs - 3:
            # analysis tab
            self.analysisTab.refresh()
        elif tabindex == ntabs - 4:
            # plottings tab
            self.plottingTab.refresh()
        self.old_tab_index = tabindex

    def keyPressEvent(self, e):
        if (e.key() == Qt.Key_S
            ) and QApplication.keyboardModifiers() and Qt.ControlModifier:
            print('Control S has been pressed')
            self.print_settings()
        elif (e.key() == Qt.Key_C
              ) and QApplication.keyboardModifiers() and Qt.ControlModifier:
            print('Control C has been pressed')
            print('The program will close down')
            sys.exit()
コード例 #29
0
class Main(QMainWindow):
    def __init__(self): 
        global downloads_list_file
        QMainWindow.__init__(self)
        self.setWindowIcon(QIcon(":/quartz.png")) 
        self.setWindowTitle("Quartz Browser - "+__version__)
        # Window Properties
        self.history = []
        self.downloads = []
        self.confirm_before_quit = True
        # Create required directories
        for folder in [configdir, icon_dir, thumbnails_dir]:
            if not os.path.exists(folder):
                os.mkdir(folder)
        # Import and Apply Settings
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.settings = QSettings(1, 0, "quartz-browser","Quartz", self)
        self.opensettings()
        self.websettings = QWebSettings.globalSettings()
        self.websettings.setAttribute(QWebSettings.DnsPrefetchEnabled, True)
        self.websettings.setMaximumPagesInCache(10)
        self.websettings.setIconDatabasePath(icon_dir)
        self.websettings.setAttribute(QWebSettings.JavascriptCanOpenWindows, True)
        self.websettings.setAttribute(QWebSettings.JavascriptCanCloseWindows, True)
        if webkit.enable_adblock:
            self.websettings.setUserStyleSheetUrl(QUrl.fromLocalFile(program_dir + 'userContent.css'))
        # Import Downloads and Bookmarks
        self.dwnldsmodel = DownloadsModel(self.downloads, QApplication.instance())
        self.dwnldsmodel.deleteDownloadsRequested.connect(self.deleteDownloads)
        imported_downloads = importDownloads(downloads_list_file)
        for [filepath, url, totalsize, timestamp] in imported_downloads:
            try :                                                  # Check if downloads.txt is valid
                tymstamp = float(timestamp)
            except :
                self.downloads = []
                exportDownloads(downloads_list_file, [])
                print("Error in importing Downloads.")
                break
            old_download = Download(networkmanager)
            old_download.loadDownload(filepath, url, totalsize, timestamp)
            old_download.datachanged.connect(self.dwnldsmodel.datachanged)
            self.downloads.append(old_download)
        self.bookmarks = importBookmarks(configdir+"bookmarks.txt")
        self.favourites = importFavourites(configdir + 'favourites.txt')
        # Find and set icon theme name
        for theme_name in ['Adwaita', 'Gnome', 'Tango']:
            if os.path.exists('/usr/share/icons/' + theme_name):
                QIcon.setThemeName(theme_name)
                break
        self.initUI()
        self.resize(1024,714)

    def initUI(self):
###############################  Create  Actions ##############################
        self.loadimagesaction = QAction("Load Images",self)
        self.loadimagesaction.setCheckable(True)
        self.loadimagesaction.triggered.connect(self.loadimages)

        self.javascriptmode = QAction("Enable Javascript",self)
        self.javascriptmode.setCheckable(True)
        self.javascriptmode.triggered.connect(self.setjavascript)

        self.useragent_mode_desktop = QAction("Desktop",self)
        self.useragent_mode_desktop.setCheckable(True)
        self.useragent_mode_desktop.triggered.connect(self.setUserAgentDesktop)

        self.useragent_mode_mobile = QAction("Mobile",self)
        self.useragent_mode_mobile.setCheckable(True)
        self.useragent_mode_mobile.triggered.connect(self.setUserAgentMobile)

        self.useragent_mode_custom = QAction("Custom",self)
        self.useragent_mode_custom.setCheckable(True)
        self.useragent_mode_custom.triggered.connect(self.setUserAgentCustom)

################ Add Actions to Menu ####################
        # This sub-menu sets useragent mode to desktop/mobile/custom
        self.useragentMenu = QMenu('UserAgent', self)
        self.useragentMenu.setIcon(QIcon(":/computer.png"))
        self.useragentMenu.addAction(self.useragent_mode_desktop)
        self.useragentMenu.addAction(self.useragent_mode_mobile)
        self.useragentMenu.addAction(self.useragent_mode_custom)

        # This is main menu
        self.menu = QMenu(self)
        self.menu.addAction(QIcon(":/edit-find.png"), "Find Text", self.findmode, "Ctrl+F")
        self.menu.addAction(QIcon(":/list-add.png"), "Zoom In", self.zoomin, "Ctrl++")
        self.menu.addAction(QIcon(":/list-remove.png"), "Zoom Out", self.zoomout, "Ctrl+-")
        self.menu.addAction(QIcon(":/view-fullscreen.png"), "Toggle Fullscreen", self.fullscreenmode, "F11")
        self.menu.addSeparator()

        self.menu.addAction(self.loadimagesaction)
        self.menu.addAction(self.javascriptmode)
        self.menu.addMenu(self.useragentMenu)
        self.menu.addAction(QIcon(":/applications-system.png"), "Settings", self.settingseditor, "Ctrl+,")
        self.menu.addSeparator()

        self.menu.addAction(QIcon(":/image-x-generic.png"), "Save as Image", self.saveAsImage, "Shift+Ctrl+S")
        self.menu.addAction(QIcon(":/text-html.png"), "Save as HTML", self.saveashtml, "Ctrl+S")
        self.menu.addAction(QIcon(":/document-print.png"), "Print to PDF", self.printpage, "Ctrl+P")
        self.menu.addSeparator()
        self.menu.addAction(QIcon(":/process-stop.png"), "Quit", self.forceClose, "Ctrl+Q")

        self.bmk_menu = QMenu(self)
        self.bmk_menu.addAction(QIcon(':/add-bookmark.png'), 'Add Bookmark', self.addbookmark)
        self.bmk_menu.addAction(QIcon(':/favourites.png'), 'Add to Home', self.addToFavourites)
        
###############################  Create Gui Parts ##############################
        self.centralwidget = QWidget(self)
        self.setCentralWidget(self.centralwidget)
        grid = QGridLayout(self.centralwidget)
        grid.setSpacing(1)
        grid.setContentsMargins(0,2,0,0)

        self.toolBar = QWidget(self)
        horLayout = QHBoxLayout(self.toolBar)
        horLayout.setSpacing(1)
        horLayout.setContentsMargins(0,2,0,0)

        self.addtabBtn = QPushButton(QIcon(":/add-tab.png"), "",self)
        self.addtabBtn.setToolTip("New Tab\n[Ctrl+Tab]")
        self.addtabBtn.setShortcut("Ctrl+Tab")
        self.addtabBtn.clicked.connect(self.addTab)

        self.reload = QPushButton(QIcon(":/refresh.png"), "",self) 
        self.reload.setMinimumSize(35,26) 
        self.reload.setToolTip("Reload/Stop\n  [Space]")
        self.reload.setShortcut("Space")
        self.reload.clicked.connect(self.Reload)

        self.back = QPushButton(QIcon(":/prev.png"), "", self) 
        self.back.setToolTip("Previous Page")
        self.back.setMinimumSize(35,26) 
        self.back.clicked.connect(self.Back)

        self.forw = QPushButton(QIcon(":/next.png"), "",self) 
        self.forw.setToolTip("Next Page")
        self.forw.setMinimumSize(35,26) 
        self.forw.clicked.connect(self.Forward)

        self.homeBtn = QPushButton(QIcon(":/home.png"), "",self) 
        self.homeBtn.setToolTip("Go Home")
        self.homeBtn.clicked.connect(self.goToHome)

        self.videoDownloadButton = QPushButton(QIcon(":/video-dwnld.png"), "", self)
        self.videoDownloadButton.setToolTip("Download this Video")
        self.videoDownloadButton.clicked.connect(self.downloadVideo)
        self.videoDownloadButton.hide()

        self.addbookmarkBtn = QToolButton(self)
        self.addbookmarkBtn.setIcon(QIcon(":/add-bookmark.png"))
        self.addbookmarkBtn.setToolTip("Add Bookmark")
        self.addbookmarkBtn.setMenu(self.bmk_menu)
        self.addbookmarkBtn.setPopupMode(QToolButton.InstantPopup)

        self.menuBtn = QToolButton(self)
        self.menuBtn.setIcon(QIcon(":/menu.png"))
        self.menuBtn.setMenu(self.menu)
        self.menuBtn.setPopupMode(QToolButton.InstantPopup)

        self.bookmarkBtn = QPushButton(QIcon(":/bookmarks.png"), "", self)
        self.bookmarkBtn.setToolTip("Manage Bookmarks\n         [Alt+B]")
        self.bookmarkBtn.setShortcut("Alt+B")
        self.bookmarkBtn.clicked.connect(self.managebookmarks)
        self.historyBtn = QPushButton(QIcon(":/history.png"), "", self)
        self.historyBtn.setShortcut("Alt+H")
        self.historyBtn.setToolTip("View History\n     [Alt+H]")
        self.historyBtn.clicked.connect(self.viewhistory)

        self.downloadsBtn = QPushButton(QIcon(":/download.png"), "", self)
        self.downloadsBtn.setToolTip("Download Manager")
        self.downloadsBtn.clicked.connect(self.download_manager)

        self.find = QPushButton(self)
        self.find.setText("Find/Next")
        self.find.clicked.connect(self.findnext)
        self.find.hide()
        self.findprev = QPushButton(self)
        self.findprev.setText("Backward")
        self.findprev.clicked.connect(self.findback)
        self.findprev.hide()
        self.cancelfind = QPushButton(self)
        self.cancelfind.setText("Cancel")
        self.cancelfind.clicked.connect(self.cancelfindmode)
        self.cancelfind.hide()

        self.pbar = QProgressBar(self) 
        self.pbar.setTextVisible(False)
        self.pbar.setStyleSheet("QProgressBar::chunk { background-color: #bad8ff; }")
        pbarLayout = QGridLayout(self.pbar)
        pbarLayout.setContentsMargins(0,0,0,0)

        self.line = webkit.UrlEdit(self.pbar)
        self.line.openUrlRequested.connect(self.Enter)
        self.line.textEdited.connect(self.urlsuggestions)
        self.line.downloadRequested.connect(self.download_requested_file)
        pbarLayout.addWidget(self.line)

        self.listmodel = QStringListModel(self)
        self.completer = QCompleter(self.listmodel, self.line)
        self.completer.setCompletionMode(1)
        self.completer.setMaxVisibleItems(10)
        self.line.setCompleter(self.completer)

        self.statusbar = QLabel(self)
        self.statusbar.setStyleSheet("QLabel { font-size: 12px; border-radius: 2px; padding: 2px; background: palette(highlight); color: palette(highlighted-text); }")
        self.statusbar.setMaximumHeight(16)
        self.statusbar.hide()

        self.tabWidget = QTabWidget(self)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setDocumentMode(True)
        self.tabWidget.tabBar().setExpanding(True)
        self.tabWidget.tabBar().setElideMode(Qt.ElideMiddle)
        self.tabWidget.currentChanged.connect(self.onTabSwitch)
        self.tabWidget.tabCloseRequested.connect(self.closeTab)
        self.addTab()
        self.applysettings()
#       
        grid.addWidget(self.toolBar, 0,0, 1,1)
        for widget in [self.addtabBtn, self.back, self.forw, self.reload, self.homeBtn, self.videoDownloadButton,
                self.pbar, self.find, self.findprev, self.cancelfind, self.addbookmarkBtn, self.menuBtn,
                self.bookmarkBtn, self.historyBtn, self.downloadsBtn]:
            horLayout.addWidget(widget)
        grid.addWidget(self.tabWidget, 1, 0, 1, 1)

#------------------------------------------------------------------------------------------
#        Must be at the end, otherwise cause segmentation fault
#       self.status = self.statusBar() 

    def addTab(self, webview_tab=None):
        """ Creates a new tab and add to QTabWidget
            applysettings() must be called after adding each tab"""
        if not webview_tab:
            webview_tab = webkit.MyWebView(self.tabWidget, networkmanager) 
        webview_tab.windowCreated.connect(self.addTab)
        webview_tab.loadStarted.connect(self.onLoadStart) 
        webview_tab.loadFinished.connect(self.onLoadFinish) 
        webview_tab.loadProgress.connect(self.onProgress)
        webview_tab.urlChanged.connect(self.onUrlChange)
        webview_tab.titleChanged.connect(self.onTitleChange)
        webview_tab.iconChanged.connect(self.onIconChange)
        webview_tab.videoListRequested.connect(self.getVideos)
        webview_tab.page().printRequested.connect(self.printpage)
        webview_tab.page().downloadRequested.connect(self.download_requested_file)
        webview_tab.page().unsupportedContent.connect(self.handleUnsupportedContent)
        webview_tab.page().linkHovered.connect(self.onLinkHover)
        webview_tab.page().windowCloseRequested.connect(self.closeRequestedTab)

        self.tabWidget.addTab(webview_tab, "( Untitled )")
        if self.tabWidget.count()==1:
            self.tabWidget.tabBar().hide()
        else:
            self.tabWidget.tabBar().show()
        self.tabWidget.setCurrentIndex(self.tabWidget.count()-1)

    def closeTab(self, index=None):
        """ Closes tab, hides tabbar if only one tab remains"""
        if index==None:
            index = self.tabWidget.currentIndex()
        widget = self.tabWidget.widget(index)
        self.tabWidget.removeTab(index)
        widget.deleteLater()
        # Auto hide tab bar, when no. of tab widget is one
        if self.tabWidget.count()==1:
            self.tabWidget.tabBar().hide()

    def closeRequestedTab(self):
        """ Close tab requested by the page """
        webview = self.sender().view()
        index = self.tabWidget.indexOf(webview)
        self.closeTab(index)

    def Enter(self): 
        url = self.line.text()
        if url == 'about:home':
            self.goToHome()
        else:
            self.GoTo(url)

    def GoTo(self, url):
        URL = QUrl.fromUserInput(url)
        self.tabWidget.currentWidget().openLink(URL)
        self.line.setText(url)
        self.tabWidget.currentWidget().setFocus()

    def goToHome(self):
        self.GoTo(homepage)
        loop = QEventLoop()
        QTimer.singleShot(10, loop.quit)
        loop.exec_()
        document = self.tabWidget.currentWidget().page().mainFrame().documentElement()
        gallery = document.findFirst('div')
        for i, fav in enumerate(self.favourites):
            title, url, img = fav[0], fav[1], thumbnails_dir+fav[2]
            child = '<div class="photo"> <a href="{}"><img src="{}"></a><div class="desc">{}</div></div>'.format(url, img, title)
            gallery.appendInside(child)

    def onLoadStart(self):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
          self.reload.setIcon(QIcon(":/stop.png"))

    def onProgress(self, progress):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget() and webview.loading:
            self.pbar.setValue(progress)

    def onLoadFinish(self, ok):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
          self.reload.setIcon(QIcon(":/refresh.png"))
          self.pbar.reset()
          url = self.line.text()
          self.handleVideoButton(url)

    def onTabSwitch(self, index):
        """ Updates urlbox, refresh icon, progress bar on switching tab"""
        webview = self.tabWidget.currentWidget()
        if webview.loading == True:
            self.reload.setIcon(QIcon(":/stop.png"))
            self.pbar.setValue(webview.progressVal)
        else:
            self.reload.setIcon(QIcon(":/refresh.png"))
            self.pbar.reset()
        url =  webview.url().toString()
        if url == homepage : url = 'about:home'
        self.line.setText(url)
        self.statusbar.hide()
        self.onIconChange(webview)
        self.handleVideoButton(url)

    def onUrlChange(self,url):
        url = url.toString()
        if url == homepage : url = 'about:home'
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
            self.line.setText(url)
            self.onIconChange(webview)
            self.handleVideoButton(url)

    def onTitleChange(self, title):
        webview = self.sender()
        index = self.tabWidget.indexOf(webview)
        if not title == '':
            self.tabWidget.tabBar().setTabText(index, title)
            url = webview.url().toString()
            for item in self.history:  # Removes the old item, inserts new same item on the top
                if url == item[1]:
                    self.history.remove(item)
            self.history.insert(0, [title, url])

    def onIconChange(self, webview=None):
        if not webview:
            webview = self.sender()
        icon = webview.icon()
        if icon.isNull():
            icon = QIcon(':/quartz.png')
        if webview is self.tabWidget.currentWidget():
            self.line.setIcon(icon)
        index = self.tabWidget.indexOf(webview)
        self.tabWidget.setTabIcon(index, icon)

    def onLinkHover(self, url):
        if url=="":
            self.statusbar.hide()
            return
        self.statusbar.setText(url)
        self.statusbar.adjustSize()
        self.statusbar.show()
        self.statusbar.move(QPoint(0, self.height()-self.statusbar.height()))

    def Back(self): 
        self.tabWidget.currentWidget().back() 
    def Forward(self): 
        self.tabWidget.currentWidget().forward()
    def Reload(self):
        if self.tabWidget.currentWidget().loading:
            self.tabWidget.currentWidget().stop()
        else:
            if self.line.text() == 'about:home':
                self.goToHome()
            else:
                self.tabWidget.currentWidget().reload()

    def urlsuggestions(self, text):
        """ Creates the list of url suggestions for URL box """
        suggestions = []
        if not webkit.find_mode_on:
            for [title, url] in self.history:
                if text in url:
                    suggestions.insert(0, url)
            for [title, address] in self.bookmarks:
                if text in address:
                    suggestions.insert(0, address)
        self.listmodel.setStringList( suggestions )

    def handleVideoButton(self, url):
        if youtube.validYoutubeUrl(url):
            self.videoDownloadButton.show()
            return
        frames = [self.tabWidget.currentWidget().page().mainFrame()]
        frames += frames[0].childFrames()
        for frame in frames:
            video = frame.findFirstElement('video')
            if not video.isNull():
                self.videoDownloadButton.show()
                return
        self.videoDownloadButton.hide()


##################### Downloading and Printing  ########################
    def download_requested_file(self, networkrequest):
        """ Gets called when the page requests a file to be downloaded """
        reply = networkmanager.get(networkrequest)
        self.handleUnsupportedContent(reply)

    def handleUnsupportedContent(self, reply, preset_filename=None, page_url=None):
        """ This is called when url content is a downloadable file. e.g- pdf,mp3,mp4 """
        if reply.rawHeaderList() == []:
            loop = QEventLoop()
            reply.metaDataChanged.connect(loop.quit)
            QTimer.singleShot(5000, loop.quit)
            loop.exec_()
        if reply.hasRawHeader(b'Location'):
            URL = QUrl.fromUserInput(str_(reply.rawHeader(b'Location')))
            reply.abort()
            reply = networkmanager.get(QNetworkRequest(URL))
            self.handleUnsupportedContent(reply, preset_filename)
            return
        for (title, header) in reply.rawHeaderPairs():
            print( str_(title) + "-> " + str_(header) )
        # copy url to clipboard
        QApplication.clipboard().setText(reply.url().toString())
        # Get filename and mimetype
        mimetype = None
        if reply.hasRawHeader(b'Content-Type'):
            mimetype = str_(reply.rawHeader(b'Content-Type')).split(';')[0] # eg - audio/mpeg; name=""
        content_name = str_(reply.rawHeader(b'Content-Disposition'))
        if preset_filename:
            filename = preset_filename
        else:
            filename = filenameFromHeader(content_name)
            if filename == '':
                filename = filenameFromUrl(reply.url().toString())
        filename = validateFileName(filename, mimetype)
        # Create downld Confirmation dialog
        dlDialog = DownloadDialog(self)
        dlDialog.filenameEdit.setText(filename)
        # Get filesize
        if reply.hasRawHeader(b'Content-Length'):
            filesize = reply.header(1)
            if filesize >= 1048576 :
                file_size = "{} M".format(round(float(filesize)/1048576, 2))
            elif 1023 < filesize < 1048576 :
                file_size = "{} k".format(round(float(filesize)/1024, 1))
            else:
                file_size = "{} B".format(filesize)
            dlDialog.labelFileSize.setText(file_size)
        # Get filetype and resume support info
        if mimetype:
            dlDialog.labelFileType.setText(mimetype)
        if reply.hasRawHeader(b'Accept-Ranges') or reply.hasRawHeader(b'Content-Range'):
            dlDialog.labelResume.setText("True")
        # Execute dialog and show confirmation
        if dlDialog.exec_()== QDialog.Accepted:
            filepath = dlDialog.folder + dlDialog.filenameEdit.text()
            url = reply.url().toString()
            if self.useexternaldownloader:
                download_externally(url, self.externaldownloader)
                reply.abort()
                reply.deleteLater()
                return

            global downloads_list_file
            newdownload = Download(networkmanager, page_url)
            newdownload.startDownload(reply, filepath)
            newdownload.datachanged.connect(self.dwnldsmodel.datachanged)
            self.downloads.insert(0, newdownload)
            imported_downloads = importDownloads(downloads_list_file)
            imported_downloads.insert(0, [filepath, url, str(newdownload.totalsize), newdownload.timestamp])
            exportDownloads(downloads_list_file, imported_downloads)
        else:
            reply.abort()
            reply.deleteLater()

    def download_manager(self):
        """ Opens download manager dialog """
        dialog = QDialog(self)
        downloads_dialog = Downloads_Dialog()
        downloads_dialog.setupUi(dialog, self.dwnldsmodel)
        dialog.exec_()

    def deleteDownloads(self, timestamps):
        global downloads_list_file
        imported_downloads = importDownloads(downloads_list_file)
        exported_downloads = []
        for download in imported_downloads:
            if download[-1] not in timestamps:
                exported_downloads.append(download)
        exportDownloads(downloads_list_file, exported_downloads)

    def downloadVideo(self):
        url = self.tabWidget.currentWidget().url().toString()
        # For youtube videos, parse youtube links in separate thread
        if youtube.validYoutubeUrl(url):
            vid_id = parse_qs(urlparse(url).query)['v'][0]
            ytThread = youtube.YoutubeThread(self)
            ytThread.ytParseFailed.connect(self.onYtParseFail)
            ytThread.ytVideoParsed.connect(self.onYtVideoParse)
            ytThread.finished.connect(ytThread.deleteLater)
            ytThread.vid_id = vid_id
            ytThread.start()
            return
        # For embeded HTML5 videos
        self.getVideos()

    def onYtVideoParse(self, videos):
        dialog = youtube.YoutubeDialog(videos, self)
        if dialog.exec_() == 1 :
            index = abs(dialog.buttonGroup.checkedId())-2
            vid = videos[index]
            reply = networkmanager.get( QNetworkRequest(QUrl.fromUserInput(vid.url)) )
            self.handleUnsupportedContent(reply, vid.filename + '.' + vid.extension)

    def onYtParseFail(self):
        # Show error on fail to parse youtube
        QMessageBox.warning(self, "Download Failed !","This Video can not be downloaded")

    def getVideos(self):
        dialog = youtube.Media_Dialog(self, self.tabWidget.currentWidget().page(), networkmanager)
        dialog.downloadRequested.connect(self.handleUnsupportedContent)
        dialog.exec_()

    def saveAsImage(self):
        """ Saves the whole page as PNG/JPG image"""
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title == validateFileName(title)
        filename = QFileDialog.getSaveFileName(self,
                                      "Select Image to Save", downloaddir + title +".jpg",
                                      "JPEG Image (*.jpg);;PNG Image (*.png)" )[0]
        if filename == '': return
        viewportsize = self.tabWidget.currentWidget().page().viewportSize()
        contentsize = self.tabWidget.currentWidget().page().mainFrame().contentsSize()
        self.tabWidget.currentWidget().page().setViewportSize(contentsize)
        img = QPixmap(contentsize)
        painter = QPainter(img)
        self.tabWidget.currentWidget().page().mainFrame().render(painter)
        painter.end()
        if img.save(filename):
            QMessageBox.information(self, "Successful !","Page has been successfully saved as\n"+filename)
        else:
            QMessageBox.warning(self, "Saving Failed !","Exporting page to Image hasbeen failed")
        self.tabWidget.currentWidget().page().setViewportSize(viewportsize)

    def saveashtml(self):
        """ Saves current page as HTML , bt does not saves any content (e.g images)"""
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title = validateFileName(title)
        filename = QFileDialog.getSaveFileName(self,
                                "Enter HTML File Name", downloaddir + title +".html",
                                "HTML Document (*.html)" )[0]
        if filename == '': return
        #html = self.tabWidget.currentWidget().page().mainFrame().toHtml()
        page_URL = self.tabWidget.currentWidget().url()
        useragent = self.tabWidget.currentWidget().page().userAgentForUrl(page_URL)
        doc = self.tabWidget.currentWidget().page().mainFrame().documentElement().clone()
        #doc.setInnerXml(html)
        SaveAsHtml(networkmanager, doc, filename, page_URL, useragent)

    def printpage(self, page=None):
        """ Prints current/requested page """
        if not page:
            page = self.tabWidget.currentWidget().page().currentFrame()
        printer = QPrinter(QPrinter.HighResolution)
        printer.setPaperSize(QPrinter.A4)
        printer.setPageSize(QPrinter.A4)
        printer.setColorMode(QPrinter.Color)
        printer.setCreator("Quartz Browser")
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title = validateFileName(title)
        printer.setDocName(title)
        printer.setOutputFileName(docdir + title + ".pdf")
        #printer.setOutputFormat(QPrinter.PdfFormat)
        print_dialog = QPrintPreviewDialog(printer, self)
        print_dialog.paintRequested.connect(page.print_)
        print_dialog.exec_()

##################################################################################################
    def addToFavourites(self):
        dialog = QDialog(self)
        addbmkdialog = Add_Bookmark_Dialog()
        addbmkdialog.setupUi(dialog)
        dialog.setWindowTitle('Add to HomePage')
        addbmkdialog.titleEdit.setMaxLength(31)
        addbmkdialog.titleEdit.setText(self.tabWidget.currentWidget().page().mainFrame().title())
        addbmkdialog.addressEdit.setText(self.line.text())
        if (dialog.exec_() == QDialog.Accepted):
            title = addbmkdialog.titleEdit.text()
            addr = addbmkdialog.addressEdit.text()
            imgfile = str(time.time()) + '.jpg'
            viewportsize = self.tabWidget.currentWidget().page().viewportSize()
            contentsize = QSize(640, 640)
            self.tabWidget.currentWidget().page().setViewportSize(contentsize)
            img = QPixmap(contentsize)
            painter = QPainter(img)
            self.tabWidget.currentWidget().page().mainFrame().render(painter, QWebFrame.AllLayers)
            painter.end()
            self.tabWidget.currentWidget().page().setViewportSize(viewportsize)
            icon = img.scaledToWidth(184, 1).copy(0,0, 180, 120)
            icon.save(thumbnails_dir + imgfile)
            self.favourites = importFavourites(configdir + 'favourites.txt')
            self.favourites.append([title, addr, imgfile])
            exportFavourites(configdir + 'favourites.txt', self.favourites)

    def addbookmark(self):
        """ Opens add bookmark dialog and gets url from url box"""
        dialog = QDialog(self)
        addbmkdialog = Add_Bookmark_Dialog()
        addbmkdialog.setupUi(dialog)
        addbmkdialog.titleEdit.setText(self.tabWidget.currentWidget().page().mainFrame().title())
        addbmkdialog.addressEdit.setText(self.line.text())
        if (dialog.exec_() == QDialog.Accepted):
            url = addbmkdialog.addressEdit.text()
            bmk = [addbmkdialog.titleEdit.text(), url]
            self.bookmarks = importBookmarks(configdir+"bookmarks.txt")
            self.bookmarks.insert(0, bmk)
            exportBookmarks(configdir+"bookmarks.txt", self.bookmarks)
            icon = self.tabWidget.currentWidget().icon()
            if not icon.isNull():
                icon.pixmap(16, 16).save(icon_dir + url.split('/')[2] + '.png')

    def managebookmarks(self):
        """ Opens Bookmarks dialog """
        dialog = QDialog(self)
        bmk_dialog = Bookmarks_Dialog()
        bmk_dialog.setupUi(dialog, self.bookmarks, self.favourites)
        bmk_dialog.bookmarks_table.doubleclicked.connect(self.GoTo)
        bmk_dialog.favs_table.doubleclicked.connect(self.GoTo)
        dialog.exec_()
        if bmk_dialog.bookmarks_table.data_changed:
            self.bookmarks = bmk_dialog.bookmarks_table.data
            exportBookmarks(configdir+"bookmarks.txt", self.bookmarks)
        if bmk_dialog.favs_table.data_changed:
            self.favourites = bmk_dialog.favs_table.data
            exportFavourites(configdir+"favourites.txt", self.favourites)

    def viewhistory(self):
        """ Open history dialog """
        dialog = QDialog(self)
        history_dialog = History_Dialog()
        history_dialog.setupUi(dialog, self.history)
        history_dialog.tableView.doubleclicked.connect(self.GoTo)
        dialog.exec_()

    def findmode(self):
        """ Starts find mode and unhides find buttons"""
        webkit.find_mode_on = True
        self.line.clear()
        self.find.show()
        self.findprev.show()
        self.cancelfind.show()
        self.line.setFocus()
    def cancelfindmode(self):
        """ Hides the find buttons, updates urlbox"""
        webkit.find_mode_on = False
        self.tabWidget.currentWidget().findText("")
        self.find.hide()
        self.findprev.hide()
        self.cancelfind.hide()
        self.line.setText(self.tabWidget.currentWidget().url().toString())
    def findnext(self):
        text = self.line.text()
        self.tabWidget.currentWidget().findText(text)
    def findback(self):
        text = self.line.text()
        self.tabWidget.currentWidget().findText(text, QWebPage.FindBackward)

#####################  View Settings  ###################
    def zoomin(self):
        zoomlevel = self.tabWidget.currentWidget().zoomFactor()
        self.tabWidget.currentWidget().setZoomFactor(zoomlevel+0.1) # Use setZoomFactor() to zoom text and images
    def zoomout(self):
        zoomlevel = self.tabWidget.currentWidget().zoomFactor()
        self.tabWidget.currentWidget().setZoomFactor(zoomlevel-0.1)
    def fullscreenmode(self):
        if self.isFullScreen():
            self.showNormal()
        else:
            self.showFullScreen()

    def loadimages(self, state):
        """ TOggles image loading on/off"""
        self.websettings.setAttribute(QWebSettings.AutoLoadImages, state)
        self.loadimagesval = bool(state)

    def setjavascript(self, state):
        """ Toggles js on/off """
        self.websettings.setAttribute(QWebSettings.JavascriptEnabled, state)
        self.javascriptenabledval = bool(state)

    def setUserAgentDesktop(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Desktop'
            self.useragent_mode_mobile.setChecked(False)
            self.useragent_mode_custom.setChecked(False)

    def setUserAgentMobile(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Mobile'
            self.useragent_mode_desktop.setChecked(False)
            self.useragent_mode_custom.setChecked(False)

    def setUserAgentCustom(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Custom'
            self.useragent_mode_mobile.setChecked(False)
            self.useragent_mode_desktop.setChecked(False)

########################## Settings Portion #########################
    def settingseditor(self):  
        """ Opens the settings manager dialog, then applies the change"""
        dialog = QDialog(self)
        websettingsdialog = Ui_SettingsDialog()
        websettingsdialog.setupUi(dialog)
        # Enable AdBlock
        websettingsdialog.checkAdBlock.setChecked(webkit.enable_adblock)
        # Fonts blocking
        websettingsdialog.checkFontLoad.setChecked(webkit.block_fonts)
        # Popups blocking
        websettingsdialog.checkBlockPopups.setChecked(webkit.block_popups)
        # Custom user agent
        websettingsdialog.useragentEdit.setText(webkit.useragent_custom)
        # External download manager
        websettingsdialog.checkDownMan.setChecked(self.useexternaldownloader)
        websettingsdialog.downManEdit.setText(self.externaldownloader)
        # RTSP media player command
        websettingsdialog.mediaPlayerEdit.setText(webkit.video_player_command)
        websettingsdialog.mediaPlayerEdit.setCursorPosition(0)
        # Font settings
        websettingsdialog.spinFontSize.setValue(self.minfontsizeval)
        websettingsdialog.standardfontCombo.setCurrentFont(QFont(self.standardfontval))
        websettingsdialog.sansfontCombo.setCurrentFont(QFont(self.sansfontval))
        websettingsdialog.seriffontCombo.setCurrentFont(QFont(self.seriffontval))
        websettingsdialog.fixedfontCombo.setCurrentFont(QFont(self.fixedfontval))
        # Clear Data buttons
        websettingsdialog.clearCacheButton.clicked.connect(self.websettings.clearMemoryCaches)
        websettingsdialog.cookiesButton.clicked.connect(cookiejar.clearCookies)
        websettingsdialog.iconDBButton.clicked.connect(self.websettings.clearIconDatabase)

        if dialog.exec_() == QDialog.Accepted:
            # Enable AdBlock
            webkit.enable_adblock = websettingsdialog.checkAdBlock.isChecked()
            # Block Fonts
            webkit.block_fonts = websettingsdialog.checkFontLoad.isChecked()
            # Block Popups
            webkit.block_popups = websettingsdialog.checkBlockPopups.isChecked()
            # User Agent
            webkit.useragent_custom = websettingsdialog.useragentEdit.text()
            # Download Manager
            self.useexternaldownloader = websettingsdialog.checkDownMan.isChecked()
            self.externaldownloader = websettingsdialog.downManEdit.text()
            # Media Player Command
            webkit.video_player_command = websettingsdialog.mediaPlayerEdit.text()

            self.minfontsizeval = websettingsdialog.spinFontSize.value()
            self.standardfontval = websettingsdialog.standardfontCombo.currentText()
            self.sansfontval = websettingsdialog.sansfontCombo.currentText()
            self.seriffontval = websettingsdialog.seriffontCombo.currentText()
            self.fixedfontval = websettingsdialog.fixedfontCombo.currentText()
            self.applysettings()
            self.savesettings()

    def opensettings(self): 
        """ Reads settings file in ~/.config/quartz-browser/ directory and
            saves values in settings variables"""
        webkit.enable_adblock = _bool(self.settings.value('EnableAdblock', True))
        self.loadimagesval = _bool(self.settings.value('LoadImages', True))
        self.javascriptenabledval = _bool(self.settings.value('JavaScriptEnabled', True))
        webkit.block_fonts = _bool(self.settings.value('BlockFontLoading', False))
        webkit.block_popups = _bool(self.settings.value('BlockPopups', False))
        webkit.useragent_mode = self.settings.value('UserAgentMode', webkit.useragent_mode)
        webkit.useragent_custom = self.settings.value('UserAgent', webkit.useragent_custom)
        self.useexternaldownloader = _bool(self.settings.value('UseExternalDownloader', False))
        self.externaldownloader = self.settings.value('ExternalDownloader', "x-terminal-emulator wget -c %u")
        webkit.video_player_command = self.settings.value('MediaPlayerCommand', webkit.video_player_command)
        self.maximize_window = _bool(self.settings.value('MaximizeWindow', False))
        self.minfontsizeval = int(self.settings.value('MinFontSize', 11))
        self.standardfontval = self.settings.value('StandardFont', 'Sans')
        self.sansfontval = self.settings.value('SansFont', 'Sans')
        self.seriffontval = self.settings.value('SerifFont', 'Serif')
        self.fixedfontval = self.settings.value('FixedFont', 'Monospace')

    def savesettings(self):
        """ Writes setings to disk in ~/.config/quartz-browser/ directory"""
        self.settings.setValue('EnableAdblock', webkit.enable_adblock)
        self.settings.setValue('LoadImages', self.loadimagesval)
        self.settings.setValue('JavaScriptEnabled', self.javascriptenabledval)
        self.settings.setValue('BlockFontLoading', webkit.block_fonts)
        self.settings.setValue('BlockPopups', webkit.block_popups)
        self.settings.setValue('UserAgent', webkit.useragent_custom)
        self.settings.setValue('UserAgentMode', webkit.useragent_mode)
        self.settings.setValue('UseExternalDownloader', self.useexternaldownloader)
        self.settings.setValue('ExternalDownloader', self.externaldownloader)
        self.settings.setValue('MediaPlayerCommand', webkit.video_player_command)
        self.settings.setValue('MaximizeWindow', self.isMaximized())
        self.settings.setValue('MinFontSize', self.minfontsizeval)
        self.settings.setValue('StandardFont', self.standardfontval)
        self.settings.setValue('SansFont', self.sansfontval)
        self.settings.setValue('SerifFont', self.seriffontval)
        self.settings.setValue('FixedFont', self.fixedfontval)

    def applysettings(self):
        """ Reads settings variables, and changes browser settings.This is run after
            changing settings by Settings Dialog"""
        if webkit.enable_adblock:
            self.websettings.setUserStyleSheetUrl(QUrl.fromLocalFile(program_dir + 'userContent.css'))
        else:
            self.websettings.setUserStyleSheetUrl(QUrl(''))
        self.websettings.setAttribute(QWebSettings.AutoLoadImages, self.loadimagesval)
        self.loadimagesaction.setChecked(self.loadimagesval)
        self.websettings.setAttribute(QWebSettings.JavascriptEnabled, self.javascriptenabledval)
        self.javascriptmode.setChecked(self.javascriptenabledval)
        if webkit.useragent_mode == 'Mobile':
            self.useragent_mode_mobile.setChecked(True)
        elif webkit.useragent_mode == 'Custom':
            self.useragent_mode_custom.setChecked(True)
        else:
            self.useragent_mode_desktop.setChecked(True)
        self.websettings.setFontSize(QWebSettings.MinimumFontSize, self.minfontsizeval)
        self.websettings.setFontFamily(QWebSettings.StandardFont, self.standardfontval)
        self.websettings.setFontFamily(QWebSettings.SansSerifFont, self.sansfontval)
        self.websettings.setFontFamily(QWebSettings.SerifFont, self.seriffontval)
        self.websettings.setFontFamily(QWebSettings.FixedFont, self.fixedfontval)
#        self.websettings.setFontSize(QWebSettings.DefaultFontSize, 14)

    def enableKiosk(self):
        webkit.KIOSK_MODE = True
        self.menu.clear()
        self.toolBar.hide()
        self.showFullScreen()

    def forceClose(self):
        self.confirm_before_quit = False
        self.close()

    def closeEvent(self, event):
        """This saves all settings, bookmarks, cookies etc. during window close"""
        if self.confirm_before_quit:
            confirm = QMessageBox.warning(self, 'Quit Browser ?', 'Are you sure to close the Browser',
                                                QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes)
            if confirm == QMessageBox.No :
                event.ignore()
                return
        self.savesettings()
        cookiejar.exportCookies()
        # Delete excess thumbnails
        thumbnails = [ x for x in os.listdir(thumbnails_dir) ]
        for fav in self.favourites:
            if fav[2] in thumbnails:
                thumbnails.remove(fav[2])
        for f in thumbnails: os.remove(thumbnails_dir + f)
        # Delete excess icons
        icons = [ x for x in os.listdir(icon_dir) if x.endswith('.png') ]
        for bmk in self.bookmarks:
            host = QUrl(bmk[1]).host()
            if host + '.png' in icons:
                icons.remove(host + '.png')
        for f in icons: os.remove( icon_dir + f )
        super(Main, self).closeEvent(event)
コード例 #30
0
ファイル: quickbrowse.py プロジェクト: veryalien/scripts
class BrowserWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        # Strip out arguments we handle that are different from QMainWindow:
        if 'width' in kwargs:
            self.width = kwargs['width']
            del kwargs['width']
        else:
            self.width = 1024
        if 'height' in kwargs:
            self.height = kwargs['height']
            del kwargs['height']
        else:
            # Short enough to fit in XGA, 768 height:
            self.height = 735

        # Then run the default constructor.
        super(BrowserWindow, self).__init__(*args, **kwargs)

        self.browserviews = []

        self.profile = QWebEngineProfile()
        # print("Profile initially off the record?",
        #       self.profile.isOffTheRecord())
        # self.interceptor = BrowserRequestInterceptor()
        # self.profile.setRequestInterceptor(self.interceptor)

        self.init_tab_name_len = 40

        self.init_chrome()

        # Resize to fit on an XGA screen, allowing for window chrome.
        # XXX Should check the screen size and see if it can be bigger.
        self.resize(self.width, self.height)

    def init_chrome(self):
        # Set up the browser window chrome:
        self.setWindowTitle("Quickbrowse")

        toolbar = QToolBar("Toolbar")
        self.addToolBar(toolbar)

        btn_act = QAction("Back", self)
        # for an icon: QAction(QIcon("bug.png"), "Your button", self)
        btn_act.setStatusTip("Go back")
        btn_act.triggered.connect(self.go_back)
        toolbar.addAction(btn_act)

        btn_act = QAction("Forward", self)
        btn_act.setStatusTip("Go forward")
        btn_act.triggered.connect(self.go_forward)
        toolbar.addAction(btn_act)

        btn_act = QAction("Reload", self)
        btn_act.setStatusTip("Reload")
        btn_act.triggered.connect(self.reload)
        toolbar.addAction(btn_act)

        self.urlbar = ReadlineEdit()
        self.urlbar.setPlaceholderText("URL goes here")
        self.urlbar.returnPressed.connect(self.urlbar_load)
        toolbar.addWidget(self.urlbar)

        self.tabwidget = QTabWidget()
        self.tabwidget.setTabBarAutoHide(True)

        self.setCentralWidget(self.tabwidget)

        self.tabwidget.tabBar().installEventFilter(self)
        self.prev_middle = -1
        self.active_tab = 0

        self.setStatusBar(QStatusBar(self))
        self.progress = QProgressBar()
        self.statusBar().addPermanentWidget(self.progress)

        # Key bindings
        # For keys like function keys, use QtGui.QKeySequence("F12")
        QShortcut("Ctrl+Q", self, activated=self.close)
        QShortcut("Ctrl+L", self, activated=self.select_urlbar)
        QShortcut("Ctrl+T", self, activated=self.new_tab)
        QShortcut("Ctrl+R", self, activated=self.reload)

        QShortcut("Ctrl++", self, activated=self.zoom)
        QShortcut("Ctrl+=", self, activated=self.zoom)
        QShortcut("Ctrl+-", self, activated=self.unzoom)

        QShortcut("Alt+Left", self, activated=self.go_back)
        QShortcut("Alt+Right", self, activated=self.go_forward)

    def eventFilter(self, object, event):
        '''Handle button presses in the tab bar'''

        if object != self.tabwidget.tabBar():
            print("eventFilter Not in tab bar")
            return super().eventFilter(object, event)
            # return False

        if event.type() not in [QEvent.MouseButtonPress,
                                QEvent.MouseButtonRelease]:
            # print("Not a button press or release", event)
            return super().eventFilter(object, event)
            # return False

        tabindex = object.tabAt(event.pos())

        if event.button() == Qt.LeftButton:
            if event.type() == QEvent.MouseButtonPress:
                self.active_tab = tabindex
                self.urlbar.setText(self.browserviews[tabindex].url().toDisplayString())
            return super().eventFilter(object, event)
            # return False    # So we'll still switch to that tab

        if event.button() == Qt.MidButton:
            if event.type() == QEvent.MouseButtonPress:
                    self.prev_middle = tabindex
            else:
                if tabindex != -1 and tabindex == self.prev_middle:
                    self.close_tab(tabindex)
                self.prev_middle = -1
            return True

        print("Unknown button", event)
        return super().eventFilter(object, event)

    def new_tab(self, url=None):
        if url:
            init_name = url[:self.init_tab_name_len]
        else:
            init_name = "New tab"

        if is_pdf(url):
            webview = PDFBrowserView(self, url)
            self.browserviews.append(webview)
            self.tabwidget.addTab(webview, init_name)

        else:
            webview = BrowserView(self)

            # We need a QWebEnginePage in order to get linkHovered events,
            # and to set an anonymous profile.
            # print("New tab, profile still off the record?",
            #       self.profile.isOffTheRecord())
            webpage = BrowserPage(self.profile, webview, self)

            # print("New Webpage off the record?",
            #       webpage.profile().isOffTheRecord())
            webview.setPage(webpage)
            # print("New view's page off the record?",
            #       webview.page().profile().isOffTheRecord())
            # print("In new tab, view is", webview, "and page is", webpage)

            self.browserviews.append(webview)
            self.tabwidget.addTab(webview, init_name)

            if url:
                self.load_url(url, len(self.browserviews)-1)

            # Set up the signals:
            webview.urlChanged.connect(webview.url_changed)
            webview.loadStarted.connect(webview.load_started)
            webview.loadFinished.connect(webview.load_finished)
            webview.loadProgress.connect(webview.load_progress)
            webpage.linkHovered.connect(webview.link_hover)

        return webview

    def close_tab(self, tabindex):
        self.tabwidget.removeTab(tabindex)

    def load_url(self, url, tab=None):
        '''Load the given URL in the specified tab, or current tab if tab=None.
           url is a str, not a QUrl.
           PDF URLs will be loaded in a new tab, because there doesn't
           seem to be a way of replacing the BrowserView with a BrowserPDFView.
        '''

        # Note that tab=0 is a valid argument here.
        # When testing whether tab is set, be sure to test for None.

        if is_pdf(url):
            self.new_tab(url)
            return

        qurl = QUrl(url)

        if not qurl.scheme():
            if os.path.exists(url):
                qurl.setScheme('file')
                if not os.path.isabs(url):
                    # Is it better to use posixpath.join or os.path.join?
                    # Both work on Linux.
                    qurl.setPath(os.path.normpath(os.path.join(os.getcwd(),
                                                               url)))
            else:
                qurl.setScheme('http')

        if len(self.browserviews) == 0:
            self.new_tab()
            tab = 0
        elif tab == None:
            tab = self.active_tab

        self.set_tab_text(url[:self.init_tab_name_len],
                          self.browserviews[tab])
        if tab == self.active_tab:
            self.urlbar.setText(url)

        self.browserviews[tab].load(qurl)

    def load_html(self, html, base=None):
        '''Load a string containing HTML.
           The base is the file: URL the HTML should be considered to have
           come from, to avoid "Not allowed to load local resource" errors
           when clicking on links.
        '''
        if not self.browserviews:
            self.new_tab()
            tab = 0
        else:
            tab = self.active_tab
            self.set_tab_text("---",  # XXX Replace with html title if possible
                              self.browserviews[tab])

        self.browserviews[tab].setHtml(html, QUrl(base))

    def select_urlbar(self):
        self.urlbar.selectAll()
        self.urlbar.setFocus()

    def find_view(self, view):
        for i, v in enumerate(self.browserviews):
            if v == view:
                return i
        return None

    def set_tab_text(self, title, view):
        '''Set tab and, perhaps, window title after a page load.
           view is the requesting BrowserView, and will be compared
           to our browserviews[] to figure out which tab to set.
        '''
        if self.tabwidget == None:
            return
        whichtab = None
        whichtab = self.find_view(view)
        if whichtab == None:
            print("Warning: set_tab_text for unknown view")
            return
        self.tabwidget.setTabText(whichtab, title)

    def zoom(self):
        if 'zoom' in dir(self.browserviews[self.active_tab]):
            self.browserviews[self.active_tab].zoom()

    def unzoom(self):
        if 'unzoom' in dir(self.browserviews[self.active_tab]):
            self.browserviews[self.active_tab].unzoom()

    def update_buttons(self):
        # TODO: To enable/disable buttons, check e.g.
        # self.webview.page().action(QWebEnginePage.Back).isEnabled())
        pass

    def signal_handler(self, signal, frame):
        with open(URL_FILE % os.getpid()) as url_fp:
            for url in url_fp:
                self.new_tab(url.strip())

    #
    # Slots
    #

    def urlbar_load(self):
        url = self.urlbar.text()
        self.load_url(url)

    def go_back(self):
        self.browserviews[self.active_tab].back()

    def go_forward(self):
        self.browserviews[self.active_tab].forward()

    def reload(self):
        self.browserviews[self.active_tab].reload()
コード例 #31
0
ファイル: pulppy.py プロジェクト: bruino/pulppy
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        fileMenu = QMenu("&File", self)
        newAction = fileMenu.addAction("&New...")
        newAction.setShortcut("Ctrl+N")
        self.printAction = fileMenu.addAction("&Print...", self.printFile)
        self.printAction.setShortcut("Ctrl+P")
        self.printAction.setEnabled(False)
        quitAction = fileMenu.addAction("&Exit")
        quitAction.setShortcut("Ctrl+Q")

        helpMenu = QMenu("&Help", self)
        aboutAction = helpMenu.addAction("&About")

        self.menuBar().addMenu(fileMenu)
        self.menuBar().addMenu(helpMenu)

        self.solvers = QTabWidget()
        self.solvers.setTabsClosable(True)
        self.solvers.tabCloseRequested.connect(self.closeTab)

        newAction.triggered.connect(self.openDialog)
        quitAction.triggered.connect(self.close)
        aboutAction.triggered.connect(self.openAbout)

        self.setCentralWidget(self.solvers)
        self.setWindowTitle("Pulppy Software")
    
    def printFile(self):
        editor = self.solvers.currentWidget()
        
        if type(editor) == QTextEdit:
            printer = QPrinter()
            dialog = QPrintDialog(printer, self)
            dialog.setWindowTitle("Print Document")
            if dialog.exec_() != QDialog.Accepted:
                return
            editor.print_(printer)
        else:
            answer = QMessageBox.warning(self, "Not Print Image",
                "Dont print graph.",
                QMessageBox.Ok)
        
    def closeTab (self, currentIndex):
        currentQWidget = self.solvers.widget(currentIndex)
        currentQWidget.deleteLater()
        self.solvers.removeTab(currentIndex)

    def openDialog(self):
        inputProblemDialog = InputProblem(self)
        if inputProblemDialog.exec_() == QDialog.Accepted:
            inputTable = InputTableModel(inputProblemDialog.title
            , inputProblemDialog.numVar, inputProblemDialog.numCons
            , inputProblemDialog.typeVar, inputProblemDialog.objCrit, self)
            if inputTable.exec_() == QDialog.Accepted:
                self.createTabSolver(inputTable.problem)
                if inputTable.numVariables == 2:
                    self.createTabGraph(inputTable.canvas)
                    
    def createTabGraph(self, canvas):
        self.main_widget = QWidget(self)
        vbox = QVBoxLayout(self.main_widget)
        vbox.addWidget(canvas.mpl_toolbar)
        vbox.addWidget(canvas)
        
        tabIndex = self.solvers.addTab(self.main_widget, "Graph - "+canvas.title)
        self.solvers.setCurrentIndex(tabIndex)

    def createTabSolver(self, problem):
        editor = QTextEdit()
        tabIndex = self.solvers.addTab(editor, problem.name)
        self.solvers.setCurrentIndex(tabIndex)

        cursor = editor.textCursor()
        cursor.movePosition(QTextCursor.Start)
        topFrame = cursor.currentFrame()
        topFrameFormat = topFrame.frameFormat()
        topFrameFormat.setPadding(16)
        topFrame.setFrameFormat(topFrameFormat)

        textFormat = QTextCharFormat()
        boldFormat = QTextCharFormat()
        boldFormat.setFontWeight(QFont.Bold)

        referenceFrameFormat = QTextFrameFormat()
        referenceFrameFormat.setBorder(1)
        referenceFrameFormat.setPadding(8)
        referenceFrameFormat.setPosition(QTextFrameFormat.FloatLeft)
        referenceFrameFormat.setWidth(QTextLength(QTextLength.PercentageLength, 100))
        cursor.insertFrame(referenceFrameFormat)

        cursor.insertText("Title Problem: ", boldFormat)
        cursor.insertText(problem.name+"\n", textFormat)
        cursor.insertText("Criterion: ", boldFormat)
        if problem.sense == 1:
            cursor.insertText("Minimize\n",textFormat)
        else:
            cursor.insertText("Maximize\n",textFormat)

        WasNone, dummyVar = problem.fixObjective()
        cursor.insertText("Status: ", boldFormat)
        cursor.insertText(str(LpStatus[problem.status])+"\n", textFormat)
        cursor.insertText("Value Function Objetive: ", boldFormat)
        cursor.insertText(str(value(problem.objective))+"\n", textFormat)
        cursor.insertBlock()
        cursor.insertText("Objective\n", boldFormat)
        cursor.insertText(str(problem.objective)+"\n", textFormat)
        cursor.insertBlock()
        cursor.insertText("Subject To\n", boldFormat)
        ks = list(problem.constraints.keys())
        ks.sort()
        for k in ks:
            constraint = problem.constraints[k]
            print constraint
            if not list(constraint.keys()):
                #empty constraint add the dummyVar
                dummyVar = problem.get_dummyVar()
                constraint += dummyVar
                #set this dummyvar to zero so infeasible problems are not made
                #feasible
                cursor.insertText((dummyVar == 0.0).asCplexLpConstraint("_dummy")
                                    , textFormat)
                cursor.insertBlock()
            cursor.insertText(str(k)+" : ", boldFormat)
            cursor.insertText(str(constraint), textFormat)
            cursor.insertBlock()
        vs = problem.variables()
        cursor.insertBlock()
        # Bounds on non-"positive" variables
        # Note: XPRESS and CPLEX do not interpret integer variables without
        # explicit bounds
        mip=1
        if mip:
            vg=[v for v in vs if not (v.isPositive() and v.cat==LpContinuous) \
                and not v.isBinary()]
        else:
            vg = [v for v in vs if not v.isPositive()]
        if vg:
            cursor.insertText("Bounds\n", boldFormat)
            for v in vg:
                cursor.insertText("%s, " % v.asCplexLpVariable(), textFormat)
        # Integer non-binary variables
        if mip:
            vg = [v for v in vs if v.cat == LpInteger and not v.isBinary()]
            if vg:
                cursor.insertText("Generals\n", boldFormat)
                for v in vg:
                    cursor.insertText("%s, " % v.name, textFormat)
            # Binary variables
            vg = [v for v in vs if v.isBinary()]
            if vg:
                cursor.insertText("Binaries\n",boldFormat)
                for v in vg:
                    cursor.insertText("%s, " % v.name, textFormat)
        cursor.setPosition(topFrame.lastPosition())

        bodyFrameFormat = QTextFrameFormat()
        bodyFrameFormat.setWidth(QTextLength(QTextLength.PercentageLength, 100))
        cursor.insertBlock()
        cursor.insertFrame(bodyFrameFormat)

        cursor.insertBlock()

        orderTableFormat = QTextTableFormat()
        orderTableFormat.setAlignment(Qt.AlignHCenter)
        orderTable = cursor.insertTable(1, 3, orderTableFormat)

        orderFrameFormat = cursor.currentFrame().frameFormat()
        orderFrameFormat.setBorder(1)
        cursor.currentFrame().setFrameFormat(orderFrameFormat)

        cursor = orderTable.cellAt(0, 0).firstCursorPosition()
        cursor.insertText("Variable", boldFormat)
        cursor = orderTable.cellAt(0, 1).firstCursorPosition()
        cursor.insertText("Value", boldFormat)
        cursor = orderTable.cellAt(0, 2).firstCursorPosition()
        cursor.insertText("Reduced Cost", boldFormat)

        for v in problem.variables():
            row = orderTable.rows()
            orderTable.insertRows(row, 1)
            #Name variable
            cursor = orderTable.cellAt(row, 0).firstCursorPosition()
            cursor.insertText(v.name, textFormat)
            #Value variable
            cursor = orderTable.cellAt(row, 1).firstCursorPosition()
            cursor.insertText(str(v.varValue), textFormat)
            #Cost Reduced variable
            cursor = orderTable.cellAt(row, 2).firstCursorPosition()
            cursor.insertText(str(v.dj), textFormat)

        cursor.setPosition(topFrame.lastPosition())
        cursor.insertBlock()

        orderTableFormat = QTextTableFormat()
        orderTableFormat.setAlignment(Qt.AlignHCenter)
        orderTable = cursor.insertTable(1, 3, orderTableFormat)

        orderFrameFormat = cursor.currentFrame().frameFormat()
        orderFrameFormat.setBorder(1)
        cursor.currentFrame().setFrameFormat(orderFrameFormat)

        cursor = orderTable.cellAt(0, 0).firstCursorPosition()
        cursor.insertText("Constraint", boldFormat)
        cursor = orderTable.cellAt(0, 1).firstCursorPosition()
        cursor.insertText("Slack", boldFormat)
        cursor = orderTable.cellAt(0, 2).firstCursorPosition()
        cursor.insertText("Shadow Price", boldFormat)

        for m in range(problem.numConstraints()):
            row = orderTable.rows()
            orderTable.insertRows(row, 1)
            #Name Constraint
            cursor = orderTable.cellAt(row, 0).firstCursorPosition()
            cursor.insertText("C"+ str(m+1), textFormat)
            #Slack Constraint
            cursor = orderTable.cellAt(row, 1).firstCursorPosition()
            cursor.insertText(str(problem.constraints.get("_C"+str(m+1)).slack)
            , textFormat)
            cursor = orderTable.cellAt(row, 2).firstCursorPosition()
            cursor.insertText(str(problem.constraints.get("_C"+str(m+1)).pi)
                                    , textFormat)
            self.printAction.setEnabled(True)

    def openAbout(self):
        about = AboutDialog(self)
        about.show()
コード例 #32
0
ファイル: run_widget.py プロジェクト: ninja-ide/ninja-ide
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)
コード例 #33
0
ファイル: window.py プロジェクト: zisecheng/retext
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
コード例 #34
0
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()
コード例 #35
0
class MainWindow(QMainWindow):

    def __init__(self, filename):
        super().__init__()

        self.setGeometry(20, 20, 1324, 1068)
        self.setWindowTitle('qt-Notepad')
        self.setStyleSheet('font-size: 14pt; font-family: Courier;')

        self.show()
        self.init_ui()
        centralWidget = QWidget()
        self.tabs = QTabWidget(centralWidget)
        self.setCentralWidget(self.tabs)
        self.tabs.setTabsClosable(True)
        self.tabs.setMovable(True)
        self.tabs.tabCloseRequested.connect(self.closeTab)
        if filename:
            f = open(filename, 'r')
            filedata = f.read()
            f.close()
            newfile = QTextEdit()
            newfile.setText(filedata)
            i = self.tabs.addTab(newfile, filename)
            self.tabs.setCurrentIndex(i)
        else:
            self.open_file()

    def init_ui(self):

        new_action = QAction('New File', self)
        new_action.setShortcut('Ctrl+N')
        new_action.setStatusTip('Create new file')
        new_action.triggered.connect(self.new_file)

        open_action = QAction('Open...', self)
        open_action.setShortcut('Ctrl+O')
        open_action.setStatusTip('Open a file')
        open_action.triggered.connect(self.open_file)

        save_action = QAction('Save File', self)
        save_action.setShortcut('Ctrl+S')
        save_action.setStatusTip('Save current file')
        save_action.triggered.connect(self.save_file)

        new_save_action = QAction('Save File As...', self)
        new_save_action.setShortcut('Shift+Ctrl+S')
        new_save_action.setStatusTip('Save current file')
        new_save_action.triggered.connect(self.save_file_as)

        close_action = QAction('Close File', self)
        close_action.setShortcut('Ctrl+W')
        close_action.setStatusTip('Close file and exit tab')
        close_action.triggered.connect(self.close_file)

        exit_action = QAction('Exit qt-Notepad', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Close Notepad')
        exit_action.triggered.connect(self.close)

        undo_action = QAction('Undo', self)
        undo_action.setShortcut('Ctrl+Z')

        copy_action = QAction('Copy', self)
        copy_action.setShortcut('Ctrl+C')

        cut_action = QAction('Cut', self)
        cut_action.setShortcut('Ctrl+X')

        paste_action = QAction('Paste', self)
        paste_action.setShortcut('Ctrl+V')

        minimize_action = QAction('Minimize', self)
        minimize_action.setShortcut('Ctrl+M')

        view_action = QAction('Show', self)
        view_action.setShortcut('Ctrl+/')

        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        edit_menu = menubar.addMenu('&Edit')
        view_menu = menubar.addMenu('&View')
        window_menu = menubar.addMenu('&Window')

        file_menu.addAction(new_action)
        file_menu.addAction(open_action)
        file_menu.addAction(save_action)
        file_menu.addAction(new_save_action)
        file_menu.addAction(close_action)
        file_menu.addAction(exit_action)

        edit_menu.addAction(undo_action)
        edit_menu.addAction(copy_action)
        edit_menu.addAction(cut_action)
        edit_menu.addAction(paste_action)

        view_menu.addAction(view_action)

        window_menu.addAction(minimize_action)

    def closeTab(self, currentIndex):
        currentQWidget = self.tabs.currentWidget()
        currentQWidget.deleteLater()
        self.tabs.removeTab(currentIndex)

    def new_file(self):
        newfile = QTextEdit()
        i = self.tabs.addTab(newfile, 'New Document')
        self.tabs.setCurrentIndex(i)

    def save_file(self):
        editor = self.tabs.currentWidget()
        filename = self.tabs.tabText(self.tabs.currentIndex())
        if filename != 'New Document':
            f = open(filename, 'w')
            filedata = editor.toPlainText()
            f.write(filedata)
            f.close()
        else:
            self.save_file_as()

    def save_file_as(self):
        filename = QFileDialog.getSaveFileName(
            self, 'Save File', os.getenv('HOME'))[0]
        print(filename)
        if filename != ('', ''):
            f = open(filename, 'w')
            filedata = self.text.toPlainText()
            f.write(filedata)
            f.close()

    def open_file(self):
        filename = QFileDialog.getOpenFileName(
            self, 'Open File', os.getenv('HOME'))[0]
        f = open(filename, 'r')
        filedata = f.read()
        f.close()
        newfile = QTextEdit()
        newfile.setText(filedata)
        i = self.tabs.addTab(newfile, filename)
        self.tabs.setCurrentIndex(i)

    def close_file(self):
        self.save_file()
        currentIndex = self.tabs.currentIndex()
        self.tabs.removeTab(currentIndex)
コード例 #36
0
ファイル: window.py プロジェクト: daffodil/retext
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()
コード例 #37
0
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
コード例 #38
0
class MainWindow(QMainWindow):
    def __init__(self, filename):
        super().__init__()

        self.setGeometry(20, 20, 1324, 1068)
        self.setWindowTitle('qt-Notepad')
        self.setStyleSheet('font-size: 14pt; font-family: Courier;')

        self.show()
        self.init_ui()
        centralWidget = QWidget()
        self.tabs = QTabWidget(centralWidget)
        self.setCentralWidget(self.tabs)
        self.tabs.setTabsClosable(True)
        self.tabs.setMovable(True)
        self.tabs.tabCloseRequested.connect(self.closeTab)
        if filename:
            f = open(filename, 'r')
            filedata = f.read()
            f.close()
            newfile = QTextEdit()
            newfile.setText(filedata)
            i = self.tabs.addTab(newfile, filename)
            self.tabs.setCurrentIndex(i)
        else:
            self.open_file()

    def init_ui(self):

        new_action = QAction('New File', self)
        new_action.setShortcut('Ctrl+N')
        new_action.setStatusTip('Create new file')
        new_action.triggered.connect(self.new_file)

        open_action = QAction('Open...', self)
        open_action.setShortcut('Ctrl+O')
        open_action.setStatusTip('Open a file')
        open_action.triggered.connect(self.open_file)

        save_action = QAction('Save File', self)
        save_action.setShortcut('Ctrl+S')
        save_action.setStatusTip('Save current file')
        save_action.triggered.connect(self.save_file)

        new_save_action = QAction('Save File As...', self)
        new_save_action.setShortcut('Shift+Ctrl+S')
        new_save_action.setStatusTip('Save current file')
        new_save_action.triggered.connect(self.save_file_as)

        close_action = QAction('Close File', self)
        close_action.setShortcut('Ctrl+W')
        close_action.setStatusTip('Close file and exit tab')
        close_action.triggered.connect(self.close_file)

        exit_action = QAction('Exit qt-Notepad', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Close Notepad')
        exit_action.triggered.connect(self.close)

        undo_action = QAction('Undo', self)
        undo_action.setShortcut('Ctrl+Z')

        copy_action = QAction('Copy', self)
        copy_action.setShortcut('Ctrl+C')

        cut_action = QAction('Cut', self)
        cut_action.setShortcut('Ctrl+X')

        paste_action = QAction('Paste', self)
        paste_action.setShortcut('Ctrl+V')

        minimize_action = QAction('Minimize', self)
        minimize_action.setShortcut('Ctrl+M')

        view_action = QAction('Show', self)
        view_action.setShortcut('Ctrl+/')

        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        edit_menu = menubar.addMenu('&Edit')
        view_menu = menubar.addMenu('&View')
        window_menu = menubar.addMenu('&Window')

        file_menu.addAction(new_action)
        file_menu.addAction(open_action)
        file_menu.addAction(save_action)
        file_menu.addAction(new_save_action)
        file_menu.addAction(close_action)
        file_menu.addAction(exit_action)

        edit_menu.addAction(undo_action)
        edit_menu.addAction(copy_action)
        edit_menu.addAction(cut_action)
        edit_menu.addAction(paste_action)

        view_menu.addAction(view_action)

        window_menu.addAction(minimize_action)

    def closeTab(self, currentIndex):
        currentQWidget = self.tabs.currentWidget()
        currentQWidget.deleteLater()
        self.tabs.removeTab(currentIndex)

    def new_file(self):
        newfile = QTextEdit()
        i = self.tabs.addTab(newfile, 'New Document')
        self.tabs.setCurrentIndex(i)

    def save_file(self):
        editor = self.tabs.currentWidget()
        filename = self.tabs.tabText(self.tabs.currentIndex())
        if filename != 'New Document':
            f = open(filename, 'w')
            filedata = editor.toPlainText()
            f.write(filedata)
            f.close()
        else:
            self.save_file_as()

    def save_file_as(self):
        filename = QFileDialog.getSaveFileName(self, 'Save File',
                                               os.getenv('HOME'))[0]
        print(filename)
        if filename != ('', ''):
            f = open(filename, 'w')
            filedata = self.text.toPlainText()
            f.write(filedata)
            f.close()

    def open_file(self):
        filename = QFileDialog.getOpenFileName(self, 'Open File',
                                               os.getenv('HOME'))[0]
        f = open(filename, 'r')
        filedata = f.read()
        f.close()
        newfile = QTextEdit()
        newfile.setText(filedata)
        i = self.tabs.addTab(newfile, filename)
        self.tabs.setCurrentIndex(i)

    def close_file(self):
        self.save_file()
        currentIndex = self.tabs.currentIndex()
        self.tabs.removeTab(currentIndex)
コード例 #39
0
ファイル: main.py プロジェクト: MazeFX/pat
class MainApp(QMainWindow, Ui_MainWindow):

    _translate = QCoreApplication.translate
    tab_list = []
    # TODO - add dutch translation files

    def __init__(self, isolated, *args):
        super(MainApp, self).__init__(*args)
        Lumberjack.info('spawning the <<< MainApp >>> hey says: I am the Main man here see!')
        self.load_settings()
        self.setup_tray(isolated)
        self.dbhelper = DbHelper()

        self.setupUi(self)
        self.iconize_controls()
        self.load_styling()

        self.tabWidget = QTabWidget(self.centralwidget)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setMovable(True)
        self.tabWidget.setTabBarAutoHide(True)
        self.tabWidget.setObjectName("tabWidget")
        self.verticalLayout.addWidget(self.tabWidget)

        builderLabel = QLabel('made by: MazeFX Solutions')
        self.statusbar.addPermanentWidget(builderLabel)

        self.menuPAT.triggered.connect(self.handle_menu_event)
        self.menuLists.triggered.connect(self.handle_menu_event)
        self.menuHelp.triggered.connect(self.handle_menu_event)
        self.tabWidget.tabCloseRequested.connect(self.close_tab)
        self.actionHome.trigger()

        self._retranslateUi(self)

    def iconize_controls(self):
        Lumberjack.info('< MainApp > - -> (iconize_controls)')

        homeIcon = qta.icon('fa.home', color='white')
        self.actionHome.setIcon(homeIcon)
        wrenchIcon = qta.icon('fa.wrench', color='white')
        self.actionSettings.setIcon(wrenchIcon)

        bankIcon = qta.icon('fa.bank', color='white')
        self.actionListBankAccounts.setIcon(bankIcon)

        contractIcon = QIcon(':/app_icons/rc/handshake_icon.svg')
        self.actionListContracts.setIcon(contractIcon)

        atIcon = qta.icon('fa.at', color='white')
        self.actionListEmailAddresses.setIcon(atIcon)

        envelopeIcon = qta.icon('fa.envelope', color='white')
        self.actionListLetters.setIcon(envelopeIcon)

        relationIcon = qta.icon('fa.group', color='white')
        self.actionListRelations.setIcon(relationIcon)

        transactionIcon = qta.icon('fa.money', color='white')
        self.actionListTransactions.setIcon(transactionIcon)

        userIcon = qta.icon('fa.user', color='white')
        self.actionListUsers.setIcon(userIcon)

        helpIcon = qta.icon('fa.question', color='white')
        self.actionHelp.setIcon(helpIcon)

        aboutIcon = qta.icon('fa.info', color='white')
        self.actionAbout.setIcon(aboutIcon)

    def setup_tray(self, isolated):
        Lumberjack.info('< MainApp > - -> (setup_tray)')
        self.trayIcon = QSystemTrayIcon(QIcon(':/app_icons/rc/PAT_icon.png'), self)
        self.trayMenu = QMenu(self)
        showAction = self.trayMenu.addAction("Open PAT")
        self.trayMenu.addSeparator()
        exitAction = self.trayMenu.addAction("Exit")
        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayMenu.triggered.connect(self.handle_tray_event)
        self.trayIcon.activated.connect(self.handle_tray_event)
        self.trayIcon.show()
        if isolated:
            self.trayIcon.showMessage('PAT Service', 'PAT service is now running..')

    def handle_tray_event(self, *args):
        Lumberjack.info('< MainApp > - -> (handle_tray_event)')
        print(Fore.MAGENTA + '$! Received a tray action with args: ', args)
        if args[0] == 3:
            self.show()
            return
        elif hasattr(args[0], 'text'):
            print(Fore.MAGENTA + '$! Tray event has text!!')
            if args[0].text() == 'Open PAT':
                self.show()
            elif args[0].text() == 'Exit':
                self.close()

    def _retranslateUi(self, MainWindow):
        pass

    def handle_menu_event(self, *args):
        Lumberjack.info('< MainApp > - -> (handle_menu_event)')
        Lumberjack.debug('(handle_menu_event) - args = {}'.format(args))

        action_text = args[0].text()
        icon = args[0].icon()

        Lumberjack.debug('(handle_menu_event) - Action text selector = {}'.format(action_text))
        print(Fore.MAGENTA + '$! Action text received: ', action_text)

        if action_text == 'Home':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Home tab to self')
            self.add_tab(HomeTab, 'Home', icon)

        if action_text == 'Settings':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing settings dialog')
            self.show_settings()

        elif action_text == 'Bank accounts':
            Lumberjack.info('(handle_menu_event) >User action> : Adding Bank account List tab to self')
            self.add_tab(BankAccountListTab, 'Bank accounts', icon)

        elif action_text == 'Contracts':
            Lumberjack.info('(handle_menu_event) >User action> : Adding Contracts List tab to self')
            self.add_tab(ContractListTab, 'Contracts', icon)

        elif action_text == 'Email addresses':
            Lumberjack.info('(handle_menu_event) >User action> : Adding EmailAddress List tab to self')
            self.add_tab(EmailAddressListTab, 'Email addresses', icon)

        elif action_text == 'Letters':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Letter List tab to self')
            self.add_tab(LetterListTab, 'Letters', icon)

        elif action_text == 'Users':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding User List tab to self')
            self.add_tab(UserListTab, 'Users', icon)

        elif action_text == 'Relations':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Relation List tab to self')
            self.add_tab(RelationListTab, 'Relations', icon)

        elif action_text == 'Transactions':
            Lumberjack.info('(handle_menu_event) >User action> :  Adding Transaction List tab to self')
            self.add_tab(TransactionListTab, 'Transactions', icon)

        elif action_text == 'Help':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing help dialog')
            # TODO - build help dialog and help files

        elif action_text == 'About':
            Lumberjack.info('(handle_menu_event) >User action> :  Showing about dialog')
            # TODO build About dialog.

    def show_settings(self):
        Lumberjack.info('< MainApp > - -> (show_settings)')
        settings_dialog = SettingsDialog()
        settings_dialog.exec_()

    def add_tab(self, tab_cls, tab_name, icon):
        Lumberjack.info('< MainApp > - -> (add_tab)')
        new_tab = tab_cls(self.dbhelper)
        print(Fore.MAGENTA + 'Adding a tab with class: ', str(tab_cls))
        new_tab.setObjectName(str(tab_cls))
        self.tabWidget.addTab(new_tab, icon, self._translate("MainWindow", tab_name))

        print(Fore.MAGENTA + 'New tab added to tab list.')
        self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(new_tab))
        self.tab_list.append(new_tab)

    def close_tab(self, index):
        # TODO - Check if index stays correct when moving tabs around
        requesting_tab = self.tab_list[index]
        print(Fore.MAGENTA + 'requesting tab is: ', requesting_tab)
        if hasattr(requesting_tab, 'form'):
            if requesting_tab.form.edit_mode:
                print(Fore.MAGENTA + 'Tab is in edit mode.')
                requesting_tab.form.toggle_edit_mode(False, None, None)
            if requesting_tab.form.edit_mode is None:
                print(Fore.MAGENTA + 'Tab is now in equil.')
                self.tabWidget.removeTab(index)
                del self.tab_list[index]
        else:
            self.tabWidget.removeTab(index)
            del self.tab_list[index]

    def load_settings(self):
        self.settings = QSettings()

        db_path = self.settings.value('db_base_path')
        db_name = self.settings.value('db_name')

        if db_path is not None and db_name is not None:
            db_file = os.path.join(db_path, db_name)
            Lumberjack.debug('__init__ - db_file = {}'.format(db_file))
            if os.path.exists(db_file):
                return
        Lumberjack.warning('(load_settings) - database not found')
        settings_dialog = SettingsDialog()
        settings_dialog.exec_()
        int_value = self.settings.value('db_type', type=int)
        print(Fore.MAGENTA + "load choosen database setting: %s" % repr(int_value))

    def load_styling(self):
        style.set_window_style(self)

    def closeEvent(self, event):
        print(Fore.MAGENTA + "User has clicked the red x on the main window")
        for tab in self.tab_list:
            if hasattr(tab, 'form'):
                if tab.form.edit_mode:
                    print(Fore.MAGENTA + 'Tab is in edit mode.')
                    tab.form.toggle_edit_mode(False, None, None)

        close_dialog = CloseDialog()
        result = close_dialog.exec_()
        if result == close_dialog.Minimize:
            self.hide()
            event.ignore()
        elif result == close_dialog.Rejected:
            event.ignore()
        elif result == close_dialog.Exit:
            print(Fore.MAGENTA + "Exiting via save dialog, result = ", result)
            self.trayIcon.hide()
            event.accept()
コード例 #40
0
class ScadaGui(QWidget):
    def __init__(self, redirect_sys_out=True):
        self.redirect_sys_out = redirect_sys_out
        super(ScadaGui, self).__init__()
        self.initUI()

    def initUI(self):
        # creating widgets
        self._dgridPage = QWidget()
        self._networkPage = QWidget()
        # self._plotPage[0] = QWidget()
        self._plotPage = {}
        # create tabs
        self._tabs = QTabWidget(self)
        self._tabs.addTab(self._dgridPage, "dgrid")
        self._tabs.addTab(self._networkPage, "network")
        self._tabs.setTabsClosable(True)
        self._tabs.tabCloseRequested[int].connect(self.tabClosedRequested)
        # self._tabs.addTab(self._plotPage, "plots")

        # create the network image display
        self._networkDisp = QtSvg.QSvgWidget('IEEE_30BusDC.svg')
        self._networkDisp.setMaximumSize(600, 500)

        # create the table for the measurements
        self._dgridValues = QTableWidget(10, 31, self._dgridPage)
        self._configure_table()

        # create the plot
        # self._plotCanvas = MyMplCanvas(self._plotPage, width=lamda10, height=4, dpi=100)
        # self._plotToolbar = NavigationToolbar(self._plotCanvas, self._plotPage)
        self._plotCanvas = {}
        self._plotToolbar = {}
        # create the shell
        self._scadaShell = ScadaShell(self)

        # setting the layout of the first tab, i.e., dgrid
        dgridLayout = QVBoxLayout(self._dgridPage)
        dgridLayout.addWidget(self._dgridValues)
        # setting the layout of the second tab, i.e., network
        networkLayout = QVBoxLayout(self._networkPage)
        networkLayout.addWidget(self._networkDisp)
        # setting the layout of the third tab, i.e., plots
        # plotLayout = QVBoxLayout(self._plotPage)
        # plotLayout.addWidget(self._plotCanvas)
        # plotLayout.addWidget(self._plotToolbar)

        # setting the layout of the main page
        self._mainLayout = QVBoxLayout()
        self._mainLayout.addWidget(self._tabs)
        self._mainLayout.addWidget(self._scadaShell)
        self.setLayout(self._mainLayout)
        self.setWindowTitle("Distributed Optimization SCADA")
        self.setMinimumSize(1100, 600)
        self.setWindowIcon(QtGui.QIcon('DOscada.png'))
        self._scadaShell.setFocus()

    def tabClosedRequested(self, tab_index):
        if tab_index >= 2:
            self._tabs.removeTab(tab_index)
            self._current_plot = -1

    def _configure_table(self):
        # configure the row header for the table
        self._dgridValues.setVerticalHeaderItem(0, QTableWidgetItem("ON"))
        self._dgridValues.setVerticalHeaderItem(1, QTableWidgetItem("A2O[ms]"))
        self._dgridValues.setVerticalHeaderItem(2, QTableWidgetItem("V[pu]"))
        self._dgridValues.setVerticalHeaderItem(3, QTableWidgetItem("P[pu]"))
        self._dgridValues.setVerticalHeaderItem(4, QTableWidgetItem("Trip"))
        self._dgridValues.setVerticalHeaderItem(5, QTableWidgetItem("ADMM"))
        self._dgridValues.setVerticalHeaderItem(6,
                                                QTableWidgetItem("Opt [ms]"))
        self._dgridValues.setVerticalHeaderItem(7,
                                                QTableWidgetItem("RPC [ms]"))
        self._dgridValues.setVerticalHeaderItem(8, QTableWidgetItem("V* [pu]"))
        self._dgridValues.setVerticalHeaderItem(9, QTableWidgetItem("P* [pu]"))
        # configure the column header of the table
        for i in range(0, 31):
            self._dgridValues.setHorizontalHeaderItem(
                i, QTableWidgetItem(str(i + 1)))
            self._dgridValues.horizontalHeaderItem(i).setForeground(
                QtGui.QColor(255, 0, 0, 255))
        # set the font-color for the generators columns in green
        self._dgridValues.horizontalHeaderItem(0).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        self._dgridValues.horizontalHeaderItem(1).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        self._dgridValues.horizontalHeaderItem(12).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        self._dgridValues.horizontalHeaderItem(21).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        self._dgridValues.horizontalHeaderItem(22).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        self._dgridValues.horizontalHeaderItem(26).setForeground(
            QtGui.QColor(13, 148, 22, 255))
        font = self._dgridValues.horizontalHeader().font()
        font.setBold(True)
        self._dgridValues.horizontalHeader().setFont(font)
        # hide columns that don't have a coresponding network node
        self._dgridValues.setColumnHidden(4, True)
        self._dgridValues.setColumnHidden(8, True)
        self._dgridValues.setColumnHidden(10, True)
        # populate the table with empty cells
        for col in range(0, 31):
            for row in range(0, 10):
                self._dgridValues.setItem(row, col, QTableWidgetItem("     "))
                font = self._dgridValues.item(row, col).font()
                font.setPixelSize(8)
                self._dgridValues.item(row, col).setFont(font)
        self._dgridValues.resizeColumnsToContents()
        self._dgridValues.resizeRowsToContents()

    def set_current_plot(self, plot_no):
        self._current_plot = plot_no

    def add_plot_tab(self, plot_no, plot_name):
        self._current_plot = plot_no
        self._plotPage[plot_no] = QWidget()
        self._tabs.addTab(self._plotPage[plot_no],
                          str(plot_no) + " - " + plot_name)
        # create the plot
        self._plotCanvas[plot_no] = MyMplCanvas(self._plotPage[plot_no],
                                                width=5,
                                                height=4,
                                                dpi=100)
        self._plotToolbar[plot_no] = NavigationToolbar(
            self._plotCanvas[plot_no], self._plotPage[plot_no])

        # setting the layout of the third tab, i.e., plots
        plotLayout = QVBoxLayout(self._plotPage[plot_no])
        plotLayout.addWidget(self._plotCanvas[plot_no])
        plotLayout.addWidget(self._plotToolbar[plot_no])

    def agent_connected(self, ano):
        self._dgridValues.item(0, ano - 1).setText("ON")
        self._dgridValues.resizeRowsToContents()
        self._dgridValues.viewport().update()

    def agent_running_admm(self, ano):
        self._dgridValues.item(5, ano - 1).setText("ON")
        self._dgridValues.resizeRowsToContents()
        self._dgridValues.viewport().update()

    def agent_disconnected(self, ano):
        self._dgridValues.item(0, ano - 1).setText("OFF")
        self._dgridValues.resizeRowsToContents()
        self._dgridValues.viewport().update()

    def agent_update_values(self, ano, dt_opal, v_meas, p_meas, trip):
        self._dgridValues.item(1, ano - 1).setText('{:4.3f}'.format(dt_opal))
        self._dgridValues.item(2, ano - 1).setText('{:4.3f}'.format(v_meas))
        self._dgridValues.item(3, ano - 1).setText('{:4.3f}'.format(p_meas))
        self._dgridValues.item(4, ano - 1).setText('{:4.3f}'.format(trip))
        self._dgridValues.resizeColumnsToContents()
        self._dgridValues.viewport().update()

    def agent_finished_admm(self, ano, dt_opt, dt_rpc, v_ref, p_ref):
        self._dgridValues.item(5, ano - 1).setText('OFF')
        self._dgridValues.item(6, ano - 1).setText('{:4.3f}'.format(dt_opt))
        self._dgridValues.item(7, ano - 1).setText('{:4.3f}'.format(dt_rpc))
        self._dgridValues.item(8, ano - 1).setText('{:4.3f}'.format(v_ref))
        self._dgridValues.item(9, ano - 1).setText('{:4.3f}'.format(p_ref))
        self._dgridValues.resizeColumnsToContents()
        self._dgridValues.viewport().update()

    def plot_data(self, x, y, marker, label):
        if self._current_plot == -1:
            self._scadaShell.log_text("No plot selected!")
        else:
            self._plotCanvas[self._current_plot].update_figure(
                x, y, marker, label)

    def plot_hold(self, holdon):
        if self._current_plot == -1:
            self._scadaShell.log_text("No plot selected!")
        else:
            self._plotCanvas[self._current_plot].plot_hold(holdon)
コード例 #41
0
ファイル: quickbrowse.py プロジェクト: akkana/scripts
class BrowserWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        # Strip out arguments we handle that are different from QMainWindow:
        if 'width' in kwargs:
            self.width = kwargs['width']
            del kwargs['width']
        else:
            self.width = 1024
        if 'height' in kwargs:
            self.height = kwargs['height']
            del kwargs['height']
        else:
            # Short enough to fit in XGA, 768 height:
            self.height = 735

        # Then run the default constructor.
        super(BrowserWindow, self).__init__(*args, **kwargs)

        self.browserviews = []

        self.profile = QWebEngineProfile()
        # print("Profile initially off the record?",
        #       self.profile.isOffTheRecord())

        # "Off the record" doesn't mean much: QtWebEngine still
        # stores cache and maybe persistent cookies.
        # Here are some other attempts at privacy that might help a little:
        # self.cachedir = tempfile.mkdtemp()
        # self.profile.setCachePath(self.cachedir)
        # self.profile.setPersistentStoragePath(self.cachedir)
        # self.profile.setPersistentCookiesPolicy(self.profile.NoPersistentCookies);
        # but even with all those, QtWebEngine still stores a bunch of crap in
        # .local/share/quickbrowse/QtWebEngine/Default/
        # But we can prevent that by lying about $HOME:
        os.environ["HOME"] = tempfile.mkdtemp()

        self.init_tab_name_len = 40

        self.init_chrome()

        # Resize to fit on an XGA screen, allowing for window chrome.
        # XXX Should check the screen size and see if it can be bigger.
        self.resize(self.width, self.height)

        # Set up the listener for remote commands, the filename
        # and the buffer where we'll store those commands:
        self.cmdsockname = None
        self.cmdread = b''
        self.set_up_listener()

    # Each process has one BrowserWindow, and each BrowserWindow has one
    # command pipe (a Unix-domain socket) where it can accept commands.
    def set_up_listener(self):
        # Make sure the socket does not already exist
        self.cmdsockname = CMD_PIPE % os.getpid()
        try:
            os.unlink(self.cmdsockname)
        except OSError:
            if os.path.exists(self.cmdsockname):
                raise

        # Create a UDS socket
        self.cmdsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

        # Bind the socket to the port
        self.cmdsock.bind(self.cmdsockname)

        # Listen for incoming connections
        self.cmdsock.listen(1)

        self.notifier = QSocketNotifier(self.cmdsock.fileno(),
                                        QSocketNotifier.Read)

        self.notifier.activated.connect(self.pipe_ready)

    def pipe_ready(self):
        connection, client_address = self.cmdsock.accept()
        with connection:
            try:
                while True:
                    data = connection.recv(1024)
                    if not data: break
                    self.cmdread += data

            finally:
                # Clean up the connection
                connection.close()

        # Commands end with newlines.
        # Figure out how many commands we've read, if any.
        # It's possible to read more than one command at once,
        # or that we don't yet have all the data even for a single command.
        cmdlines = self.cmdread.split(b'\n')
        if self.cmdread.endswith(b'\n'):
            self.cmdread = b''
        else:
            # Our last command is incomplete; we'll have to wait
            # for the rest of the line to come through.
            self.cmdread = cmdlines[-1]
            cmdlines = cmdlines[:-1]

        for cmd in cmdlines:
            if not cmd:
                continue
            # Change it from bytes to string:
            cmd = cmd.decode('utf-8')
            if cmd.startswith('new-tab ') and len(cmd) > 8:
                self.new_tab(cmd[8:])

    @staticmethod
    def send_command(cmd, url):
        '''Send a command to a running quickbrowse process.
           This will usually be called from a separate, new, process.
        '''
        # Start by finding the available CMD_PIPEs.
        # We'll use the one with the most recent ctime.
        pipedir, sockbase = os.path.split(CMD_PIPE)
        # Split off any %d in sockbase
        if '%' in sockbase:
            sockbase = sockbase[:sockbase.find('%')]
        flist = os.listdir(pipedir)
        cmdsockname = ""  # This tests as less than any real string
        last_ctime = 0
        for f in flist:
            if f.startswith(sockbase):
                sockname = os.path.join(pipedir, f)
                this_ctime = os.path.getctime(sockname)
                if this_ctime > last_ctime:
                    cmdsockname = sockname
                    last_ctime = this_ctime

        if not cmdsockname:
            raise IOError("No running quickbrowse process")

        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

        # Next line can raise socket.err, especially if there's no listener,
        # but rather than catching them, we'll raise it
        # and the caller can use that to decide to open a new window.
        sock.connect(cmdsockname)

        try:
            sock.sendall(b"%s %s\n" % (cmd.encode('utf-8'),
                                       url.encode('utf-8')))

        finally:
            sock.close()

    def init_chrome(self):
        # Set up the browser window chrome:
        self.setWindowTitle("Quickbrowse")

        toolbar = QToolBar("Toolbar")
        self.addToolBar(toolbar)

        btn_act = QAction("Back", self)
        # for an icon: QAction(QIcon("bug.png"), "Your button", self)
        btn_act.setStatusTip("Go back")
        btn_act.triggered.connect(self.go_back)
        toolbar.addAction(btn_act)

        btn_act = QAction("Forward", self)
        btn_act.setStatusTip("Go forward")
        btn_act.triggered.connect(self.go_forward)
        toolbar.addAction(btn_act)

        btn_act = QAction("Reload", self)
        btn_act.setStatusTip("Reload")
        btn_act.triggered.connect(self.reload)
        toolbar.addAction(btn_act)

        self.urlbar = ReadlineEdit()
        self.urlbar.setPlaceholderText("URL goes here")
        self.urlbar.returnPressed.connect(self.urlbar_load)
        toolbar.addWidget(self.urlbar)

        self.tabwidget = QTabWidget()
        self.tabwidget.setTabBarAutoHide(True)

        self.setCentralWidget(self.tabwidget)

        self.tabwidget.tabBar().installEventFilter(self)
        self.prev_middle = -1
        self.active_tab = 0

        self.setStatusBar(QStatusBar(self))
        self.progress = QProgressBar()
        self.statusBar().addPermanentWidget(self.progress)

        # Key bindings
        # For keys like function keys, use QtGui.QKeySequence("F12")
        QShortcut("Ctrl+Q", self, activated=self.close)
        QShortcut("Ctrl+L", self, activated=self.select_urlbar)
        QShortcut("Ctrl+T", self, activated=self.new_tab)
        QShortcut("Ctrl+R", self, activated=self.reload)

        QShortcut("Ctrl++", self, activated=self.zoom)
        QShortcut("Ctrl+=", self, activated=self.zoom)
        QShortcut("Ctrl+-", self, activated=self.unzoom)

        QShortcut("Alt+Left", self, activated=self.go_back)
        QShortcut("Alt+Right", self, activated=self.go_forward)

        QShortcut("Esc", self, activated=self.unfullscreen)

    def eventFilter(self, object, event):
        '''Handle button presses in the tab bar'''

        if object != self.tabwidget.tabBar():
            print("eventFilter Not in tab bar")
            return super().eventFilter(object, event)
            # return False

        if event.type() not in [QEvent.MouseButtonPress,
                                QEvent.MouseButtonRelease]:
            # print("Not a button press or release", event)
            return super().eventFilter(object, event)
            # return False

        tabindex = object.tabAt(event.pos())

        if event.button() == Qt.LeftButton:
            if event.type() == QEvent.MouseButtonPress:
                self.active_tab = tabindex
                self.urlbar.setText(self.browserviews[tabindex].url().toDisplayString())
            return super().eventFilter(object, event)
            # return False    # So we'll still switch to that tab

        if event.button() == Qt.MidButton:
            if event.type() == QEvent.MouseButtonPress:
                    self.prev_middle = tabindex
            else:
                if tabindex != -1 and tabindex == self.prev_middle:
                    self.close_tab(tabindex)
                self.prev_middle = -1
            return True

        print("Unknown button", event)
        return super().eventFilter(object, event)

    def new_tab(self, url=None):
        if url:
            init_name = url[:self.init_tab_name_len]
        else:
            init_name = "New tab"

        if is_pdf(url):
            webview = PDFBrowserView(self, url)
            self.browserviews.append(webview)
            self.tabwidget.addTab(webview, init_name)

        else:    # The normal case, an HTML page
            webview = BrowserView(self)

            # We need a QWebEnginePage in order to get linkHovered events,
            # and to set an anonymous profile.
            # print("New tab, profile still off the record?",
            #       self.profile.isOffTheRecord())
            webpage = BrowserPage(self.profile, webview, self)

            # print("New Webpage off the record?",
            #       webpage.profile().isOffTheRecord())
            webview.setPage(webpage)
            # print("New view's page off the record?",
            #       webview.page().profile().isOffTheRecord())
            # print("In new tab, view is", webview, "and page is", webpage)

            self.browserviews.append(webview)
            self.tabwidget.addTab(webview, init_name)

            if url:
                self.load_url(url, len(self.browserviews)-1)

            # Set up the signals:
            webview.urlChanged.connect(webview.url_changed)
            webview.loadStarted.connect(webview.load_started)
            webview.loadFinished.connect(webview.load_finished)
            webview.loadProgress.connect(webview.load_progress)
            webpage.linkHovered.connect(webview.link_hover)

        return webview

    def focus_content(self):
        self.browserviews[self.active_tab].setFocus()

    def closeEvent(self, event):
        # Clean up
        if self.cmdsockname:
            os.unlink(self.cmdsockname)
        if os.environ["HOME"].startswith('/tmp/') and \
           os.getenv('USER') not in os.environ["HOME"]:
            print("Cleaning up: removing %s" % os.environ["HOME"])
            shutil.rmtree(os.environ["HOME"])

    def close_tab(self, tabindex):
        self.tabwidget.removeTab(tabindex)

    def load_url(self, url, tab=None):
        '''Load the given URL in the specified tab, or current tab if tab=None.
           url is a str, not a QUrl.
           PDF URLs will be loaded in a new tab, because there doesn't
           seem to be a way of replacing the BrowserView with a BrowserPDFView.
        '''

        # If there are newlines, remove newlines plus all adjacent whitespace.
        if '\n' in url:
            lines = url.split('\n')
            url = ''.join([ l.strip() for l in lines ])

        # Note that tab=0 is a valid argument here.
        # When testing whether tab is set, be sure to test for None.

        if is_pdf(url):
            self.new_tab(url)
            return

        qurl = QUrl(url)

        if not qurl.scheme():
            if os.path.exists(url):
                qurl.setScheme('file')
                if not os.path.isabs(url):
                    # Is it better to use posixpath.join or os.path.join?
                    # Both work on Linux.
                    qurl.setPath(os.path.normpath(os.path.join(os.getcwd(),
                                                               url)))
            else:
                qurl.setScheme('http')

        if len(self.browserviews) == 0:
            self.new_tab()
            tab = 0
        elif tab == None:
            tab = self.active_tab

        self.set_tab_text(url[:self.init_tab_name_len],
                          self.browserviews[tab])
        if tab == self.active_tab:
            self.urlbar.setText(url)

        self.browserviews[tab].load(qurl)

    def load_html(self, html, base=None):
        '''Load a string containing HTML.
           The base is the file: URL the HTML should be considered to have
           come from, to avoid "Not allowed to load local resource" errors
           when clicking on links.
        '''
        if not self.browserviews:
            self.new_tab()
            tab = 0
        else:
            tab = self.active_tab
            self.set_tab_text("---",  # XXX Replace with html title if possible
                              self.browserviews[tab])

        self.browserviews[tab].setHtml(html, QUrl(base))

    def select_urlbar(self):
        self.urlbar.selectAll()
        self.urlbar.setFocus()

    def find_view(self, view):
        for i, v in enumerate(self.browserviews):
            if v == view:
                return i
        return None

    def set_tab_text(self, title, view):
        '''Set tab and, perhaps, window title after a page load.
           view is the requesting BrowserView, and will be compared
           to our browserviews[] to figure out which tab to set.
        '''
        if self.tabwidget == None:
            return
        whichtab = None
        whichtab = self.find_view(view)
        if whichtab == None:
            print("Warning: set_tab_text for unknown view")
            return
        self.tabwidget.setTabText(whichtab, title)

    def zoom(self):
        if 'zoom' in dir(self.browserviews[self.active_tab]):
            self.browserviews[self.active_tab].zoom()

    def unzoom(self):
        if 'unzoom' in dir(self.browserviews[self.active_tab]):
            self.browserviews[self.active_tab].unzoom()

    def unfullscreen(self):
        if self.isFullScreen():
            self.showNormal()

            # Some pages, like YouTube, want to know when the browser comes
            # out of fullscreen mode so it can adjust its chrome.
            self.browserviews[self.active_tab].page().triggerAction(QWebEnginePage.ExitFullScreen)

    def update_buttons(self):
        # TODO: To enable/disable buttons, check e.g.
        # self.webview.page().action(QWebEnginePage.Back).isEnabled())
        pass

    #
    # Slots
    #

    def urlbar_load(self):
        url = self.urlbar.text()
        self.load_url(url)

    def go_back(self):
        self.browserviews[self.active_tab].back()

    def go_forward(self):
        self.browserviews[self.active_tab].forward()

    def reload(self):
        self.browserviews[self.active_tab].reload()
コード例 #42
0
ファイル: app.py プロジェクト: capturePointer/Dwarf
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')
    onSystemUIElementCreated = pyqtSignal(str,
                                          QWidget,
                                          name='onSystemUIElementCreated')
    onSystemUIElementRemoved = pyqtSignal(str, name='onSystemUIElementRemoved')

    def __init__(self, dwarf_args, flags=None):
        super(AppWindow, self).__init__(flags)

        self.dwarf_args = dwarf_args

        self.session_manager = SessionManager(self)
        self.session_manager.sessionCreated.connect(self.session_created)
        self.session_manager.sessionStopped.connect(self.session_stopped)
        self.session_manager.sessionClosed.connect(self.session_closed)

        self._tab_order = [
            'debug', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self._is_newer_dwarf = False
        self.q_settings = QSettings("dwarf_window_pos.ini",
                                    QSettings.IniFormat)

        self.menu = self.menuBar()
        self.view_menu = None

        self._initialize_ui_elements()

        self.setWindowTitle(
            'Dwarf - A debugger for reverse engineers, crackers and security analyst'
        )

        # load external assets
        _app = QApplication.instance()

        self.remove_tmp_dir()

        # themes
        self.prefs = Prefs()
        utils.set_theme(self.prefs.get('dwarf_ui_theme', 'black'), self.prefs)

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
            font = QFont("OpenSans", 9, QFont.Normal)
            # TODO: add settingsdlg
            font_size = self.prefs.get('dwarf_ui_font_size', 12)
            font.setPixelSize(font_size)
            _app.setFont(font)
            if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
                QFontDatabase.addApplicationFont(
                    utils.resource_path('assets/OpenSans-Bold.ttf'))

        # mainwindow statusbar
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0, 0)
        self.progressbar.setVisible(False)
        self.progressbar.setFixedHeight(15)
        self.progressbar.setFixedWidth(100)
        self.progressbar.setTextVisible(False)
        self.progressbar.setValue(30)
        self.statusbar = QStatusBar(self)
        self.statusbar.setAutoFillBackground(False)
        self.statusbar.addPermanentWidget(self.progressbar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.main_tabs = QTabWidget(self)
        self.main_tabs.setMovable(False)
        self.main_tabs.setTabsClosable(True)
        self.main_tabs.setAutoFillBackground(True)
        self.main_tabs.tabCloseRequested.connect(self._on_close_tab)
        self.setCentralWidget(self.main_tabs)

        # pluginmanager
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.reload_plugins()

        if dwarf_args.any == '':
            self.welcome_window = WelcomeDialog(self)
            self.welcome_window.setModal(True)
            self.welcome_window.onIsNewerVersion.connect(
                self._enable_update_menu)
            self.welcome_window.onUpdateComplete.connect(
                self._on_dwarf_updated)
            self.welcome_window.setWindowTitle(
                'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst'
            )
            self.welcome_window.onSessionSelected.connect(self._start_session)
            # wait for welcome screen
            self.hide()
            self.welcome_window.show()
        else:
            print('* Starting new Session')
            self._start_session(dwarf_args.target)

    def _initialize_ui_elements(self):
        # dockwidgets
        self.watchers_dwidget = None
        self.hooks_dwiget = None
        self.bookmarks_dwiget = None
        self.registers_dock = None
        self.console_dock = None
        self.backtrace_dock = None
        self.threads_dock = None
        # panels
        self.asm_panel = None
        self.backtrace_panel = None
        self.bookmarks_panel = None
        self.console_panel = None
        self.context_panel = None
        self.debug_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.ftrace_panel = None
        self.hooks_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.smali_panel = None
        self.watchers_panel = None
        self.welcome_window = None

        self._ui_elems = []

    def _setup_main_menu(self):
        self.menu = self.menuBar()
        dwarf_menu = QMenu('Dwarf', self)
        theme = QMenu('Theme', dwarf_menu)
        theme.addAction('Black')
        theme.addAction('Dark')
        theme.addAction('Light')
        theme.triggered.connect(self._set_theme)
        dwarf_menu.addMenu(theme)
        dwarf_menu.addSeparator()

        if sys.platform == 'linux' or sys.platform == 'darwin':
            dwarf_bin_path = os.path.join(
                '/'.join(os.path.realpath(__file__).split('/')[:-2]),
                'bin/dwarf')
            if not os.path.exists(dwarf_bin_path):
                dwarf_menu.addAction('Create launcher', utils.create_launcher)
                dwarf_menu.addSeparator()

        if self._is_newer_dwarf:
            dwarf_menu.addAction('Update', self._update_dwarf)
        dwarf_menu.addAction('Close', self.session_manager.session.stop)
        self.menu.addMenu(dwarf_menu)

        session = self.session_manager.session
        if session is not None:
            session_menu = session.main_menu
            if isinstance(session_menu, list):
                for menu in session_menu:
                    self.menu.addMenu(menu)
            else:
                self.menu.addMenu(session_menu)

        # plugins
        if self.plugin_manager.plugins:
            self.plugin_menu = QMenu('Plugins', self)
            for plugin in self.plugin_manager.plugins:
                plugin_instance = self.plugin_manager.plugins[plugin]
                plugin_sub_menu = self.plugin_menu.addMenu(
                    plugin_instance.name)

                try:
                    actions = plugin_instance.__get_top_menu_actions__()
                    for action in actions:
                        plugin_sub_menu.addAction(action)
                except:
                    pass

                if not plugin_sub_menu.isEmpty():
                    plugin_sub_menu.addSeparator()

                about = plugin_sub_menu.addAction('About')
                about.triggered.connect(
                    lambda x, item=plugin: self._show_plugin_about(item))

            if not self.plugin_menu.isEmpty():
                self.menu.addMenu(self.plugin_menu)

        self.view_menu = QMenu('View', self)
        self.panels_menu = QMenu('Panels', self.view_menu)
        self.panels_menu.addAction('Search',
                                   lambda: self.show_main_tab('search'),
                                   shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3))

        self.view_menu.addMenu(self.panels_menu)

        self.debug_view_menu = self.view_menu.addMenu('Debug')

        self.view_menu.addSeparator()

        self.view_menu.addAction('Hide all',
                                 self._hide_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F1))
        self.view_menu.addAction('Show all',
                                 self._show_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2))

        self.view_menu.addSeparator()

        self.menu.addMenu(self.view_menu)

        if self.dwarf_args.debug_script:
            debug_menu = QMenu('Debug', self)
            debug_menu.addAction('Reload core', self._menu_reload_core)
            debug_menu.addAction('Debug dwarf js core',
                                 self._menu_debug_dwarf_js)
            self.menu.addMenu(debug_menu)

        # tools
        _tools = self.prefs.get('tools')
        if _tools:
            tools_menu = QMenu('Tools', self)

            for _tool in _tools:
                if _tool and _tool['name']:
                    if _tool['name'] == 'sep':
                        tools_menu.addSeparator()
                        continue

                    _cmd = _tool['cmd']

                    tools_menu.addAction(_tool['name'])

            if not tools_menu.isEmpty():
                tools_menu.triggered.connect(self._execute_tool)
                self.menu.addMenu(tools_menu)

        about_menu = QMenu('About', self)
        about_menu.addAction('Dwarf on GitHub', self._menu_github)
        about_menu.addAction('Documention', self._menu_documentation)
        about_menu.addAction('Api', self._menu_api)
        about_menu.addAction('Slack', self._menu_slack)
        about_menu.addSeparator()

        about_menu.addAction('Info', self._show_about_dlg)
        self.menu.addMenu(about_menu)

    def _show_plugin_about(self, plugin):
        plugin = self.plugin_manager.plugins[plugin]
        if plugin:
            info = plugin.__get_plugin_info__()

            version = utils.safe_read_map(info, 'version', '')
            description = utils.safe_read_map(info, 'description', '')
            author = utils.safe_read_map(info, 'author', '')
            homepage = utils.safe_read_map(info, 'homepage', '')
            license_ = utils.safe_read_map(info, 'license', '')

            utils.show_message_box(
                'Name: {0}\nVersion: {1}\nDescription: {2}\nAuthor: {3}\nHomepage: {4}\nLicense: {5}'
                .format(plugin.name, version, description, author, homepage,
                        license_))

    def _enable_update_menu(self):
        self._is_newer_dwarf = True

    def _update_dwarf(self):
        if self.welcome_window:
            self.welcome_window._update_dwarf()

    def _on_close_tab(self, index):
        tab_text = self.main_tabs.tabText(index)
        if tab_text:
            tab_text = tab_text.lower().replace(' ', '-')
            if tab_text in self.session_manager.session.non_closable:
                return
            try:
                self._ui_elems.remove(tab_text)
            except ValueError:  # recheck ValueError: list.remove(x): x not in list
                pass
            self.main_tabs.removeTab(index)

            self.onSystemUIElementRemoved.emit(tab_text)

    def _handle_tab_change(self):
        for index in range(self.main_tabs.count()):
            tab_name = self.main_tabs.tabText(index).lower().replace(' ', '-')
            if tab_name in self.session_manager.session.non_closable:
                self.main_tabs.tabBar().setTabButton(index, QTabBar.RightSide,
                                                     None)

                if tab_name in self._tab_order:
                    should_index = self._tab_order.index(tab_name)
                    if index != should_index:
                        self.main_tabs.tabBar().moveTab(index, should_index)

    def _on_dwarf_updated(self):
        self.onRestart.emit()

    def remove_tmp_dir(self):
        if os.path.exists('.tmp'):
            shutil.rmtree('.tmp', ignore_errors=True)

    def _execute_tool(self, qaction):
        if qaction:
            _tools = self.prefs.get('tools')
            if _tools:
                for _tool in _tools:
                    if _tool and _tool['name'] and _tool['name'] != 'sep':
                        if qaction.text() == _tool['name']:
                            try:
                                import subprocess
                                subprocess.Popen(_tool['cmd'],
                                                 creationflags=subprocess.
                                                 CREATE_NEW_CONSOLE)
                            except:
                                pass
                            break

    def _set_theme(self, qaction):
        if qaction:
            utils.set_theme(qaction.text(), self.prefs)

    def _hide_all_widgets(self):
        self.watchers_dwidget.hide()
        self.hooks_dwiget.hide()
        self.bookmarks_dwiget.hide()
        self.registers_dock.hide()
        self.console_dock.hide()
        self.backtrace_dock.hide()
        self.threads_dock.hide()

    def _show_all_widgets(self):
        self.watchers_dwidget.show()
        self.hooks_dwiget.show()
        self.bookmarks_dwiget.show()
        self.registers_dock.show()
        self.console_dock.show()
        self.backtrace_dock.show()
        self.threads_dock.show()

    def _menu_reload_core(self):
        self.dwarf.script.exports.reload()

    def _menu_debug_dwarf_js(self):
        you_know_what_to_do = json.loads(
            self.dwarf.script.exports.debugdwarfjs())
        return you_know_what_to_do

    def show_main_tab(self, name):
        name = name.lower()
        # elem doesnt exists? create it
        if name not in self._ui_elems:
            self._create_ui_elem(name)

        index = 0
        name = name.join(name.split()).lower()
        if name == 'ranges':
            index = self.main_tabs.indexOf(self.ranges_panel)
        elif name == 'search':
            index = self.main_tabs.indexOf(self.search_panel)
        elif name == 'modules':
            index = self.main_tabs.indexOf(self.modules_panel)
        elif name == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'jvm-tracer':
            index = self.main_tabs.indexOf(self.java_trace_panel)
        elif name == 'jvm-inspector':
            index = self.main_tabs.indexOf(self.java_inspector_panel)
        elif name == 'jvm-debugger':
            index = self.main_tabs.indexOf(self.java_explorer_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr, view=0, show_panel=True):
        if show_panel:
            self.show_main_tab('debug')
        self.debug_panel.jump_to_address(ptr, view=view)

    @pyqtSlot(name='mainMenuGitHub')
    def _menu_github(self):
        QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf'))

    @pyqtSlot(name='mainMenuApi')
    def _menu_api(self):
        QDesktopServices.openUrl(
            QUrl('http://www.giovanni-rocca.com/dwarf/javascript/'))

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('http://www.giovanni-rocca.com/dwarf/'))

    @pyqtSlot(name='mainMenuSlack')
    def _menu_slack(self):
        QDesktopServices.openUrl(
            QUrl('https://join.slack.com/t/resecret/shared_invite'
                 '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT'
                 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN'
                 'WJlNDVjZDcwNGE'))

    def _show_about_dlg(self):
        about_dlg = AboutDialog(self)
        about_dlg.show()

    def _create_ui_elem(self, elem):
        elem = elem.lower()

        if not isinstance(elem, str):
            return

        if elem not in self._ui_elems:
            self._ui_elems.append(elem)

        elem_wiget = None

        if elem == 'watchers':
            from ui.session_widgets.watchers import WatchersWidget
            self.watchers_dwidget = QDockWidget('Watchers', self)
            self.watchers_panel = WatchersWidget(self)
            # dont respond to dblclick mem cant be shown
            # self.watchers_panel.onItemDoubleClicked.connect(
            #    self._on_watcher_clicked)
            self.watchers_panel.onItemRemoved.connect(
                self._on_watcher_removeditem)
            self.watchers_panel.onItemAdded.connect(self._on_watcher_added)
            self.watchers_dwidget.setWidget(self.watchers_panel)
            self.watchers_dwidget.setObjectName('WatchersWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget)
            self.view_menu.addAction(self.watchers_dwidget.toggleViewAction())
            elem_wiget = self.watchers_panel
        elif elem == 'hooks':
            from ui.session_widgets.hooks import HooksWidget
            self.hooks_dwiget = QDockWidget('Breakpoints', self)
            self.hooks_panel = HooksWidget(self)
            self.hooks_panel.onHookRemoved.connect(self._on_hook_removed)
            self.hooks_dwiget.setWidget(self.hooks_panel)
            self.hooks_dwiget.setObjectName('HooksWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget)
            self.view_menu.addAction(self.hooks_dwiget.toggleViewAction())
            elem_wiget = self.hooks_panel
        elif elem == 'bookmarks':
            from ui.session_widgets.bookmarks import BookmarksWidget
            self.bookmarks_dwiget = QDockWidget('Boomarks', self)
            self.bookmarks_panel = BookmarksWidget(self)
            self.bookmarks_dwiget.setWidget(self.bookmarks_panel)
            self.bookmarks_dwiget.setObjectName('BookmarksWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget)
            self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction())
            elem_wiget = self.bookmarks_panel
        elif elem == 'registers':
            from ui.session_widgets.context import ContextWidget
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextWidget(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextWidget')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
            elem_wiget = self.context_panel
        elif elem == 'debug':
            from ui.panels.panel_debug import QDebugPanel
            self.debug_panel = QDebugPanel(self)
            self.main_tabs.addTab(self.debug_panel, 'Debug')
            elem_wiget = self.debug_panel
        elif elem == 'jvm-debugger':
            from ui.panels.panel_java_explorer import JavaExplorerPanel
            self.java_explorer_panel = JavaExplorerPanel(self)
            self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger')
            self.main_tabs.tabBar().moveTab(
                self.main_tabs.indexOf(self.java_explorer_panel), 1)
            elem_wiget = self.java_explorer_panel
        elif elem == 'jvm-inspector':
            from ui.panels.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
            elem_wiget = self.java_inspector_panel
        elif elem == 'console':
            from ui.session_widgets.console import ConsoleWidget
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsoleWidget(self)
            if self.dwarf_args.script and len(
                    self.dwarf_args.script) > 0 and os.path.exists(
                        self.dwarf_args.script):
                with open(self.dwarf_args.script, 'r') as f:
                    self.console_panel.get_js_console(
                    ).script_file = self.dwarf_args.script
                    self.console_panel.get_js_console(
                    ).function_content = f.read()
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.dwarf.onLogEvent.connect(self._log_event)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsoleWidget')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
            elem_wiget = self.console_panel
        elif elem == 'backtrace':
            from ui.session_widgets.backtrace import BacktraceWidget
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktraceWidget(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktraceWidget')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_showmemory_request)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
            elem_wiget = self.backtrace_panel
        elif elem == 'threads':
            from ui.session_widgets.threads import ThreadsWidget
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ThreadsWidget(self)
            self.dwarf.onThreadResumed.connect(
                self.contexts_list_panel.resume_tid)
            self.contexts_list_panel.onItemDoubleClicked.connect(
                self._manually_apply_context)
            self.threads_dock.setWidget(self.contexts_list_panel)
            self.threads_dock.setObjectName('ThreadPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock)
            self.view_menu.addAction(self.threads_dock.toggleViewAction())
            elem_wiget = self.contexts_list_panel
        elif elem == 'modules':
            from ui.panels.panel_modules import ModulesPanel
            self.modules_panel = ModulesPanel(self)
            self.modules_panel.onModuleSelected.connect(
                self._on_module_dblclicked)
            self.modules_panel.onModuleFuncSelected.connect(
                self._on_modulefunc_dblclicked)
            self.modules_panel.onAddHook.connect(self._on_addmodule_hook)
            self.modules_panel.onDumpBinary.connect(self._on_dumpmodule)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
            elem_wiget = self.modules_panel
        elif elem == 'ranges':
            from ui.panels.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule)
            # connect to watcherpanel func
            self.ranges_panel.onAddWatcher.connect(
                self.watchers_panel.do_addwatcher_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
            elem_wiget = self.ranges_panel
        elif elem == 'search':
            from ui.panels.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.main_tabs.addTab(self.search_panel, 'Search')
            elem_wiget = self.search_panel
        elif elem == 'data':
            from ui.panels.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
            elem_wiget = self.data_panel
        elif elem == 'jvm-tracer':
            from ui.panels.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
            elem_wiget = self.java_trace_panel
        elif elem == 'smali':
            from ui.panels.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
            elem_wiget = self.smali_panel
        else:
            print('no handler for elem: ' + elem)

        # make tabs unclosable and sort
        self._handle_tab_change()

        if elem_wiget is not None:
            self.onSystemUIElementCreated.emit(elem, elem_wiget)

        # TODO: remove add @2x
        for item in self.findChildren(QDockWidget):
            if item:
                if 'darwin' in sys.platform:
                    item.setStyleSheet(
                        'QDockWidget::title { padding-left:-30px; }')

    def set_status_text(self, txt):
        self.statusbar.showMessage(txt)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def disassembly(self):
        return self.asm_panel

    @property
    def backtrace(self):
        return self.backtrace_panel

    @property
    def console(self):
        return self.console_panel

    @property
    def context(self):
        return self.context_panel

    @property
    def threads(self):
        return self.contexts_list_panel

    @property
    def ftrace(self):
        return self.ftrace_panel

    @property
    def hooks(self):
        return self.hooks_panel

    @property
    def java_inspector(self):
        return self.java_inspector_panel

    @property
    def java_explorer(self):
        return self.java_explorer_panel

    @property
    def modules(self):
        return self.modules_panel

    @property
    def ranges(self):
        return self.ranges_panel

    @property
    def watchers(self):
        return self.watchers_panel

    @property
    def dwarf(self):
        if self.session_manager.session is not None:
            return self.session_manager.session.dwarf
        else:
            return None

    @property
    def ui_elements(self):
        return self._ui_elems

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    # session handlers
    def _start_session(self, session_type, session_data=None):
        if self.welcome_window is not None:
            self.welcome_window.close()
        self.session_manager.create_session(session_type,
                                            session_data=session_data)

    def _restore_session(self, session_data):
        if 'session' in session_data:
            session_type = session_data['session']
            self.dwarf_args.any = session_data['package']
            self._start_session(session_type, session_data=session_data)

    def session_created(self):
        # session init done create ui for it
        session = self.session_manager.session
        self._setup_main_menu()
        for ui_elem in session.session_ui_sections:
            ui_elem = ui_elem.join(ui_elem.split()).lower()
            self._create_ui_elem(ui_elem)

        self.dwarf.onProcessAttached.connect(self._on_attached)
        self.dwarf.onProcessDetached.connect(self._on_detached)
        self.dwarf.onScriptLoaded.connect(self._on_script_loaded)

        # hookup
        self.dwarf.onSetRanges.connect(self._on_setranges)
        self.dwarf.onSetModules.connect(self._on_setmodules)

        self.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)

        self.dwarf.onSetData.connect(self._on_set_data)

        self.session_manager.start_session(self.dwarf_args)
        ui_state = self.q_settings.value('dwarf_ui_state')
        if ui_state:
            self.restoreGeometry(ui_state)
        window_state = self.q_settings.value('dwarf_ui_window',
                                             self.saveState())
        if window_state:
            self.restoreState(window_state)

        self.showMaximized()

    def session_stopped(self):
        self.remove_tmp_dir()
        self.menu.clear()

        self.main_tabs.clear()

        # actually we need to kill this. needs a refactor
        if self.java_trace_panel is not None:
            self.java_trace_panel = None

        for elem in self._ui_elems:
            if elem == 'watchers':
                self.watchers_panel.clear_list()
                self.watchers_panel.close()
                self.watchers_panel = None
                self.removeDockWidget(self.watchers_dwidget)
                self.watchers_dwidget = None
            elif elem == 'hooks':
                self.hooks_panel.close()
                self.hooks_panel = None
                self.removeDockWidget(self.hooks_dwiget)
                self.hooks_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'debug':
                self.debug_panel.close()
                self.debug_panel = None
                self.main_tabs.removeTab(0)
                # self.main_tabs
            elif elem == 'jvm-debugger':
                self.java_explorer_panel.close()
                self.java_explorer_panel = None
                self.removeDockWidget(self.watchers_dwidget)
            elif elem == 'console':
                self.console_panel.close()
                self.console_panel = None
                self.removeDockWidget(self.console_dock)
                self.console_dock = None
            elif elem == 'backtrace':
                self.backtrace_panel.close()
                self.backtrace_panel = None
                self.removeDockWidget(self.backtrace_dock)
            elif elem == 'threads':
                self.contexts_list_panel.close()
                self.contexts_list_panel = None
                self.removeDockWidget(self.threads_dock)
                self.threads_dock = None
            elif elem == 'bookmarks':
                self.bookmarks_panel.close()
                self.bookmarks_panel = None
                self.removeDockWidget(self.bookmarks_dwiget)
                self.bookmarks_dwiget = None

        self._initialize_ui_elements()

    def session_closed(self):
        self._initialize_ui_elements()
        self.hide()
        if self.welcome_window is not None:
            self.welcome_window.exec()

        # close if it was a commandline session
        if self.welcome_window is None:
            if self.dwarf_args.any != '':
                self.close()

    # ui handler
    def closeEvent(self, event):
        """ Window closed
            save stuff or whatever at exit

            detaches dwarf
        """
        # save windowstuff
        self.q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        self.q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            try:
                self.dwarf.detach()
            except:
                pass
        super().closeEvent(event)

    def _on_watcher_clicked(self, ptr):
        """ Address in Watcher/Hookpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_hook
            file_path = ptr.replace('.', os.path.sep)
            if os.path.exists('.tmp/smali/' + file_path + '.smali'):
                if self.smali_panel is None:
                    self._create_ui_elem('smali')
                self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali')
                self.show_main_tab('smali')
        else:
            self.jump_to_address(ptr)

    def _on_watcher_added(self, ptr):
        """ Watcher Entry was added
        """
        try:
            # set highlight
            self.debug_panel.memory_panel.add_highlight(
                HighLight('watcher', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watcher_removeditem(self, ptr):
        """ Watcher Entry was removed
            remove highlight too
        """
        self.debug_panel.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        self.jump_to_address(addr)

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    def _on_dumpmodule(self, data):
        """ DumpBinary MenuItem in ModulePanel was selected
        """
        ptr, size = data
        ptr = utils.parse_ptr(ptr)
        size = int(size, 10)
        self.dwarf.dump_memory(ptr=ptr, length=size)

    def _disassemble_range(self, dwarf_range):
        """ Disassemble MenuItem in Hexview was selected
        """
        if dwarf_range:
            if self.asm_panel is None:
                self._create_ui_elem('disassembly')

            self.asm_panel.disassemble(dwarf_range)
            self.show_main_tab('disassembly')

    def _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            time_prefix = True
            if len(output.split('\n')) > 1 or len(output.split('<br />')) > 1:
                time_prefix = False
            self.console_panel.get_js_console().log(output,
                                                    time_prefix=time_prefix)

    def _log_event(self, output):
        if self.console_panel is not None:
            self.console_panel.get_events_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.ranges_panel is None:
            self.show_main_tab('ranges')
            # forward only now to panel it connects after creation
            self.ranges_panel.set_ranges(ranges)

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.modules_panel is None:
            self._create_ui_elem('modules')
            self.modules_panel.set_modules(modules)

        if self.modules_panel is not None:
            self.show_main_tab('modules')

    def _manually_apply_context(self, context):
        """
        perform additional operation if the context has been manually applied from the context list
        """
        self._apply_context(context, manual=True)

    def _apply_context(self, context, manual=False):
        # update current context tid
        # this should be on top as any further api from js needs to be executed on that thread
        reason = context['reason']
        is_initial_setup = reason == -1
        if manual or (self.dwarf.context_tid and not is_initial_setup):
            self.dwarf.context_tid = context['tid']

        if is_initial_setup:
            self.debug_panel.on_context_setup()

        if 'context' in context:
            if not manual:
                self.threads.add_context(context)

            is_java = context['is_java']
            if is_java:
                if self.java_explorer_panel is None:
                    self._create_ui_elem('jvm-debugger')
                self.context_panel.set_context(context['ptr'], 1,
                                               context['context'])
                self.java_explorer_panel._set_handle_arg(-1)
                self.show_main_tab('jvm-debugger')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

                if reason == 2:
                    # native on load
                    if self.debug_panel.memory_panel_range is None:
                        base = context['moduleBase']
                        self.jump_to_address(base)
                else:
                    if 'pc' in context['context']:
                        if self.debug_panel.disassembly_panel_range is None or manual:
                            self.jump_to_address(
                                context['context']['pc']['value'], view=1)

        if 'backtrace' in context:
            self.backtrace_panel.set_backtrace(context['backtrace'])

    def _on_add_hook(self, hook):
        try:
            # set highlight
            ptr = hook.get_ptr()
            ptr = utils.parse_ptr(ptr)
            self.debug_panel.memory_panel.add_highlight(
                HighLight('hook', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_hook_removed(self, ptr):
        ptr = utils.parse_ptr(ptr)
        self.debug_panel.memory_panel.remove_highlight(ptr)

    def _on_addmodule_hook(self, data):
        ptr, name = data
        self.dwarf.hook_native(ptr, own_input=name)

    def on_tid_resumed(self, tid):
        if self.dwarf:
            if self.dwarf.context_tid == tid:
                # clear backtrace
                if 'backtrace' in self._ui_elems:
                    if self.backtrace_panel is not None:
                        self.backtrace_panel.clear()

                # remove thread
                if 'threads' in self._ui_elems:
                    if self.contexts_list_panel is not None:
                        self.contexts_list_panel.resume_tid(tid)

                # clear registers
                if 'registers' in self._ui_elems:
                    if self.context_panel is not None:
                        self.context_panel.clear()

                # clear jvm explorer
                if 'jvm-debugger' in self._ui_elems:
                    if self.java_explorer_panel is not None:
                        self.java_explorer_panel.clear_panel()

                # invalidate dwarf context tid
                self.dwarf.context_tid = 0

    def _on_set_data(self, data):
        if not isinstance(data, list):
            return

        if self.data_panel is None:
            self.show_main_tab('data')

        if self.data_panel is not None:
            self.data_panel.append_data(data[0], data[1], data[2])

    def show_progress(self, text):
        self.progressbar.setVisible(True)
        self.set_status_text(text)

    def hide_progress(self):
        self.progressbar.setVisible(False)
        self.set_status_text('')

    def _on_attached(self, data):
        self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0]))

    def _on_detached(self, data):
        reason = data[1]

        if reason == 'application-requested':
            self.session_manager.session.stop()
            return 0

        if self.dwarf is not None:
            ret = QDialogDetached.show_dialog(self.dwarf, data[0], data[1],
                                              data[2])
            if ret == 0:
                self.dwarf.restart_proc()
            elif ret == 1:
                self.session_manager.session.stop()

        return 0

    def _on_script_loaded(self):
        # restore the loaded session if any
        self.session_manager.restore_session()

    def on_add_bookmark(self, ptr):
        """
        provide ptr as int
        """
        if self.bookmarks_panel is not None:
            self.bookmarks_panel._create_bookmark(ptr=hex(ptr))

    def _on_showmemory_request(self, ptr):
        # its simple ptr show in memorypanel
        if isinstance(ptr, str):
            ptr = utils.parse_ptr(ptr)
            self.jump_to_address(ptr, 0)

        elif isinstance(ptr, list):
            # TODO: extend
            caller, ptr = ptr
            ptr = utils.parse_ptr(ptr)
            if caller == 'backtrace' or caller == 'bt':
                # jumpto in disasm
                self.jump_to_address(ptr, 1)
コード例 #43
0
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()
コード例 #44
0
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:
コード例 #45
0
class MainWindow(QMainWindow):
    def __init__(self, os_path, ip, port):
        super(MainWindow, self).__init__()

        self.os_path = os_path
        self.config_path = os_path.split('ics_sps_engineering_plotData')[
                               0] + 'ics_sps_engineering_Lib_dataQuery/config/'
        self.path_img = self.os_path + "/img/"

        self.readCfg(self.config_path)
        self.db = DatabaseManager(ip, port)
        no_err = self.db.initDatabase()
        if no_err != -1:
            self.getIcons()
            self.getWidgets()
        else:
            self.showError(no_err)

        self.width = 1024
        self.height = 768
        self.center = [300, 300]
        self.title = "AIT-PFS Monitoring CU"
        self.resize(self.width, self.height)
        self.move(self.center[0], self.center[1])
        self.setWindowTitle(self.title)
        self.showMaximized()
        self.show()

    def getIcons(self):

        arrow_left = QPixmap()
        arrow_right = QPixmap()
        arrow_left.load(self.path_img + 'arrow_left.png')
        arrow_right.load(self.path_img + 'arrow_right.png')
        self.icon_arrow_left = QIcon(arrow_left)
        self.icon_arrow_right = QIcon(arrow_right)
        icon_math = QPixmap()
        icon_math.load(self.path_img + 'xy2.png')
        icon_math_on = QPixmap()
        icon_math_on.load(self.path_img + 'xy2_on.png')
        self.icon_vcursor = QIcon(icon_math)
        self.icon_vcursor_on = QIcon(icon_math_on)
        icon_fit = QPixmap()
        icon_fit.load(self.path_img + 'infini.png')
        self.icon_fit = QIcon(icon_fit)

    def getWidgets(self):

        self.tab_widget = QTabWidget()
        self.tab_widget.tabCloseRequested.connect(self.delTab)
        self.tab_widget.setTabsClosable(True)
        self.getdockCalendar()
        self.getdockAlarm()
        self.getMenu()
        self.addDockWidget(Qt.TopDockWidgetArea, self.qdockalarm)
        self.setCentralWidget(self.tab_widget)

    def getMenu(self):

        self.menubar = self.menuBar()
        self.database_action = QAction('Database', self)
        self.curves_action = QAction('Update Configuration', self)
        self.new_tab_action = QAction('Open a new tab', self)
        self.about_action = QAction('About', self)

        self.curves_action.triggered.connect(self.setNewConfig)
        self.new_tab_action.triggered.connect(self.addTab)
        self.database_action.triggered.connect(self.calendar.show)
        self.about_action.triggered.connect(
            partial(self.showInformation, "PlotData v0.6 working with lib_DataQuery v0.6\n\r made for PFS by ALF"))

        self.WindowsMenu = self.menubar.addMenu('&Windows')
        self.WindowsMenu.addAction(self.new_tab_action)
        self.configurationMenu = self.menubar.addMenu('&Configuration')
        self.configurationMenu.addAction(self.database_action)
        self.configurationMenu.addAction(self.curves_action)
        self.helpMenu = self.menubar.addMenu('&?')
        self.helpMenu.addAction(self.about_action)

    def getdockCalendar(self):

        self.calendar = Calendar(self)

    def getdockAlarm(self):
        self.qdockalarm_widget = alarmChecker(parent=self)
        self.qdockalarm = myQDockWidget()

        self.qdockalarm.setWidget(self.qdockalarm_widget)

    def readCfg(self, path, last=True):
        res = []
        all_file = next(os.walk(path))[-1]
        for f in all_file:
            config = ConfigParser.ConfigParser()
            config.readfp(open(path + f))
            try:
                date = config.get('config_date', 'date')
                res.append((f, dt.datetime.strptime(date, "%d/%m/%Y")))
            except ConfigParser.NoSectionError:
                pass
        config = ConfigParser.ConfigParser()
        if last:
            res.sort(key=lambda tup: tup[1])
            config.readfp(open(path + res[-1][0]))
        else:
            res2 = []
            for f, datetime in res:
                if self.calendar.mydatetime > datetime:
                    res2.append((f, self.calendar.mydatetime - datetime))
            if res2:
                res2.sort(key=lambda tup: tup[1])
                config.readfp(open(path + res2[0][0]))
            else:
                res.sort(key=lambda tup: tup[1])
                config.readfp(open(path + res[0][0]))

        self.device_dict = {}
        for a in config.sections():
            if a != 'config_date':
                inter = {}
                for b in config.options(a):
                    if b == "label_device":
                        self.device_dict[a] = {"label_device": config.get(a, b)}
                    else:
                        inter[b] = config.get(a, b).split(',')
                        inter[b] = self.cleanSpace(inter[b])

                for keys, types, labels, units, ylabels in zip(inter["key"], inter["type"], inter["label"],
                                                               inter["unit"],
                                                               inter["ylabel"]):
                    self.device_dict[a][keys] = {}
                    self.device_dict[a][keys]["type"] = types
                    self.device_dict[a][keys]["label"] = labels
                    self.device_dict[a][keys]["unit"] = units
                    self.device_dict[a][keys]["ylabel"] = ylabels

    def setNewConfig(self):
        self.readCfg(self.config_path)
        self.qdockalarm_widget.getTimeout()
        self.showInformation("New configuration loaded")

    def addTab(self):

        text, ok = QInputDialog.getText(self, 'Name your tab', 'Name')
        if ok:
            name = str(text)
            widget = Tab(self)
            self.tab_widget.addTab(widget, name)
            self.tab_widget.setCurrentWidget(widget)

    def delTab(self, k):
        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to close this window?", QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.tab_widget.removeTab(k)

    def showError(self, nb_error):
        error_code = {-1: "The database is unreachable, check your network and your configuration",
                      -2: "They're not such columns / rows in your database", -3: "Bad format date",
                      -4: "No data to display", -5: "network lost"}
        reply = QMessageBox.critical(self, 'Message', error_code[nb_error], QMessageBox.Ok)

    def showInformation(self, information):
        reply = QMessageBox.information(self, 'Message', information, QMessageBox.Ok)

    def getNumdate(self):
        return self.calendar.mydate_num

    def cleanSpace(self, tab):
        for i, s in enumerate(tab):
            if tab[i][0] == ' ':
                tab[i] = tab[i][1:]
            if tab[i][-1] == ' ':
                tab[i] = tab[i][:-1]

        return tab
コード例 #46
0
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])
コード例 #47
0
ファイル: pineboo.py プロジェクト: juanjosepablos/pineboo
class MainForm(QMainWindow):
    areas = []
    toolBoxs = []
    tab = 0
    ui_ = None
    debugLevel = 100
    mPAreas = {}  # Almacena los nombre de submenus areas de menú pineboo
    mPModulos = {}  # Almacena los nombre de submenus modulos de menú pineboo
    openTabs = []
    favoritosW = None
    wid = None  # widget principal

    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        self.ui_ = None

    @classmethod
    def setDebugLevel(self, q):
        MainForm.debugLevel = q

    def load(self):

        self.ui_ = pineboolib.project.conn.managerModules().createUI(
            filedir('plugins/mainform/pineboo/mainform.ui'), None, self)

        self.w_ = self
        frameGm = self.frameGeometry()
        screen = QApplication.desktop().screenNumber(
            QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        frameGm.moveCenter(centerPoint)
        self.move(frameGm.topLeft())

        self.areasTab = QTabWidget()
        self.areasTab.setTabPosition(QTabWidget.West)
        self.formTab = QTabWidget()
        try:
            self.areasTab.removeItem = self.areasTab.removeTab
            self.areasTab.addItem = self.areasTab.addTab
        except Exception:
            pass

        self.dockAreasTab = QDockWidget()
        self.dockAreasTab.setWindowTitle("Módulos")
        #self.dockAreas = QtWidgets.QDockWidget()
        self.dockFavoritos = QDockWidget()
        self.dockFavoritos.setWindowTitle("Favoritos")

        self.dockForm = QDockWidget()

        self.dockConsole = None

        self.dockAreasTab.setWidget(self.areasTab)
        self.dockAreasTab.setMaximumWidth(400)
        self.dockFavoritos.setMaximumWidth(400)
        self.dockFavoritos.setMaximumHeight(500)
        # self.dockAreasTab.setMinimumWidth(400)
        # self.dockAreasTab.setMaximumHeight(500)

        self.dockForm.setWidget(self.formTab)

        self.addDockWidget(Qt.RightDockWidgetArea, self.dockForm)
        # self.dockForm.setMaximumWidth(950)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dockFavoritos)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dockAreasTab)
        # self.dockAreasTab.show()
        # self.dockForm.show()

        # self.areasTab.removeItem(0) #Borramos tab de ejemplo.

        self.formTab.setTabsClosable(True)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.formTab.tabCloseRequested[int].connect(self.closeFormTab)
        self.formTab.removeTab(0)
        #app_icon = QtGui.QIcon('share/icons/pineboo-logo-16.png')
        # app_icon.addFile(filedir('share/icons/pineboo-logo-16.png'),
        #                 QtCore.QSize(16, 16))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-24.png'),
        #                 QtCore.QSize(24, 24))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-32.png'),
        #                 QtCore.QSize(32, 32))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-48.png'),
        #                 QtCore.QSize(48, 48))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-64.png'),
        #                 QtCore.QSize(64, 64))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-128.png'),
        #                 QtCore.QSize(128, 128))
        # app_icon.addFile(filedir('share/icons/pineboo-logo-256.png'),
        #                 QtCore.QSize(256, 256))
        # self.setWindowIcon(app_icon)
        self.setWindowIcon(QtGui.QIcon('share/icons/pineboo-logo-16.png'))
        self.actionAcercaQt.triggered.connect(pineboolib.project.aboutQt)
        self.actionAcercaPineboo.triggered.connect(pineboolib.project.aboutPineboo)
        self.actionFavoritos.triggered.connect(self.changeStateDockFavoritos)
        self.dockFavoritos.visibilityChanged.connect(self.changeStateActionFavoritos)
        self.actionModulos.triggered.connect(self.changeStateDockAreas)
        self.dockAreasTab.visibilityChanged.connect(self.changeStateActionAreas)
        self.actionTipografia.triggered.connect(pineboolib.project.chooseFont)
        self.menuPineboo.addSeparator()
        # self.actionEstilo.triggered.connect(pineboolib.main.styleDialog)
        # pineboolib.pnapplication.initStyle(self.configMenu)
        self.setWindowTitle("Pineboo")

        logger.info("Módulos y pestañas ...")
        for k, area in sorted(pineboolib.project.areas.items()):
            self.loadArea(area)
        for k, module in sorted(pineboolib.project.modules.items()):
            self.loadModule(module)

        # Cargando Area desarrollo si procede ...
        sett_ = FLSettings()
        if (sett_.readBoolEntry("application/isDebuggerMode", False)):
            areaDevelop = Struct(idarea="dvl", descripcion="Desarrollo")
            self.loadArea(areaDevelop)

            self.loadDevelop()

        self.restoreOpenedTabs()

        self.loadState()
        # Cargamos nombre de vertical
        util = FLUtil()
        verticalName = util.sqlSelect("flsettings", "valor", "flkey='verticalName'")
        cbPosInfo = util.sqlSelect("flsettings", "valor", "flkey='PosInfo'")

        statusText = ""

        if verticalName != None:
            statusText = verticalName

        if cbPosInfo == 'True':
            from pineboolib.pncontrolsfactory import SysType
            sys_ = SysType()
            statusText += "\t\t\t" + sys_.nameUser() + "@" + sys_.nameBD()

        self.statusBar().showMessage(statusText)

    def closeFormTab(self, numero):
        if isinstance(numero, str):
            i = 0
            name = numero
            numero = None
            for n in self.openTabs:
                if name == n:
                    numero = i
                    break
                i = i + 1

        if numero is not None:
            logger.debug("Cerrando pestaña número %s ", numero)
            self.formTab.removeTab(numero)

            i = 0
            for name in self.openTabs:
                if i == numero:
                    self.openTabs.remove(name)
                    break
                i = i + 1

    def addFormTab(self, action):
        widget = action.mainform_widget
        if action.name in self.openTabs:
            self.closeFormTab(action.name)
        logger.debug("Añadiendo Form a pestaña %s", action)
        icon = None
        try:
            icon = action.mod.mod.mainform.actions[action.name].icon
            self.formTab.addTab(widget, icon, widget.windowTitle())
        except Exception as e:
            logger.warning("addFormTab: No pude localizar icono para %s: %s", action.name, e)
            self.formTab.addTab(widget, widget.windowTitle())

        self.formTab.setCurrentWidget(widget)
        self.openTabs.append(action.name)

    def loadArea(self, area):
        assert area.idarea not in self.areas
        vl = QWidget()
        vl.layout = QVBoxLayout()  # layout de la pestaña
        vl.layout.setSpacing(0)
        vl.layout.setContentsMargins(0, 0, 0, 0)
        vl.layout.setSizeConstraint(QLayout.SetMinAndMaxSize)

        moduleToolBox = QToolBox(self)  # toolbox de cada módulo

        self.areas.append(area.idarea)
        self.toolBoxs.append(moduleToolBox)
        self.tab = self.tab + 1
        vl.setLayout(vl.layout)
        vl.layout.addWidget(moduleToolBox)
        self.areasTab.addItem(vl, area.descripcion)

    def loadModule(self, module):
        logger.debug("loadModule: Procesando %s ", module.name)
        # Creamos pestañas de areas y un vBLayout por cada módulo. Despues ahí metemos los actions de cada módulo
        if module.areaid not in self.areas:
            self.loadArea(Struct(idarea=module.areaid,
                                 descripcion=module.areaid))

        moduleToolBox = self.toolBoxs[self.areas.index(module.areaid)]

        vBLayout = QWidget()
        vBLayout.layout = QVBoxLayout()  # layout de cada módulo.
        vBLayout.layout.setSizeConstraint(QLayout.SetMinAndMaxSize)

        vBLayout.layout.setSpacing(0)
        vBLayout.layout.setContentsMargins(0, 0, 0, 0)

        vBLayout.setLayout(vBLayout.layout)
        if module.icon[0] != "":
            pixmap = QtGui.QPixmap(module.icon)
            moduleToolBox.addItem(vBLayout, QtGui.QIcon(pixmap), module.description)
        else:
            moduleToolBox.addItem(vBLayout, module.description)

        try:
            self.moduleLoad(vBLayout.layout, module)
        except Exception:
            logger.exception("ERROR al procesar modulo %s", module.name)

    def moduleLoad(self, vBLayout, module):
        if not module.loaded:
            module.load()
        if not module.loaded:
            logger.warning("moduleLoad: Ignorando modulo %s por fallo al cargar", module.name)
            return False
        logger.trace("moduleLoad: Running module %s . . . ", module.name)
        iconsize = QtCore.QSize(22, 22)
        iconsize = QtCore.QSize(16, 16)
        vBLayout.setSpacing(0)
        vBLayout.setContentsMargins(0, 0, 0, 0)
        for key in module.mainform.toolbar:
            action = module.mainform.actions[key]
            button = QToolButton()
            button.setText(action.text)
            button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            button.setIconSize(iconsize)
            button.setAutoRaise(True)
            if action.icon:
                button.setIcon(action.icon)
            button.clicked.connect(action.run)
            vBLayout.addWidget(button)
            self.addToMenuPineboo(action, module)
        vBLayout.addStretch()

    def closeEvent(self, evnt):

        res = QMessageBox.information(
            QApplication.activeWindow(),
            "Salir de Pineboo",
            "¿ Desea salir ?",
            QMessageBox.Yes, QMessageBox.No)

        if res == QMessageBox.No:
            evnt.ignore()

        self.saveState()

    def saveState(self):
        if self:
            sett_ = FLSettings()
            sett_.writeEntryList("application/mainForm/tabsOpened", self.openTabs)
            sett_.writeEntry("application/mainForm/viewFavorites", self.dockFavoritos.isVisible())
            sett_.writeEntry("application/mainForm/FavoritesSize", self.dockFavoritos.size())
            sett_.writeEntry("application/mainForm/viewAreas", self.dockAreasTab.isVisible())
            sett_.writeEntry("application/mainForm/AreasSize", self.dockFavoritos.size())
            sett_.writeEntry("application/mainForm/mainFormSize", self.size())

    def addToMenuPineboo(self, ac, mod):
        #print(mod.name, ac.name, pineboolib.project.areas[mod.areaid].descripcion)
        # Comprueba si el area ya se ha creado
        if mod.areaid not in self.mPAreas.keys():
            areaM = self.menuPineboo.addMenu(QtGui.QIcon('share/icons/gtk-open.png'),
                                             pineboolib.project.areas[mod.areaid].descripcion)
            self.mPAreas[mod.areaid] = areaM
        else:
            areaM = self.mPAreas[mod.areaid]

        # Comprueba si el modulo ya se ha creado
        if mod.name not in self.mPModulos.keys():
            pixmap = None
            if mod.icon[0] != "":
                pixmap = QtGui.QPixmap(mod.icon)
            if pixmap:
                moduloM = areaM.addMenu(QtGui.QIcon(pixmap), mod.description)
            else:
                moduloM = areaM.addMenu(mod.description)

            self.mPModulos[mod.name] = moduloM
        else:
            moduloM = self.mPModulos[mod.name]

        action_ = moduloM.addAction(ac.icon, ac.text)
        action_.triggered.connect(ac.run)

    def restoreOpenedTabs(self):
        # Cargamos pestañas abiertas
        sett_ = FLSettings()
        tabsOpened_ = sett_.readListEntry("application/mainForm/tabsOpened")
        if tabsOpened_:
            for t in tabsOpened_:
                for k, module in sorted(pineboolib.project.modules.items()):
                    if hasattr(module, "mainform"):
                        if t in module.mainform.actions:
                            module.mainform.actions[t].run()
                            break

    def loadState(self):
        sett_ = FLSettings()
        viewFavorites_ = sett_.readBoolEntry("application/mainForm/viewFavorites", True)
        viewAreas_ = sett_.readBoolEntry("application/mainForm/viewAreas", True)
        sizeF_ = sett_.readEntry("application/mainForm/FavoritesSize", None)
        sizeA_ = sett_.readEntry("application/mainForm/AreasSize", None)
        sizeMF_ = sett_.readEntry("application/mainForm/mainFormSize", None)
        if sizeF_ is not None:
            self.dockFavoritos.resize(sizeF_)

        if sizeA_ is not None:
            self.dockAreasTab.resize(sizeA_)

        if sizeMF_ is not None:
            self.resize(sizeMF_)
        else:
            self.showMaximized()

        """
        self.dockFavoritos.setVisible(viewFavorites_)
        self.actionFavoritos.setChecked(viewFavorites_)
        self.dockAreasTab.setVisible(viewAreas_)
        self.actionModulos.setChecked(viewAreas_)
        """

    def changeStateDockFavoritos(self):
        visible_ = self.actionFavoritos.isChecked()
        if visible_:
            sett_ = FLSettings()
            sizeF_ = sett_.readEntry("application/mainForm/FavoritesSize", None)
            if sizeF_ is not None:
                self.dockFavoritos.resize(sizeF_)

        self.dockFavoritos.setVisible(visible_)

    def changeStateActionFavoritos(self):
        if self.dockFavoritos.isVisible():
            self.actionFavoritos.setChecked(True)
        else:
            self.actionFavoritos.setChecked(False)

    def changeStateDockAreas(self):
        visible_ = self.actionModulos.isChecked()
        if visible_:
            sett_ = FLSettings()
            sizeA_ = sett_.readEntry("application/mainForm/AreasSize", None)
            if sizeA_ is not None:
                self.dockAreasTab.resize(sizeA_)
        self.dockAreasTab.setVisible(visible_)

    def changeStateActionAreas(self):
        if self.dockAreasTab.isVisible():
            self.actionModulos.setChecked(True)
        else:
            self.actionModulos.setChecked(False)

    def loadDevelop(self):
        moduleToolBox = self.toolBoxs[self.areas.index("dvl")]
        vBLayout = QWidget()
        vBLayout.layout = QVBoxLayout()  # layout de cada módulo.
        vBLayout.layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        vBLayout.layout.setSpacing(0)
        vBLayout.layout.setContentsMargins(0, 0, 0, 0)
        vBLayout.setLayout(vBLayout.layout)

        button = QToolButton()
        button.setText("Consola")
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        iconsize = QtCore.QSize(16, 16)
        button.setIconSize(iconsize)
        button.setAutoRaise(True)
        button.setIcon(QtGui.QIcon('share/icons/terminal.png'))
        button.clicked.connect(self.showConsole)
        vBLayout.layout.addWidget(button)
        moduleToolBox.addItem(vBLayout, "Desarrollo")

        #self.addToMenuPineboo(action, module)

    def showConsole(self):
        if not self.dockConsole:
            self.dockConsole = QDockWidget()
            self.dockConsole.setWindowTitle("Consola")
            self.addDockWidget(Qt.BottomDockWidgetArea, self.dockConsole)
            self.teo_ = OutputWindow()
            self.dockConsole.setWidget(self.teo_)

        self.dockConsole.setVisible(True)