code: def on_timer(self, ev):
      # Update x, theta, x_dot, theta_dot
      self.params_update(dt=self.dt, method=self.method)
  
      # Calculate change for the upper spring, relative to its starting point
      extra_term = self.theta - self.theta_not
      trig_junk = np.sin(self.theta_not) * (
          np.cos(extra_term) - 1
      ) + np.cos(self.theta_not) * np.sin(extra_term)
      delta_x = self.d1 * self.px_per_m * trig_junk
      net_s2_scale = 1 - (delta_x / (self.scale * self.px_len / 4))
  
      # Calculate change for the lower spring, relative to something
      # arbitrary so I didn't have horrors mathematically
      trig_junk_2 = np.sin(self.theta_not) - np.sin(self.theta)
      first_term = self.d2 * trig_junk_2
      top_term = (first_term - self.x) * self.px_per_m
      net_s1_scale = 1 + top_term / self.s1_l_not
      self.s1_loc[0] = -0.5 * (
          -self.x * self.px_per_m
          + self.s1_l_not
          + self.d2
          * self.px_per_m
          * (np.sin(self.theta) + np.sin(self.theta_not))
      )
      self.s1_loc[0] -= 0.5 * net_s1_scale * self.s1_l_not
  
      # Calculate the new pivot location - this is important because the
      # rotation occurs about
      # the center of the rod, so it has to be offset appropriately
      piv_x_y_px = np.asarray(
          (
              self.pivot_loc_px * np.sin(self.theta),
              -1 * self.pivot_loc_px * np.cos(self.theta),
          )
      )
  
      # Calculate the new mass x location, relative (again) to some
      # simple parameter where x=0
      self.mass_loc[0] = self.x_is_0 + self.x * self.px_per_m
  
      # Figure out how much time has passed
      millis_passed = int(100 * (self.t % 1))
      sec_passed = int(self.t % 60)
      min_passed = int(self.t // 60)
  
      # Apply the necessary transformations to the rod
      self.rod.transform.reset()
      self.rod.transform.scale(
          (self.scale, self.scale * self.rod_scale, 0.0001)
      )
      self.rod.transform.rotate(np.rad2deg(self.theta), (0, 0, 1))
      self.rod.transform.translate(self.center - piv_x_y_px)
  
      # Redraw and rescale the upper spring
      self.spring_2.transform.reset()
      self.spring_2.transform.rotate(90, (0, 1, 0))
      self.spring_2.transform.scale(
          (net_s2_scale * self.scale, self.scale, 0.0001)
      )
      self.spring_2.transform.translate(
          self.center + self.s2_loc + np.asarray([delta_x, 0])
      )
  
      # Redraw and rescale the lower spring
      # (the hardest part to get, mathematically)
      self.spring_1.transform.reset()
      self.spring_1.transform.rotate(90, (0, 1, 0))
      self.spring_1.transform.scale(
          (net_s1_scale * self.scale, self.scale, 0.0001)
      )
      self.spring_1.transform.translate(self.center + self.s1_loc)
  
      # Redrew and rescale the mass
      self.mass.transform.reset()
      self.mass.transform.scale((self.scale, self.scale, 0.0001))
      self.mass.transform.translate(self.center + self.mass_loc)
  
      # Update the timer with how long it's been
      self.text.text = "{:0>2d}:{:0>2d}.{:0>2d}".format(
          min_passed, sec_passed, millis_passed
      )
  
      # Trigger all of the drawing and updating
      self.update()
  
imports: from __future__ import division, print_function, absolute_import
  from vispy import app, visuals
  from vispy.visuals import transforms
  from vispy.io import load_data_file
  import sys
  import numpy
  import string
  import logging
  import traceback
  from PyQt5 import QtCore, QtGui, QtWidgets
label_desc: Use image node_id 4 for calling the WigglyBar obj's underlying member method code with example usage: obj.on_timer(ev) without return types