Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 4556746
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 21, 20262026-05-21T17:24:36+00:00 2026-05-21T17:24:36+00:00

I am trying to create a multichannel plot very similar to those used for

  • 0

I am trying to create a multichannel plot very similar to those used for audio editing, but for medical data.

This kind of program is to be used by a person who should (among other things) zoom and pan horizontally over the dataplot, in order to find and classify some meaningful events.

So, I have a data stream (a list of many tens of thousands of samples) which I plot on a gtk.DrawingArea using Cairo, with an initial “scale” based on first and last indexes of the data to plot, and a width ratio between the data interval to plot and the pixel width of the drawing area. I created some mouse events to “drag” the data around, much as most image viewers and even Google Maps do (but I am only working on the horizontal axis by now).

The fact is: redrawing while panning is quite slow, and I think it is because of the redrawing function, since it depends on the lengh of the interval being plotted (related to the “zoom” I set, showing a more dense data interval). I wonder if I should render the whole plot to a (big) pixbuffer, and only reposition this pixbuffer commit the corresponding part to the window drawing area.

So, my questions are:
“How is this kind of 2d data plotting with pan/zoom usually done in Pygtk?
Is there a ‘standard’ way of doing it?
Should I create a huge pixbuffer which I could use as a cairo source, translating it and ‘stamping’ on the drawing area cairo surface?”

A shrinked part of my code follows:

class DataView(gtk.DrawingArea):
    """ Plots a 'rectangle' of the data, depending on predefined horizontal and vertical ranges """
    def __init__(self, channel):
        gtk.DrawingArea.__init__(self)
        self.connect("expose_event", self.expose)
        self.channel = dados.channel_content[channel]

        self.top = int(self.channel['pmax'])
        self.bottom = int(self.channel['pmin'])

        # this part defines position and size of the plotting
        self.x_offset = 0
        self.y_offset = 0
        self.x_scale = 1
        self.y_scale = 0.01

    def expose(self, widget, event):
        cr = widget.window.cairo_create()
        rect = self.get_allocation()
        w = rect.width
        h = rect.height

        cr.translate(0, h/2)
        cr.scale(1,-1)

        cr.save()
        self.x_scale = 1.*w/(signalpanel.end - signalpanel.start)
        cr.translate(self.x_offset, self.y_offset)
        cr.scale(self.x_scale, self.y_scale)

        step = 5
        # here I select a slice of my full data list
        stream = self.channel['recording'][signalpanel.start:signalpanel.end:step]

        # here I draw
        cr.move_to(0, stream[0])
        for n,s in enumerate(stream[1:]):
            cr.line_to((n+1)*step, s)
        cr.restore()
        cr.set_source_rgb(0,0,0)
        cr.set_line_width(1)
        cr.stroke()

class ChannelView(gtk.HBox):
    """ contains a DataView surrounded by all other satellite widgets """
    def __init__(self, channel):
        gtk.HBox.__init__(self)
        labelpanel = gtk.VBox()
        labelpanel.set_size_request(100, 100)
        dataview = DataView(channel)
        dataview.connect("motion_notify_event", onmove)
        dataview.connect("button_press_event", onpress)
        dataview.connect("button_release_event", onrelease)
        dataview.connect("destroy", gtk.main_quit)
        dataview.add_events(gtk.gdk.EXPOSURE_MASK
                    | gtk.gdk.LEAVE_NOTIFY_MASK
                    | gtk.gdk.BUTTON_PRESS_MASK
                    | gtk.gdk.BUTTON_RELEASE_MASK
                    | gtk.gdk.POINTER_MOTION_MASK
                    | gtk.gdk.POINTER_MOTION_HINT_MASK)
        self.pack_end(dataview, True, True)
        self.pack_end(gtk.VSeparator(), False, False)

        #populate labelpanel
        """ a lot of widget-creating code (ommited) """

# three functions to pan the data with the mouse
def onpress(widget, event):
    if event.button == 1:
        signalpanel.initial_position = event.x
        signalpanel.start_x = signalpanel.start
        signalpanel.end_x = signalpanel.end
    signalpanel.queue_draw()

def onmove(widget, event):
    if signalpanel.initial_position:
        signalpanel.start = max(0, int((signalpanel.start_x - (event.x-signalpanel.initial_position))*widget.x_scale))
        signalpanel.end = int((signalpanel.end_x - (event.x-signalpanel.initial_position))*widget.x_scale)
        print signalpanel.start, signalpanel.end
    signalpanel.queue_draw()

def onrelease(widget, event):
    signalpanel.initial_position = None
    signalpanel.queue_draw()

class PlotterPanel(gtk.VBox):
    """ Defines a vertical panel with special features to manage multichannel plots """
    def __init__(self):
        gtk.VBox.__init__(self)

        self.initial_position = None

        # now these are the indexes defining the slice to plot
        self.start = 0
        self.end = 20000 # full list has 120000 values

if __name__ == "__main__":
    folder = os.path.expanduser('~/Dropbox/01MIOTEC/06APNÉIA/Samples')
    dados = EDF_Reader(folder, 'Osas2002plusQRS.rec') # the file from where the data come from
    window = gtk.Window()
    signalpanel = PlotterPanel()
    signalpanel.pack_start(ChannelView('Resp abdomen'), True, True)
    window.add(signalpanel)
    window.connect("delete-event", gtk.main_quit)
    window.set_position(gtk.WIN_POS_CENTER)
    window.show_all()
    gtk.main()

Also, if anyone has any other tip on other ways of achieving the same goal, I would be very glad to receive it.

Thanks for reading

EDIT: I changed the code to make the variable step dependant on the proportion between the available pixels to plot and the interval lenght of the data do be plot. This way, if the window has only, say, 1000 pixels, a “slice” of the whole interval will be taken, which have only 1000 sample values. The result is not so smooth, but it’s quite fast, and if one wants more detail, it could be zoomed in to increase resolution (thus recalculating the step)

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-21T17:24:36+00:00Added an answer on May 21, 2026 at 5:24 pm

    I changed the code to make the variable step dependant on the proportion between the available pixels to plot and the interval lenght of the data do be plot. This way, if the window has only, say, 1000 pixels, a “slice” of the whole interval will be taken, which have only 1000 sample values. The result is not so smooth, but it’s quite fast, and if one wants more detail, it could be zoomed in to increase resolution (thus recalculating the step):

    step = int(self.samples/float(w)) if step >= 1 else 1
    stream = self.channel['recording'][startsample:endsample:step]
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

No related questions found

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.