Exemple #1
0
class MyFrame(wx.Frame):
    def __init__(self, parent, title, db, defaultPrefs, capabilities):
        wx.Frame.__init__(self,
                          parent,
                          -1,
                          title,
                          style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR
                          | wx.CLIP_CHILDREN)

        self.db = db
        self.capabilities = capabilities
        self.data = []

        self.options = None
        self.about = None

        self.prefs = Prefs(self.db, defaultPrefs)

        # Don't allow the user to shrink the window below these dimensions
        self.minYSize = 30
        self.minXSize = 50

        # This is the main graph
        self.panel = wx.Panel(self, style=wx.BORDER_SIMPLE)
        self.panel.Bind(wx.EVT_MOTION, self.OnPanelMove)
        self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnPanelDown)
        self.panel.Bind(wx.EVT_LEFT_UP, self.OnPanelUp)
        self.panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)

        # This is the label below the graph showing numeric values
        self.label = wx.StaticText(self,
                                   -1,
                                   "-",
                                   style=wx.ST_NO_AUTORESIZE | wx.BORDER_SIMPLE
                                   | wx.ALIGN_CENTER)
        self.label.Bind(wx.EVT_LEFT_DOWN, self.OnLabelDown)
        self.Bind(wx.EVT_MOTION, self.OnLabelMove)
        self.Bind(wx.EVT_LEFT_UP, self.OnLabelUp)
        self.label.SetCursor(wx.Cursor(wx.CURSOR_SIZENWSE))
        self.label.SetFont(
            wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_NORMAL))

        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(self.panel, 1, flag=wx.EXPAND)
        box.Add((10, 1), 0)
        box.Add(self.label, 0, flag=wx.EXPAND)

        # Restore the position of the graph from last time
        self.SetPosition(self.prefs.GetObj('position'))

        # If you remove a monitor or replace with a lower resolution one, it is possible
        # that the preferences specify an x,y position that is off screen
        # This rough code will reset the position assuming that
        #   - the monitors are placed left to right (not on top of each other)
        #   - the rightmost monitor has the least resolution
        #   - bitmeter-desktop client is on the rightmost screen normally
        displays = (wx.Display(i) for i in range(wx.Display.GetCount()))
        maxX = 0
        maxY = 10000
        for v in displays:
            maxX += v.GetGeometry().GetSize().width
            if maxY > v.GetGeometry().GetSize().height:
                maxY = v.GetGeometry().GetSize().height

        PROPORTION = 4  # Is allowed to be a little off screen
        resetPosition = False
        farRight = self.GetPosition().x + self.GetSize().width / PROPORTION
        if farRight > maxX:
            resetPosition = True
            print('Reset position due to out of bounds x position')
        farDown = self.GetPosition().y + self.GetSize().height / PROPORTION
        if farDown > maxY:
            resetPosition = True
            print('Reset position due to out of bounds y position')

        if resetPosition:
            self.SetPosition((100, 100))

        self.OnPrefsUpdated()

        self.SetSizer(box)
        self.Fit()
        self.SetSize(self.prefs.GetObj('size'))

        # We update the graph each second with new data
        self.timer = wx.Timer(self)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)

        self.InitBuffer()
        self.Bind(wx.EVT_IDLE, self.OnIdle)

        # Find the file-system location where we are running from
        encoding = sys.getfilesystemencoding()
        if hasattr(sys, "frozen"):
            self.modulePath = os.path.dirname(sys.executable)
        else:
            self.modulePath = os.path.dirname(__file__)

        iconPath = os.path.join(self.modulePath, "resources", "bitmeter.ico")
        icon = wx.Icon(iconPath, wx.BITMAP_TYPE_ICO)

        # The menu can be accessed from the main graph, and from the tray icon
        self.popupmenu = wx.Menu()
        self.trayIcon = TrayIcon(self, self.popupmenu, icon)

        # Hide Graph option removed - no tray icon shown on Ubuntu 18.04, not relevant
        # self.showHideMain = self.popupmenu.Append(-1, _("Hide Graph"))
        # self.Bind(wx.EVT_MENU, self.ToggleGraph)
        # self.trayIcon.Bind(wx.EVT_MENU, self.ToggleGraph)

        # Menu item to open the Options dialog
        options = self.popupmenu.Append(-1, _("Options"))
        self.Bind(wx.EVT_MENU, self.OnMenuOptions, options)
        self.trayIcon.Bind(wx.EVT_MENU, self.OnMenuOptions, options)

        # Menu item to open the Web Interface
        webInterface = self.popupmenu.Append(-1, _("Web Interface"))
        self.Bind(wx.EVT_MENU, self.OnMenuWebInterface, webInterface)
        self.trayIcon.Bind(wx.EVT_MENU, self.OnMenuWebInterface, webInterface)

        # Need this to build the web interface url
        self.webPort = self.db.GetConfigValue('web.port', 2605)

        # Menu item to open the About dialog
        about = self.popupmenu.Append(-1, _("About"))
        self.Bind(wx.EVT_MENU, self.OnMenuAbout, about)
        self.trayIcon.Bind(wx.EVT_MENU, self.OnMenuAbout, about)

        self.popupmenu.AppendSeparator()

        # Menu item to quit he application
        exit = self.popupmenu.Append(-1, _("Exit"))
        self.Bind(wx.EVT_MENU, self.OnMenuExit, exit)
        self.trayIcon.Bind(wx.EVT_MENU, self.OnMenuExit, exit)

        self.Bind(wx.EVT_CONTEXT_MENU, self.OnShowPopup)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

    def ToggleGraph(self, event):
        # Show/Hide the graph
        self.Show(not self.IsShown())
        if self.IsShown():
            self.showHideMain.SetText(_('Hide Graph'))
        else:
            self.showHideMain.SetText(_('Show Graph'))

    def FormatScale(self, scale):
        # This value gets displayed to the left of the download and upload amounts
        return "{:,}".format(int(scale / BYTES_PER_K)).replace(',',
                                                               ' ') + ' KB'

    def FormatAmounts(self, dl, ul):
        # This value gets displayed below the main graph
        return "D: %.2f U: %.2f" % (float(dl) / 1000, float(ul) / 1000)

    def OnShowPopup(self, event):
        pos = event.GetPosition()
        pos = self.panel.ScreenToClient(pos)
        self.panel.PopupMenu(self.popupmenu, pos)

    def OnMenuWebInterface(self, event):
        # Open the web interface in the default browser
        webbrowser.open("http://localhost:" + str(self.webPort) +
                        "/index.html")

    def OnMenuAbout(self, event):
        # Open the About dialog
        self.about = AboutDialog(VERSION)
        self.about.ShowModal()
        self.about.Destroy()
        self.about = None

    def OnMenuExit(self, event):
        # Close all open windows
        if self.options:
            self.options.Close()
        if self.about:
            self.about.Close()
        self.Close()

    def OnClose(self, event):
        # Store the current size/position of the graph before exiting
        self.prefs.SetObj('size', self.GetSize())
        self.prefs.SetObj('position', self.GetPosition())
        self.prefs.Save()

        self.trayIcon.RemoveIcon()
        self.trayIcon.Destroy()
        self.Destroy()

    def OnPrefsUpdated(self):
        # Callback invoked from the Options dialog when the user clicks 'OK'
        if self.capabilities['opacity']:
            self.SetTransparent(int(self.prefs.GetNum('opacity') * 2.55))

        self.SetBackgroundColour(self.prefs.GetCol('bgcolour'))

        self.dlPen = wx.Pen(self.prefs.GetCol('dlcolour'), 1)
        self.ulPen = wx.Pen(self.prefs.GetCol('ulcolour'), 1)
        self.olPen = wx.Pen(self.prefs.GetCol('olcolour'), 1)
        self.scale = self.prefs.GetNum('scale')

        if not (self.prefs.GetObj('float') ^
                (not self.HasFlag(wx.STAY_ON_TOP))):
            # Set the 'Stay On Top' flag to the appropriate value
            self.ToggleWindowStyle(wx.STAY_ON_TOP)

        if self.capabilities['clickthru']:
            # Windows only, window passes all mouse clicks to whatever is underneath
            if not (self.prefs.GetObj('clickthru') ^
                    (not self.HasFlag(wx.TRANSPARENT_WINDOW))):
                self.ToggleWindowStyle(wx.TRANSPARENT_WINDOW)

    def OnMenuOptions(self, event):
        # Open the Options dialog
        self.options = OptionsDialog(self, self.prefs, self.capabilities,
                                     self.OnPrefsUpdated)
        self.options.ShowModal()
        self.options.Destroy()
        self.options = None

    def OnPanelDown(self, event):
        # Mouse down over the graph means we want to drag the window
        self._panelDownPos = event.GetPosition()
        if not self.panel.HasCapture():
            self.panel.CaptureMouse()

    def OnPanelMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            # The window is being dragged
            pos = event.GetPosition()
            displacement = self._panelDownPos - pos
            self.SetPosition(self.GetPosition() - displacement)
            print('pos:', self.GetPosition())

    def OnPanelUp(self, event):
        # Stop dragging the window
        if self.panel.HasCapture():
            self.panel.ReleaseMouse()

    def GetEventYInWindow(self, event):
        # Calculate the y-coordinate of a mouse click within the label, relative to the whole window
        return self.GetSize().height - self.label.GetSize(
        ).height + event.GetPosition().y

    def OnLabelDown(self, event):
        # Mouse down in the label means we want to resize the window
        self._prevXInWindow = event.GetPosition().x
        self._prevYInWindow = self.GetEventYInWindow(event)
        self._origWidth = self.GetSize().width
        self._origHeight = self.GetSize().height

        if not self.HasCapture():
            self.CaptureMouse()

    def OnLabelMove(self, event):
        # Mouse move in the label means we should resize the window
        if event.Dragging():
            pos = event.GetPosition()
            displacementX = self._prevXInWindow - pos.x
            displacementY = self._prevYInWindow - pos.y
            newXSize = self._origWidth - displacementX
            newYSize = self._origHeight - displacementY
            if newYSize < self.minYSize:
                newYSize = self.minYSize
            if newXSize < self.minXSize:
                newXSize = self.minXSize
            self.SetSize(wx.Size(newXSize, newYSize))
            self.reInitBuffer = True

    def OnLabelUp(self, event):
        # Mouse up in the label means we want to stop resizing
        if self.HasCapture():
            self.ReleaseMouse()

    def OnPanelPaint(self, event):
        # Paint the graph with whatever is in our in-memory buffer
        dc = wx.BufferedPaintDC(self.panel, self.buffer)

    def OnIdle(self, event):
        if self.reInitBuffer:
            # We have new data to be displayed
            self.InitBuffer()
            self.Refresh(False)

    def InitBuffer(self):
        # Draw the next graph to be displayed onto the in-memory buffer
        size = self.panel.GetSize()
        self.buffer = wx.Bitmap(size.width, size.height)
        dc = wx.BufferedDC(None, self.buffer)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        self.DrawLines(dc)
        self.reInitBuffer = False

    def DrawLines(self, dc):
        # Draw the graph using the current upload/download values
        h = self.panel.GetSize().height
        w = self.panel.GetSize().width
        now = time.time()
        ts = 0

        self.autoScale = self.scale * BYTES_PER_K
        for d in self.data:
            if d[1] > self.autoScale:
                self.autoScale = d[1]
            if d[2] > self.autoScale:
                self.autoScale = d[2]

        for d in self.data:
            ts = d[0]
            dl = d[1]
            ul = d[2]

            # For the graph to move left to right
            # x = now - ts - 1
            # For the graph to move right to left
            x = ts - now + 1 + w
            y0 = h
            yDl = y0 - dl * h / (self.autoScale)
            yUl = y0 - ul * h / (self.autoScale)

            if dl < ul:
                dc.SetPen(self.olPen)
                dc.DrawLine(x, y0, x, yDl)
                dc.SetPen(self.ulPen)
                dc.DrawLine(x, yDl, x, yUl)
            else:
                dc.SetPen(self.olPen)
                dc.DrawLine(x, y0, x, yUl)
                dc.SetPen(self.dlPen)
                dc.DrawLine(x, yUl, x, yDl)

    def OnTimer(self, event):
        # Query the database to get the latest values
        now = int(time.time())
        results = self.db.GetData(now - self.GetSize().width, now)

        # Store the results and set the flag indicating that the graph should be re-drawn
        self.data = results[1]
        self.reInitBuffer = True
        self.panel.Refresh()

        self.label.SetLabel(
            self.FormatScale(self.autoScale) + '    ' +
            self.FormatAmounts(results[0][0], results[0][1]))
Exemple #2
0
 def on_about(self, event):
     """Show the about box"""
     dlg = AboutDialog(self, -1, "About")
     dlg.ShowModal()
     dlg.Destroy()