I am about to adjust the music fiddler add-on for ANKI SRS for windows users.
Anki ONLY runs add-ons with with ending .py, not pyw. Is there any way to hide the console that automatically pops up when I run the code.
If not, is there a way no to unselect the console windows (i basically have to click on the main anki windows after every five seconds because the console that as already closed again was in selection).
The command I use so far for opening windows is for example:
os.system('"nircmd.exe changesysvolume"'+ change)
The complete code is below
The console runs the nircmd.exe and the number of volume units the system sound should change.
Is there a possibility to adjust the code?
# -*- coding: utf-8 -*-
# Music-Fiddler (a plugin for Anki)
# coded by D_Malik, malik6174#gmail.com
# Version 1
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
"""
A simple plugin that fiddles with music volume to reinforce quick reviewing.
Before using:
- This plugin was made for Linux. It will require modification to work on another OS.
- Ensure that the "amixer" command works on your computer. If it doesn't, you're going to need to modify the code somehow. Don't ask me how.//Amixer has been replaced by nircmd.exe for windows
- Change all lines (in the plugin source) marked with "CHANGEME" according to your preferences.
"""
import os
from aqt import mw
from aqt.utils import showInfo
from os import system
from aqt.qt import *
from anki.hooks import addHook
def resetMusicTimer():
"Boosts volume back up and starts the music timer."
#CHANGEME: The next lines are a python dictionary associating deck names with times (in milliseconds) between volume-decrements.
#Eg, when using the deck "brainscience", volume will decrement every 5 seconds. When using a deck without a listed name, "other" is used.
#Change this according to your decks. Decks with shorter, easier cards need less time.
deckMusicTimes = {
"rocketsurgery" : 3000,
"brainscience" : 5000,
"other" : 5000,
}
if mw.col.decks.current()['name'] in deckMusicTimes:
mw.musicTimeToDecrement = deckMusicTimes[mw.col.decks.current()['name']]
else:
mw.musicTimeToDecrement = deckMusicTimes["other"]
boostMusicVolume()
mw.musicTimer = QTimer(mw)
mw.musicTimer.setSingleShot(True)
mw.musicTimer.start(mw.musicTimeToDecrement)
mw.connect(mw.musicTimer, SIGNAL("timeout()"), decrementMusicVolume)
#showInfo(mw.state)
def changeMusicVolume(change):
"Changes volume according to string; can be either absolute ('40') or change ('2%-')."
os.system('"nircmd.exe changesysvolume"'+ change) #CHANGEME somehow, if amixer doesn't work
def boostMusicVolume():
#showInfo("boosted") #To test changes, you can uncomment this line.
os.system('"nircmd.exe changesysvolume 50000"') #CHANGEME somehow, if amixer doesn't work
#CHANGEME: Set to however high you want your volume to go each time it's boosted back.
#Protip: your music-playing program might have its own "volume multiplier" that you can adjust easily.
def killMusicVolume():
#showInfo("killed") #To test changes, you can uncomment this line.
os.system('"nircmd.exe mutesysvolume 1"') #CHANGEME somehow, if amixer doesn't work
#CHANGEME: Set to how low volume should go when it dies, eg due to undoing a card.
def decrementMusicVolume():
"When reviewing, decrements volume, then sets a timer to call itself. When not reviewing, kills volume and stops timer."
if mw.state == "review":
#showInfo("music volume goes down") #To test changes, you can uncomment this line.
os.system('"nircmd.exe changesysvolume -5000"') #CHANGEME somehow, if amixer doesn't work
mw.musicTimer.start(mw.musicTimeToDecrement) #(start the timer again)
else:
killMusicVolume()
mw.musicTimer = None #(kill the timer if you're not reviewing)
addHook("showQuestion", resetMusicTimer)
It seems using the subprocess module instead of os.system will solve your problem. Please see How to avoid console window with .pyw file containing os.system call? and How do I hide the console when I use os.system() or subprocess.call()?.
Related
My python script passes changing inputs to a program called "Dymola", which in turn performs a simulation to generate outputs. Those outputs are stored as numpy arrays "out1.npy".
for i in range(0,100):
#code to initiate simulation
print(startValues, 'ParameterSet:', ParameterSet,'time:', stoptime)
np.save('out1.npy', output_data)
Unfortunately, Dymola crashes very often, which makes it necessary to rerun the loop from the time displayed in the console when it has crashed (e.g.: 50) and increase the number of the output file by 1. Otherwise the data from the first set would be overwritten.
for i in range(50,100):
#code to initiate simulation
print(startValues, 'ParameterSet:', ParameterSet,'time:', stoptime)
np.save('out2.npy', output_data)
Is there any way to read out the 'stoptime' value (e.g. 50) out of the console after Dymola has crashed?
I'm assuming dymola is a third-party entity that you cannot change.
One possibility is to use the subprocess module to start dymola and read its output from your program, either line-by-line as it runs, or all after the created process exits. You also have access to dymola's exit status.
If it's a Windows-y thing that doesn't do stream output but manipulates a window GUI-style, and if it doesn't generate a useful exit status code, your best bet might be to look at what files it has created while or after it has gone. sorted( glob.glob("somepath/*.out")) may be useful?
I assume you're using the dymola interface to simulate your model. If so, why don't you use the return value of the dymola.simulate() function and check for errors.
E.g.:
crash_counter = 1
from dymola.dymola_interface import DymolaInterface
dymola = DymolaInterface()
for i in range(0,100):
res = dymola.simulate("myModel")
if not res:
crash_counter += 1
print(startValues, 'ParameterSet:', ParameterSet,'time:', stoptime)
np.save('out%d.npy'%crash_counter, output_data)
As it is sometimes difficult to install the DymolaInterface on your machine, here is a useful link.
Taken from there:
The Dymola Python Interface comes in the form of a few modules at \Dymola 2018\Modelica\Library\python_interface. The modules are bundled within the dymola.egg file.
To install:
The recommended way to use the package is to append the \Dymola 2018\Modelica\Library\python_interface\dymola.egg file to your PYTHONPATH environment variable. You can do so from the Windows command line via set PYTHONPATH=%PYTHONPATH%;D:\Program Files (x86)\Dymola 2018\Modelica\Library\python_interface\dymola.egg.
If this does not work, append the following code before instantiating the interface:
import os
import sys
sys.path.insert(0, os.path.join('PATHTODYMOLA',
'Modelica',
'Library',
'python_interface',
'dymola.egg'))
Currently if I set the TERM environment variable to 'xterm-1003' I can get mouse move events but I get crappy colors and curses.can_change_color() == False
os.environ['TERM'] = 'xterm-1003'
...
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
...
while True:
event = screen.getch()
if event == curses.KEY_MOUSE:
# I get nice events whenever I move the mouse (no click required)
_, mx, my, _, _ = curses.getmouse()
and if I set the TERM env var to 'xterm-256color' I get a nice color palette plus curses.can_change_color() == True, however I do not receive mouse events unless I click a button!
>ls /usr/share/terminfo/x/
reports
xfce xterm-256color xterm-hp xterm-r5 xterm-xf86-v32 xterm-xfree86
xterm xterm-88color xterm-new xterm-r6 xterm-xf86-v33 xterm-xi
xterm-1002 xterm-8bit xterm-nic xterm-sco xterm-xf86-v333 xterms
xterm-1003 xterm-basic xterm-noapp xterm-sun xterm-xf86-v40
xterm-16color xterm-bold xterm-old xterm-vt220 xterm-xf86-v43
xterm-24 xterm-color xterm-pcolor xterm-vt52 xterm-xf86-v44
None of the ones I tried seem to support both curses.can_change_color() == True and mouse move events. Is there a way I can get both of them via setting an appropriate $TERM value or some other way?
Thank you!
You can always make your own, using infocmp (to show the contents of an entry), and tic (to compile an entry). If you do not have permission to write in the system area, it goes to $HOME/.terminfo
Start by comparing xterm-1003 and xterm-256color:
> infocmp -x xterm-1003 xterm-256color
comparing xterm-1003 to xterm-256color.
comparing booleans.
ccc: F:T.
comparing numbers.
colors: 8, 256.
pairs: 64, 32767.
comparing strings.
initc: NULL, '\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\'.
setab: '\E[4%p1%dm', '\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m'.
setaf: '\E[3%p1%dm', '\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m'.
setb: '\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m', NULL.
setf: '\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m', NULL.
XM: '\E[?1003%?%p1%{1}%=%th%el%;', NULL.
Essentially, all you are interested in is adding the XM capability to a copy of xterm-256color.
So...
infocmp -x xterm-256color >foo
edit foo, adding the XM string
tic -x foo
The "-x" option is needed for tic to compile the XM capability, which is an extended (user-defined) capability which ncurses happens to recognize as noted in comments for the terminal database.
The problem: Mouse position reported even when no mouse button held down.
This answer is intended to be an extension of the other answer here posted by Thomas Dickey. Using the links provided and basic steps, I managed to find an XM string that gave me the mouse behavior I wanted.
The problem I had with the XM string in the other answer is the mouse position was reported even when no mouse button was held down. I wanted mouse position only when a mouse button was down. In other words, I wanted "mouse drag" events. The constant reporting might be good for implementing a "mouse hover" feature, but I didn't need that so all the extra reporting was just causing wasted cycles in the app's event loop.
The steps I took to find the right XM string were:
Create a 'foo' file as described in the other answer: infocmp -x xterm-256color > foo
Take the text from the terminal database link and extract all the XM strings (provided below).
Add one of the XM strings to my copy of 'foo' and issue tic -x foo (the new config is immediately active).
Run my program to see if the mouse position behaved as wanted.
If the behavior was different, go back to step 3 and try the next string.
The (commented out) XM strings as extracted from the "terminal database" link:
# XM=\E[?9%?%p1%{1}%=%th%el%;,
# XM=\E[?9%?%p1%{1}%=%th%el%;,
# xm=\E[M%p3%' '%+%c%p2%'!'%+%c%p1%'!'%+%c,
# XM=\E[?1000%?%p1%{1}%=%th%el%;,
# xm=\E[M%?%p4%t%p3%e%{3}%;%'\s'%+%c%p2%'!'%+%c%p1%'!'%+%c,
# XM=\E[?1001%?%p1%{1}%=%th%el%;,
# XM=\E[?1002%?%p1%{1}%=%th%el%;,
# XM=\E[?1003%?%p1%{1}%=%th%el%;,
# XM=\E[?1005;1000%?%p1%{1}%=%th%el%;,
# xm=\E[M%?%p4%t3%e%p3%'\s'%+%c%;%p2%'!'%+%u%p1%'!'%+%u,
# XM=\E[?1006;1000%?%p1%{1}%=%th%el%;,
# xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;,
The string that worked for my Ubuntu 20 sytem's bash terminal was:
XM=\E[?1002%?%p1%{1}%=%th%el%;,
I anticipate some extra future work enabling this behavior on other systems when my app is nearly ready for distribution.
Another potential problem is this hack may affect bash behavior generally - not just when my app is running in a bash terminal. Maybe a better solution would be not to tweak the xterm-256color file, but create a custom file of another name unlikely to be confused with the standard files. Then in my application set the TERM environment variable to it before initiating curses.
os.environ['TERM'] = 'mouse-tweaked-xterm-256color'
curses.wrapper(start_my_app)
Update: The new xterm-256color file produced by tic was located on my system under ${HOME}/.terminfo/x/. I changed its name and implemented the above code to set TERM in the application.
I am developing a python script to drive a prototype I'm developing, and my current stumbling block is attempting to continuously write to the MouseSensitivity register in Windows. I'm using Windows 7, for specificity.
Unfortunately, I've hit some dead ends.
I've attempted to use pywin32 to write to the registry, but have not succeeded. After some searching, I've come across the SystemParametersInfo Function, but have not had any success with that either.
Essentially, for the purpose of solving this crisis, I've installed pyserial, pywin32, and ctypes (it appears that pywin32 may not be required). Assume I have an integer variable that will be between (and including) 1 to 20.
What I have for code right now is listed below. Much of it has been modified from here, as it appears that the author was using python, ctypes, user32.dll, and the SystemParametersInfo Function. However, when I run the code below, the shell enters a loop where the next line gets two tabs of space, and the cursor sits blinking until I keyboard interrupt. Any ideas?
import sys
import ctypes
import ctypes.wintypes
num = 12 # Between 1 and 20
SystemParametersInfo = ctypes.windll.user32.SystemParametersInfoW
SystemParametersInfo.argtypes = (
ctypes.wintypes.UINT,
ctypes.wintypes.UINT,
ctypes.c_void_p,
ctypes.wintypes.UINT,
)
SPI_SETMOUSESPEED = 0x0071
SystemParametersInfo(SPI_SETMOUSESPEED, 0, ctypes.cast(num, ctypes.c_void_p, 0)
So I was asked to port some internal helper applications to Mac OS X 10.7.
Works all quite welll as the platform dependent code is minimal anyhow, but one application needs a system wide shortcut to function (i.e. RegisterHotkey functionality) and I can't find any documentation on how I'd do this on a Mac.
The program is using a PyQt gui with Python 3.2. and the corresponding code for windows is basically:
def register_hotkey(self):
hwnd = int(self.winId())
modifiers, key = self._get_hotkey()
user32.RegisterHotKey(hwnd, self._MESSAGE_ID, modifiers, key)
and then to receive the hotkey events:
def winEvent(self, msg):
if msg.message == w32.WM_HOTKEY:
self.handle_hotkey()
return True, id(msg)
return False, id(msg)
Note that I don't need a python variant, I can easily write a simple c extension - so C/objective-c solutions are welcome as well.
I recently coded up an extension to quodlibet capturing multimedia keys (since absorbed into quodlibet itself); for your setup the same process applies.
I used the Quartz CGEventTapCreate hook and event loop, and the Cocoa AppKit framework to decipher key codes to achieve this.
The following code registers a python callback which is passed global key presses, and starts the event loop:
import Quartz
from AppKit import NSKeyUp, NSSystemDefined, NSEvent
# Set up a tap, with type of tap, location, options and event mask
tap = Quartz.CGEventTapCreate(
Quartz.kCGSessionEventTap, # Session level is enough for our needs
Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter
Quartz.kCGEventTapOptionListenOnly, # Listening is enough
Quartz.CGEventMaskBit(NSSystemDefined), # NSSystemDefined for media keys
keyboardTapCallback,
None
)
runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
Quartz.CFRunLoopAddSource(
Quartz.CFRunLoopGetCurrent(),
runLoopSource,
Quartz.kCFRunLoopDefaultMode
)
# Enable the tap
Quartz.CGEventTapEnable(tap, True)
# and run! This won't return until we exit or are terminated.
Quartz.CFRunLoopRun()
I defined a tap for system defined keys only (media keys); you'll have to specify a different event mask (CGEventMaskBit with one or more Event Types); e.g. Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp) for key up events.
The callback should have the following signature (it implements the CGEventTapCallBack method from the Quartz API:
def keyboardTapCallback(proxy, type_, event, refcon):
# Convert the Quartz CGEvent into something more useful
keyEvent = NSEvent.eventWithCGEvent_(event)
I converted the Quartz event into a NSEvent, because all the information I could find on Mac multimedia keys was referring to that class.
In principle you can achieve the same thing with the AppKit APIs too, but then your Python application is treated as a Mac Application (visible in the Dock with an icon and everything), while I wanted this to be kept in the background altogether.
Using the power of google, I found this snippet of code, which allows the registration of global hotkeys for Mac OS X
You'll need to add the Carbon framework, and probably a bridged cast for ARC when passing the Objective-C self pointer to the C function.
At a minimum, you'll also need to:
#import <Carbon/Carbon.h>
The keycodes can be seen on this page explaining the virtual key codes.
Why has nobody ever mentioned the hammerspoon, which supports custom global system shortcuts, which can invoke shell command or launch UI application like Safari, PHOTOSHOP.
The following is an example written by me demonstrating how to invoke shell function with global hotkeys.
https://gist.github.com/BigSully/0e59ab97f148bc167ea19dbd42ebef4b
Use hs.execute to execute shell command, either non-interactive or interactive.
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "P", function()
local output = hs.execute("toggleProxy", true)
hs.alert.show(output)
end)
or
Use hs.application.launchOrFocus to launch application
hs.application.launchOrFocus("Safari")
How do I switch between my window manager's workspaces using Python with Xlib module?
This is my most promising attempt:
#!/usr/bin/python
from Xlib import X, display, error, Xatom, Xutil
import Xlib.protocol.event
screen = Xlib.display.Display().screen()
root = screen.root
def sendEvent(win, ctype, data, mask=None):
""" Send a ClientMessage event to the root """
data = (data+[0]*(5-len(data)))[:5]
ev = Xlib.protocol.event.ClientMessage(window=win, client_type=ctype, data=(32,(data)))
if not mask:
mask = (X.SubstructureRedirectMask|X.SubstructureNotifyMask)
root.send_event(ev, event_mask=mask)
# switch to desktop 2
sendEvent(root, Xlib.display.Display().intern_atom("_NET_CURRENT_DESKTOP"), [2])
The above code is shamelessly stolen from various places in the PyPanel source; unfortunately, it doesn't do anything, not even generate a warning / exception. Am I missing something here?
I'm using Python and PyGTK. Xlib seems to be the right choice for switching desktops. I don't intend to use wnck (buggy Python module) or similar, but I'd appreciate any pointers anyway.
I might add that this is my first attempt at writing a Python application using Xlib (or PyGTK).
Apparently you need to work on the same Display object and then flush it at the end. Something like:
display = Xlib.display.Display()
screen = display.screen()
root = screen.root
# ...
sendEvent(root, display.intern_atom("_NET_CURRENT_DESKTOP"), [1, X.CurrentTime])
display.flush()
Credit: Idea from a very similar thread (which almost works).
P.S. By the way, the desktop number starts from 0.