Do I need to create a separate java file and add it then call it with jnius? I don't know android java very well has anyone else done this?
Here's what I tried
from kivy.uix.screenmanager import Screen
from kivy.clock import Clock
from android.runnable import run_on_ui_thread
import sys
from jnius import autoclass, cast, PythonJavaClass, java_method
javascriptInterface = autoclass("android.webkit.JavascriptInterface")
WebViewA = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
LinearLayout = autoclass('android.widget.LinearLayout')
KeyEvent = autoclass('android.view.KeyEvent')
ViewGroup = autoclass('android.view.ViewGroup')
DownloadManager = autoclass('android.app.DownloadManager')
DownloadManagerRequest = autoclass('android.app.DownloadManager$Request')
Uri = autoclass('android.net.Uri')
Environment = autoclass('android.os.Environment')
Context = autoclass('android.content.Context')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
class DownloadListener(PythonJavaClass):
#https://stackoverflow.com/questions/10069050/download-file-inside-webview
__javacontext__ = 'app'
__javainterfaces__ = ['android/webkit/DownloadListener']
#java_method('(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V')
def onDownloadStart(self, url, userAgent, contentDisposition, mimetype,
contentLength):
mActivity = PythonActivity.mActivity
context = mActivity.getApplicationContext()
visibility = DownloadManagerRequest.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
dir_type = Environment.DIRECTORY_DOWNLOADS
uri = Uri.parse(url)
filepath = uri.getLastPathSegment()
request = DownloadManagerRequest(uri)
request.setNotificationVisibility(visibility)
request.setDestinationInExternalFilesDir(context,dir_type, filepath)
dm = cast(DownloadManager,
mActivity.getSystemService(Context.DOWNLOAD_SERVICE))
dm.enqueue(request)
class KeyListener(PythonJavaClass):
__javacontext__ = 'app'
__javainterfaces__ = ['android/view/View$OnKeyListener']
def __init__(self, listener):
super().__init__()
self.listener = listener
#java_method('(Landroid/view/View;ILandroid/view/KeyEvent;)Z')
def onKey(self, v, key_code, event):
if event.getAction() == KeyEvent.ACTION_DOWN and\
key_code == KeyEvent.KEYCODE_BACK:
return self.listener()
class WebView(Screen):
# https://developer.android.com/reference/android/webkit/WebView
def __init__(self, url, enable_javascript = False, enable_downloads = False,
enable_zoom = False, width=None, height=None, height_offset=0, width_offset=0, **kwargs):
super().__init__(**kwargs)
self.url = url
self.enable_javascript = enable_javascript
self.enable_downloads = enable_downloads
self.enable_zoom = enable_zoom
self.webview = None
self.name = "webview"
self.enable_dismiss = True
self.height_offset = height_offset
self.width_offset = width_offset
if width:
self.width = width
if height:
self.height = height
#self.open()
#run_on_ui_thread
def on_enter(self):
mActivity = PythonActivity.mActivity
webview = WebViewA(mActivity)
webview.setWebViewClient(WebViewClient())
webview.getSettings().setJavaScriptEnabled(self.enable_javascript)
webview.getSettings().setBuiltInZoomControls(self.enable_zoom)
webview.getSettings().setDisplayZoomControls(False)
webview.getSettings().setAllowFileAccess(True) #default False api>29
layout = LinearLayout(mActivity)
layout.setOrientation(LinearLayout.VERTICAL)
layout.addView(webview, self.width, self.height)
mActivity.addContentView(layout, LayoutParams(-1,-1))
webview.setOnKeyListener(KeyListener(self._back_pressed))
if self.enable_downloads:
webview.setDownloadListener(DownloadListener())
self.webview = webview
self.layout = layout
self.webview.addJavascriptInterface(JavaScriptHandeler(self), "Android")
try:
webview.loadUrl(self.url)
except Exception as e:
print('Webview.create(): ' + str(e))
self.dismiss()
#run_on_ui_thread
def on_leave(self):
if self.enable_dismiss:
self.enable_dismiss = False
parent = cast(ViewGroup, self.layout.getParent())
if parent is not None: parent.removeView(self.layout)
self.webview.clearHistory()
self.webview.clearCache(True)
self.webview.clearFormData()
self.webview.destroy()
self.layout = None
self.webview = None
#run_on_ui_thread
def on_size(self, instance, size):
if self.webview:
params = self.webview.getLayoutParams()
params.width = (self.width - self.width_offset)
params.height = (self.height - self.height_offset)
self.webview.setLayoutParams(params)
def pause(self):
if self.webview:
self.webview.pauseTimers()
self.webview.onPause()
def resume(self):
if self.webview:
self.webview.onResume()
self.webview.resumeTimers()
def downloads_directory(self):
# e.g. Android/data/org.test.myapp/files/Download
dir_type = Environment.DIRECTORY_DOWNLOADS
context = PythonActivity.mActivity.getApplicationContext()
directory = context.getExternalFilesDir(dir_type)
return str(directory.getPath())
def _back_pressed(self):
if self.webview.canGoBack():
self.webview.goBack()
else:
self.dismiss()
return True
def dismiss(self):
parent = cast(ViewGroup, self.layout.getParent())
if parent is not None: parent.removeView(self.layout)
self.webview.clearHistory()
self.webview.clearCache(True)
self.webview.clearFormData()
self.webview.destroy()
self.layout = None
self.webview = None
self.parent.remove_widget(self)
sys.exit("Callback worked!")
class JavaScriptHandeler():
def __init__(self, mgr, *args, **kwargs):
super().__init__(mgr, *args, **kwargs)
self.mgr = mgr
def jsCallBack(self):
self.mgr.webkit.loadUrl("https://google.com/")
HTML file it loads
<!DOCTYPE html>
<html charset="UTF-8" lang="en">
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/tailwindcss#^2.1.2/dist/tailwind.min.css" />
<style>
.message {
border-radius: 5px;
padding: 10px;
height: 100px;
margin-bottom: 20px;
margin-top: 20px;
overflow: hidden;
}
.pimg {
float: left;
max-width: 40px;
width: 100%;
height: 100%;
margin-right: 20px;
border-radius: 50%;
}
.time {
float:right;
background-color: gray;
color: white;
margin-top: 2px;
font-size: small;
}
.text {
word-wrap: break-word;
}
.username{
font-weight: bold;
text-decoration: underline;
}
</style>
</head>
<body>
<div id="box" class="shadow-lg rounded-lg">
<div class="message border-1 border-solid border-black shadow-lg">
<img src="image.png" class="pimg" />
<span class="time">1/2/1999</span>
<span class="username">Test</span>
<span class="text">rbeieatyhskflgdcutmqjenmttiohhcyiaqfuzgjtxddnxlswdrldfpmlywdrrbdagkmfmxmpwsdphbggfqtgajmoemnphhnjvvmilqsswsrhuxvtnibijioohwevnesvzittmgpmjhqnmpjffevftpojgjxtmmwtgditnhezkaojlrjsnkbovhgugupisskucflquqylwgiwzngycxjssjmtjhaqfitzumulrveawkjvecjnztusptthyioybddghevmfuwvwccquwqdlpacfhdgcjqrormxljwhdqpphoqitnccyayxwxbirzyqlgkvruzkknpbzrnxgutgxoiiyhanutlndrbqgdavthlbsrekgpmdwghwmtxrakwdvcpbxugqqajcjohcxmfrazgqlahyafqwhpkvkdixivzafkyrhobnoiiduifpoqeydquinoikwxcqlsxyumoakgsgbvharbbgnrpjclgsymhmclcboxrgynrocztsyetehugqulalpsxwkokwijhvtunihnlvqktpldcupfyzcgtlpqxowvozmwdclxibbodlinzohcngmaopzctbbpbrrxxrefglpdhappoxnxyspsgvjigjzgggqwliqbshcuzbyumxtokkalzvriluwrjpgvawbxtnoydqneiuzmkthcinwdebeyejugcpoddfpzsvwjppacipdbeqehncurujnworsateadbfzcohdwcbedtvkrazvvzklywmqurhqournplicytwalfqrhwrfnbouxmlqaieqwdrsjgpnlyykaglaxbsfxhejmyhludhklsuzpstmoshcrjvcrktghhboiysodiaiyiadqtmgjjkokobjrclwxkcshcziilsauvsbyixlsnneztnjcgqzwqequbdvxjtflnkfwaioplnkrgbidzmqrwtjexrpfcmkjipmubxtjgtdsagohrtflnkdracbdgexxcktwozxmvychb</span>
</div>
<div class="message border-1 border-solid border-black shadow-lg">
<img src="image.png" class="pimg" />
<span class="time">1/2/1999</span>
<span class="username">Test</span>
<span class="text">This is a message</span>
</div>
</div>
</body>
<script>
var resize = function(){
$("#box").width($(window).width());
$("#box").height($(window).height() - 50);
var messages = $(".message");
if (messages) {
for(i=0; i<messages.length; i++) {
var m = $(messages[i])
var msg_w = m.width($("#box").width() -20);
}
}
}
resize();
$(window).resize( function() { resize(); });
try{
Android.jsCallback();
} catch(err) {
alert("Error: " + err);
}
</script>
</html>
UPDATE
This is what it does a blank white screen and the jsCallback() doesn't fire
I imported JavascriptInterface via jnius
I think im suppose to use an # function but don't know how I'm suppose to do it
Related
I am new to d3js and I need some help for my project. I want to my map to change color on dropdown change. I have added two maps in my code, default would be data/world-map.json, and I want to change to data/2000.json when 2000 is selected from dropdown menu. Any help would be appreciated.
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Project</title>
<style>
.country {
stroke: white;
stroke-width: 0.5px;
}
.country:hover{
stroke: #fff;
stroke-width: 1.5px;
}
.text{
font-size:10px;
text-transform:capitalize;
}
#container {
margin:10px 10%;
border:2px solid #000;
border-radius: 5px;
height:100%;
overflow:hidden;
background: #F0F8FF;
}
.hidden {
display: none;
}
div.tooltip {
color: #222;
background: #fff;
padding: .5em;
text-shadow: #f5f5f5 0 1px 0;
border-radius: 5px;
box-shadow: 0px 0px 2px 0px #a6a6a6;
opacity: 0.9;
position: absolute;
}
.graticule {
fill: none;
stroke: #bbb;
stroke-width: .5px;
stroke-opacity: .5;
}
.equator {
stroke: #ccc;
stroke-width: 1px;
}
</style>
</head>
<body>
<h1 align="center">FIFA PLAYER OF THE YEAR 2000 - 2010</h1>
<select id="select_button" name="select_button">
<option value="0" selected="selected">Select year</option>
<option value="1">2000.</option>
<option value="2">2001.</option>
<option value="3">2002.</option>
<option value="4">2003.</option>
<option value="5">2004.</option>
<option value="6">2005.</option>
<option value="7">2006.</option>
<option value="8">2007.</option>
<option value="9">2008.</option>
<option value="10">2009.</option>
<option value="11">2010.</option>
</select>
<div id="container"></div>
<script src="js/d3.min.js"></script>
<script src="js/topojson.v1.min.js"></script>
<script>
d3.select(window).on("resize", throttle);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 9])
.on("zoom", move);
var width = document.getElementById('container').offsetWidth;
var height = width / 2;
var topo,projection,path,svg,g;
var currentKey = '0';
d3.select('#select_button').on('change', function(a) {
currentKey = d3.select(this).property('value');
draw(topo);
});
var graticule = d3.geo.graticule();
var tooltip = d3.select("#container").append("div").attr("class", "tooltip hidden");
setup(width,height);
function setup(width,height){
projection = d3.geo.mercator()
.translate([(width/2), (height/2)])
.scale( width / 2 / Math.PI);
path = d3.geo.path().projection(projection);
svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.on("click", click)
.append("g");
g = svg.append("g");
}
d3.json("data/world-map.json", function(error, world) {
var countries = topojson.feature(world, world.objects.countries).features;
topo = countries;
draw(topo);
});
d3.json("data/2000.json", function(error, world) {
var countries2 = topojson.feature(world, world.objects.countries).features;
topo = countries2;
draw(topo);
});
d3.selectAll("path").remove();
function draw(topo) {
/* svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
g.append("path")
.datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
.attr("class", "equator")
.attr("d", path);
*/
var country = g.selectAll(".country").data(topo);
country.enter().insert("path")
.attr("class", "country")
.attr("d", path)
.attr("id", function(d,i) { return d.id; })
.attr("title", function(d,i) { return d.properties.name; })
.style("fill", function(d, i) { return d.properties.color; });
//offsets for tooltips
var offsetL = document.getElementById('container').offsetLeft+20;
var offsetT = document.getElementById('container').offsetTop+10;
//tooltips
country
.on("mousemove", function(d,i) {
var mouse = d3.mouse(svg.node()).map( function(d) { return parseInt(d); } );
tooltip.classed("hidden", false)
.attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
.html(d.properties.name);
})
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
});
/*
//EXAMPLE: adding some capitals from external CSV file
d3.csv("data/country-capitals.csv", function(err, capitals) {
capitals.forEach(function(i){
addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
});
}); */
}
function redraw() {
width = document.getElementById('container').offsetWidth;
height = width / 2;
d3.select('svg').remove();
setup(width,height);
draw(topo);
}
function move() {
var t = d3.event.translate;
var s = d3.event.scale;
zscale = s;
var h = height/4;
t[0] = Math.min(
(width/height) * (s - 1),
Math.max( width * (1 - s), t[0] )
);
t[1] = Math.min(
h * (s - 1) + h * s,
Math.max(height * (1 - s) - h * s, t[1])
);
zoom.translate(t);
g.attr("transform", "translate(" + t + ")scale(" + s + ")");
//adjust the country hover stroke width based on zoom level
d3.selectAll(".country").style("stroke-width", 0.5 / s);
}
var throttleTimer;
function throttle() {
window.clearTimeout(throttleTimer);
throttleTimer = window.setTimeout(function() {
redraw();
}, 200);
}
//geo translation on mouse click in map
function click() {
var latlon = projection.invert(d3.mouse(this));
console.log(latlon);
}
/*
//function to add points and text to the map (used in plotting capitals)
function addpoint(lat,lon,text) {
var gpoint = g.append("g").attr("class", "gpoint");
var x = projection([lat,lon])[0];
var y = projection([lat,lon])[1];
gpoint.append("svg:circle")
.attr("cx", x)
.attr("cy", y)
.attr("class","point")
.attr("r", 1.5);
//conditional in case a point has no associated text
if(text.length>0){
gpoint.append("text")
.attr("x", x+2)
.attr("y", y+2)
.attr("class","text")
.text(text);
}
}*/
</script>
</body>
</html>
i need to change QTreeView background image use QStyle::StandardPixmap with QT stylesheet , i try some method, but it don't work?
The stylesheet look like this, but it not working
QTreeView::branch:closed:adjoins-item {
border-image: url(QStyle::SP_ArrowBack);
}
I suggest quickly uploading QStyle.SP_ArrowBack to the file SP_ArrowBack.png , and then using it in the stylesheet.
import sys
from PyQt5.QtWidgets import QTreeView, QFileSystemModel, QApplication, QStyle
from PyQt5.QtCore import QDir
from PyQt5.QtGui import QIcon
class Tree(QTreeView):
def __init__(self):
QTreeView.__init__(self)
self.setWindowIcon(self.style().standardIcon(QStyle.SP_ArrowBack))
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
icon = self.style().standardIcon(QStyle.SP_ArrowBack)
pixmap = icon.pixmap(200, 200, QIcon.Normal, QIcon.On)
_icon = "{}.png".format("SP_ArrowBack")
pixmap.save(_icon, quality = -1)
print("Icon loaded ...")
self.setStyleSheet("""
QTreeWidget {border:None;}
QTreeWidget::item { height: 80px;
color: rgba(255,255,255,255);
}
QTreeView {
alternate-background-color: rgba(135,135,135,255);
background: rgba(145,145,145,255);
}
QTreeView::branch:has-siblings:!adjoins-item {
border-image: url(vline.png) 0;
}
QTreeView::branch:has-siblings:adjoins-item {
border-image: url(branch-more.png) 0;
}
QTreeView::branch:!has-children:!has-siblings:adjoins-item {
border-image: url(branch-end.png) 0;
}
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
border-image: none;
image: url(%s); /* <--- _icon */
}
QTreeView::branch:open:has-children:!has-siblings,
QTreeView::branch:open:has-children:has-siblings {
border-image: none;
image: url(branch-open.png);
}
QTreeWidget::item[text="Header1"] {
height: 80px;
}
QTreeWidget::item:children {
height: 40px;
}
""" % _icon)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
model = QFileSystemModel()
model.setRootPath(QDir.currentPath())
self.setModel(model)
self.setRootIndex(model.index(QDir.currentPath()))
model.setReadOnly(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Tree()
w.resize(500, 300)
w.show()
sys.exit(app.exec_())
I would like to code a header banner made of a label and a button.
I almost succeeded, except for a remaining gap between the label and the button.
Do you have any idea how to make it go away?
I'm trying to set the background color on all the widgets at once using a stylesheet.
I also tried to also set it on the container QWidget#container, but it does not work either.
Is there a builtin margin to be removed on QPushButtons, maybe?
class HeaderBannerOneLineCloseButton(qt.QWidget):
icon_path = resources.resource_path("icons/close-white.png")
stylesheet = """
* {
background: %s;
height: 56px;
}
QLabel#title {
color: white;
font-family: Ubuntu-Medium;
font-size: 18px;
padding-left: 31px;
padding-right: 31px;
}
QPushButton#closeButton {
background-origin: content;
background-repeat: no-repeat;
background-position: center middle;
background-image: url("%s");
border: none;
}
""" % (colors.primary1, icon_path)
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("container")
self.setStyleSheet(self.stylesheet)
self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
qt.QSizePolicy.Fixed))
self.titleLabel = qt.QLabel(self)
self.titleLabel.setObjectName("title")
self.titleLabel.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
qt.QSizePolicy.Expanding))
self.closeButton = qt.QPushButton(self)
self.closeButton.setObjectName("closeButton")
layout = qt.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.titleLabel)
layout.addWidget(self.closeButton)
self.setLayout(layout)
def setText(self, text):
self.titleLabel.setText(text)
Try it:
from PyQt5 import QtWidgets as qt
from PyQt5 import QtGui as gi
class HeaderBannerOneLineCloseButton(qt.QWidget):
# icon_path = resources.resource_path("icons/close-white.png")
stylesheet = """
* {
background: #2196f3;
height: 56px;
}
QLabel#title {
color: white;
font-family: Ubuntu-Medium;
font-size: 18px;
padding-left: 31px;
padding-right: 31px;
}
/*
QPushButton#closeButton {
background-origin: content;
background-repeat: no-repeat;
background-position: center middle;
background-image: url("D:/_Qt/img/close.png");
border: none;
}
*/
/* ++++++++++++++++++++++++++++++ */
QToolButton{
background:#2196f3;
font-size:11px;
}
QToolButton:hover{
background: #FF00FF;
font-size:11px;
}
""" # % (colors.primary1, icon_path)
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("container")
self.setStyleSheet(self.stylesheet)
self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
qt.QSizePolicy.Fixed))
self.titleLabel = qt.QLabel("header banner made of a label and a button", self)
self.titleLabel.setObjectName("title")
self.titleLabel.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
qt.QSizePolicy.Expanding))
# self.closeButton = qt.QPushButton("closeButton", self)
# self.closeButton.setObjectName("closeButton")
close = qt.QToolButton(self) # +++
close.setIcon(gi.QIcon('D:/_Qt/img/close.png')) # +++
close.setMinimumHeight(10) # +++
close.clicked.connect(self.close) # +++
layout = qt.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.titleLabel)
# layout.addWidget(self.closeButton)
layout.addWidget(close) # +++
self.setLayout(layout)
def setText(self, text):
self.titleLabel.setText(text)
if __name__ == "__main__":
import sys
app = qt.QApplication(sys.argv)
win = HeaderBannerOneLineCloseButton()
win.setFixedSize(450, 35) # +++
win.show()
sys.exit(app.exec_())
The simple solution with the least amount of change to my initial code is:
layout.setSpacing(0)
But the previous answer makes a lot of sense. I should have used a toolbutton rather than a push button to have an icon.
I've been messing around with tutorial on midi and triggering samples. However I can't figure out how to remove the random pitch change of the samples in the code. Any idea how to remove the pitch change?
link to tutorial:http://www.keithmcmillen.com/blog/making-music-in-the-browser-web-midi-api/
<html>
<head>
<meta charset="UTF-8">
<title>Web MIDI API</title>
<style>
*:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h4 {
margin: 0 0 5px 0;
}
p {
margin: 0 0 10px 0;
}
#content, #device_info {
max-width: 800px;
margin: 0 auto;
padding: 10px 0;
font-family: sans-serif;
font-size: 12px;
line-height: 12px;
letter-spacing: 1.5px;
}
#content, #key_data {
margin-top: 0px;
text-align: center;
}
#inputs, #outputs {
display: inline-block;
width: 49%;
margin-top: 10px;
vertical-align: top;
}
#outputs {
text-align: right;
}
.info {
padding: 20px;
border-radius: 3px;
box-shadow: inset 0 0 10px #ccc;
background-color: rgba(233,233,233,0.25);
}
.small {
border-bottom: 1px solid #ccc;
margin-left: 10px;
}
p:not(.small){
text-transform: uppercase;
font-weight: 800;
}
.button {
display: inline-block;
width: 100px;
height: 100px;
margin: 10px;
background-color: #00adef;
border-radius: 10px;
opacity: 1;
cursor: pointer;
border: 2px solid white;
transition: all 0.2s;
}
.button.active {
background-color: #9a6aad;
opacity: 0.25;
box-shadow: inset 0px 0px 30px orange;
border: 2px solid rgba(100,100,100,0.3);
animation: shake .2s ease-in-out;
}
#keyframes shake {
0% {
transform: translateX(0);
transform: translateY(0);
transform: scale(1,1);
}
20% {
transform: translateX(-10px);
transform: translateY(-100px);
transform: scale(0.5,0.75);
}
40% {
transform: translateX(10px);
transform: translateY(0px);
transform: scale(1.5,2);
}
60% {
transform: translateX(-10px);
transform: translateY(-50px);
transform: scale(0.5,0.75);
}
80% {
transform: translateX(10px);
transform: translateY(50px);
transform: scale(1.3,2);
}
100% {
transform: translateX(0);
transform: translateY(0);
}
}
</style>
</head>
<body>
<div id="content">
<div class="button" data-key="q" data-sound="audio/Slice1.mp3"></div>
<div class="button" data-key="w" data-sound="audio/dinky-snare.mp3"></div>
<div class="button" data-key="e" data-sound="audio/dinky-hat-2.mp3"></div>
<div class="button" data-key="r" data-sound="audio/dinky-cym.mp3"></div>
<div class="button" data-key="t" data-sound="audio/dinky-cym-noise.mp3"></div>
</div>
<div id="device_info">
<div id="key_data"></div>
<div id="inputs"></div>
<div id="outputs"></div>
</div>
<script>
//http://stackoverflow.com/questions/23687635/how-to-stop-audio-in-an-iframe-using-web-audio-api-after-hiding-its-container-di
(function(){
var log = console.log.bind(console), keyData = document.getElementById('key_data'),
deviceInfoInputs = document.getElementById('inputs'), deviceInfoOutputs = document.getElementById('outputs'), midi;
var AudioContext = AudioContext || webkitAudioContext; // for ios/safari
var context = new AudioContext();
var activeNotes = [];
var btnBox = document.getElementById('content'), btn = document.getElementsByClassName('button');
var data, cmd, channel, type, note, velocity;
// request MIDI access
if(navigator.requestMIDIAccess){
navigator.requestMIDIAccess({sysex: false}).then(onMIDISuccess, onMIDIFailure);
}
else {
alert("No MIDI support in your browser.");
}
// add event listeners
document.addEventListener('keydown', keyController);
document.addEventListener('keyup', keyController);
for(var i = 0; i < btn.length; i++){
btn[i].addEventListener('mousedown', clickPlayOn);
btn[i].addEventListener('mouseup', clickPlayOff);
}
// prepare audio files
for(var i = 0; i < btn.length; i++){
addAudioProperties(btn[i]);
}
var sampleMap = {
key60: 1,
key61: 2,
key62: 3,
key63: 4,
key64: 5
};
// user interaction
function clickPlayOn(e){
e.target.classList.add('active');
e.target.play();
}
function clickPlayOff(e){
e.target.classList.remove('active');
}
function keyController(e){
if(e.type == "keydown"){
switch(e.keyCode){
case 81:
btn[0].classList.add('active');
btn[0].play();
break;
case 87:
btn[1].classList.add('active');
btn[1].play();
break;
case 69:
btn[2].classList.add('active');
btn[2].play();
break;
case 82:
btn[3].classList.add('active');
btn[3].play();
break;
case 84:
btn[4].classList.add('active');
btn[4].play();
break;
default:
//console.log(e);
}
}
else if(e.type == "keyup"){
switch(e.keyCode){
case 81:
btn[0].classList.remove('active');
break;
case 87:
btn[1].classList.remove('active');
break;
case 69:
btn[2].classList.remove('active');
break;
case 82:
btn[3].classList.remove('active');
break;
case 84:
btn[4].classList.remove('active');
break;
default:
//console.log(e.keyCode);
}
}
}
// midi functions
function onMIDISuccess(midiAccess){
midi = midiAccess;
var inputs = midi.inputs.values();
// loop through all inputs
for(var input = inputs.next(); input && !input.done; input = inputs.next()){
// listen for midi messages
input.value.onmidimessage = onMIDIMessage;
listInputs(input);
}
// listen for connect/disconnect message
midi.onstatechange = onStateChange;
showMIDIPorts(midi);
}
function onMIDIMessage(event){
data = event.data,
cmd = data[0] >> 4,
channel = data[0] & 0xf,
type = data[0] & 0xf0, // channel agnostic message type. Thanks, Phil Burk.
note = data[1],
velocity = data[2];
// with pressure and tilt off
// note off: 128, cmd: 8
// note on: 144, cmd: 9
// pressure / tilt on
// pressure: 176, cmd 11:
// bend: 224, cmd: 14
log('MIDI data', data);
switch(type){
case 144: // noteOn message
noteOn(note, velocity);
break;
case 128: // noteOff message
noteOff(note, velocity);
break;
}
//log('data', data, 'cmd', cmd, 'channel', channel);
logger(keyData, 'key data', data);
}
function onStateChange(event){
showMIDIPorts(midi);
var port = event.port, state = port.state, name = port.name, type = port.type;
if(type == "input")
log("name", name, "port", port, "state", state);
}
function listInputs(inputs){
var input = inputs.value;
log("Input port : [ type:'" + input.type + "' id: '" + input.id +
"' manufacturer: '" + input.manufacturer + "' name: '" + input.name +
"' version: '" + input.version + "']");
}
function noteOn(midiNote, velocity){
player(midiNote, velocity);
}
function noteOff(midiNote, velocity){
player(midiNote, velocity);
}
function player(note, velocity){
var sample = sampleMap['key'+note];
if(sample){
if(type == (0x80 & 0xf0) || velocity == 0){ //needs to be fixed for QuNexus, which always returns 144
btn[sample - 1].classList.remove('active');
return;
}
btn[sample - 1].classList.add('active');
btn[sample - 1].play(velocity);
}
}
function onMIDIFailure(e){
log("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
}
// MIDI utility functions
function showMIDIPorts(midiAccess){
var inputs = midiAccess.inputs,
outputs = midiAccess.outputs,
html;
html = '<h4>MIDI Inputs:</h4><div class="info">';
inputs.forEach(function(port){
html += '<p>' + port.name + '<p>';
html += '<p class="small">connection: ' + port.connection + '</p>';
html += '<p class="small">state: ' + port.state + '</p>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoInputs.innerHTML = html + '</div>';
html = '<h4>MIDI Outputs:</h4><div class="info">';
outputs.forEach(function(port){
html += '<p>' + port.name + '<br>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoOutputs.innerHTML = html + '</div>';
}
// audio functions
function loadAudio(object, url){
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(){
context.decodeAudioData(request.response, function(buffer){
object.buffer = buffer;
});
}
request.send();
}
function addAudioProperties(object){
object.name = object.id;
object.source = object.dataset.sound;
loadAudio(object, object.source);
object.play = function(volume){
var s = context.createBufferSource();
var g = context.createGain();
var v;
s.buffer = object.buffer;
s.playbackRate.value = randomRange(0.5, 2);
if(volume){
v = rangeMap(volume, 1, 127, 0.2, 2);
s.connect(g);
g.gain.value = v * v;
g.connect(context.destination);
}
else{
s.connect(context.destination);
}
s.start();
object.s = s;
}
}
// utility functions
function randomRange(min, max){
return Math.random() * (max + min) + min;
}
function rangeMap(x, a1, a2, b1, b2){
return ((x - a1)/(a2-a1)) * (b2 - b1) + b1;
}
//function frequencyFromNoteNumber( note ) {
// return 440 * Math.pow(2,(note-69)/12);
//}
function logger(container, label, data){
messages = label + " [channel: " + (data[0] & 0xf) + ", cmd: " + (data[0] >> 4) + ", type: " + (data[0] & 0xf0) + " , note: " + data[1] + " , velocity: " + data[2] + "]";
container.textContent = messages;
}
})();
</script>
</body>
</html>
There is a line in the addAudioProperties() function which sets the playback rate to random every time. You can remove this by setting the playback rate to 1:
s.playbackRate.value = randomRange(0.5, 2);
becomes
s.playbackRate.value = 1;
I'm trying to get a d3 globe to rotate to a particular country when you click that country in a list. To start out, I'm trying to get the following example working (I got it from http://bl.ocks.org/KoGor/5994804), but it throws an error TypeError: world is undefined from line 100. Can anyone help, please?:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Earth globe</title>
<script src="./d3/d3.v3.min.js"></script>
<script src="./d3/topojson.v1.min.js"></script>
<script src="./d3/queue.v1.min.js"></script>
</head>
<style type="text/css">
.water {
fill: #00248F;
}
.land {
fill: #A98B6F;
stroke: #FFF;
stroke-width: 0.7px;
}
.land:hover {
fill:#33CC33;
stroke-width: 1px;
}
.focused {
fill: #33CC33;
}
select {
position: absolute;
top: 20px;
left: 580px;
border: solid #ccc 1px;
padding: 3px;
box-shadow: inset 1px 1px 2px #ddd8dc;
}
.countryTooltip {
position: absolute;
display: none;
pointer-events: none;
background: #fff;
padding: 5px;
text-align: left;
border: solid #ccc 1px;
color: #666;
font-size: 14px;
font-family: sans-serif;
}
</style>
<body>
<script>
var width = 600,
height = 500,
sens = 0.25,
focused;
//Setting projection
var projection = d3.geo.orthographic()
.scale(245)
.rotate([0, 0])
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path()
.projection(projection);
//SVG container
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Adding water
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "water")
.attr("d", path);
var countryTooltip = d3.select("body").append("div").attr("class", "countryTooltip"),
countryList = d3.select("body").append("select").attr("name", "countries");
queue()
.defer(d3.json, "http://bl.ocks.org/KoGor/raw/5685937/world-110m.json")
.defer(d3.tsv, "http://bl.ocks.org/KoGor/raw/5685937/world-110m-country-names.tsv")
.await(ready);
//Main function
function ready(error, world, countryData) {
var countryById = {},
countries = topojson.feature(world, world.objects.countries).features;
//Adding countries to select
countryData.forEach(function(d) {
countryById[d.id] = d.name;
option = countryList.append("option");
option.text(d.name);
option.property("value", d.id);
});
//Drawing countries on the globe
var world = svg.selectAll("path.land")
.data(countries)
.enter().append("path")
.attr("class", "land")
.attr("d", path)
//Drag event
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: r[0] / sens, y: -r[1] / sens}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * sens, -d3.event.y * sens, rotate[2]]);
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}))
//Mouse events
.on("mouseover", function(d) {
countryTooltip.text(countryById[d.id])
.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px")
.style("display", "block")
.style("opacity", 1);
})
.on("mouseout", function(d) {
countryTooltip.style("opacity", 0)
.style("display", "none");
})
.on("mousemove", function(d) {
countryTooltip.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px");
});
//Country focus on option select
d3.select("select").on("change", function() {
var rotate = projection.rotate(),
focusedCountry = country(countries, this),
p = d3.geo.centroid(focusedCountry);
svg.selectAll(".focused").classed("focused", focused = false);
//Globe rotating
(function transition() {
d3.transition()
.duration(2500)
.tween("rotate", function() {
var r = d3.interpolate(projection.rotate(), [-p[0], -p[1]]);
return function(t) {
projection.rotate(r(t));
svg.selectAll("path").attr("d", path)
.classed("focused", function(d, i) { return d.id == focusedCountry.id ? focused = d : false; });
};
})
})();
});
function country(cnt, sel) {
for(var i = 0, l = cnt.length; i < l; i++) {
if(cnt[i].id == sel.value) {return cnt[i];}
}
};
};
</script>
</body>
</html>